Metaprogramming Rails Helper Module for Accessible Attributes
Tags: ruby, attributes, getters, setters
Published: 2021-02-08 00:57:28

---
title: Metaprogramming Rails Helper Module for Accessible Attributes
published: true
date: 2020-04-16 05:00:00 UTC
tags: ruby,attributes,getters,setters
canonical_url: https://jmoore53.com/ruby/attributes/getters/setters/object/dynamic%20programming%20languages/2020/04/16/ruby-metaprogramming-modules.html
---

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

```

## 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_url`returning the value stored in the environment, in the secrets, or in the database.

## Links

- [SO Link to Creating Instance Methods Dynamically](https://stackoverflow.com/a/15025036)