I'm just getting started with my first Ruby on Rails webapp. I've got a bunch of different models, views, controllers, and so on.
I'm wanting to find a good place to stick definitions of truly global constants, that apply across my whole app. In particular, they apply both in the logic of my models, and in the decisions taken in my views. I cannot find any DRY place to put these definitions where they're available both to all my models and also in all my views.
To take a specific example, I want a constant COLOURS = ['white', 'blue', 'black', 'red', 'green']
. This is used all over the place, in both models and views. Where can I define it in just one place so that it's accessible?
What I've tried:
@@COLOURS = [...]
. But I couldn't find a sane way to define it so that I can write in my views Card.COLOURS
rather than something kludgy like Card.first.COLOURS
.def colours ['white',...] end
- same problem.Is there just no way to define anything to be accessible both from models and from views? I mean, I know models and views should be separate, but surely in some domains there'll be times they need to refer to the same domain-specific knowledge?
A constant which is needed in more than one functions can be declared a global constant by declaring it a constant using the reserve word const, initializing it and placing it outside of the body of all the functions, including the main function.
Although constants look like local variables with capital letters, they have the visibility of global variables: they can be used anywhere in a Ruby program without regard to scope.
How to Define Constants. A constant doesn't require any special symbol or syntax to declare. You just need to make the first letter an uppercase letter.
Global Variable has global scope and accessible from anywhere in the program. Assigning to global variables from any point in the program has global implications. Global variable are always prefixed with a dollar sign ($).
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.
Some options:
Using a constant:
class Card
COLOURS = ['white', 'blue', 'black', 'red', 'green', 'yellow'].freeze
end
Lazy loaded using class instance variable:
class Card
def self.colours
@colours ||= ['white', 'blue', 'black', 'red', 'green', 'yellow'].freeze
end
end
If it is a truly global constant (avoid global constants of this nature, though), you could also consider putting
a top-level constant in config/initializers/my_constants.rb
for example.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With