Adding to Rails.application.config before initializers run - ruby-on-rails

I want to inject a custom property (hash map) into my Rails.application.config. It seems that the way to do it is simply to assign a new variable in environment.rb:
Rails.application.config.feature_map = { :email => true }
I need to access this map through various places in my application, like the user model, controllers, and rake tasks.
Other gems, like devise, also need access to this. The problem is that adding it to environment.rb seems to be too early in the application life-cycle.
I have code in initializers/devise.rb like this:
if Rails.application.config.feature_map[:email] = true
The server complains that this field doesn't exist.
I also use it to add additional validation in my user model:
if Rails.application.config.feature_map.enabled?(:username)
validates_length_of :username, :in => 3..50
I also get a runtime error here about undefined feature Rails.application.config.feature_map
Where can I move this so that I can access it as early as in initializers and in my model class? I tried moving it into a new initializers/feature_map.rb file, but that didn't work either.

Put it in config/application.rb:
module MyRailsApp
class Application < Rails::Application
config.feature_map = ActiveSupport::OrderedOptions.new
config.feature_map.email = true
end
end
Anything you set in there will be default for all environments, but can be overridden per environment in config/environments/*.rb.

Gems like Figaro and .env will help you load up your config even before the loading of initializer.rb
Unless there is a strong reason that you wouldn't wanna use environment variables, I would recommend using either of those gems since they are the recommended way of adding your custom configs.

Edit: See Jimmy Cuadra's answer above, which I ended up going with.
I found an alternative solution: this answer to manipulate the order of initializers.
I can rename my initializer to 00_feature_map.rb and it loads first.

Related

How do I create my own method to change a string in Ruby on Rails? [duplicate]

I want to build an index for different objects in my Rails project and would like to add a 'count_occurences' method that I can call on String objects.
I saw I could do something like
class String
def self.count_occurences
do_something_here
end
end
What's the exact way to define this method, and where to put the code in my Rails project?
Thanks
You can define a new class in your application at lib/ext/string.rb and put this content in it:
class String
def to_magic
"magic"
end
end
To load this class, you will need to require it in your config/application.rb file or in an initializer. If you had many of these extensions, an initializer is better! The way to load it is simple:
require 'ext/string'
The to_magic method will then be available on instances of the String class inside your application / console, i.e.:
>> "not magic".to_magic
=> "magic"
No plugins necessary.
I know this is an old thread, but it doesn't seem as if the accepted solution works in Rails 4+ (at least not for me). Putting the extension rb file in to config/initializers worked.
Alternatively, you can add /lib to the Rails autoloader (in config/application.rb, in the Application class:
config.autoload_paths += %W(#{config.root}/lib)
require 'ext/string'
See this:
http://brettu.com/rails-ruby-tips-203-load-lib-files-in-rails-4/
When you want to extend some core class then you usually want to create a plugin (it is handy when need this code in another application). Here you can find a guide how to create a plugin http://guides.rubyonrails.org/plugins.html and point #3 show you how to extend String class: http://guides.rubyonrails.org/plugins.html#extending-core-classes

Where is a good place to initialize an API?

I wanted to use this api: https://github.com/coinbase/coinbase-ruby and the first step is to initialize the API, like this:
coinbase = Coinbase::Client.new(ENV['COINBASE_API_KEY'], ENV['COINBASE_API_SECRET'])
I was wondering what the best place to put this code is, and how would I access it if I put it "there"? I want this variable (coinbase) to be accessible ANYWHERE in the application.
Thanks!
The answer to this question really depends on your use case and your approach. My geral recommendation, however, is to create a Service Object (in the DDD sense) (see the section named "Domain Objects Should Not Know Anything About Infrastructure Underneath" in that link), that handles all communication with the Coinbase API. And then, within this service object, you can simply initialize the Coinbase::Client object once for however many times you call into it. Here's an example:
# app/services/coinbase_service.rb
class CoinbaseService
cattr_reader :coinbase_client, instance_accessor: false do
Coinbase::Client.new(ENV['COINBASE_API_KEY'], ENV['COINBASE_API_SECRET'])
end
def self.do_something
coinbase_client.do_something_in_their_api
end
def self.do_something_else
coinbase_client.do_something_else_in_their_api
end
end
So then you might do, e.g.:
# From MyController#action_1
if CoinbaseService.do_something
# ...
else
# ...
end
Or:
# From MyModel
def do_something
CoinbaseService.do_something_else
end
To get the service object working, you may need to add app/services to your autoload paths in application.rb file. I normally just add this:
# config/application.rb
config.autoload_paths += %W(#{config.root}/app)
I find this Service Object approach to be very beneficial organizationally, more efficient (only 1 invocation of the new Coinbase client needed), easier to test (easy to mock-out calls to Coinbase::Client), and simply joyful :).
One way to go about having a global variable can be done as similar as initializing redis in a Rails application by creating an initializer in config/initializers/coinbase.rb with:
$coinbase = Coinbase::Client.new(ENV['COINBASE_API_KEY'], ENV['COINBASE_API_SECRET'])
Now, you can access $coinbase anywhere in the application!
In the file config/initializers/coinbase.rb
Rails.application.config.after_initialize do
CoinbaseClient = Coinbase::Client.new(
Rails.application.credentials.coinbase[:api_key],
Rails.application.credentials.coinbase[:api_secret])
end
In place of the encrypted credentials, you could also use environment variables: ENV['COINBASE_API_KEY'], ENV['COINBASE_API_SECRET']
The above will make the constant CoinbaseClient available everywhere in your app. It will also ensure all your gems are loaded before the client is initialized.
Note: I am using Rails 6.1.4.4, and Ruby 2.7.5

Rails: Have initializer load before application.rb

I have an initializer named _settings.rb that looks like this
class Settings < Settingslogic
source "#{Rails.root}/config/application.yml"
namespace Rails.env
end
My application.yml defines a value for a custom setting I call environhost.
I call it using:
Settings.environhost
This works fine, EXCEPT for when I try to call the value in my /app/config/application.rb
config.action_controller.asset_host = Settings.environhost
For this, I get an uninitialized constant.
Is there anyway I can put a pointer in my application.rb to load _settings.rb before
config.action_controller.asset_host = Settings.environhost
is loaded? What's the best way to do this?
http://guides.rubyonrails.org/initialization.html
Rails own configuration will be always loaded before any custom things, that's for sure. Otherwise can you imagine what a mess :)
The solution is not to try to load before Rails configuration. Instead, hook into initializer to add your own logic to override Rails default.
Railtie is the place you can do that without sweat. Here you can access config method shared in all initializers including Rails.
module MySettings
def self.environhost
"foobar"
end
class MySettingsRailtie < Rails::Railtie
config.action_controller.asset_host = MySettings.environhost
end
end
Side note: In most cases you should be fine to set assets host as mu_is_too_short commented. If you need anything other than that, you can use custom intializer by Railtie.

Where can I store site-wide variables in Rails 4?

I am new to Rails and come from a ColdFusion background, where we would store global / site-wide variables in the 'application' scope. This persists the variable across any view or controller. Does Rails 4 have an equivalent functionality for this type of thing?
The site-wide variable won't typically change often so it doesn't need protecting in any way.
For example, in my situation, I want to store the website's domain name. One for testing and one for live environments. Localhost for development and xxxxxx.com for production.
Any tips or pointers would help. I have Googled this extensively and solutions seem to be far too complicated to achieve what seems to be such a trivial task. What's the best elegant solution for Rails 4?
The simplest, basic and default way is to use the Rails.application.config store.
Rails.application.config.my_config = 'foo'
You can assign a config in your environment:
# application.rb
module MyApp
class Application < Rails::Application
config.my_config = 'foo'
end
end
and read it with
Rails.application.config.my_config
# => 'foo'
This approach works well for very simple applications, but if you want something more advanced there are several gems available.
I'm currently using SimpleConfig. The main advantages are:
per-environment configuration. You can configure default configurations for the application, then override defaults with environment specific configurations
local.rb file for custom overrides
capistrano-like configuration style
it works nicely with the dotenv gem, very useful to avoid storing sensitive credentials in your repo.
This sounds like a perfect example for configuration values stored in config/environments/production.rb and config/environments/development.rb. Just store any value there:
config.my_special_value = 'val'
And access it in your application like this:
Rails.application.config.my_special_value
Always the value of your environment is active.
If you just want to have a „global“ value, store it in your application controller. All your view controllers are derived from your app controller, so you can save any value there as an instance or class variable:
class ApplicationController < ActionController::Base
MY_CONSTANT_VALUE = "foo"
end
class MyViewController < ApplicationController
def index
raise MY_CONSTANT_VALUE.inspect
end
end
You also could implement an helper:
# app/helpers/application_helper.rb
module ApplicationHelper
FOO = "bar"
end
# app/controllers/foo_controller.rb
class FooController < ApplicationController
def index
raise FOO
end
end
I can recommend good method to store variable. I use this on production
Passwords can be stored easier to .env file
like this
#Root dir create file ".env"
PASSWORD=123456
and load password
#Somewhere in app
ENV['PASSWORD'] #=> 123456
it works I hope will help you
You can use gem figaro
write your variables in config/application.yml
HELLO: world
development:
HELLO: developers
production:
HELLO: users
Then you can fetch
ENV["HELLO"]
In rails there is gem named as
gem 'dotenv-rails'
By using it we can assign the variables to system level and used in application.
By using simple steps
First create a simple filed in system level at any place with named extension .env
//in application.rb
require 'dotenv'
Dotenv.load('path-of-your-file.env')
And restart your application
Source
Please got the link for the desscription of dot env gem

application wide global variable

In Rails, where should I define the variable which can be recognized by every layer of Rails stacks.
For example, I would like to have a CUSTOMER_NAME='John' variable which can be accessed in helper, rake task, controller and model. Where should I define this variable in Rails app?
I am using Rails v2.3.2
In an initializer in /app/config/initializers all .rb files in here get loaded, I usually create one called preferences.rb for things like this.
See: http://guides.rubyonrails.org/configuring.html#using-initializer-files
An alternative approach is to set a key on the config object in config/application.rb, like so:
MyApp::Application.configure do
# ...
config.my_key = 'some "global" value'
end
You can then access my_key from anywhere in your app with just this:
MyApp::Application.config.my_key
Also, Mike Perham has described a similar, though a more comprehensive approach in his blog post.
You want a true global constant? Use ::COSTUMER_NAME.
You want a true global variable? Use $COSTUMER_NAME (discouraged).
You want a request-global variable? Use the Hash in the #env method.

Resources