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
Controller Spec Unknown Keyword: Id
How to Format the Value Shown in a Rails Edit Field
What Does Class_Eval ≪≪-"End_Eval", _File_, _Line_ Mean in Ruby
Why Are Methods in Ruby Documentation Preceded by a Hash Sign
How to Redirect Stderr and Stdout to File For a Ruby Script
How to Make an Http Get With Modified Headers
Unable to Obtain Stable Firefox Connection in 60 Seconds (127.0.0.1:7055)
Undefined Method Attr_Accessible
Ruby/Rails - Change the Timezone of a Time, Without Changing the Value
Undefined Method 'Visit' When Using Rspec and Capybara in Rails
Difference Between $Stdout and Stdout in Ruby
What Is a Regex to Match a String Not At the End of a Line
Rspec: Expect VS Expect With Block - What's the Difference
Unzip (Zip, Tar, Tag.Gz) Files With Ruby
In Rails, How to Add a New Method to String Class