Rails - Global Variables to be used anywhere, including controller, views, and models - ruby-on-rails

My question is seemingly simple but has been pretty elusive. I want one value that can be called anywhere in the application - it's a minimum value we are using for validations in the model, we want to inform the user of that value, etc.
So far, I have done the following:
In config/initializers, I created a new .rb file (under suggestion I found on another thread):
minimum = 15
and only got that it couldn't be found after starting rails server.
I found a strange suggestion to put the value in en.yml as minimum: 15, same issue as above.
Obviously, application helper and controller won't help as I need the value in my models. I did however find one solution, which was to put it in the class Application in config/application.rb:
config.after_initialize do
::Minimum = 15
end
and afterwards call it like this:
MyApp::Application::Minimum
Obviously, this solution, while it does work, isn't ideal. If I could find a way to alias MyApp::Application::Minimum as just "minimum", I'll be completely satisfied, but getting it done in the initializers is, from what I've heard, a better solution.

You can stick something in a model if you want and call it from anywhere:
Couriers = %w[TNT UPS RoyalMail Bike Hand Pigeon Other]
But I prefer putting them in a yaml file.
Create a config.yml file in your configs directory and put your vars in there:
defaults: &defaults
node_1: '10.8'
node_2: '10.9'
node_3: '10.0'
whatever: 1
thereis: 2
development:
<<: *defaults
playground:
<<: *defaults
production:
<<: *defaults
poststaging:
<<: *defaults
test:
<<: *defaults
Load the config file using something like this in your initializers directory. Create a file in /config/initializers called 'load_vars.rb' and put the following in there:
SETTINGS = YAML.load_file("#{Rails.root}/config/config.yml")[Rails.env]
You'll have to restart your server after changing these though.
-- EDIT --
To use these variables, you can load with something like this from your model, controller, views:
SETTINGS['var_1']

Related

Does Rails 6 finally provide a buit-in settings.yml feature like the old settingslogic gem

What (if any) method is the built-in "Rails 6 way" to including an environment-aware, ERB-aware, non-encypted method for storing app settings in the code? E.g., something just like the newer encrypted credentials, but without the obfuscating encryption.
For example, so you can have a file something like this:
# config/application.yml
defaults: &defaults
cool:
saweet: nested settings
neat_setting: 24
awesome_setting: <%= "Did you know 5 + 5 = #{5 + 5}?" %>
development:
<<: *defaults
neat_setting: 800
test:
<<: *defaults
production:
<<: *defaults
and fetch a (environment specific) setting something like this:
Settings.neat_setting
I've used the settingslogic gem for a decade, in many projects, but it isn't maintained so I hate to base a brand new Rails 6 app on it (even if it works ok today).
I find a lot of posts re Rails 3,4,5 but so far nothing new for Rails 6... and some of those same manual methods presumably work (make yml, make rb to load yml, figure out how to namespace the settings, etc) ... but surely by now Rails has some out-of-the-box (no 3rd party gem required) support for a simple app-specific .yml file?

Rails 4.2 constants - where to put them: application.yml vs custom config file.rb

