Use Global or Constant Variable in Ruby/Rails

How do you use global variables or constant values in Ruby?

One thing you need to realize is in Ruby everything is an object. Given that, if you don't define your methods within Module or Class, Ruby will put it within the Object class. So, your code will be local to the Object scope.

A typical approach on Object Oriented Programming is encapsulate all logic within a class:

class Point
attr_accessor :x, :y

# If we don't specify coordinates, we start at 0.
def initialize(x = 0, y = 0)
# Notice that `@` indicates instance variables.
@x = x
@y = y
end

# Here we override the `+' operator.
def +(point)
Point.new(self.x + point.x, self.y + point.y)
end

# Here we draw the point.
def draw(offset = nil)
if offset.nil?
new_point = self
else
new_point = self + offset
end
new_point.draw_absolute
end

def draw_absolute
puts "x: #{self.x}, y: #{self.y}"
end
end

first_point = Point.new(100, 200)
second_point = Point.new(3, 4)

second_point.draw(first_point)

Hope this clarifies a bit.

Ruby on Rails: Where to define global constants?

If your model is really "responsible" for the constants you should stick them there. You can create class methods to access them without creating a new object instance:

class Card < ActiveRecord::Base
def self.colours
['white', 'blue']
end
end

# accessible like this
Card.colours

Alternatively, you can create class variables and an accessor. This is however discouraged as class variables might act kind of surprising with inheritance and in multi-thread environments.

class Card < ActiveRecord::Base
@@colours = ['white', 'blue'].freeze
cattr_reader :colours
end

# accessible the same as above
Card.colours

The two options above allow you to change the returned array on each invocation of the accessor method if required. If you have true a truly unchangeable constant, you can also define it on the model class:

class Card < ActiveRecord::Base
COLOURS = ['white', 'blue'].freeze
end

# accessible as
Card::COLOURS

You could also create global constants which are accessible from everywhere in an initializer like in the following example. This is probably the best place, if your colours are really global and used in more than one model context.

# put this into config/initializers/my_constants.rb
COLOURS = ['white', 'blue'].freeze

# accessible as a top-level constant this time
COLOURS

Note: when we define constants above, often we want to freeze the array. That prevents other code from later (inadvertently) modifying the array by e.g. adding a new element. Once an object is frozen, it can't be changed anymore.

Difference between constant variables and global variables

Global variables are global, meaning that even if you put them in a class which is very specifically scoped, they're still available everywhere. They are also explicitly variables (meaning one should not be surprised if their value changes).

For example:

module TopLevel
module MiddleLevel
module LowLevel
class SpecificSomething

$my_global = "duff man says a lot of things"

end
end
end
end

module TopLevel
def self.global
p $my_global
end
end

TopLevel.global
#=> "duff man says a lot of things"

Constants are accessible where they are defined - that is, they are NOT global. They are also constants (as the link you provided points out), so one would NOT expect them to change (although ruby does allow them to be changed).

Why do Global Variables exist in Ruby?

Global variables are not bad. They're not evil. They're just incredibly, incredibly powerful. Which is why you shouldn't use them.

Global variables are global- they can be accessed and modified anywhere in the code. A single global variable has the potential to affect all of your classes, all of your functions, all of the classes and functions of every single library or dependency you load into your project, and all of the classes and functions of every single project which loads your project as a dependency, as well as the projects that load those projects, and so and and so forth, for ever and always, for the rest of time.

The second people start feeling comfortable using global variables, the namespace gets insanely cluttered and we get conflicts left and right and the stability of the programming language itself is threatened. Which is why the use of global variables is emphatically and repeatedly discouraged.

But global variables are not bad. They're like the highway lanes labeled "for emergency vehicles only," or like those fire-axes behind glass labeled "break glass in case of emergency."

It's entirely possible that at some point, in the distant future, you will have an incredibly unusual situation which merits the use of a single global variable. But that day is not today. And it is probably not tomorrow, or a month from now, or a year from now. Daily life, daily code- it just doesn't call for the unbridled power of a global variable.


$stdout is a great example of why global variables are sometimes important. $stdout is the default stream in ruby- the one where things will print if no other stream is specified. $stdout should be accessible from every class and every function in every library because it acts like a giant funnel, shoveling all output to a single location. The whole world knows and agrees that $stdout exists in ruby, and its uses are well-documented, so its power is well-managed.

This isn't to be confused with STDOUT which is a constant representing the actual pipe which sets up a stream between ruby and its parent program (usually a terminal). $stdout = STDOUT by default, but $stdout can be changed to anything. If you want your program to print to a file, you can change $stdout to a file stream.

I don't think this name choice is confusing for a seasoned rubyist. A variable is designed to be modified and a constant is designed to be constant. The difference between $stdout and STDOUT is that the former can be modified to change the standard output location of your program, and the latter is a constant, always pointing to the stdout stream. The capitalization makes a world of difference and conveys very different meanings.


As for why global constants are uninitialized and global variables are nil, that actually has nothing to do with globals. Ruby automatically initializes all variables as nil. You can easily see this with instance variables such as @foo or @@foo. In almost every situation, an undefined local variable will throw a NameError because ruby cannot tell whether it is a variable or a method. But in strange situations, they too are initialized as nil:

puts foo # => NameError: undefined local variable or method 'foo'
foo = 42 if false
puts foo # => nil

puts bar # => NameError
bar = bar
puts bar # => nil

It was a conscious design choice in Ruby not to automatically initialize constants. Because a constant is, by definition, something which is initialized once and then never changed, it would break the definition for a constant to be nil at first and then a different value later in the code.


I should also mention that global constants are considered acceptable, even among people who tout global variables as bad. The difference is that constants can only be assigned once and generally throw a warning or error if they get assigned again. This protects programmers from situations where conflicting global constants might cause problems.

Constant Global object on a Ruby on Rails server

Put this in an initializer.

And to respect Ruby's convention, capitalize the whole name.

I can't see any performance issue regarding this method.

In Ruby on Rails, how to make global constants autoreloadable in development mode, without server restart?

Initializers are designed to fire only once on the initialization of the app. If you want something reloadable, put it in a separate module inside lib folder. Here is a full topic about Autoloading and Reloading Constants:

  module Colors
RED = '0xff0000'
end

Note, you will also have to adjust your application.rb with

 config.autoload_paths << "#{Rails.root}/lib"

So, for your case it would be a file constants.rb inside lib:

module Constants
KB = 1000
end

Then in your class you can use your constant:

class Something
include Constants
MB = KB * KB
end

Here MB will change if you change the value of KB on page reload, and it will not require a restart.

why does this variable in constants.rb need to be declared to be global?

First make sure your constants.rb file is in /config/initializers/. Second, try using all caps for hebrew_constants. From the comments in this SO question it sounds like the all caps is necessary.



Related Topics



Leave a reply



Submit