在用Rails编码了几年之后,我仍然不了解Rails的更高级概念。Rails的方式对于约定而不是配置是如此具体,但是当您进入企业编码时,所有规则都会消失,每个人都会用非标准的方式编写博客。下面是另一种情况:上下文验证,其中的上下文更加复杂(字段相互依赖)。基本上,在送货应用程序中,我需要用请求参数和一些计算值初始化AR对象。计算值依赖于请求参数,我不确定如何初始化和验证我的成员变量。
table mailpieces
mail_class
weight
sort_code
t_indicator_id
end
class Mailpiece
validates_presence_of: :mail_class
validates_presence_of: :weight
validates_presence_of: :sort_code
validates_presence_of: :t_indicator_id
def some_kind_of_initializer
if mail_class == 'Priority'
sort_code = '123'
elsif mail_class == 'Express'
if weight < 1
sort_code = '456'
else
sort_code = '789'
end
end
t_indicator = ndicator.find_by(name: 'blah')
if sort_code = '456'
t_indicator = Indicator.find_by(name: 'foobar')
end
end
end
mailpiece = Mailpiece.new(
mail_class: params[:mail_class],
weight: params[:weight])
#mailpiece.some_kind_of_initializer ?!
raise 'SomeError' if !mailpiece.valid?some_kind_of_initializer应该是什么?
无论哪种选择,问题在于sort_code和t_indicator的初始化取决于mail_class和权重是否有效。考虑到在输入mail_class之前,some_kind_of_initializer和write不应该为null,那么我应该如何编写验证呢?
选项3代码重写
def some_kind_of_initializer
if mail_class && weight
if (mail_class == 'Priority')
sort_code = '123'
elsif (mail_class == 'Express')
if weight < 1
sort_code = '456'
else
sort_code = '789'
end
end
end
if sort_code
t_indicator = Indicator.find_by(name: 'blah')
if sort_code = '456'
t_indicator = Indicator.find_by(name: 'foobar')
end
end
end我很想听听你对此的看法。在我看来,这是一个非常流行的AR用例,我不知道该怎么做。同样,这只是一个简单的案例。我的邮件模型有许多其他引用,它们对邮件对象属性具有依赖关系,并且具有与上面相同的样式的相互依赖关系。
发布于 2018-08-23 23:54:09
我不确定这是否有帮助,或者我正确地理解了您的问题,但是也许使用"case“而不是if/elsif/else,三元运算符、一些懒惰的赋值和一些额外的验证可以给您的代码一种更好的"rails”感觉。
使用延迟赋值,我的意思是您可以等待初始化sort_code和t_indicator,直到您实际需要使用它或保存它。
def sort_code
return self[:sort_code] if self[:sort_code].present?
sort_code = case mail_class
when 'Priority' then '123'
when 'Express'
weight < 1 ? '456' : '789' if weight
end
self[:sort_code] = sort_code
end这样,sort_code在您需要第一次使用它之前就会被初始化,这样您就可以执行Mailpiece.new(mail_class:‘That’),而不用马上初始化sort_code。
当您需要保存对象并且从未调用mailpiece.sort_code时,问题就会出现。您可以有一个before_validation回调,它只是调用sort_code来初始化它,以防它还没有实现。
对于t_indicator也可以这样做。
我还会在您的验证中添加一些上下文。
validates_presence_of :weight, if: Proc.new{|record| 'Express' == record.mail_class} #you could add some helper method "is_express?" so you can just do "if: :is_express?"
validates_presence_of :sort_code, if: Proc.new{|record| 'Priority' == record.mail_class or 'Express' == record.mail_class && record.weight.present?}对不起,如果我没有回答你的问题,我甚至没有使用你的选择哈哈。
发布于 2018-08-24 04:18:34
不如:
class Mailpiece
validates_presence_of: :mail_class
validates_presence_of: :weight
validates_presence_of: :sort_code
validates_presence_of: :t_indicator_id
def default_mail_class
'Priority'
end
def default_sort_code
mail_class = mail_class || default_mail_class
if mail_class == 'Priority'
'123'
elsif mail_class == 'Express'
weight < 1 ? '456' : '789'
end
end
end然后,当您需要计算出t_indicator时,只需按需要执行:
def is_foobar_indicator?
sort_code || default_sort_code == '456'
end
def t_indicator
indicator_params = {name: is_foobar_indicator? ? 'foobar' : 'blah'}
Indicator.find_by(indicator_params)
end因此,您仍然可以在sort_code上进行验证(假设提供了用户),但在查看t_indicator时仍然使用默认值。我不知道模型的其余部分有多复杂,但我建议在需要它们之前不要查找值。此外,after_initialize风险很大,因为它在每次初始化之后都会运行您的代码,因此,如果您对N个项运行查询,但只在它们上查找,或者最终不使用您设置的默认值,那么after_initialize运行N次都是免费的。
https://stackoverflow.com/questions/51995015
复制相似问题