Ruby add method to class

Suppose I have a class:

class Foo end 

To add a method to this class, I know 2 options:

  • Reopening the class and implementing the method:

     class Foo def bar end end 
  • using class_eval to implement the method:

     Foo.class_eval { def bar; end} 

What is the difference? Which one is better?

+7
methods ruby class
source share
2 answers

In fact, there are several other ways to add new methods to a class. For example, you can also define methods in a module and mix the module into a source class.

 module ExtraMethods def bar end end Foo.class_eval { include ExtraMethods } class Foo include ExtraMethods end 

There is nothing better or worse. The two (or three) methods you mentioned have a different behavior, and you can use one or another option depending on your needs (or preferences). In most cases, this is subjective. In other cases, it really depends on how your code is structured.

The main object difference between re-opening the vs class using class_eval is that the first one is also a class definition, while the second requires that the original class is already defined.

In practice, reopening a class in some cases can cause some unexpected side effects. Suppose you define Foo in the lib/foo.rb file using many methods. Then you open Foo again in config/initializers/extra.rb and add the bar method.

In myclass.rb you use Foo , but instead of requiring lib/foo.rb manually, you rely on the autoload function.

If extra.rb loads before lib/foo.rb , it may happen that the Foo class is already defined in your environment and your code will not load lib/foo.rb You will have a Foo class containing only the bar extension that you defined, and not the original Foo .

In other words, if for some reason you open the class again to add some methods without making sure that the first definition of the origina class is loaded first (or after), your code may break if it relies on autoload.

Conversely, Foo.class_eval calls a method on Foo , so it expects that the original definition of Foo already present when trying to add new methods. This ensures that when you add new methods, the Foo class will already be defined.

In conclusion, the main difference is that reopening a class allows you (better or worse) to add methods to a class that has not yet been loaded, while class_eval requires that the class be already defined.

In the general case, if I do not define subclasses by name or re-open classes, I have full control, I prefer the second approach, since it saves the code more maintainable in large code files. In fact, I usually use mixins if I extend third-party classes so that I can keep a whole chain of method ancestors if I need to override existing methods.

+15
source share

The second approach is very convenient when you need some kind of dynamic thing. Ruby actually has several areas:

 # scope one, opened with `class` keyword class ... # scope two, opened with `def` keyword def ... end end 

With class_eval you can exchange areas.

 >> foo = 1 => 1 >> class Foo >> puts foo >> def bar >> puts foo >> end >> end NameError: undefined local variable or method 'foo' for Foo:Class from (irb):3:in <class:Foo> from (irb):2 >> Foo => Foo >> Foo.class_eval { ?> puts foo >> define_method :bar do >> puts foo >> end >> } 1 => :bar >> Foo.new.bar 1 
+5
source share

All Articles