Override a method such as "#==" on every object in Ruby? - ruby

Disclaimer: This is just to satisfy my curiosity – I have no plans on using this for anything. I realise this would rarely be a good idea!
I would love to hear any ideas for how this could be achieved, whether they're sensible or not so sensible.
How could I override a method such as #== on every object (BasicObject) in Ruby?
I could override it on BasicObject:
class BasicObject
def ==(other); puts "custom for #{self} and #{other}!"; end
end
But this wouldn't work when a subclass also overrides it:
true == false # Runs my custom code!
"a" == "b" # Doesn't!
# Because of this:
true.method(:==) # => #<Method: TrueClass(BasicObject)#==(_)>
"hi".method(:==) # => #<Method: String#==(_)>
Prepending into BasicObject doesn't help, either – the subclass still wins – the prepended module is too far down the list:
String.ancestors # => [String, Comparable, Object, Kernel, MyPrependedMod, BasicObject]
I could manually override it on String as well – but trying to do it manually on every class that overrides #== would be very error prone, especially since anyone could override it on some user-defined class at any time.
What are some ways of achieving this?

Here's one solution I came up with. To be clear, this is very likely not something you should add to a codebase. It's just for fun.
ObjectSpace.each_object(Class) do |klass|
next if klass.frozen? # `Object` is frozen.
klass.define_method(:==) { |other| puts "custom for #{self} and #{other}!" }
end
# These all work.
true == false
"a" == "b"
1 == 2
:hi == :bye
If you want to prepend (e.g. to call super in your override), there's a gotcha:
ObjectSpace.each_object(Class) do |klass|
next if klass.frozen? # `Object` is frozen.
# We can't use `prepend` here since e.g. `String` overrides it.
# `prepend_features` is essentially the same thing.
(Module.new do
def ==(other); puts "custom for #{self} and #{other}!"; super; end
end).send(:prepend_features, klass)
end
# These all work.
true == false
"a" == "b"
1 == 2
:hi == :bye
Instead of using prepend_features, we could alternatively make sure to call BasicObject's prepend and not the one overridden in a subclass:
# Some classes, like `String`, override "prepend".
prep = BasicObject.method(:prepend).unbind
ObjectSpace.each_object(Class) do |klass|
next if klass.frozen? # `Object` is frozen.
prep.bind(klass).call(Module.new do
def ==(other); puts "custom for #{self} and #{other}!"; super; end
end)
end
# These all work.
true == false
"a" == "b"
1 == 2
:hi == :bye

Related

Method chaining in ruby

