Using application-wide variables in Ruby on Rails

Rails offers various ways to implement methods, but implementing global variables is not as straightforward. Currenly, there are several approaches.

Constants

One Solution on Stackoverflow is to put them in config/application.rb as follow:

module MyAppName
  class Application < Rails::Application
    MY_CONSTANT = "test"
  end
end

It is fine, but that breaks the entity of the file. Since custom global variables meaning custom business logic, it would be better to put them into a separate file.

Global Variables

Global variables are defined with the dollar sign `$`. It can be as easy as:

$my_variable = 4

However, global variables are usually considered an anti-pattern. Mainly because it has no context, and it is mutable. Mutable global variables in a multi-threading application would create race conditions. Variables should be scoped properly, and using global variables means that we want a specific variable to be shared in the application.

Some alternative approaches:

Constants

If the variable is immutable, use constants. It provides the context that this variable is immutable.

class Calculator
  DEFAULT_MULTIPLIER = 5
end

Class or instance variable

Another alternative is to use class or instance variable to store such variables:

class Calculator
  @@default_multiplier = 5

  def self.default_multiplier
    @@default_multiplier
  end

  # optional
  def self.set_default_multiplier(n = 5)
    @@default_multiplier = n
  end
end

It provides the benefit of having a clear namespace (and context). Also, it adds an extra layer of flexibility to modify the variable. It also allows a lock mechanism if this variable is mutable in the application.

Gem figaro & simpleconfig

Another method is to depend on gems. Both Simpleconfig and Figaro offers a good way to configure variables in a different logic. I didn't use simpleconfig before, but I did use Figaro, which is quite handy.

Basically, Figaro creates an config/application.yml file and you store variables for different environments like:

development:
  key: "ad69caf9a44dcac1fb28"
  secret: "83ca7aa160fedaf3b350"

test:
  key: "ad69caf9a44dcac1fb28"
  secret: "83ca7aa160fedaf3b350"

And retrieve the variables like:

Env['key']

It somehow simulates the way we manage variables in Bash or Zsh, which I don't think is neat and straightforward.

Using initializers

Rails loads all files in config/initializers/ folder, which is a good place to init application-wide variables here. For instance, we can create a file business.rb under the initializers directory:

module Business
  class Logic
    Here = "hello world"
  end
end

Business::Logic::Here
# => 'hello world'

Filename and module name can be anything. I suggest use something directly related to the business logic as the module name, like your company name or what. It offers better understanding when reading the code.

Thus you can use it anywhere, in controllers, for example:

class ApplicationController < ActionController::Base
  # Prevent CSRF attacks by raising an exception.
  # For APIs, you may want to use :null_session instead.
  protect_from_forgery with: :exception

  def set_logic
    @logic ||= Business::Logic::Here
  end
end

or in any file in the project.

One bad thing is that we have to restart server at any change in this file since it is only 'initialized' at server start.

However, using initializers ensure the scope of different variables are in the same class, and we can create different files, modules, and classes for variables in different scopes. I believe this is the best way to manage application-wide variables so far.

Read more