`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?
.
Rspec - Argument error after overwriting method_missing and respond_to_missing
Ruby Delegator#respond_to_missing?
is method take responsible for returning whether a missing method be able to handled by the object or not, it takes 2 parameters: the missing method name
and the option include_private
.
The best practice is: always define respond_to_missing?
when overriding method_missing
.
However i do not prefer the way you applied, the reason behind that is The Rule of Least Surprise, take a look:
class DataHelper
def method_missing(method_name, *args, &block)
if method_name.to_s.start_with?('delegate')
puts "a delegate method"
else
super
end
end
def respond_to_missing?(method_name, include_private = false)
true
end
end
d = DataHelper.new
d.respond_to?(:answer) # true
d.answer # `method_missing': undefined method `answer' ... SURPRISE
as you can see, d
response that he can responsible for the answer
method but when call that method, a method_missing
error be raised.
So, you need to make both method_missing
and respond_to_missing?
match together:
class DataHelper
def method_missing(method_name, *args, &block)
if can_handle?(method_name)
puts "a delegate method"
else
super
end
end
def respond_to_missing?(method_name, include_private = false)
return true if can_handle?(method_name)
super
end
private
def can_handle?(method_name)
method_name.to_s.start_with?('delegate')
end
end
d = D.new
d.respond_to?(:delegate_answer) # true
d.delegate_answer # delegate method
d.respond_to?(:answer) # false
d.answer # error
Ruby respond_to_missing? Call super or not?
It depends on the implementation of the class and the behavior you want out of #respond_to_missing?
. Looking at ActiveSupport::TimeWithZone
, it is a proxy wrapper for Time
. It tries to mimic it, fooling you into thinking it is an instance of Time
. TimeWithZone#is_a?
would respond true
when passed Time
, for example.
# Say we're a Time to thwart type checking.
def is_a?(klass)
klass == ::Time || super
end
alias_method :kind_of?, :is_a?
respond_to_missing?
should catch cases that would be caught by method_missing
, so you have to look at both methods. TimeWithZone#method_missing
delegates missing methods to Time
instead of super
.
def method_missing(sym, *args, &block)
wrap_with_time_zone time.__send__(sym, *args, &block)
rescue NoMethodError => e
raise e, e.message.sub(time.inspect, inspect), e.backtrace
end
So it makes sense that it would delegate respond_to_missing?
to Time
as well.
# Ensure proxy class responds to all methods that underlying time instance
# responds to.
def respond_to_missing?(sym, include_priv)
return false if sym.to_sym == :acts_like_date?
time.respond_to?(sym, include_priv)
end
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.
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 overridingmethod_missing
.Generate your access methods when setting
request_params
(meta-programming your accessors).Use OpenStruct since these can be initialized with a
Hash
such as yourrequest_params
. If you add delegation on top, you should be cool.
Hope this helps.
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?
.
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
Rare error about missing view welcome/index (:formats=[image/*])
You can add respond_with
in your action to avoid this error.
For example, in users_contoller:
respond_to :html
def index
@users = User.all
respond_with(@users)
end
If there is a request of /users.jpg, it will return 406 Not Acceptable.
Related Topics
Suitability of Rails, Padrino, and Sinatra for Building a Prepaid Mobile Service
Using Ruby's Optionparser to Parse Sub-Commands
How to Structure a Layout Template in Haml
Using Typeahead from Twitter Bootstrap in a Form (Formtastic)
Finding All the Users That Have Duplicate Names
What Is the Preferred Way (Better Style) to Name a Namespace in Ruby? Singular or Plural
How to Get All Field Names of the Mongoid Document
Context Aware Authorization Using Cancan
Rails 3: Generate Unique Codes (Coupons)
What's the Best Way to Parse a Tab-Delimited File in Ruby
How to Read the Body Text of an Email Using Ruby's Net/Imap Library
How to Use One Line Regular Expression to Get Matched Content