Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pass a Class or stringified class name?

Is it better to pass a class or a stringified class name around in Ruby?

For example, I have a parser/builder that can determine the type of class of a parsed record. I pass this to an importer class, but I can pass the name of the class as a string or as its constant. Is there a reason not to do one or the other?

x = ParsedRecord.new(value: 1, type: MyClass)

vs

x = ParsedRecord.new(value: 2, type: 'MyClass')

And the importer would either:

x.type.new

vs

x.type.constantize.new

Passing the class seems easier but I don't see it around a lot (ie. Rails makes you stringify the class names for associations). Perhaps it has to do with load order/autoloading?

like image 597
Greg Olsen Avatar asked Feb 04 '26 15:02

Greg Olsen


2 Answers

Unless you need to lazy-load the class name, it's better to pass the constant directly.

The reason is because converting a String into the corresponding class-name constant is an extra-step and will add some overhead to the execution. If you already have the class name, just pass it. There is no advantage of converting it into a string, and then back into a constant.

The reason because you see strings passed around in other libraries, is because in order to pass MyClass as parameter the class definition must be loaded. In some cases, for example in Rails, you are referencing a class name of a class that will be lazy-loaded (that's the typical case of the rescue_from macro). Using a String doesn't force you to load the class definition until you effectively need it.

like image 159
Simone Carletti Avatar answered Feb 06 '26 05:02

Simone Carletti


A class is an object just like any other. So, this is really not about classes.

Just ask yourself: would you rather pass an object as an argument, or a String containing the name of some variable in some scope somewhere, which you then metaprogrammatically turn into an object again?

Oh, and what about classes that have no name? Or whose name does not correspond to the variable through which they are reachable?

a = Class.new
a.name
# => nil
# has no name

A = a
a.name
# => 'A'
# has name A, is reachable via A and a

A = nil
a.name
# => 'A'
# still has name A, but is no longer reachable via A, only a

B = a
a = nil
B.name
# => 'A'
# still has name A, but is now only reachable via B

Passing the class itself also allows you to just create an anonymous class inline, e.g. as a stub in unit testing:

ParsedRecord.new(value: 1, type: Class.new)
ParsedRecord.new(value: 2, type: Class.new do def initialize; @foo = 42 end end)
like image 35
Jörg W Mittag Avatar answered Feb 06 '26 05:02

Jörg W Mittag