I have some constants I am using throughout my app.
I'd say there are three types:
some are really passwords to external stuff like sendgrid login/password
some are internal "secret codes" like devise secret key
some are just what I'd call global constants that is to say there are constants that we use a lot throughout the app and don't want anybody to easily change this. They nearly never change. Some examples: the ID of our facebook page, or the number of deals "per page" we define in our infinite scrolling on the homepage (critical for UX), or the url of our brand-validated logo on aws cdn(present in many view files html.erb and assets javascript .js files). One aspect I also have in mind when asking my question, is that even if they seem less critical than a devise key, for those constants, I would like to gather, to make sure very few people and only authorized developers can change those critical data.
Here's what we do today:
For 1 and 2: I put the constants on config/application.yml
And for 3 : I put the constants on a custom .rb config file as described by official Rails guide (guides.rubyonrails.org/v4.2/configuring.html#custom-configuration)
For example: on /config/initializers/app_custom.rb
Rails.application.configure do
config.x.infite_scrollhp_feed_per_page_deals = 30
end
then it is called in a controller this way:
nb_per_page = Rails.configuration.x.infite_scrollhp_feed_per_page_deals
But each time I am unsure if I should put global constants inside config/application.yml or in my custom config .rb file?
What is the recommended "Rails -way"? How to decide where to put them ? Is there a generally accepted "good-sense"/"proven to be efficient/well-structuring" practice/rule ?
Also is there a different in performance that is to say are application.yml "injected" faster into view (.html) and assets(.js) files when user load a page than if they are defined on my custom config file ?
I would advise using config/secrets.yml for sensitive data (1 and 2):
development:
some_password: ...
test:
some_password: ...
production:
some_password: <%= ENV["SOME_PASSWORD"] %>
This way you can put different settings for different environments (for development, test and production).
And I would highly recommend keeping the production sensitive data in ENV variables.
When it goes for non sensitive data (3) I would put in config/environments/*.rb files when different settings are needed for different environments.
If that's not the case and the setting is the same for all environments, I would put it into application.rb, application.yml or in a custom file in initializers/* or even as class constants.

Setting variable depending upon RAILS_ENV

This is my current controller : 'trace_controller.rb'
rule_oms = Rule.new("localhost","root","","oms_local")
rule_warehouse=Rule.new("localhost","root","","warehouse_local")
rule_payment=Rule.new("localhost","root","","payment_local")
...
....
We have 2 different modes - Staging and Production. They have Hostname, Pwd, User, Database name which are unique.
How can I change these settings from environment.rb? Can you set variables depending upon them?
Depending upon environment, I get the hostname, pwd, user, db_name for all the different databases. Unlike most Rails app, I connect to several databases irrespective of the environment.
Any ideas what I should be doing? (Using latest version of Rails).
in config folder --> environments --> add another file with your environment name
by default, development.rb, , test.rb and production.rb are present.
add lets say qa_1.rb for your qa_1 environment.
Set your required config in this file, you can copy it from any of the existing environment files and change them as needed.
run your rails app with RAILS_ENV=qa_1
it will take the config from qa_1.rb file
you can set probably settings_logic gem, to set envirornment wise values
gem 'settingslogic'
Then in app/models/settings.rb add
class Settings < Settingslogic
source "#{Rails.root}/config/application.yml"
namespace Rails.env
end
and in /config/application.yml
set you environment specific data
defaults: &defaults
db: default_db
development:
user: dev_user
test:
user: test_user
production:
user: prod_user
db: prod_db
qa_1:
user: qa_1_user
db: qa_1_db
in database.yml also you can use
qa_1:
db: qa_db
user: user
I'm not sure what you're trying to achieve but if you place this in environment.rb, then I can only guess that you want some sort of global constant. If this is what you want, I suggest you create a file inside config/initializers called constants.rb then place the following there.
RULE_OMS = Rule.new("oms-#{Rails.env}")
RULE_WAREHOUSE = Rule.new("warehouse_#{Rails.env}")
RULE_PAYMENT = Rule.new("payment_#{Rails.env}")
then just call RULE_OMS anywhere in your app.
This is what worked for me -:
1) Creation of local.rb in config/environments (Simply a copy of development)
2) Defining the parameters for local in database.yml
3) Setting parameters in config/environment.rb
if Rails.env.local?
OMS_HOST="localhost"
OMS_DB="oms_local"
OMS_USER="root"
OMS_PWD=""
WAREHOUSE_HOST="localhost"
WAREHOUSE_DB="warehouse_local"
WAREHOUSE_USER="root"
WAREHOUSE_PWD=""
PAYMENT_HOST="localhost"
PAYMENT_DB="payment_local"
PAYMENT_USER="root"
PAYMENT_PWD=""
end
if Rails.env.development?
OMS_HOST="amt.com"
OMS_DB="oms_staging"
OMS_USER="user1"
OMS_PWD="xyz"
....
.....
4) In the trace_controller.rb, I used these constants to initialize my Rule model.
5) Add this in the .gitignore file, if you are using it.
6) Don't forget to restart the server.
Apologize for the way my question was framed as it was pretty unclear. Hope this answer will help somebody in the future.

Rails global state variable

I need to keep some kind of state in my Rails project.
Some kind of global variable or something like that, which has an initial value at the server startup, and could be accessed and modified everywhere in the project. How to do that? What is the best way to achieve it?
Create a file in config/initializers folder and create your global variable there.
# config/initializers/my_var.rb
$my_global_var = 'hello'
But usually there are "cleaner" ways to do this. Global variables are not thread-safe and this is kinda hot topic these days.
I think that the best solution here could be settingslogic gem . You define settings in yaml file and then you can access them anywhere in the code. Read and set them. At the same it enables you to have more than just one 'global variable'. In most of the projects I've worked with, settings weren't changed during the runtime, but if you need that behaviour, you can just do it.
# config/application.yml
defaults: &defaults
my_global_variable: 100
development:
<<: *defaults
production:
<<: *defaults
#And then anywhere in the application you can do:
Settings['my_global_variable'] = 200

Accessing config from application.rb in Controller (Rails 3)

I'm trying to add two extra config options to my application.rb so I can read them out in controllers.
# Extra
config.twitter.key = 'foo'
config.twitter.secret = 'bar'
I am trying to access them using three suggested methods:
self.config.twitter.key # Should be extended through ApplicationController Base
config.twitter.key # Inherited but with different syntax
CONFIG['twitter']['key'] #some massive magical array that apparently exists somewhere
They all give me different kinds of error when I pass them through the "debug" method, E.g:
debug self.config.twitter.key # undefined method `key' for nil:NilClass
So, whats going on?
I believe you've got a slightly incorrect idea behind what your expectations for the config/application.rb is providing you. The ActiveRecord::Base and ActiveController::Base eigenclasses use the Rails::Application::Configuration class that is configured in config/application.rb. The attributes aren't available in classes that descend from either of the Base classes, nor their eigenclasses. This is why you are running into errors in ApplicationController.
There are generally two ways to make configuration initializations in a Rails app. The first way is to create a configuration module and then load values into it via initializer:
First, create a Twiter Config module:
#lib/twitter_config.rb
module TwitterConfig
def self.config
##config ||= {}
end
def self.config=(hash)
##config = hash
end
end
Create a YAML config file:
# config/twitter.yaml
development: &base
key: "foo"
secret: "bar"
test:
<<: *base
key: "foo2"
production:
<<: *base
secret: "barbar"
Alternatively, if you don't intend to add config/twitter.yaml to your SCM, you can just skip this and set the key and secret via environment variables. This would be the suggested solution for an application with a public SCM repository deploying on Heroku.
Then load and set the value via an initializer:
#config/initializers/01_twitter.rb
require 'twitter_config'
TwitterConfig.config = YAML.load_file("config/config.yml")[Rails.env].symbolize_keys
It's generally a best practice to number your initializer files as Rails will load them in order according to their filename. If you are initializing a datastore and that is critical for other steps, then it needs the lowest number. Alternatively, if you are using environment variables, this would be the init file:
#config/initializers/01_twitter.rb
require 'twitter_config'
TwitterConfig.config[:key] = ENV['twitter_config_key']
TwitterConfig.config[:secret] = ENV['twitter_config_secret']
Throughout the Rails application, you now have access to the config values with TwitterConfig.config[:key] & TwitterConfig.config[:secret]. You can include the module as well, just watch out for conflicts.
You can also just load the values as a global constant. It feels a bit ugly to me though:
#config/application.rb
TWITTER_CONFIG = YAML.load_file("config/twitter.yaml")[Rails.env]
I've tried this and seems to be working, you can use ::Rails.application.config.
For example I'm using it to get the correct time_zone set in the application like this:
Rails.application.config.time_zone
I found it thanks to the less-rails code: https://github.com/metaskills/less-rails/blob/master/lib/less/rails/helpers.rb
So you can declare this in your application.rb or in any enviroment file:
config.twitter_key = 'foo'
And then read it like so anywhere in your code:
Rails.application.config.twitter_key
You might want to consider using a yaml file approach.
In application.rb
CONFIG = YAML.load_file("config/config.yml")[Rails.env]
In config/config.yml
development: &base_config
twitter_key = "foo"
twitter_secret = "bar"
test:
<<: *base_config
twitter_key = "foo2"
production:
<<: *base_config
twitter_secret = "barbar"
Same usage as before with definable attributes on an environment level with overloading.
CONFIG['twitter_key']
A small update to the widely accepted answer here : Accessing config from application.rb in Controller (Rails 3)
The methods inside the module TwitterConfig should be class methods (or module methods if you prefer it that way). They can't be instance methods.
Sorry to put this in an answer, but I could not find a way to comment on that answer.
Just put a file in config/initializers/ like app_config.rb If you use ENV constant you can later on easily deploy to Heroku setting the values with the heroku config:add twitter_key=mypublickey command.
Something like this:
## config/initializers/app_config.rb
unless Rails.env.production?
ENV['twitter_key'] = 'foo'
ENV['twitter_secret'] = 'bar'
end
You keep your production keys out of revision control and don't need to dribble with YAML-files.

Resources