为什么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代替。 它会正确读取输入。

https://github.com/pry/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 

同样,这是非常简化的,只是在实际代码的精神,但这基本上是它的工作原理。