I want to build an API client that has an interface similar to rails active record. I want the consumers to be able to chain methods and after the last method is chained, the client requests a url based on the methods called. So it's method chaining with some lazy evaluation. I looked into Active Record but this is very complicated (spawning proceses, etc).
Here is a toy example of the sort of thing I am talking about. You can chain as many 'bar' methods together as you like before calling 'get', like this:
puts Foo.bar.bar.get # => 'bar,bar'
puts Foo.bar.bar.bar.get # => 'bar,bar,bar'
I have successfully implemented this, but I would rather not need to call the 'get' method. So what I want is this:
puts Foo.bar.bar # => 'bar,bar'
But my current implementation does this:
puts Foo.bar.bar #=> [:bar, :bar]
I have thought of overriding array methods like each and to_s but I am sure there is a better solution.
How would I chain the methods and know which was the last one so I could return something like the string returned in the get method?
Here is my current implementation:
#!/usr/bin/env ruby
class Bar
def get(args)
# does a request to an API and returns things but this will do for now.
args.join(',')
end
end
class Foo < Array
def self.bar
#q = new
#q << :bar
#q
end
def bar
self << :bar
self
end
def get
Bar.new.get(self)
end
end
Also see: Ruby Challenge - Method chaining and Lazy Evaluation
How it works with activerecord is that the relation is a wrapper around the array, delegating any undefined method to this internal array (called target). So what you need is to start with a BasicObject instead of Object:
class Foo < BasicObject
then you need to create internal variable, to which you will delegate all the methods:
def method_missing(*args, &block)
reload! unless loaded?
#target.send(*args, &block)
end
def reload!
# your logic to populate target, e.g:
#target = #counter
#loaded = true
end
def loaded?
!!#loaded
end
To chain methods, your methods need to return new instance of your class, e.g:
def initialize(counter=0)
#counter = counter
end
def bar
_class.new(#counter + 1)
end
private
# BasicObject does not define class method. If you want to wrap your target
# completely (like ActiveRecord does before rails 4), you want to delegate it
# to #target as well. Still you need to access the instance class to create
# new instances. That's the way (if there are any suggestion how to improve it,
# please comment!)
def _class
(class << self; self end).superclass
end
Now you can check it in action:
p Foo.new.bar.bar.bar #=> 3
(f = Foo.new) && nil # '&& nil' added to prevent execution of inspect
# object in the console , as it will force #target
# to be loaded
f.loaded? #=> false
puts f #=> 0
f.loaded? #=> true
A (very simple, maybe simplistic) option would be to implement the to_s method - as it is used to "coerce" to string (for instance in a puts), you could have your specific "this is the end of the chain" code there.

how rails delegate method works?

After reading the answer by jvans below and looking at the source code a few more time I get it now :). And in case anyone is still wondering how exactly rails delegates works. All rails is doing is creating a new method with (module_eval) in the file/class that you ran the delegate method from.
So for example:
class A
delegate :hello, :to => :b
end
class B
def hello
p hello
end
end
At the point when delegate is called rails will create a hello method with (*args, &block) in class A (technically in the file that class A is written in) and in that method all rails do is uses the ":to" value(which should be an object or a Class that is already defined within the class A) and assign it to a local variable _, then just calls the method on that object or Class passing in the params.
So in order for delegate to work without raising an exception... with our previous example. An instance of A must already have a instance variable referencing to an instance of class B.
class A
attr_accessor :b
def b
#b ||= B.new
end
delegate :hello, :to => :b
end
class B
def hello
p hello
end
end
This is not a question on "how to use the delegate method in rails", which I already know. I'm wondering how exactly "delegate" delegates methods :D. In Rails 4 source code delegate is defined in the core Ruby Module class, which makes it available as a class method in all rails app.
Actually my first question would be how is Ruby's Module class included? I mean every Ruby class has ancestors of > Object > Kernel > BasicObject and any module in ruby has the same ancestors. So how exactly how does ruby add methods to all ruby class/modules when someone reopens the Module class?
My second question is.. I understand that the delegate method in rails uses module_eval do the actual delegation but I don't really understand how module_eval works.
def delegate(*methods)
options = methods.pop
unless options.is_a?(Hash) && to = options[:to]
raise ArgumentError, 'Delegation needs a target. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, to: :greeter).'
end
prefix, allow_nil = options.values_at(:prefix, :allow_nil)
if prefix == true && to =~ /^[^a-z_]/
raise ArgumentError, 'Can only automatically set the delegation prefix when delegating to a method.'
end
method_prefix = \
if prefix
"#{prefix == true ? to : prefix}_"
else
''
end
file, line = caller.first.split(':', 2)
line = line.to_i
to = to.to_s
to = 'self.class' if to == 'class'
methods.each do |method|
# Attribute writer methods only accept one argument. Makes sure []=
# methods still accept two arguments.
definition = (method =~ /[^\]]=$/) ? 'arg' : '*args, &block'
# The following generated methods call the target exactly once, storing
# the returned value in a dummy variable.
#
# Reason is twofold: On one hand doing less calls is in general better.
# On the other hand it could be that the target has side-effects,
# whereas conceptually, from the user point of view, the delegator should
# be doing one call.
if allow_nil
module_eval(<<-EOS, file, line - 3)
def #{method_prefix}#{method}(#{definition}) # def customer_name(*args, &block)
_ = #{to} # _ = client
if !_.nil? || nil.respond_to?(:#{method}) # if !_.nil? || nil.respond_to?(:name)
_.#{method}(#{definition}) # _.name(*args, &block)
end # end
end # end
EOS
else
exception = %(raise DelegationError, "#{self}##{method_prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")
module_eval(<<-EOS, file, line - 2)
def #{method_prefix}#{method}(#{definition}) # def customer_name(*args, &block)
_ = #{to} # _ = client
_.#{method}(#{definition}) # _.name(*args, &block)
rescue NoMethodError => e # rescue NoMethodError => e
if _.nil? && e.name == :#{method} # if _.nil? && e.name == :name
#{exception} # # add helpful message to the exception
else # else
raise # raise
end # end
end # end
EOS
end
end
end
Ruby isn't reopening the module class here. In ruby the class Module and the class Class are almost identical.
Class.instance_methods - Module.instance_methods #=> [:allocate, :new, :superclass]
The main difference is that you can't 'new' a module.
Module's are ruby's version of multiple inheritance so when you do:
module A
end
module B
end
class C
include A
include B
end
behind the scenes ruby is actually creating something called an anonymous class. so the above is actually equivalent to:
class A
end
class B < A
end
class C < B
end
module_eval here is a little deceptive. Nothing from the code you're looking at is dealing with modules. class_eval and module_eval are the same thing and they just reopen the class that they're called on so if you want to add methods to a class C you can do:
C.class_eval do
def my_new_method
end
end
or
C.module_eval do
def my_new_method
end
end
both of which are equivalent to manually reopening the class and defining the method
class C
end
class C
def my_new_method
end
end
so when they're calling module_eval in the source above, they're just reopening the current class it's being called it and dynamically defining the methods that you're delegating
I think this will answer your question better:
Class.ancestors #=> [Module, Object, PP::ObjectMixin, Kernel, BasicObject]
since everything in ruby is a class, the method lookup chain will go through all of these objects until it finds what it's looking for. By reoping module you add behavior to everything. The ancestor chain here is a little deceptive, since BasicObject.class #=> Class and Module is in Class's lookup hierarchy, even BasicObject inherits behavior from repening module. The advantage of reopening Module here over Class is that you can now call this method from within a module as well as within a class! Very cool, learned something here myself.
After reading the answer by jvans below and looking at the source code a few more time I get it now :). And in case anyone is still wondering how exactly rails delegates works. All rails is doing is creating a new method with (module_eval) in the file/class that you ran the delegate method from.
So for example:
class A
delegate :hello, :to => :b
end
class B
def hello
p hello
end
end
At the point when delegate is called rails will create a hello method with (*args, &block) in class A (technically in the file that class A is written in) and in that method all rails do is uses the ":to" value(which should be an object or a Class that is already defined within the class A) and assign it to a local variable _, then just calls the method on that object or Class passing in the params.
So in order for delegate to work without raising an exception... with our previous example. An instance of A must already have a instance variable referencing to an instance of class B.
class A
attr_accessor :b
def b
#b ||= B.new
end
delegate :hello, :to => :b
end
class B
def hello
p hello
end
end

