How to Dynamically Alter Inheritance in Ruby

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.

Ruby How to inherit from object given in constructor

You probably wish to use Decorator pattern, when all methods are proxied to given model:

class TestClass
def initialize(object)
@object = object
end

def method_missing(method, *args, &block)
@object.send(method, *args, &block)
end
end

v = TestClass.new([1,2,3,4,5,6,7,8,9])
v.each { |number| print number}

Dynamically create a class inherited from ActiveRecord?

Nailed it:

def create_arec(table_name, &block)
klass = Class.new(ActiveRecord::Base){self.table_name = table_name}
klass.class_eval &block
klass
end

thanks @phoet

How to dynamically declare subclass in Ruby?

When you refer to something in Ruby, it first look up something in local bindings, if it fails, it then look up self.something. self represents a context of the evaluation, and this context changes on class definition class C; self; end, method definition class C; def m; self; end; end, however, it won't change on block definition. The block captures the current self at the point of block definition.

module Command
define :quit do
foo "bar" # self is Command, calls Command.foo by default
end
end

If you want to modify the self context inside a block, you can use BasicObject.instance_eval (or instance_exec, class_eval, class_exec).

For your example, the block passed to define should be evaluated under the self context of an instance of the concrete command.

Here is an example. I added some mock method definition in class Command::Command:

module Command
class Command
# remove this accessor if you want to make `name` readonly
attr_accessor :name

def exec(&block)
@exec = block
end

def foo(msg)
puts "FOO => #{msg}"
end

def run
@exec.call if @exec
end
end

def self.define(name, &block)
klass = Class.new(Command) do
define_method(:initialize) do
method(:name=).call(name) # it would be better to make it readonly
instance_eval(&block)
end
# readonly
# define_method(:name) { name }
end

::Command.const_set("Command_#{name}", klass)
end

define :quit do
foo "bar"
exec do
puts "EXEC => #{name}"
end
end
end

quit = Command::Command_quit.new #=> FOO => bar
quit.run #=> EXEC => quit
puts quit.class #=> Command::Command_quit

Ruby: How to dynamically create a subclass of an existing class?

Class.new accepts a parameter, which will be the superclass.

Documentation: Class.new.

Ruby exception inheritance with dynamically generated classes

Ok, I'll try to help here :

First a module is not a class, it allows you to mix behaviour in a class. second see the following example :

module A
module B
module Error
def foobar
puts "foo"
end
end
end
end

class StandardError
include A::B::Error
end

StandardError.new.kind_of?(A::B::Error)
StandardError.new.kind_of?(A::B)
StandardError.included_modules #=> [A::B::Error,Kernel]

kind_of? tells you that yes, Error does possess All of A::B::Error behaviour (which is normal since it includes A::B::Error) however it does not include all the behaviour from A::B and therefore is not of the A::B kind. (duck typing)

Now there is a very good chance that ruby-aws reopens one of the superclass of NameError and includes Amazon::AWS:Error in there. (monkey patching)

You can find out programatically where the module is included in the hierarchy with the following :

class Class
def has_module?(module_ref)
if self.included_modules.include?(module_ref) and not self.superclass.included_modules.include?(module_ref)
puts self.name+" has module "+ module_ref.name
else
self.superclass.nil? ? false : self.superclass.has_module?(module_ref)
end
end
end
StandardError.has_module?(A::B::Error)
NameError.has_module?(A::B::Error)

Regarding your second question I can't see anything better than

begin 
#do AWS error prone stuff
rescue Exception => e
if Amazon::AWS::Error.constants.include?(e.class.name)
#awsError
else
whatever
end
end

(edit -- above code doesn't work as is : name includes module prefix which is not the case of the constants arrays. You should definitely contact the lib maintainer the AWSError class looks more like a factory class to me :/ )

I don't have ruby-aws here and the caliban site is blocked by the company's firewall so I can't test much further.

Regarding the include : that might be the thing doing the monkey patching on the StandardError hierarchy. I am not sure anymore but most likely doing it at the root of a file outside every context is including the module on Object or on the Object metaclass. (this is what would happen in IRB, where the default context is Object, not sure about in a file)

from the pickaxe on modules :

A couple of points about the include statement before we go on. First, it has nothing to do with files. C programmers use a preprocessor directive called #include to insert the contents of one file into another during compilation. The Ruby include statement simply makes a reference to a named module. If that module is in a separate file, you must use require to drag that file in before using include.

(edit -- I can't seem to be able to comment using this browser :/ yay for locked in platforms)

Using dynamically created classes in a Single Table Inheritance mechanism

Your error message stands that 'FooObject' class cannot be located.

In your code, the dynamic generated class name shoudl be 'FooDynObject'.

Just check you don't have old test records in your database before loading DynObject.

@edit:
Another thing is also to know on which class you affect the dynamic class name.

class DynObject < ActiveRecord::Base
const_set 'FooDynObject', Class.new(DynObject)
end

Will result in DynObject::FooDynObject, and ActiveRecord won't be able to load it when it will see 'FooDynObject' type.

Personnally, I would do someting like

class DynObject < ActiveRecord::Base
Object.const_set 'FooDynObject', Class.new(DynObject)
end

Can you change the default inheritance of classes in Ruby?

Sure you could do this by modifying the relevant part of the Ruby source and recompiling Ruby:

VALUE
rb_define_class_id(ID id, VALUE super)
{
VALUE klass;

if (!super) super = rb_cObject; // <-- where the default is set
klass = rb_class_new(super);
// ...

But that’s a huge hassle and requires patching and running a custom Ruby and probably has a lot of gotchas and things that are hard-coded to assume Object is the default.

And, on top of that, what’s the point? If you replace Object with something else as the default superclass, every class—including those in Ruby core—will now inherit from this new default superclass. You could get the same effect (just without the different name) far more easily and without needing a custom Ruby by just changing Object itself. That’s the beauty of being able to reopen classes! For example:

class Object
def foo
'bar!'
end
end

class A; end

A.new.foo #=> 'bar!'

If you wanted to be kind you might even just put all the relevant methods in an Entity module instead of a class and then include it into Object.



Related Topics



Leave a reply



Submit