Search code examples
rubymetaprogrammingblock

add instance method using open class to Object class in ruby?


I have a class Operator that executes a block of code of some instances of other objects:

class Operator

    def operation(&block)
        #execution of a block in some object
    end
end

In the other hand, I have the class Criature

class Criature

    operational

    def level_up
        @level+=1
    end

end

And I need to monkeypatch the class Object with open class.

So, I need to define the method operational in Object , because, every time that an instance of Operator executes operation, with (for example) the block: { c.level_up() } will be executed for every instance with operational in it's class body.

How can I do to do this?


Solution

  • executes a block of code of some instances of some objects:

    If I well understand, with the method

        operational
    

    that you want to execute in the body of some classes like Criature, you want to flag their instances, saying "Hey, give me a call when operation is performed."

    From The Pickaxe :

    ObjectSpace.each_object( ‹ class_or_mod› )

    Calls the block once for each living, nonimmediate object in this Ruby process. If class_or_mod is specified, calls the block for only those classes or modules that match (or are a subclass of) class_or_mod.

    With ObjectSpace it's possible to iterate over the objects of a certain class. This gives me the idea that the signature, this flag that you want to give to certain instances, can be accomplished with a superclass.

    ObjectSpace.each_object(Operational)
    

    will collect all instances of classes having this "signature".

    File tc.rb :

    class Operational
        
    end
    
    class Criature < Operational
        def initialize(p_name)
            @name  = p_name
            @level = 0
        end
            
        def level_up
            @level+=1
            puts "level is now #{@level}"
        end
    end
    
    class XYZ < Operational
        def initialize(p_name)
            @name = p_name
        end
        
    end
    

    File to.rb :

    require_relative 'tc'
    
    class Operator
    
        def operation(&block)
            #execution of a block in some object
            Special.currently_operational.each do | obj |
                puts "operation on #{obj.inspect}"
                block.call(obj) if obj.respond_to? 'level_up'
            end
        end
    end
    
    class Special
        def self.currently_operational
            operationals = []
    
            ObjectSpace.each_object(Operational) do | object |
                operationals << object
            end
    
            puts "#{operationals.size} objects are flagged as operational"
            operationals
        end
    end
    
    Criature.new('c1')
    Criature.new('c2')
    Criature.new('c3')
    
    block = Proc.new{ | x | x.level_up }
    puts 'first round'
    Operator.new.operation(&block)
    
    # later ...
    XYZ.new('x1')
    XYZ.new('x2')
    
    puts 'second round'
    Operator.new.operation(&block)
    

    Execution :

    $ ruby -w to.rb 
    first round
    3 objects are flagged as operational
    operation on #<Criature:0x007fc45683d2f0 @name="c3", @level=0>
    level is now 1
    operation on #<Criature:0x007fc45683d368 @name="c2", @level=0>
    level is now 1
    operation on #<Criature:0x007fc45683d3b8 @name="c1", @level=0>
    level is now 1
    second round
    5 objects are flagged as operational
    operation on #<XYZ:0x007fc45683cad0 @name="x2">
    operation on #<XYZ:0x007fc45683cb48 @name="x1">
    operation on #<Criature:0x007fc45683d2f0 @name="c3", @level=1>
    level is now 2
    operation on #<Criature:0x007fc45683d368 @name="c2", @level=1>
    level is now 2
    operation on #<Criature:0x007fc45683d3b8 @name="c1", @level=1>
    level is now 2