导轨< 4.0“尝试”方法抛出NoMethodError?

发布于 2024-12-04 10:39:14 字数 1056 浏览 4 评论 0 原文

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

ruby-1.9.2-p180 :101 > User.first.try(:something)
NoMethodError: undefined method `something' for #<User:0x000001046ad128>
    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 `<top (required)>'
    from script/rails:6:in `require'
    from script/rails:6:in `<main>'

编辑:

谢谢大家,现在我明白了。

有没有一种方法可以在不使用 respond_to? 的情况下完成我想要的操作,这样 User.try(:something) 返回 nil 而不是抛出错误?

Why is try throwing an error? Doesnt that defeat the whole purpose? Maybe its just in the console?

ruby-1.9.2-p180 :101 > User.first.try(:something)
NoMethodError: undefined method `something' for #<User:0x000001046ad128>
    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 `<top (required)>'
    from script/rails:6:in `require'
    from script/rails:6:in `<main>'

EDIT:

Thanks guys, now I get it.

Is there a way to do what I wanted without doing using respond_to?, such that User.try(:something) returns nil instead of throwing the error?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

合久必婚 2024-12-11 10:39:14

Rails 3

您误解了 try 的工作原理,来自 精美手册

尝试(*a, &b)
调用由符号方法标识的方法,向其传递任何参数和/或指定的块,就像常规 Ruby Object#send 所做的那样。

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

以及 try 的版本被修补到 <代码>NilClass

尝试(*args)
nil 调用 try 始终返回 nil

因此 try 不会忽略您对对象调用不存在的方法的尝试,它会忽略您对 nil 调用方法的尝试并返回 nil< /code> 而不是引发异常。 try 方法只是一种避免在方法调用链中的每一步都检查 nil 的简单方法。


Rails 4

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

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

所以现在 try 可以同时处理这两项检查。如果您想要 Rails 3 的行为,可以尝试!

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

Rails 3

You misunderstand how try works, from the fine manual:

try(*a, &b)
Invokes the method identified by the symbol method, passing it any arguments and/or the block specified, just like the regular Ruby Object#send does.

Unlike that method however, a NoMethodError exception will not be raised and nil will be returned instead, if the receiving object is a nil object or NilClass.

And the version of try that is patched into NilClass:

try(*args)
Calling try on nil always returns nil.

So try doesn't ignore your attempt to call a non-existent method on an object, it ignores your attempt to call a method on nil and returns nil instead of raising an exception. The try method is just an easy way to avoid having to check for nil at every step in a chain of method calls.


Rails 4

The behavior of try has changed in Rails 4 so now it:

Invokes the public method whose name goes as first argument just like public_send does, except that if the receiver does not respond to it the call returns nil rather than raising an exception.

So now try takes care of both checks at once. If you want the Rails 3 behavior, there is try!:

Same as try, but will raise a NoMethodError exception if the receiving [sic] is not nil and does not implemented [sic] the tried method.

弄潮 2024-12-11 10:39:14

这就是尝试所做的

调用符号方法标识的方法,向其传递任何
参数和/或指定的块,就像常规 Ruby 一样
Object#send 确实如此。然而,与该方法不同的是,NoMethodError
如果以下情况,则不会引发异常,而是返回 nil
接收对象是一个 nil 对象或 NilClass。

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

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

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

This is what try does

Invokes the method identified by the symbol method, passing it any
arguments and/or the block specified, just like the regular Ruby
Object#send does. Unlike that method however, a NoMethodError
exception will not be raised and nil will be returned instead, if the
receiving object is a nil object or NilClass.

So, let's say you setup @user in your controller but you didn't instantiate it then @user.try(:foo)
=> nil
instead of

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

The important point here is that try is an instance method. It also doesn't return nil if the object you try on isn't nil.

柒夜笙歌凉 2024-12-11 10:39:14

我知道这已经过时了,但它可能对其他人有帮助,因为这是我用谷歌搜索这个问题时出现的第一件事。我“借用”了 try 的代码,并实现了我自己的 try_method 方法,其行为就像 try 一样,只不过它首先检查是否方法在调用send之前就存在。我在 Object 中实现了这个并将其放入初始值设定项中,现在我可以在任何对象上调用它。

class Object
  # Invokes the method identified by _method_, passing it any
  # arguments specified, just like the regular Ruby <tt>Object#send</tt> does.
  #
  # *Unlike* that method however, a +NoMethodError+ exception will *not* be raised
  # if the method does not exist.
  #
  # This differs from the regular Ruby <tt>Object#try</tt> 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) && [email protected]? && @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

I know this is old, but it may help somebody else, because this is the first thing that popped up when I Googled this issue. I "borrowed" the code for try and implemented my own try_method method which acts just like try, except that it first checks to see if the method exists before calling send. I implemented this in Object and put it in an initializer, and I can now call it on any object.

class Object
  # Invokes the method identified by _method_, passing it any
  # arguments specified, just like the regular Ruby <tt>Object#send</tt> does.
  #
  # *Unlike* that method however, a +NoMethodError+ exception will *not* be raised
  # if the method does not exist.
  #
  # This differs from the regular Ruby <tt>Object#try</tt> 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) && [email protected]? && @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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文