Creating a class dynamically
Solution 1
A class gains its name when it is assigned to a constant. So It's easy to do in a generic fashion with const_set
.
For example, let's say you want to use Struct
to build a class with some attributes, you can:
name = "Person"
attributes = [:name, :age]
klass = Object.const_set name, Struct.new(*attributes)
# Now use klass or Person or const_get(name) to refer to your class:
Person.new("John Doe", 42) # => #<struct Person name="John Doe", age=42>
To inherit from another class, replace the Struct.new
by Class.new(MyBaseClass)
, say:
class MyBaseClass; end
klass = Class.new(MyBaseClass) do
ATTRIBUTES = attributes
attr_accessor *ATTRIBUTES
def initialize(*args)
raise ArgumentError, "Too many arguments" if args.size > ATTRIBUTES.size
ATTRIBUTES.zip(args) do |attr, val|
send "#{attr}=", val
end
end
end
Object.const_set name, klass
Person.new("John Doe", 42) # => #<Person:0x007f934a975830 @name="John Doe", @age=42>
Solution 2
Your code would look something akin to this:
variable = "SomeClassName"
klass = Class.new(ParentClass)
# ...maybe evaluate some code in the context of the new, anonymous class
klass.class_eval { }
# ...or define some methods
klass.send(:title, :Person)
klass.send(:attribute, :name, String)
# Finally, name that class!
ParentClass.send(:const_set, variable, klass)
...or you could just use eval:
eval <<DYNAMIC
class #{name}
title :Person
attribute :name, String
# ...or substitute other stuff in here.
end
DYNAMIC
![BSG](https://i.stack.imgur.com/oTC9M.jpg?s=256&g=1)
Comments
-
BSG about 2 years
I'm trying to create a new class, without knowing the name of the class until it's supposed to be created.
Something like this;
variable = "ValidClassName" class variable end Test = ValidClassName.new
If possible, i'd also appreciate som hints on how to dynamically add attributes (and methods) to this new class.
I'll be retreiving 'settings' for the class, and they will look something like this:
title :Person attribute :name, String attribute :age, Fixnum
But should not be designed to accept only that explicit file, the attributes might differ in number end type.
Which in the end will generate a class that should look something like:
class Person def initialize(name, age) @name_out = name @age_out = age end end
Help?
-
BSG over 12 yearsSorry, i'm really out of my depth here. Could you please try to explain it as if i was three years old..? :p
-
Daniel Pittman over 12 yearsI am not sure this gets that much easier. The second case is probably easier to understand:
eval
takes a string, and evaluates it at the time of the call as if it is Ruby code. So, you create a string with the source code for your dynamic class at runtime, then evaluate it. The first case creates a new class object, does things to it to create the desired methods, etc, and then gives it a name - which in Ruby is the same as assigning the class object to a constant. -
Jorge de los Santos almost 10 yearsI'm getting and error while declaring the klass, the name should be a constant, so I capitalized klass to Klass in 2, 3 lines and now it's working.