为什么Rails可以使用`if`作为哈希键而不是Ruby
在纯Ruby irb中,不能键入{if: 1}
。 该声明不会终止,因为irb认为if
不是符号,而是if语句的开头。
那么为什么Rails有before_filter
接受if作为参数? 该指南的代码如下:
class Order < ApplicationRecord before_save :normalize_card_number, if: :paid_with_card? end
unless
同样如此。
众所周知,IRb的解析器已被破坏。 (事实上,你遇到的错误已在几个月前报告过: 错误#12177: 使用if:
作为irb控制台中新哈希语法的哈希符号不起作用 。)只需忽略它。 IRb和Ruby之间的行为还存在其他差异,语义不同,而不仅仅是语法。 例如,在顶级定义的方法是隐式public
而不是隐式private
因为它们应该是。
IRb尝试使用自己的解析器解析代码以确定,例如,当您按Enter时是否将其提交到引擎或在下一行等待您继续代码。 但是,因为Ruby的语法非常复杂,所以很难正确解析它,并且已知IRb的解析器偏离了Ruby。
其他REPL采用不同的方法,例如Pry实际上使用Ruby的解析器而不是它自己的解析器。
这是一个问题,而不是Ruby。
bash=> ruby -e "puts({if: 1})" bash=# {:if=>1}
你可以使用pry
代替。 它会正确读取输入。
您的示例中的代码是Rails DSL的一部分。 您实际设置的是一个哈希,恰好看起来有点像代码。
在内部,Rails将评估此哈希值,指定before_save
调用的条件。
在一个非常简化的版本中,Rails基本上在保存时执行此操作:
class ActiveRecord::Base @before_save_rules = [] def self.before_save(method, options={}) @before_save_rules << [method, options] end def self.before_save_rules @before_save_rules end def save # Evaluate the defined rules and decide if we should perform the # before_save action or not self.class.before_safe_rules.each do |method, options| do_perform = true if options.key?(:if) do_perform = false unless send(options[:if]) end if options.key?(:unless) do_perform = false if send(options[:unless]) end send(method) if do_perform end # now perform the actual save to the database # ... end end
同样,这是非常简化的,只是在实际代码的精神,但这基本上是它的工作原理。