Ruby respond_to_missing? Call super or not? - ruby-on-rails

Looking through the Rails codebase sometimes respond_to_missing? invokes super and sometimes not. Are there cases where you should not invoke super from respond_to_missing?

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

respond_to_missing? appears in Ruby version 1.9.2 as a solution to the method method. Here's a blog post by a Ruby core committer about it: http://blog.marc-andre.ca/2010/11/15/methodmissing-politely/
The reason for calling super however, is so that in the event your logic returns false, the call will bubble up the class hierarchy to Object which returns false. If your class is a subclass to a class that implements respond_to_missing? then you would want to call super in the event your logic returns false. This is generally an issue for library code and not so much for application code.

Related

Ruby on Rails decorator for caching a result?

In Python, you can write a decorator for memoizing a function's response.
Is there something similar for Ruby on Rails? I have a model's method that makes a query, which I would like to cache.
I know I can do something inside the method, like:
def foo(param)
if self.cache[param].nil?
self.cache[param] = self.get_query_result(param)
else
self.cache[param]
end
end
However, given that I would do this often, I'd prefer a decorator syntax. It is clearer and better IMO.
Is there something like this for Ruby on Rails?
I usually do this using custom accessors, instance variables, and the ||= operator:
def foo
#foo ||= something_or_other
end
something_or_other could be a private method on the same class that returns the object that foo should be.
EDIT
Here's a slightly more complicated solution that lets you cache any method based on the arguments used to call them.
class MyClass
attr_reader :cache
def initialize
#cache = {}
end
class << self
def cacheable(symbol)
alias_method :"_#{symbol}_uncached", symbol
define_method(symbol) do |*args|
cache[[symbol, *args]] ||= (send :"_#{symbol}_uncached", *args)
end
end
end
end
How this works:
class MyClass
def foo(a, b)
a + b
end
cacheable :foo
end
First, the method is defined normally. Then the class method cacheable is called, which aliases the original method to a new name, then redefines it under the original name to be executed only if it's not already cached. It first checks the cache for anything using the same method and arguments, returns the value if present, and executes the original method if not.
http://martinfowler.com/bliki/TwoHardThings.html:
There are only two hard things in Computer Science: cache invalidation and naming things.
-- Phil Karlton
Rails has a lot of built in caching(including query caching). You might not need to do anything:
http://guides.rubyonrails.org/caching_with_rails.html
Here is a recent blog post about problems with roll your own caching:
http://cmme.org/tdumitrescu/blog/2014/01/careful-what-you-memoize/

Ruby, inheritance, self.inherited and instance_methods()

I'm working on rails app (3.2.11), and i'm implementing services as singleton, using the Ruby Library's one. I was trying to avoid calling :instance method every time I need it (SomethingService.instance.get(some_id)), solving my problem like this
class SomethingService
include Singleton
class << self
extend Forwardable
def_delegators :instance, *ShoppingListService.instance_methods(false)
end
end
So this solution was working perfectly fine but i've got a lot of services, and i don't want to add this code to all my classes! Instead i was trying to put in in a super class like this :
class ServiceBase
include Singleton
def self.inherited(subclass)
super(subclass) # needed because inherited is override by Singleton
class << subclass
extend Forwardable
def_delegators :instance, *self.instance_methods(false)
end
end
end
But this is giving me a stack level too deep error... Any ideas guys?
It's probably a better idea to use method_missing here. def_delegators is executed when the class is evaluated, and may happen before your methods are even defined, simply because you are inheriting from that base class.
You could try something like this instead, which forwards any undefined message onto the instance:
class ServiceBase
class << self
def method_missing(name, *args, &block)
instance.send(name, *args, &block)
end
end
end
It may look like a bit of a scattershot approach when compared to delegation. You could do a check to see if the instance method exists first, but I don't think that's necessary - you're simply transferring the undefined method handling to the instance rather than the class.
One final point/question - can you elaborate on what benefit you get from the Singleton class in the first place?

What does MyApp::Application.initialize! calling?