How are respond_to and respond_to_missing different?

I'm confused when to use each of this methods.
From respond_to? documentation:
Returns true if obj responds to the given method. Private methods
are included in the search only if the optional second parameter
evaluates to true.
If the method is not implemented, as Process.fork on Windows,
File.lchmod on GNU/Linux, etc., false is returned.
If the method is not defined, respond_to_missing? method is called and
the result is returned.
And respond_to_missing?:
Hook method to return whether the obj can respond to id method or
not.
See #respond_to?.
Both methods takes 2 arguments.
Both methods seems to the same thing(check if some object respond to given method) so why we should use(have) both?
Defining 'resond_to_missing?` gives you ability to take methods:
class A
def method_missing name, *args, &block
if name == :meth1
puts 'YES!'
else
raise NoMethodError
end
end
def respond_to_missing? name, flag = true
if name == :meth1
true
else
false
end
end
end
[65] pry(main)> A.new.method :meth1
# => #<Method: A#meth1>
Why respond_to? couldn't do this?
What I guess:
respond_to? checks if method is in:
Current object.
Parent object.
Included modules.
respond_to_missing? checks if method is:
Defined via method_missing:
Via array of possible methods:
def method_missing name, *args, &block
arr = [:a, :b, :c]
if arr.include? name
puts name
else
raise NoMethodError
end
end
Delegating it to different object:
class A
def initialize name
#str = String name
end
def method_missing name, *args, &block
#str.send name, *args, &block
end
end
2 . Other way that I'm not aware of.
Where should both be defined/used(my guessing too):
Starting from 1.9.3(as fair I remember) define only respond_to_missing? but use only respond_to?
Last questions:
Am I right? Did I missed something? Correct everything that is bad and/or answer questions asked in this question.
respond_to_missing? is supposed to be updated when you make available additional methods using the method missing technique. This will cause the Ruby interpreter to better understand the existence of the new method.
In fact, without using respond_to_missing?, you can't get the method using method.
Marc-André posted a great article about the respond_to_missing?.
In order for respond_to? to return true, one can specialize it, as follows:
class StereoPlayer
# def method_missing ...
# ...
# end
def respond_to?(method, *)
method.to_s =~ /play_(\w+)/ || super
end
end
p.respond_to? :play_some_Beethoven # => true
This is better, but it still doesn’t make play_some_Beethoven behave exactly like a method. Indeed:
p.method :play_some_Beethoven
# => NameError: undefined method `play_some_Beethoven'
# for class `StereoPlayer'
Ruby 1.9.2 introduces respond_to_missing? that provides for a clean solution to the problem. Instead of specializing respond_to? one specializes respond_to_missing?. Here’s a full example:
class StereoPlayer
# def method_missing ...
# ...
# end
def respond_to_missing?(method, *)
method =~ /play_(\w+)/ || super
end
end
p = StereoPlayer.new
p.play_some_Beethoven # => "Here's some_Beethoven"
p.respond_to? :play_some_Beethoven # => true
m = p.method(:play_some_Beethoven) # => #<Method: StereoPlayer#play_some_Beethoven>
# m acts like any other method:
m.call # => "Here's some_Beethoven"
m == p.method(:play_some_Beethoven) # => true
m.name # => :play_some_Beethoven
StereoPlayer.send :define_method, :ludwig, m
p.ludwig # => "Here's some_Beethoven"
See also Always Define respond_to_missing? When Overriding method_missing.

