web-dev-qa-db-ja.com

railsメソッドがNoMethodErrorをスローしてみますか?

なぜエラーをスローしようとするのですか?それは目的全体を打ち負かしませんか?たぶんそれはコンソールだけですか?

_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を返すように、やりたいことを実行する方法はありますか?

23
hb922

Rails 3

細かいマニュアル から、tryがどのように機能するかを誤解しています:

try(* a、&b)
通常のRuby Object#sendと同じように、symbolメソッドで識別されるメソッドを呼び出し、引数や指定されたブロックを渡します。

ただし、そのメソッドとは異なり、受信オブジェクトがNoMethodErrorオブジェクトまたはnilの場合、nil例外は発生せず、代わりにNilClassが返されます。

そして、tryにパッチが適用されている NilClassのバージョン

try(* args)
trynilを呼び出すと、常にnilが返されます。

したがって、tryは、オブジェクトで存在しないメソッドを呼び出そうとする試みを無視せず、nilでメソッドを呼び出そうとする試みを無視し、発生させる代わりにnilを返します。例外。 tryメソッドは、一連のメソッド呼び出しのすべてのステップでnilをチェックする必要をなくす簡単な方法です。


Rails 4

tryの動作はRails 4で変更されたため、次のようになります。

public_sendと同じように、名前が最初の引数となるpublicメソッドを呼び出します。ただし、受信者がそれに応答しない場合、呼び出しは例外を発生させるのではなくnilを返します。

したがって、tryは両方のチェックを同時に処理します。 Rails 3の動作が必要な場合は、 try!

tryと同じですが、受信側の[sic]がNoMethodErrorでなく、試行されたメソッドを実装していない場合、nil例外が発生します。

55
mu is too short

これはtryが行うことです

通常のRuby Object#sendと同じように、symbolメソッドで識別されるメソッドを呼び出し、引数や指定されたブロックを渡します。ただし、そのメソッドとは異なり、NoMethodError例外は発生しません。受信オブジェクトがnilオブジェクトまたはNilClassの場合、raisedとnilが代わりに返されます。

したがって、コントローラーで_@user_をセットアップしたが、インスタンス化せず、代わりに@user.try(:foo) => nilをインスタンス化したとします。

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

ここで重要な点は、tryはインスタンスメソッドであるということです。また、 試行したオブジェクトがnilでない場合、nilを返しません

8

私はこれが古いことを知っていますが、これは私がこの問題をグーグルで検索したときに最初に現れたものなので、他の誰かを助けるかもしれません。 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
5
Scott Nelson