ActiveRecord의 attributes API를 사용하여 새로운 유형을 만들고 싶습니다.
14817 단어 루비RailsActiveRecord
하고 싶은 일
배경
# users テーブルには birthday という date 型の属性を持っていることを前提としている.
#
class User < ApplicationRecord
# ユーザは誕生日の属性を持っている。そして、誕生日から導出可能な年齢を知っている。
def age(today: Date.current)
return nil unless birthday
result = today.year - birthday.year
result -= 1 if today < birthday + result.years
result
end
end
이 코드의 문제점으로는, 예를 들면 User 외에 Administrator 라고 하는 비슷한 모델이 있어, 그 쪽에도 생일이 있었다고 했을 때에 비슷한 (그대로) 코드가 복수 개소에 존재하게 되기 어려워 라는 것.
# users と同様に、 administrators テーブルには birthday という date 型の属性を持っていることを前提としています.
#
class Administrator < ApplicationRecord
# 同じことを書いてる。。。。。。。。。
def age(today: Date.current)
return nil unless birthday
result = today.year - birthday.year
result -= 1 if today < birthday + result.years
result
end
end
or 모듈로 작성?
app/models/concerns/age_calculable.rbmodule AgeCalculable
def age(today: Date.current)
return nil unless birthday
result = today.year - birthday.year
result -= 1 if today < birthday + result.years
result
end
end
User.include AgeCalculable
Administrator.include AgeCalculable
or 생일부터 연령을 도출하는 클래스 정의를 만들어 거기에 위양하는 손은 있다.
class AgeCalculator
def initialize(birthday)
@birthday = birthday
end
def age(today: Date.current)
return nil unless birthday
result = today.year - birthday.year
result -= 1 if today < birthday + result.years
result
end
private
attr_reader :birthday
end
class User < ApplicationRecord
delegate :age, to: :age_calculator
private
def age_calculator
AgeCalculator.new(birthday)
end
end
가능하면 이 케이스의 이상으로서는 생일 클래스는 연령을 알고 있는 것이 좋다(※)라고 생각된다.
이런 코드로 쓰고 싶다
User
모델의 birthday
속성은 Birthday 클래스의 인스턴스입니다.
Birthday 클래스는, 메소드 age
를 말할 수가 있다.
user = User.new(birthday: Date.new(2000, 1, 1))
# birthday が age を知っている
user.birthday.age #=> 19
# 上記前提で age メッセージの応答を birthday に委譲するパターン
class User
delegate :age, to: :birthday, allow_nil: true
end
user = User.new(birthday: Date.new(2000, 1, 1))
user.age #=> 19
구현
Birthday 및 Birthday::Type
우선은 Birthday 클래스를 정의해, ActiveRecord 레이어에서의 시리얼라이즈 및 디시리얼라이즈 방법을 가지는 클래스도 정의한다.
Date 나 Integer 등, ActiveRecord 의 속성에 매핑 된 클래스에 위양하는 클래스를 만들면 비교적 간단. 그렇지 않은 경우는 ActiveModel::Type::Value 클래스 구현 (을)를 씹어, 이것을 상속해 덧쓰기해야 할 메소드 정의를 기술하게 된다.
app/values/birthday.rbrequire 'delegate'
class Birthday < DelegateClass(Date)
# Birthday クラス独自のメソッド記述
def age(today: Date.current)
result = today.year - year
result -= 1 if today < self + result.years
result
end
class Type < ActiveRecord::Type::Date
def type
:birthday
end
private
def cast_value(value)
# 自身の型であるなら dup を返す. そうでない場合は基底クラスに任せたあとその初期化結果をもとに自身のインスタンスを作る.
case value
when Birthday then value.dup
else super.then { |ret| Birthday.new(ret) if ret }
end
end
end
end
ActiveRecord::Type.register로 형식 등록
config/initializers/active_record.rbrequire 'active_record/type'
Rails.application.config.to_prepare do
ActiveModel::Type.register(:birthday, Birthday::Type)
ActiveRecord::Type.register(:birthday, Birthday::Type)
end
사용
# 今回の場合、実際のデータベースの型は基底の date 型で
bundle exec rails g model User birthday:date
app/models/user.rbclass User < ApplicationRecord
# attribute :birthday, :date
attribute :birthday, :birthday # date 型改め birthday 型
delegate :age, to: :birthday, allow_nil: true
end
할 수 있었다
user = User.new(birthday: Date.new(2000, 1, 1))
user.age #=> 19
user = User.new(birthday: '2010-12-31')
user.age #=> 8
요약
# users テーブルには birthday という date 型の属性を持っていることを前提としている.
#
class User < ApplicationRecord
# ユーザは誕生日の属性を持っている。そして、誕生日から導出可能な年齢を知っている。
def age(today: Date.current)
return nil unless birthday
result = today.year - birthday.year
result -= 1 if today < birthday + result.years
result
end
end
# users と同様に、 administrators テーブルには birthday という date 型の属性を持っていることを前提としています.
#
class Administrator < ApplicationRecord
# 同じことを書いてる。。。。。。。。。
def age(today: Date.current)
return nil unless birthday
result = today.year - birthday.year
result -= 1 if today < birthday + result.years
result
end
end
module AgeCalculable
def age(today: Date.current)
return nil unless birthday
result = today.year - birthday.year
result -= 1 if today < birthday + result.years
result
end
end
User.include AgeCalculable
Administrator.include AgeCalculable
class AgeCalculator
def initialize(birthday)
@birthday = birthday
end
def age(today: Date.current)
return nil unless birthday
result = today.year - birthday.year
result -= 1 if today < birthday + result.years
result
end
private
attr_reader :birthday
end
class User < ApplicationRecord
delegate :age, to: :age_calculator
private
def age_calculator
AgeCalculator.new(birthday)
end
end
User
모델의 birthday
속성은 Birthday 클래스의 인스턴스입니다.Birthday 클래스는, 메소드
age
를 말할 수가 있다.user = User.new(birthday: Date.new(2000, 1, 1))
# birthday が age を知っている
user.birthday.age #=> 19
# 上記前提で age メッセージの応答を birthday に委譲するパターン
class User
delegate :age, to: :birthday, allow_nil: true
end
user = User.new(birthday: Date.new(2000, 1, 1))
user.age #=> 19
구현
Birthday 및 Birthday::Type
우선은 Birthday 클래스를 정의해, ActiveRecord 레이어에서의 시리얼라이즈 및 디시리얼라이즈 방법을 가지는 클래스도 정의한다.
Date 나 Integer 등, ActiveRecord 의 속성에 매핑 된 클래스에 위양하는 클래스를 만들면 비교적 간단. 그렇지 않은 경우는 ActiveModel::Type::Value 클래스 구현 (을)를 씹어, 이것을 상속해 덧쓰기해야 할 메소드 정의를 기술하게 된다.
app/values/birthday.rbrequire 'delegate'
class Birthday < DelegateClass(Date)
# Birthday クラス独自のメソッド記述
def age(today: Date.current)
result = today.year - year
result -= 1 if today < self + result.years
result
end
class Type < ActiveRecord::Type::Date
def type
:birthday
end
private
def cast_value(value)
# 自身の型であるなら dup を返す. そうでない場合は基底クラスに任せたあとその初期化結果をもとに自身のインスタンスを作る.
case value
when Birthday then value.dup
else super.then { |ret| Birthday.new(ret) if ret }
end
end
end
end
ActiveRecord::Type.register로 형식 등록
config/initializers/active_record.rbrequire 'active_record/type'
Rails.application.config.to_prepare do
ActiveModel::Type.register(:birthday, Birthday::Type)
ActiveRecord::Type.register(:birthday, Birthday::Type)
end
사용
# 今回の場合、実際のデータベースの型は基底の date 型で
bundle exec rails g model User birthday:date
app/models/user.rbclass User < ApplicationRecord
# attribute :birthday, :date
attribute :birthday, :birthday # date 型改め birthday 型
delegate :age, to: :birthday, allow_nil: true
end
할 수 있었다
user = User.new(birthday: Date.new(2000, 1, 1))
user.age #=> 19
user = User.new(birthday: '2010-12-31')
user.age #=> 8
요약
require 'delegate'
class Birthday < DelegateClass(Date)
# Birthday クラス独自のメソッド記述
def age(today: Date.current)
result = today.year - year
result -= 1 if today < self + result.years
result
end
class Type < ActiveRecord::Type::Date
def type
:birthday
end
private
def cast_value(value)
# 自身の型であるなら dup を返す. そうでない場合は基底クラスに任せたあとその初期化結果をもとに自身のインスタンスを作る.
case value
when Birthday then value.dup
else super.then { |ret| Birthday.new(ret) if ret }
end
end
end
end
require 'active_record/type'
Rails.application.config.to_prepare do
ActiveModel::Type.register(:birthday, Birthday::Type)
ActiveRecord::Type.register(:birthday, Birthday::Type)
end
# 今回の場合、実際のデータベースの型は基底の date 型で
bundle exec rails g model User birthday:date
class User < ApplicationRecord
# attribute :birthday, :date
attribute :birthday, :birthday # date 型改め birthday 型
delegate :age, to: :birthday, allow_nil: true
end
user = User.new(birthday: Date.new(2000, 1, 1))
user.age #=> 19
user = User.new(birthday: '2010-12-31')
user.age #=> 8
※ 프레임워크에 밀접하게 의존하지 않는 편이 좋다는 관점이 있습니다. 하지만 여기서는 일단 그렇게하기로 결정하십시오
Reference
이 문제에 관하여(ActiveRecord의 attributes API를 사용하여 새로운 유형을 만들고 싶습니다.), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://qiita.com/hamajyotan/items/0c1281d0156f89dcbe98텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)