Ruby define_method - ruby

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.

Related

Maintaining same class using delegation in Ruby

I'm trying to wrap my head around delegation vs. inheritance so I'm manually delegating a version of Array. One of the specific reasons I read to do this is because when you use things like enumerables, your returned value on the inherited methods reverts back to the parent class (i.e. Array). So I did this:
module PeepData
# A list of Peeps
class Peeps
include Enumerable
def initialize(list = [])
#list = list
end
def [](index)
#list[index]
end
def each(...)
#list.each(...)
end
def reverse
Peeps.new(#list.reverse)
end
def last
#list.last
end
def join(...)
#list.join(...)
end
def from_csv(csv_table)
#list = []
csv_table.each { |row| #list << Peep.new(row.to_h) }
end
def include(field, value)
Peeps.new(select { |row| row[field] == value })
end
def exclude(field, value)
Peeps.new(select { |row| row[field] != value })
end
def count_by_field(field)
result = {}
#list.each do |row|
result[row[field]] = result[row[field]].to_i + 1
end
result
end
protected
attr_reader :list
end
end
When I instantiate this, my include and exclude function great and return a Peeps class but when using an enumerable like select, it returns Array, which prevents me from chaining further Peeps specific methods after the select. This is exactly what I'm trying to avoid with learning about delegation.
p = Peeps.new
p.from_csv(csv_generated_array_of_hashes)
p.select(&:certified?).class
returns Array
If I override select, wrapping it in Peeps.new(), I get a "SystemStackError: stack level too deep". It seems to be recursively burying the list deeper into the list during the select enumeration.
def select(...)
Peeps.new(#list.select(...))
end
Any help and THANKS!
I would recommend using both Forwardable and Enumerable. Use Forwardable to delegate the each method to your list (to satisfy the Enumerable interface requirement), and also forward any Array methods you might want to include that are not part of the Enumerable module, such as size. I would also suggest not overriding the behavior of select as it is supposed to return an array and would at the very least lead to confusion. I would suggest something like the subset provided below to implement the behavior you are looking for.
require 'forwardable'
class Peeps
include Enumerable
extend Forwardable
def_delegators :#list, :each, :size
def initialize(list = [])
#list = list
end
def subset(&block)
selected = #list.select(&block)
Peeps.new(selected)
end
protected
attr_reader :list
end
Example usage:
peeps = Peeps.new([:a,:b,:c])
subset = peeps.subset {|s| s != :b}
puts subset.class
peeps.each do |peep|
puts peep
end
puts peeps.size
puts subset.size
produces:
Peeps
a
b
c
3
2
I think that if Peeps#select will return an Array, then it is OK to include Enumerable. But, you want Peeps#select to return a Peeps. I don't think you should include Enumerable. It's misleading to claim to be an Enumerable if you don't conform to its interface. This is just my opinion. There is no clear consensus on this in the ecosystem. See "Examples from the ecosystem" below.
If we accept that we cannot include Enumerable, here's the first implementation that comes to my mind.
require 'minitest/autorun'
class Peeps
ARRAY_METHODS = %i[flat_map map reject select]
ELEMENT_METHODS = %i[first include? last]
def initialize(list)
#list = list
end
def inspect
#list.join(', ')
end
def method_missing(mth, *args, &block)
if ARRAY_METHODS.include?(mth)
self.class.new(#list.send(mth, *args, &block))
elsif ELEMENT_METHODS.include?(mth)
#list.send(mth, *args, &block)
else
super
end
end
end
class PeepsTest < Minitest::Test
def test_first
assert_equal('alice', Peeps.new(%w[alice bob charlie]).first)
end
def test_include?
assert Peeps.new(%w[alice bob charlie]).include?('bob')
end
def test_select
peeps = Peeps.new(%w[alice bob charlie]).select { |i| i < 'c' }
assert_instance_of(Peeps, peeps)
assert_equal('alice, bob', peeps.inspect)
end
end
I don't normally use method_missing, but it seemed convenient.
Examples from the ecosystem
There doesn't seem to be a consensus on how strictly to follow interfaces.
ActionController::Parameters used to inherit Hash. Inheritance ceased in Rails 5.1.
ActiveSupport::HashWithIndifferentAccess still inherits Hash.
As mentioned in the other answer, this isn't really proper usage of Enumerable. That said, you could still include Enumerable and use some meta-programming to override the methods that you want to be peep-chainable:
module PeepData
class Peeps
include Enumerable
PEEP_CHAINABLES = [:map, :select]
PEEP_CHAINABLES.each do |method_name|
define_method(method_name) do |&block|
self.class.new(super(&block))
end
end
# solution for select without meta-programming looks like this:
# def select
# Peeps.new(super)
# end
end
end
Just so you know, this really has nothing to do with inheritance vs delegation. If Peeps extended Array, you would have the exact same issue, and the exact solution above would still work.

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 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 nil-like object

How can I create an Object in ruby that will be evaluated to false in logical expressions similar to nil?
My intention is to enable nested calls on other Objects where somewhere half way down the chain a value would normally be nil, but allow all the calls to continue - returning my nil-like object instead of nil itself. The object will return itself in response to any received messages that it does not know how to handle and I anticipate that I will need to implement some override methods such as nil?.
For example:
fizz.buzz.foo.bar
If the buzz property of fizz was not available I would return my nil-like object, which would accept calls all the way down to bar returning itself. Ultimately, the statement above should evaluate to false.
Edit:
Based on all the great answers below I have come up with the following:
class NilClass
attr_accessor :forgiving
def method_missing(name, *args, &block)
return self if #forgiving
super
end
def forgive
#forgiving = true
yield if block_given?
#forgiving = false
end
end
This allows for some dastardly tricks like so:
nil.forgiving {
hash = {}
value = hash[:key].i.dont.care.that.you.dont.exist
if value.nil?
# great, we found out without checking all its parents too
else
# got the value without checking its parents, yaldi
end
}
Obviously you could wrap this block up transparently inside of some function call/class/module/wherever.
This is a pretty long answer with a bunch of ideas and code samples of how to approach the problem.
try
Rails has a try method that let's you program like that. This is kind of how it's implemented:
class Object
def try(*args, &b)
__send__(*a, &b)
end
end
class NilClass # NilClass is the class of the nil singleton object
def try(*args)
nil
end
end
You can program with it like this:
fizz.try(:buzz).try(:foo).try(:bar)
You could conceivably modify this to work a little differently to support a more elegant API:
class Object
def try(*args)
if args.length > 0
method = args.shift # get the first method
__send__(method).try(*args) # Call `try` recursively on the result method
else
self # No more methods in chain return result
end
end
end
# And keep NilClass same as above
Then you could do:
fizz.try(:buzz, :foo, :bar)
andand
andand uses a more nefarious technique, hacking the fact that you can't directly instantiate NilClass subclasses:
class Object
def andand
if self
self
else # this branch is chosen if `self.nil? or self == false`
Mock.new(self) # might want to modify if you have useful methods on false
end
end
end
class Mock < BasicObject
def initialize(me)
super()
#me = me
end
def method_missing(*args) # if any method is called return the original object
#me
end
end
This allows you to program this way:
fizz.andand.buzz.andand.foo.andand.bar
Combine with some fancy rewriting
Again you could expand on this technique:
class Object
def method_missing(m, *args, &blk) # `m` is the name of the method
if m[0] == '_' and respond_to? m[1..-1] # if it starts with '_' and the object
Mock.new(self.send(m[1..-1])) # responds to the rest wrap it.
else # otherwise throw exception or use
super # object specific method_missing
end
end
end
class Mock < BasicObject
def initialize(me)
super()
#me = me
end
def method_missing(m, *args, &blk)
if m[-1] == '_' # If method ends with '_'
# If #me isn't nil call m without final '_' and return its result.
# If #me is nil then return `nil`.
#me.send(m[0...-1], *args, &blk) if #me
else
#me = #me.send(m, *args, &blk) if #me # Otherwise call method on `#me` and
self # store result then return mock.
end
end
end
To explain what's going on: when you call an underscored method you trigger mock mode, the result of _meth is wrapped automatically in a Mock object. Anytime you call a method on that mock it checks whether its not holding a nil and then forwards your method to that object (here stored in the #me variable). The mock then replaces the original object with the result of your function call. When you call meth_ it ends mock mode and returns the actual return value of meth.
This allows for an api like this (I used underscores, but you could use really anything):
fizz._buzz.foo.bum.yum.bar_
Brutal monkey-patching approach
This is really quite nasty, but it allows for an elegant API and doesn't necessarily screw up error reporting in your whole app:
class NilClass
attr_accessor :complain
def method_missing(*args)
if #complain
super
else
self
end
end
end
nil.complain = true
Use like this:
nil.complain = false
fizz.buzz.foo.bar
nil.complain = true
As far as I'm aware there's no really easy way to do this. Some work has been done in the Ruby community that implements the functionality you're talking about; you may want to take a look at:
The andand gem
Rails's try method
The andand gem is used like this:
require 'andand'
...
fizz.buzz.andand.foo.andand.bar
You can modify the NilClass class to use method_missing() to respond to any
not-yet-defined methods.
> class NilClass
> def method_missing(name)
> return self
> end
> end
=> nil
> if nil:
* puts "true"
> end
=> nil
> nil.foo.bar.baz
=> nil
There is a principle called the Law of Demeter [1] which suggests that what you're trying to do is not good practice, as your objects shouldn't necessarily know so much about the relationships of other objects.
However, we all do it :-)
In simple cases I tend to delegate the chaining of attributes to a method that checks for existence:
class Fizz
def buzz_foo_bar
self.buzz.foo.bar if buzz && buzz.foo && buzz.foo.bar
end
end
So I can now call fizz.buzz_foo_bar knowing I won't get an exception.
But I've also got a snippet of code (at work, and I can't grab it until next week) that handles method missing and looks for underscores and tests reflected associations to see if they respond to the remainder of the chain. This means I don't now have to write the delegate methods and more - just include the method_missing patch:
module ActiveRecord
class Base
def children_names
association_names=self.class.reflect_on_all_associations.find_all{|x| x.instance_variable_get("#macro")==:belongs_to}
association_names.map{|x| x.instance_variable_get("#name").to_s} | association_names.map{|x| x.instance_variable_get("#name").to_s.gsub(/^#{self.class.name.underscore}_/,'')}
end
def reflected_children_regex
Regexp.new("^(" << children_names.join('|') << ")_(.*)")
end
def method_missing(method_id, *args, &block)
begin
super
rescue NoMethodError, NameError
if match_data=method_id.to_s.match(reflected_children_regex)
association_name=self.methods.include?(match_data[1]) ? match_data[1] : "#{self.class.name.underscore}_#{match_data[1]}"
if association=send(association_name)
association.send(match_data[2],*args,&block)
end
else
raise
end
end
end
end
end
[1] http://en.wikipedia.org/wiki/Law_of_Demeter

Is there an elegant way to test if one instance method is an alias for another?

In a unit test I need to test whether alias methods defined by alias_method have been properly defined. I could simply use the same tests on the aliases used for their originals, but I'm wondering whether there's a more definitive or efficient solution. For instance, is there a way to 1) dereference a method alias and return its original's name, 2) get and compare some kind of underlying method identifier or address, or 3) get and compare method definitions? For example:
class MyClass
def foo
# do something
end
alias_method :bar, :foo
end
describe MyClass do
it "method bar should be an alias for method foo" do
m = MyClass.new
# ??? identity(m.bar).should == identity(m.foo) ???
end
end
Suggestions?
According to the documentation for Method,
Two method objects are equal if that
are bound to the same object and
contain the same body.
Calling Object#method and comparing the Method objects that it returns will verify that the methods are equivalent:
m.method(:bar) == m.method(:foo)
bk1e's method works most of the time, but I just happened to hit the case where it doesn't work:
class Stream
class << self
alias_method :open, :new
end
end
open = Stream.method(:open)
new = Stream.method(:new)
p open, new # => #<Method: Stream.new>, #<Method: Class#new>
p open.receiver, new.receiver # => Stream, Stream
p open == new # => false
The output is produced in Ruby 1.9, not sure if it's a bug or not since Ruby 1.8 produces true for the last line. So, if you are using 1.9, be careful if you are aliasing an inherited class method (like Class#new), These two methods are bound to the same object (the class object Stream), but they are considered not equivalent by Ruby 1.9.
My workaround is simple - alias the original method again and test the equality of the two aliases:
class << Stream; alias_method :alias_test_open, :new; end
open = Stream.method(:open)
alias_test_open = Stream.method(:alias_test_open)
p open, alias_test_open # => #<Method: Stream.new>, #<Method: Stream.new>
p open.receiver, alias_test_open.receiver # => Stream, Stream
p open == alias_test_open # => true
Hope this helps.
UPDATE:
See http://bugs.ruby-lang.org/issues/7613
So Method#== should return false in this case since a super call would invoke different methods; it is not a bug.
Calling MyClass.instance_method(:foo) will result UnboundMethod instance, which has eql? method.
So the answer is:
describe MyClass do
subject { described_class }
specify do
expect(subject.instance_method(:foo)).to be_eql(subject.instance_method(:bar))
end
end

Resources