Ruby: having callbacks on 'attr' objects - ruby

Essentially I'm wondering how to place callbacks on objects in ruby, so that when an object is changed in anyway I can automatically trigger other changes:
(EDIT: I confused myself in my own example! Not a good sign… As #proxy is a URI object it has it's own methods, changing the URI object by using it's own methods doesn't call my own proxy= method and update the #http object)
class MyClass
attr_reader :proxy
def proxy=(string_proxy = "")
begin
#proxy = URI.parse("http://"+((string_proxy.empty?) ? ENV['HTTP_PROXY'] : string_proxy))
#http = Net::HTTP::Proxy.new(#proxy.host,#proxy.port)
rescue
#http = Net::HTTP
end
end
end
m = MyClass.new
m.proxy = "myproxy.com:8080"
p m.proxy
# => <URI: #host="myproxy.com" #port=8080>
m.proxy.host = 'otherproxy.com'
p m.proxy
# => <URI: #host="otherproxy.com" #port=8080>
# But accessing a website with #http.get('http://google.com') will still travel through myproxy.com as the #http object hasn't been changed when m.proxy.host was.

Your line m.proxy = nil will raise a NoMethodError exception, since nil does no respond to empty?. Thus #http is set to Net::HTTP, as in the rescue clause.
This has nothing to do with callbacks/setters. You should modify your code to do what you want (e.g. calling string_proxy.blank? if using activesupport).

I managed to figure this one out for myself!
# Unobtrusive modifications to the Class class.
class Class
# Pass a block to attr_reader and the block will be evaluated in the context of the class instance before
# the instance variable is returned.
def attr_reader(*params,&block)
if block_given?
params.each do |sym|
# Create the reader method
define_method(sym) do
# Force the block to execute before we…
self.instance_eval(&block)
# … return the instance variable
self.instance_variable_get("##{sym}")
end
end
else # Keep the original function of attr_reader
params.each do |sym|
attr sym
end
end
end
end
If you add that code somewhere it'll extend the attr_reader method so that if you now do the following:
attr_reader :special_attr { p "This happens before I give you #special_attr" }
It'll trigger the block before it gives you the #special_attr. It's executed in the instance scope so you can use it, for example, in classes where attributes are downloaded from the internet. If you define a method like get_details which does all retrieval and sets #details_retrieved to true then you can define the attr like this:
attr_reader :name, :address, :blah { get_details if #details_retrieved.nil? }

Related

Access outer attribute from a function override

I have a wrapper class which redefines a method of the wrapped class. Is there any way the wrapper's state can be accessed from inside the override method?
class WidgetWrapper
attr_accessor :result_saved_by_widget
def initialize(widget)
#widget = widget
# we intercept the widget's usual "save" method so we can see
# what the widget tries to save
def #widget.save_result(result) # this override works fine ...
OUTER.result_saved_by_widget = result # .. but I need something like this inside it!
end
end
def call
widget.calculate # this will call "save_result" at some stage
end
end
# How it gets used
wrapper = Wrapper.new(Widget.new)
wrapper.call
puts wrapper.result_saved_by_widget
Based on your example, I would extend the object with a module:
module WidgetExtension
attr_accessor :results_saved_by_widget
def save_result(result)
#results_saved_by_widget = result
super
end
end
w = Widget.new
w.extend(WidgetExtension)
w.calculate
w.results_saved_by_widget #=> stored value
Solved this with a perfectly stupid hack - injecting the wrapper object beforehand, using instance_variable_set.
class WidgetWrapper
attr_accessor :result_saved_by_widget
def initialize(widget)
#widget = widget
#widget.instance_variable_set :#wrapper, self
# we intercept the widget's usual "save" method so we can see
# what the widget tries to save
def #widget.save_result(result) # this override works fine ...
#wrapper.result_saved_by_widget = result # ... and this works too :)
end
end
def call
widget.calculate # this will call "save_result" at some stage
end
end
# How it gets used
wrapper = Wrapper.new(Widget.new)
wrapper.call
puts wrapper.result_saved_by_widget
I don't quite understand your question but I think did something quite similar in the past, maybe the following lines can help you :
documents_to_wrap.each do |doc|
doc.define_singleton_method(:method){override_code}
tmp = doc.instance_variable_get(:#instance_var).
doc.instance_variable_set(:#other_instance_var, tmp.do_something)
end
Actually, it's not that hard. A couple of points:
You probably want to call the original save_result. Otherwise, it's not much of a wrapper.
You need to use closure to capture current lexical context (meaning, memorize that we're in WidgetWrapper)
class Widget
def calculate
save_result(3)
end
def save_result(arg)
puts "original save_result: #{arg}"
end
end
class WidgetWrapper
attr_accessor :result_saved_by_widget, :widget
def initialize(widget)
#widget = widget
wrapper = self # `self` can/will unpredictably change.
#widget.define_singleton_method :save_result do |result|
wrapper.result_saved_by_widget = result
super(result)
end
end
def call
widget.calculate
end
end
# How it gets used
wrapper = WidgetWrapper.new(Widget.new)
wrapper.call
puts 'intercepted value'
puts wrapper.result_saved_by_widget
# >> original save_result: 3
# >> intercepted value
# >> 3

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 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

Resources