DEV Community

Jack Moore
Jack Moore

Posted on • Originally published at jmoore53.com on

Metaprogramming Rails Helper Module for Accessible Attributes

Dynamically creating instance methods:

module RundeckHelper

    def self.attr_value(*args)
        # Singleton Very Important
        singleton_class = class << self; self; end
        args.each do |key|
            name = key
            key = key.to_s.upcase
            val = nil
            if !ENV[key].nil?
                if ENV[key] != ""
                    val = ENV[key]
                else
                    val = nil
                end
            elsif !Rails.application.credentials.rundeck.nil?
                key2 = key.downcase
                if !Rails.application.credentials.rundeck[key2].nil?
                    val = Rails.application.credentials.rundeck[key2]
                else
                    val = nil
                end
            elsif !RundeckConfigurationOption.find_by(name: key).nil?
                val = RundeckConfigurationOption.find_by(name: key).value
            else
                val = nil
            end
            # Send the method to the instance
            singleton_class.send(:define_method, name) do
                return val
            end
        end
    end

    attr_value :base_url, :create_job_id, :project_id, :create_environment_id, :create_instance_id
end

Enter fullscreen mode Exit fullscreen mode

What is this code and how does it work?

Going line by line, this code exists in a Rails module. This is why I believe this was difficult. Creating custom attr_’s had to be hacked as everything in this module is called as an instance method. The module isn’t instantiated like a class would be. This module in particular has all its methods called as instance methods. The module basically provides helpers to the classes that require this module.

Breaking down the key lines:

  • def self.attr_value(*args)
  • singleton_class = class << self; self; end
  • singleton_class.send(:define_method, name) do
  • attr_value :base_url, :create_job_id, :project_id, :create_environment_id, :create_instance_id

These three lines are crucial to creating custom attributes for the variables passed into the attr_value function.

Starting from the top: def self.attr_value(*args)

  • This line defines the method and the method accepts multiple arguments.
  • It is an instance method meaning it can be called from within the module

Moving on: singleton_class = class << self; self; end

  • We define a singleton_class inside this method to accept from the current object which is the module

Sending Methods: singleton_class.send(:define_method, name) do

  • We take the singleton_class we created that is attached to the module and send it the method
  • the name variable, as seen above, is just the name of the symbol that was passed

Calling the Attribute: attr_value :base_url, :create_job_id, :project_id, :create_environment_id, :create_instance_id

  • basically just instantiates the values we need and allows us to use the methods RundeckHelper.base_url and self.base_urlreturning the value stored in the environment, in the secrets, or in the database.

Links

Image of Timescale

Timescale – the developer's data platform for modern apps, built on PostgreSQL

Timescale Cloud is PostgreSQL optimized for speed, scale, and performance. Over 3 million IoT, AI, crypto, and dev tool apps are powered by Timescale. Try it free today! No credit card required.

Try free

Top comments (0)

Warp.dev image

Warp is the highest-rated coding agent—proven by benchmarks.

Warp outperforms every other coding agent on the market, and gives you full control over which model you use. Get started now for free, or upgrade and unlock 2.5x AI credits on Warp's paid plans.

Download Warp

👋 Kindness is contagious

Discover fresh viewpoints in this insightful post, supported by our vibrant DEV Community. Every developer’s experience matters—add your thoughts and help us grow together.

A simple “thank you” can uplift the author and spark new discussions—leave yours below!

On DEV, knowledge-sharing connects us and drives innovation. Found this useful? A quick note of appreciation makes a real impact.

Okay