电子邮件有多个变种(用于ab测试目的)并且始终有一个主
我想确保电子邮件始终具有基于初始化的主变体。
我还想委托attr访问者' subject'和'身体'到主变体。
我最初尝试使用
delegate :subject, :body, to: :master
但是rails抱怨主人没事。
所以我尝试手动滚动我自己的subject = setter方法并通过pry我发现虽然我的主人被设置在after_initialize回调中,但后来对subject =抱怨主人的调用是零。我不明白为什么。
class Email < ActiveRecord::Base
has_one :master,
-> { where is_master: true },
class_name: 'Tinycourse::Variant',
dependent: :destroy,
inverse_of: :email
def subject=(str)
master.subject = str # Rails says master is nil here
end
#
# Callbacks
#-----------------------------------------
after_initialize :ensure_master
def ensure_master
return unless new_record?
self.master ||= build_master
end
end
Email.new(:subject => 'yah') # undefined method `subject=' for nil:NilClass
答案 0 :(得分:1)
当您的电子邮件实例初始化时,master
为零,您需要在设置任何内容之前触发build_master
怎么样:
# app/models/email.rb
class Email < ActiveRecord::Base
def subject=(str)
master.subject = str # Rails says master is nil here
end
def master
super || build_master
end
end
不知道您的项目需要after_initialize
回调的时间和原因,但如果您确定需要此功能,我会考虑使用自定义服务类来实现此目的
# app/models/email.rb
class Email < ActiveRecord::Base
has_one :master,
-> { where is_master: true },
class_name: 'Tinycourse::Variant',
dependent: :destroy,
inverse_of: :email
def subject=(str)
master.subject = str # this way Rails won't says master is nil here
end
end
# app/lib/email_builder.rb
class EmailBuilder
attr_reader :args
def self.build(args={})
new(args).build
end
def initialize(args)
@args = args
end
def build
email = Email.new
email.build_master
email.attributes = args
email
end
end
email = EmailBuilder.build subject: 'yah'
email.class # => Email
...以及持久记录的另一种变体
更新
甚至更好
# app/models/email.rb
class Email < ActiveRecord::Base
has_one :master,
-> { where is_master: true },
class_name: 'Tinycourse::Variant',
dependent: :destroy,
inverse_of: :email
end
# app/lib/email_builder.rb
class EmailBuilder
attr_reader :args, :subject
def self.build(args={})
new(args).build
end
def initialize(args)
@subject = args.fetch(:subject)
@args = args
end
def build
email = Email.new args
email.build_master
email.master.subject = subject
email
end
end
email = EmailBuilder.build subject: 'yah'
email.class # => Email