Is There a Hook Similar to Class#Inherited That's Triggered Only After a Ruby Class Definition

How to have an inherited callback in ruby that is triggered after the child class is defined instead of before

You can trace until you find the end of the class definition. I did it in a method which I called after_inherited:

class Class
def after_inherited child = nil, &blk
line_class = nil
set_trace_func(lambda do |event, file, line, id, binding, classname|
unless line_class
# save the line of the inherited class entry
line_class = line if event == 'class'
else
# check the end of inherited class
if line == line_class && event == 'end'
# if so, turn off the trace and call the block
set_trace_func nil
blk.call child
end
end
end)
end
end

# testing...

class A
def self.inherited(child)
after_inherited do
puts "XXX"
end
end
end

class B < A
puts "YYY"
# .... code here can include class << self, etc.
end

Output:

YYY
XXX

How to discover which classes were loaded when requiring a Ruby file

If you are only interested in classes and modules which are defined at the top level, i.e. not inside a Module, you can do it like this:

previously_defined = Object.constants
require 'foobar'
just_defined = Object.constants - previously_defined

(You'll have to filter out the names of modules if you don't want them.)

If you need to get all classes, you can do:

previously_defined = []
ObjectSpace.each_object(Class) { |cls| previously_defined << cls }
require 'foobar'
just_defined = []
ObjectSpace.each_object(Class) { |cls| just_defined << cls }
just_defined -= previously_defined

Can I make a parent class methods private, but allow them to be called by their child classes via inheritance only?

Why doesn't private work in this case?

Because private affects only instance methods, not class methods. I normally use private_class_method for this

  private_class_method def self.subclasses
# list all subclasses
ObjectSpace.each_object(Class).select { |c| c < self }
end

Alternatively, you can define this method in a singleton class where it will be an instance method.

class << self
private

def subclasses
# list all subclasses
ObjectSpace.each_object(Class).select { |c| c < self }
end
end

Note that the privateness is inherited and you won't be able to call the method on House either. I would leave the method public and raise from it if self is Building

  def self.subclasses
raise 'use a subclass' if self == Building

# list all subclasses
ObjectSpace.each_object(Class).select { |c| c < self }
end

Does Ruby provide a constant_added hook method?

I once did it in Ruby 1.9 using Kernel.set_trace_func. You can keep checking for "line" events. Whenever such event occurs, take the difference of the constants from the previous time using the constants method. If you detect a difference, then that is the moment a constant was added/removed.

Kernel.set_trace_func ->event, _, _, _, _, _{
case event
when "line"
some_routine
end
}

Ruby 2.0 may have more powerful API, which allows for a more straightforward way to do it, but I am not sure.

How to dynamically alter inheritance in Ruby

Joshua has already given you a great list of alternatives, but to answer your question: You can't change the superclass of a class after the class has been created in ruby. That's simply not possible.



Related Topics



Leave a reply



Submit