How to manually delegate an object to Ruby

Asked

Viewed 80 times

1

To better understand the meta-programming in Ruby I would like to make an object, when instantiated, delegate all methods of an object passed as parameter. And your class will pass to delegate the methods that the class of it contains. But I would like the Object and Class methods,: send and object_id, which may cause errors if changed, are set

I thought of something like:

 def delegate_all
    delegate_instance if !@delegated
    delegate_singleton if !@@delegated
end
def delegate_instance
    ((@model.class.instance_methods-Object.instance_methods)-Class.instance_methods).each do |name|
            name = name.to_sym
            puts name.to_s
            self.class.send(:define_method, name){ |*args|
                method = @model.method name
                #if method.arity > 0
                    method.call args
                #else
                #   method.call
                #end        
            }
    end
    @delegated = true
end 
def delegate_singleton
    ((@model.class.singleton_methods-Class.singleton_methods)-Object.singleton_methods).each do |name|
        name = name.to_sym
        puts name.to_s
        self.class.define_singleton_method(name){|*args|
            name = name.to_sym
            method = @model.singleton_method name
            #if method.arity > 0
                method.call args
            #else
            #   method.call
            #end    
        }
    end
    @@delegated = true  
end

Does anyone have any idea how I can do this? , but I should also consider methods with parameters.

2 answers

1


The simplest way is to work with method_missing, thus:

class SimpleDelegator < BasicObject
  def initialize(obj)
    @obj = obj
  end
  def method_missing(method, *args, &block)
    SimpleDelegator.new(@obj.send(method, *args, &block))
  end
  def inspect
    "SimpleDelegator for #{@obj.inspect}"
  end
  def to_s
    "SimpleDelegator for #{@obj.to_s}"
  end
end

x = SimpleDelegator.new(3)
y = x+2
print y  #=> SimpleDelegator for 5
print y.class #=> SimpleDelegator

The advantage of doing so is that you will not need to know what are the object methods before creating the delegator, any non-standard method will be immediately passed on to the delegated object. Notice I used the inheritance of BasicObject to have the smallest possible amount of class-specific methods.

BasicObject.instance_methods
# => [:==, :equal?, :!, :!=, :instance_eval, :instance_exec,
#     :__send__, :__id__, :__binding__]

But note that even these methods are not necessary for the class to work. You can quietly set like this:

class SimpleDelegator < BasicObject
  instance_methods.each {|m| undef_method m } # remove todo e qualquer método
  def initialize(obj)
    @obj = obj
  end
  def method_missing(method, *args, &block)
    SimpleDelegator.new(@obj.send(method, *args, &block))
  end
end

x = SimpleDelegator.new(3)
y = x+2
print y  #=> 5
print y.class #=> Fixnum (note que não deixou de ser um SimpleDelegator)
  • I implemented my delegator this way, but I want to delegate a model of Ruby on Rails, and I would like to redecorate it as if it were the model, e.g.: '<%= render my_presenter %>', some hint of how to implement it

  • As far as I can see, there’s no reason why a delegate should fail to function in this case. What exactly is the problem you have? (It would be a good thing to open a new question on this)

  • I resolved to change the methods 'methods', 'instance_methods', 'singleton_methods' by increasing the methods of the delegated object

0

Browser other questions tagged

You are not signed in. Login or sign up in order to post.