Ruby define_method

I have the following test which I must pass:
def test_can_find_by_arbitrary_fields
assert #library.respond_to? :find_by_artist
assert !#library.respond_to?(:find_by_bitrate)
#library.add_song({ :artist => 'Green Day',
:name => 'American Idiot',
:bitrate => 192 })
assert #library.respond_to?(:find_by_bitrate)
end
and I am not sure how I can do it.
I tried doing:
def respond_to?(method)
if self.public_methods.include? method
true
elsif (method == :find_by_bitrate)
define_method :find_by_bitrate, ->(default = nrb) { #songs.select |a| a[:bitrate] == nrb }
false
else
false
end
but it says "define_method is undefined". Are there any ways I can define the find_by_bitrate method?
You may define methods the first time they're called in method_missing.
Whether or not you should is open to some debate, but it's a better option than respond_to?.
class Foo
def method_missing(sym)
puts "Method missing; defining."
self.class.send(:define_method, sym) do
puts "Called #{sym}."
end
end
end
Sanity check:
f = Foo.new
=> #<Foo:0x007fa6aa09d3c0>
f.wat
=> Method wat missing; defining.
f.wat
=> Called wat.
f2 = Foo.new
=> Called wat.
I don't think you should be redefining respond_to? method. The point of the test is (probably) that the #library object should have a find_by_artist method defined and no find_by_bitrate until you add a song with a bitrate. I.e. the add_song method should define method find_by_bitrate when it sees a song with a bitrate (?).
Also, define_method is a private method of Class. Above, you're trying to call it from an instance method. See "Ruby: define_method vs. def", there's more on this stuff.
There's a lot of info missing to properly answer this. The test implies that find_by_artist is always defined even when #library is empty, but that there are dynamic methods available on other attributes (eg: bitrate) that are valid only when library contains a record with such a method.
One should not redefine respond_to? in any case. There is an explicit hook method for answering respond_to? for dynamic methods: Object#respond_to_missing?.
So a simple way to make your test pass is to be sure the #library object has a concrete method #find_by_artist and a respond to hook that checks whether any of it's elements a have the requested attribute. If I assume #library is a collection object Library which keeps an enumeration of songs in #songs
class Library
def find_by_artist artist
#songs.select { |song| song['artist'] == artist }
end
def method_missing meth, arg
m = /^find_by_(.+)$/.match meth.to_s
return super unless attr = m && m[1]
#songs.select { |song| song[attr] == arg }
end
def respond_to_missing? meth, include_private
m = /^find_by_(.+)$/.match meth.to_s
return super unless attr = m && m[1]
#songs.any? { |song| song.has_key? attr }
end
end
This has a performance problem in that respond_to? now incurs a search of all the songs. One could optimize by keeping a set of the union of all attributes contained in #songs and updating it in methods which add/update/delete elements in the collection.

Delegating instance methods to the class method

In Ruby, suppose I have a class Foo to allow me to catalogue my large collection of Foos. It's a fundamental law of nature that all Foos are green and spherical, so I have defined class methods as follows:
class Foo
def self.colour
"green"
end
def self.is_spherical?
true
end
end
This lets me do
Foo.colour # "green"
but not
my_foo = Foo.new
my_foo.colour # Error!
despite the fact that my_foo is plainly green.
Obviously, I could define an instance method colour which calls self.class.colour, but that gets unwieldy if I have many such fundamental characteristics.
I can also presumably do it by defining method_missing to try the class for any missing methods, but I'm unclear whether this is something I should be doing or an ugly hack, or how to do it safely (especially as I'm actually under ActiveRecord in Rails, which I understand does some Clever Fun Stuff with method_missing).
What would you recommend?
The Forwardable module that comes with Ruby will do this nicely:
#!/usr/bin/ruby1.8
require 'forwardable'
class Foo
extend Forwardable
def self.color
"green"
end
def_delegator self, :color
def self.is_spherical?
true
end
def_delegator self, :is_spherical?
end
p Foo.color # "green"
p Foo.is_spherical? # true
p Foo.new.color # "green"
p Foo.new.is_spherical? # true
If it's plain Ruby then using Forwardable is the right answer
In case it's Rails I would have used delegate, e.g.
class Foo
delegate :colour, to: :class
def self.colour
"green"
end
end
irb(main):012:0> my_foo = Foo.new
=> #<Foo:0x007f9913110d60>
irb(main):013:0> my_foo.colour
=> "green"
You could use a module:
module FooProperties
def colour ; "green" ; end
def is_spherical? ; true ; end
end
class Foo
extend FooProperties
include FooProperties
end
A little ugly, but better than using method_missing. I'll try to put other options in other answers...
From a design perspective, I would argue that, even though the answer is the same for all Foos, colour and spherical? are properties of instances of Foo and as such should be defined as instance methods rather than class methods.
I can however see some cases where you would want this behaviour e.g. when you have Bars in your system as well all of which are blue and you are passed a class somewhere in your code and would like to know what colour an instance will be before you call new on the class.
Also, you are correct that ActiveRecord does make extensive use of method_missing e.g. for dynamic finders so if you went down that route you would need to ensure that your method_missing called the one from the superclass if it determined that the method name was not one that it could handle itself.
I think that the best way to do this would be to use the Dwemthy's array method.
I'm going to look it up and fill in details, but here's the skeleton
EDIT: Yay! Working!
class Object
# class where singleton methods for an object are stored
def metaclass
class<<self;self;end
end
def metaclass_eval &block
metaclass.instance_eval &block
end
end
module Defaults
def self.included(klass, defaults = [])
klass.metaclass_eval do
define_method(:add_default) do |attr_name|
# first, define getters and setters for the instances
# i.e <class>.new.<attr_name> and <class>.new.<attr_name>=
attr_accessor attr_name
# open the class's class
metaclass_eval do
# now define our getter and setters for the class
# i.e. <class>.<attr_name> and <class>.<attr_name>=
attr_accessor attr_name
end
# add to our list of defaults
defaults << attr_name
end
define_method(:inherited) do |subclass|
# make sure any defaults added to the child are stored with the child
# not with the parent
Defaults.included( subclass, defaults.dup )
defaults.each do |attr_name|
# copy the parent's current default values
subclass.instance_variable_set "##{attr_name}", self.send(attr_name)
end
end
end
klass.class_eval do
# define an initialize method that grabs the defaults from the class to
# set up the initial values for those attributes
define_method(:initialize) do
defaults.each do |attr_name|
instance_variable_set "##{attr_name}", self.class.send(attr_name)
end
end
end
end
end
class Foo
include Defaults
add_default :color
# you can use the setter
# (without `self.` it would think `color` was a local variable,
# not an instance method)
self.color = "green"
add_default :is_spherical
# or the class instance variable directly
#is_spherical = true
end
Foo.color #=> "green"
foo1 = Foo.new
Foo.color = "blue"
Foo.color #=> "blue"
foo2 = Foo.new
foo1.color #=> "green"
foo2.color #=> "blue"
class Bar < Foo
add_defaults :texture
#texture = "rough"
# be sure to call the original initialize when overwriting it
alias :load_defaults :initialize
def initialize
load_defaults
#color = += " (default value)"
end
end
Bar.color #=> "blue"
Bar.texture #=> "rough"
Bar.new.color #=> "blue (default value)"
Bar.color = "red"
Bar.color #=> "red"
Foo.color #=> "blue"
You can also do this:
def self.color your_args; your_expression end
define_method :color, &method(:color)
You could define a passthrough facility:
module Passthrough
def passthrough(*methods)
methods.each do |method|
## make sure the argument is the right type.
raise ArgumentError if ! method.is_a?(Symbol)
method_str = method.to_s
self.class_eval("def #{method_str}(*args) ; self.class.#{method_str}(*args) ; end")
end
end
end
class Foo
extend Passthrough
def self::colour ; "green" ; end
def self::is_spherical? ; true ; end
passthrough :colour, :is_spherical?
end
f = Foo.new
puts(f.colour)
puts(Foo.colour)
I don't generally like using eval, but it should be pretty safe, here.
This is going to sound like a bit of a cop out, but in practice there's rarely a need to do this, when you can call Foo.color just as easily. The exception is if you have many classes with color methods defined. #var might be one of several classes, and you want to display the color regardless.
When that's the case, I'd ask yourself where you're using the method more - on the class, or on the model? It's almost always one or the other, and there's nothing wrong with making it an instance method even though it's expected to be the same across all instances.
In the rare event you want the method "callable" by both, you can either do #var.class.color (without creating a special method) or create a special method like so:
def color
self.class.color
end
I'd definitely avoid the catch-all (method_missing) solution, because it excuses you from really considering the usage of each method, and whether it belongs at the class or instance level.

Resources