rails try方法抛出NoMethodError?

为什么尝试抛出错误? 这不是打败了整个目的吗? 也许只是在控制台?

ruby-1.9.2-p180 :101 > User.first.try(:something) NoMethodError: undefined method `something' for # from /Users/me/.rvm/gems/ruby-1.9.2-p180/gems/activemodel-3.0.10/lib/active_model/attribute_methods.rb:392:in `method_missing' from /Users/me/.rvm/gems/ruby-1.9.2-p180/gems/activerecord-3.0.10/lib/active_record/attribute_methods.rb:46:in `method_missing' from (irb):101 from /Users/me/.rvm/gems/ruby-1.9.2-p180/gems/railties-3.0.10/lib/rails/commands/console.rb:44:in `start' from /Users/me/.rvm/gems/ruby-1.9.2-p180/gems/railties-3.0.10/lib/rails/commands/console.rb:8:in `start' from /Users/me/.rvm/gems/ruby-1.9.2-p180/gems/railties-3.0.10/lib/rails/commands.rb:23:in `' from script/rails:6:in `require' from script/rails:6:in `' 

编辑:

谢谢你们,现在我明白了。

有没有办法在不使用respond_to?情况下做我想做的事情respond_to? ,这样User.try(:something)返回nil而不是抛出错误?

Rails 3

从精细的手册中你误解了try是如何运作的:

试试(* a,&b)
调用symbol方法标识的方法,传递任何参数和/或指定的块,就像常规的Ruby Object#send一样。

但是,与该方法不同,如果接收对象是nil对象或NilClass ,则不会引发NoMethodErrorexception,而是返回nil

以及修补到NilClasstry版本:

尝试(*参数)
调用try on nil总是返回nil

因此, try不会忽略您在对象上调用不存在的方法的尝试,它会忽略您尝试在nil上调用方法并返回nil而不是引发exception。 try方法只是一种简单的方法,可以避免在方法调用链的每一步都检查nil


Rails 4

try的行为在Rails 4中发生了变化,所以现在它:

调用名称作为第一个参数的公共方法,就像public_send一样,除了如果接收者没有响应它,则调用返回nil而不是引发exception。

所以现在try处理两个检查。 如果你想要Rails 3的行为,那就try!

try相同,但如果接收[sic]不是nil且未实现[sic]尝试的方法,则会引发NoMethodErrorexception。

这就是尝试

调用symbol方法标识的方法,传递任何参数和/或指定的块,就像常规的Ruby Object #send一样。 但是,与该方法不同,如果接收对象是nil对象或NilClass,则不会引发NoMethodErrorexception,而是返回nil。

所以,假设您在控制器中设置了@user但是没有实例化它,那么@user.try(:foo) => nil而不是

 @user.foo NoMethodError: undefined method `foo' for nil:NilClass 

这里重点是try是一个实例方法。 如果您尝试的对象不是nil,它也不会返回nil 。

我知道这是旧的,但它可能会帮助其他人,因为这是我搜索这个问题时出现的第一件事。 我“借用”了try的代码并实现了我自己的try_method方法,该方法就像try一样,除了它在调用send之前首先检查方法是否存在。 我在Object中实现了它并将它放在初始化器中,现在我可以在任何对象上调用它。

 class Object # Invokes the method identified by _method_, passing it any # arguments specified, just like the regular Ruby Object#send does. # # *Unlike* that method however, a +NoMethodError+ exception will *not* be raised # if the method does not exist. # # This differs from the regular Ruby Object#try method which only # suppresses the +NoMethodError+ exception if the object is Nil # # If try_method is called without a method to call, it will yield any given block with the object. # # Please also note that +try_method+ is defined on +Object+, therefore it won't work with # subclasses of +BasicObject+. For example, using try_method with +SimpleDelegator+ will # delegate +try_method+ to target instead of calling it on delegator itself. # # ==== Examples # # Without +try_method+ # @person && @person.respond_to?(:name) && @person.name # or # (@person && @person.respond_to?(:name)) ? @person.name : nil # # With +try_method+ # @person.try_method(:name) # # +try_method+ also accepts arguments and/or a block, for the method it is trying # Person.try_method(:find, 1) # @people.try_method(:collect) {|p| p.name} # # Without a method argument try_method will yield to the block unless the receiver is nil. # @person.try_method { |p| "#{p.first_name} #{p.last_name}" } #-- # +try_method+ behaves like +Object#send+, unless called on +NilClass+ or a class that does not implement _method_. def try_method(method=nil, *args, &block) if method == nil && block_given? yield self elsif respond_to?(method) __send__(method, *args, &block) else nil end end end class NilClass # Calling +try_method+ on +nil+ always returns +nil+. # It becomes specially helpful when navigating through associations that may return +nil+. # # === Examples # # nil.try_method(:name) # => nil # # Without +try_method+ # @person && @person.respond_to(:children) && !@person.children.blank? && @person.children.respond_to(:first) && @person.children.first.respond_to(:name) && @person.children.first.name # # With +try_method+ # @person.try_method(:children).try_method(:first).try_method(:name) def try_method(*args) nil end end