Is 'Respond_To_Missing''s Second Argument Useful for Anything

Is `respond_to_missing?`'s second argument useful for anything?

I think that respond_to_missing? has a second argument for the same reason that respond_to? does. In both cases, it allows code to ask an object what methods it responds to in a way that respects method privacy. If used properly, it can help you encapsulate your objects better.

You have pointed out a missing feature in method_missing, namely that it should have an argument that says whether the method was called in a public or private context. Maybe method_missing will have that feature some day. Until then, all functionality of the object that is implemented through method_missing will effectively be public, but you can still discourage people from using it in your documentation and via respond_to_missing?.

respond_to? and protected methods

It is under debate if respond_to? should look for protected methods or not (check this issue)

Matz has stated that it will probably change in Ruby 2.0.

Note some classes might use #method_missing and specialize #respond_to? (or better by specify a #respond_to_missing? in Ruby 1.9.2+), in which case your obj.methods.include? will not be reliable.

`respond_to?` vs. `respond_to_missing?`

Without respond_to_missing? defined, trying to get the method via method will fail:

class Foo
def method_missing name, *args
p args
end

def respond_to? name, include_private = false
true
end
end

f = Foo.new
f.bar #=> []
f.respond_to? :bar #=> true
f.method :bar # NameError: undefined method `bar' for class `Foo'

class Foo
def respond_to? *args; super; end # “Reverting” previous redefinition

def respond_to_missing? *args
true
end
end

f.method :bar #=> #<Method: Foo#bar>

Marc-André (a Ruby core committer) has a good blog post on respond_to_missing?.

method_missing gotchas in Ruby

A somewhat obvious one: always redefine respond_to? if you redefine method_missing. If method_missing(:sym) works, respond_to?(:sym) should always return true. There are many libraries that rely on this.

Later:

An example:

# Wrap a Foo; don't expose the internal guts.
# Pass any method that starts with 'a' on to the
# Foo.
class FooWrapper
def initialize(foo)
@foo = foo
end
def some_method_that_doesnt_start_with_a
'bar'
end
def a_method_that_does_start_with_a
'baz'
end
def respond_to?(sym, include_private = false)
pass_sym_to_foo?(sym) || super(sym, include_private)
end
def method_missing(sym, *args, &block)
return foo.call(sym, *args, &block) if pass_sym_to_foo?(sym)
super(sym, *args, &block)
end
private
def pass_sym_to_foo?(sym)
sym.to_s =~ /^a/ && @foo.respond_to?(sym)
end
end

class Foo
def argh
'argh'
end
def blech
'blech'
end
end

w = FooWrapper.new(Foo.new)

w.respond_to?(:some_method_that_doesnt_start_with_a)
# => true
w.some_method_that_doesnt_start_with_a
# => 'bar'

w.respond_to?(:a_method_that_does_start_with_a)
# => true
w.a_method_that_does_start_with_a
# => 'baz'

w.respond_to?(:argh)
# => true
w.argh
# => 'argh'

w.respond_to?(:blech)
# => false
w.blech
# NoMethodError

w.respond_to?(:glem!)
# => false
w.glem!
# NoMethodError

w.respond_to?(:apples?)
w.apples?
# NoMethodError

How to solve Rubocop respond_to_missing? offence

Rubocop expects super to be called without arguments. As the arguments you are passing to super are the same as those you received, you can simply remove the arguments:

def method_missing(name, *args, &block)
if name =~ /(.+)\=/
self[$1.to_sym] = args[0]
elsif has_index?(name)
self[name]
else
super
end
end

Ruby - actually get ALL methods for an instance

Maybe you missed the last paragraph in the accepted answer of linked post How to list all methods for an object in Ruby?:

Added Note that :has_many does not add methods directly. Instead, the ActiveRecord machinery uses the Ruby method_missing and responds_to techniques to handle method calls on the fly. As a result, the methods are not listed in the methods method result.

To make it clearer what happens a code example:

class Foo
def hello
puts 'Hello'
end

def method_missing(name, *args, &block)
case name
when :unknown_method
puts "handle unknown method %s" % name # name is a symbol
else
super #raises NoMethodError unless there is something else defined
end
end
end

foo = Foo.new
p foo.respond_to?(:hello) #-> true
p foo.respond_to?(:unknown_method) #-> false
foo.unknown_method #-> 'handle unknown method unknown_method'
foo.another_unknown_method #-> Exception

The method unknown_method is never defined, but there is a method to handle unknown methods. So the class makes the impression of an existing method, but there is none.

Maybe How can I get source code of a method dynamically and also which file is this method locate in helps the get information about the content:

Foo.instance_method(:method_missing).source_location

Addition

When you define your own method_missing, then you should also change the behaviour of respond_to? with respond_to_missing?

  def respond_to_missing?(method, *)
return method == :unknown_method || super
#or if you have a list of methods:
#~ return %i{unknown_method}.include?(method) || super
#or with a regex for
#~ method =~ /another_(\w+)/ || super
end
end

See also `respond_to?` vs. `respond_to_missing?` for details.

argument in system call

It counts as two. Since there's a space in between the two, the shell sees it and splits it as -P and /public/google (if you've worked with arguments, even in ruby, the shell would pass those in as separate arguments to the script).

Rails/Rspec respond_to a method_missing lookup

Use respond_to_missing. More infos here.

Now, with all this being said. Your pattern will still look hackish if you ask me.

Refactors

Ruby has tons of way to clean this.

  1. Use a delegation pattern

    delegate :method_name, :to => :request_params

    (check other options in doc). This should solve your problems by having a method in your object so respond_to? will work and you will avoid overriding method_missing.

  2. Generate your access methods when setting request_params (meta-programming your accessors).

  3. Use OpenStruct since these can be initialized with a Hash such as your request_params. If you add delegation on top, you should be cool.

Hope this helps.



Related Topics



Leave a reply



Submit