Imagine that you are implementing some form object because you are fed up with treating ActiveRecord models as such, and you need some extra flexibility. You start with a straightforward implementation for a base class of a form object where you can just whitelist attributes. That could look like this:

class FormObject def self . attributes_registry @attributes_registry ||= [] end def self . attribute ( attribute_name ) attributes_registry << attribute_name define_method ( attribute_name ) do instance_variable_get ( "@ #{ attribute_name } " ) end define_method ( " #{ attribute_name } =" ) do | value | instance_variable_set ( "@ #{ attribute_name } " , value ) end end end

Since the base class is ready, you can create a first form object that would inherit from this class:

class MyForm < FormObject attribute :some_attribute end

Initially, it does the job, but then it turns out that you might need a default value if some_attribute turns out to be nil. So you try something like that:

class MyFormWithDefaultValue < FormObject attribute :some_attribute def some_attribute super || "Default" end end

After checking if the default value works, this is what you get:

> MyFormWithDefaultValue.new.some_attribute => NoMethodError: super: no superclass method `some_attribute' for #<MyFormWithDefaultValue:0x007f84a50ae8e0>

Whoops! How did it happen? The method was defined in the superclass so it should be inheritable, right?

Well, this is not really true. However, the problem is easy to fix.

Anatomy Of The Problem

The primary question we should answer in the first place is: where are all those new methods defined using define_method in that particular way? Is it a superclass?

FormObject.instance_methods - Object.methods => []

It’s definitely not a superclass - there are no any instance methods defined there, there are only the ones inherited from Object . What about MyFormWithDefaultValue ?

> MyFormWithDefaultValue.instance_methods - Object.methods => [:some_attribute, :some_attribute=]

Now the error that we initially got makes way more sense. The entire issue is caused by the fact that the declaration of the attribute happens in MyFormWithDefaultValue , if it were defined in a base class, there would be no any issue. We can verify it with a simple example:

class MyForm < FormObject attribute :some_attribute end class MyFormWithDefaultValue < MyForm def some_attribute super || "Default" end end

> MyFormWithDefaultValueA.new.some_attribute => "Default"

Solution

Now that we fully understand the problem let’s think about the solution. Ideally, about the one, that doesn’t require defining explicitly an intermediate class that we can inherit from.

How about defining a module instead? Modules are also included in the inheritance chain, and for using super , it doesn’t matter if the method is defined in the class or a module.

The exact solution to the problem would be wrapping the definition of new methods that happens in FormObject inside some module, it could be even an anonymous one, and including it right away:

class FormObject def self . attributes_registry @attributes_registry ||= [] end def self . attribute ( attribute_name ) attributes_registry << attribute_name wrapper = Module . new do define_method ( attribute_name ) do instance_variable_get ( "@ #{ attribute_name } " ) end define_method ( " #{ attribute_name } =" ) do | value | instance_variable_set ( "@ #{ attribute_name } " , value ) end end include wrapper end end

Let’s verify if the new solution works:

> MyFormWithDefaultValue.new.some_attribute => "Default"

Yay! It does exactly what we wanted to achieve.

Wrapping Up

Metaprogramming in Ruby is a powerful tool, however; it can lead to some issues that might not be obvious why they happen. Fortunately, with enough knowledge of the language, those problems can be solved elegantly.