Is there a hook similar to Class#inherited that's triggered only after a Ruby class definition?

前端 未结 8 2054
无人共我
无人共我 2020-12-03 13:35

#inherited is called right after the class Foo statement. I want something that\'ll run only after the end statement that closes the c

相关标签:
8条回答
  • 2020-12-03 14:29

    No, there no such a hook to my knowledge, but the good thing is you can kind of do it yourself. Here is a possible implementation:

    Is not super clean, but it works:

    puts RUBY_VERSION # 2.4.1
    
    class Father
      def self.engage_super_setup(sub)
        puts "self:#{self} sub:#{sub}"
        sub.class_eval do
          puts "toy:#{@toy}"
        end
      end
    
      def self.super_setup
        if self.superclass.singleton_methods.include?(:engage_super_setup)
          superclass.engage_super_setup(self)
        end
      end
    end
    
    Son = Class.new(Father) do
      @toy = 'ball'
    end.tap { |new_class| new_class.super_setup } # this is needed to:
    # 1. call the super_setup method in the new class.
    # 2. we use tap to return the new Class, so this class is assigned to the Son constant.
    
    puts Son.name # Son
    
    

    Output:

    self:Father sub:#<Class:0x0055d5ab44c038> #here the subclass is still anonymous since it was not yet assigned to the constant "Son"
    toy:ball # here we can see we have acess to the @toy instance variable in Son but from the :engage_super_setup in the Father class
    Son # the of the class has been assigned after the constant, since ruby does this automatically when a class is assigned to a constant 
    

    So this is obviously not as clean as a hook, but I think at the end we have a pretty good result.

    If we had tried to do the same with :inherited sadly is not possible, because :inherited is called even before the execution entoer in the body of the class:

    puts RUBY_VERSION # 2.4.1
    
    class Father
      def self.inherited(sub)
        puts "self:#{self} sub:#{sub}"
        sub.class_eval do
          puts "toy:#{@toy.inspect}"
        end
      end
    
    end
    
    class Son < Father
      puts "we are in the body of Son"
      @toy = 'ball'
    end
    
    puts Son.name # Son
    

    Output:

    self:Father sub:Son # as you can see here the hook is executed before the body of the declaration Son class runs
    toy:nil # we dont have access yet to the instance variables
    we are in the body of Son # the body of the class declaration begins to run after the :inherited hook.
    Son
    
    0 讨论(0)
  • 2020-12-03 14:33

    If you are willing to assume your Ruby implements ObjectSpaces, you could could look up all model instances after the fact, and then modify them appropriately. Google suggests http://phrogz.net/ProgrammingRuby/ospace.html

    0 讨论(0)
提交回复
热议问题