Rails way of defining singleton class

While surveying ActiveSupport::CurrentAttributes code I found that rails way of defining singleton class is a little odd or unusual at least.

Rails way:

class RailsWay
  attr_reader :attributes

  def initialize
    @attributes = {}
  end

  class << self
    def instance
      @instance ||= new
    end

    def attribute(*names)
      generated_attribute_methods.module_eval do
        names.each do |name|
          self.define_method(name) do
            attributes[name.to_sym]
          end

          self.define_method("#{name}=") do |value|
            attributes[name.to_sym] = value
          end
        end
      end

      names.each do |name|
        define_singleton_method(name) do
          instance.public_send(name)
        end

        define_singleton_method("#{name}=") do |attribute|
          instance.public_send("#{name}=", attribute)
        end
      end
    end

    private

    def generated_attribute_methods
      @generated_attribute_methods ||= Module.new.tap { |mod| include mod }
    end
  end
end

class Klass < RailsWay
  attribute :a, :b
end

Klass.a = "RailsWay"

I am most curious about why use generated_attribute_methods and define instance methods in Module and then include it at runtime instead of defining them directly in class directly.

Simple/My way:

If we don’t introduce the additional module we can implement it by following:

NOTE: It is based on my current understanding of the code mentioned above, I am not 100% sure if it mirrors the behavior of the above code snippet. Please do educate me, if I am missing anything!

class MyWay
  attr_reader :attributes

  def initialize
    @attributes = {}
  end

  class << self
    def instance
      @instance ||= new
    end

    def attribute(*names)
      names.each do |name|
        self.define_method(name) do
          attributes[name.to_sym]
        end

        self.define_method("#{name}=") do |value|
          attributes[name.to_sym] = value
        end
      end

      names.each do |name|
        define_singleton_method(name) do
          instance.public_send(name)
        end

        define_singleton_method("#{name}=") do |attribute|
          instance.public_send("#{name}=", attribute)
        end
      end
    end
  end
end

class Klass < MyWay
  attribute :a, :b
end

Klass.a = "myway"

I know I am missing something here and please do educate us on why the current design was opted! Thank you.

P.S: Rails is an amazing framework!! and thank you rails-core for maintaining it and making web development awesome!

The separate-module-included-at-runtime approach is necessary so applications can override attribute methods and still call super to get framework behavior. It’s used in multiple places in Rails besides ActiveSupport::CurrentAttributes, e.g. for Active Record attribute and association methods.

irb(main):001:1* class Foo
irb(main):002:2*   include Module.new {
irb(main):003:3*     def bar
irb(main):004:3*       "bar"
irb(main):005:2*     end
irb(main):006:1*   }
irb(main):007:1*   
irb(main):008:2*   def bar
irb(main):009:2*     "foo" + super
irb(main):010:1*   end
irb(main):011:0> end
=> :bar
irb(main):012:0> Foo.new.bar
=> "foobar"
2 Likes

Thank you for your answer!! It clears it up for me.