I am trying to figure out the initialization of rails 3. And i know that every application will calling the following function in environment.rb to initialize the whole app:
MyApp::Application.initialize!
From it we should expect that initialize! is a class method of Rails::Application. But after i red the source code, i found that instead initialize! is an instance method which is actually called:
def initialize!(group=:default) #:nodoc:
raise "Application has been already initialized." if #initialized
run_initializers(group, self)
#initialized = true
self
end
So why is the instance method called although we expect a class method? Is there some trick like method delegation or else?
Yes. there is a little trick delegation. if you look at file lib/rails/railtie/configurable.rb in railties gem you will see following code that delegates to instance.
def method_missing(*args, &block)
instance.send(*args, &block)
end
in case you are not aware of method_missing hook, you can read up more about it here: http://rubylearning.com/satishtalim/ruby_method_missing.html

custom method_missing ignored if instance is defined via an ActiveRecord association

I have submissions that might be in various states and wrote a method_missing override that allows me to check their state with calls like
submission.draft?
submission.published?
This works wonderfully.
I also, for various reasons that might not be so great, have a model called Packlet that belongs_to a meeting and belongs_to a submission. However, I was surprised to find that
packlet.submission.draft?
returns a NoMethodError. On the other hand, if I hard-code a #draft? method into Submission, the above method call works.
How do I get my method_missing methods to be recognized even when the instance is defined via an ActiveRecord association?
Have you added the draft? method to your respond_to? method for that object? My guess would be that the issue might arise there. What happens when you type:
submission.respond_to?(:draft?)
To fix this, actually write a respond_to? method like this:
def respond_to?(method, include_priv = false) #:nodoc:
case method.to_sym
when :draft?, :published?
true
else
super(method, include_priv)
end
end
My recommendation would be to implement this without using method_missing instead though, so by doing some meta-programming like this:
class Submission
[:draft, :published].each do |status|
define_method "#{status}?" do
status == "#{status}?"
end
end
end

Calling Super from an Aliased Method

I am trying to DRY up my code a bit so I am writing a method to defer or delegate certain methods to a different object, but only if it exists. Here is the basic idea: I have Shipment < AbstractShipment which could have a Reroute < AbstractShipment. Either a Shipment or it's Reroute can have a Delivery (or deliveries), but not both.
When I call shipment.deliveries, I want it to check to see if it has a reroute first. If not, then simply call AbstractShipment's deliveries method; if so, delegate the method to the reroute.
I tried this with the simple code below:
module Kernel
private
def this_method
caller[0] =~ /`([^']*)'/ and $1
end
end
class Shipment < AbstractShipment
...
def deferToReroute
if self.reroute.present?
self.reroute.send(this_method)
else
super
end
end
alias_method :isComplete?, :deferToReroute
alias_method :quantityReceived, :deferToReroute
alias_method :receiptDate, :deferToReroute
end
The Kernel.this_method is just a convenience to find out which method was called. However, calling super throws
super: no superclass method `deferToReroute'
I searched a bit and found this link which discusses that this is a bug in Ruby 1.8 but is fixed in 1.9. Unfortunately, I can't upgrade this code to 1.9 yet, so does anyone have any suggestions for workarounds?
Thanks :-)
Edit: After a bit of looking at my code, I realized that I don't actually need to alias all of the methods that I did, I actually only needed to overwrite the deliveries method since the other three actually call it for their calculations. However, I would still love to know y'all's thoughts since I have run into this before.
Rather than using alias_method here, you might be better served by hard-overriding these methods, like so:
class Shipment < AbstractShipment
def isComplete?
return super unless reroute
reroute.isComplete?
end
end
if you find you are doing this 5-10 times per class, you can make it nicer like so:
class Shipment < AbstractShipment
def self.deferred_to_reroute(*method_names)
method_names.each do |method_name|
eval "def #{method_name}; return super unless reroute; reroute.#{method_name}; end"
end
end
deferred_to_reroute :isComplete?, :quantityReceived, :receiptDate
end
Using a straight eval offers good performance characteristics and allows you to have a simple, declarative syntax for what you are doing within your class definition.

Resources