Best way to manage set config variables in gem? - ruby-on-rails

Can any one tell me the best practice for initializing config variables and read that variables in gems?
Have tried with the following steps:
This code is written in gem
config = YAML.load_file("#{RAILS_ROOT}/config/config.yml")
#key = config["config"]["key"]
server = config["config"]["server"]
and created yml file in config/config.yml in rails application.
Thanks in advance,
Jagdish

I did it once like following:
module YourGem
class YourClass
#config = { :username => "foo", :password => "bar" } # or #config = SomeHelperClass.default_config if the config is more complex
#valid_config_keys = #config.keys
# Configure through hash
def self.configure(opts = {})
opts.each { |k,v| #config[k.to_sym] = v if #valid_config_keys.include? k.to_sym }
end
# Configure through yaml file
def self.configure_with(path_to_yaml_file)
begin
config = YAML::load(IO.read(path_to_yaml_file))
rescue => e
raise "YAML configuration file couldn't be found: #{e}"
end
configure(config)
end
end
end
And in your Rails Application, where you require your gem, you can add an initializer and configure like following:
config/initializers/your_initializer.rb
YourGem::YourClass.configure_with(path_to_the_yml_config_file)
This solution provides a default config, and the possibility to add a own yaml file to change the default values.

I've found my favourite way to set config variables in rails is using the figaro gem. Figaro basically makes use of the ENV['x'] method that is available throughout rails. It stores all your config variables inside a common application.yml file and makes all the constants accessible via the ENV variable.
The bonus is that this translates 1 to 1 with the way Heroku does things as well.

Related

Using the backup gem how can I get database authentications details from rails database.yml

I'm testing out the backup gem
http://backup.github.io/backup/v4/utilities/
I understand that I've to create a db_backup.rb with the configuration for example
Model.new(:my_backup, 'My Backup') do
database MySQL do |db|
# To dump all databases, set `db.name = :all` (or leave blank)
db.name = "my_database_name"
db.username = "my_username"
db.password = "my_password"
db.host = "localhost"
db.port = 3306
However I'm not able to find out how to get those details from the Rails database.yml. I've tried something like this:
env = defined?(RAILS_ENV) ? RAILS_ENV : 'development'
#settings = YAML.load(File.read(File.join( "config", "database.yml")))
But I guess there should be a better way.
I would do something like this:
env = defined?(RAILS_ENV) ? RAILS_ENV : 'development'
config = YAML.load_file(File.join('config', 'database.yml'))[env]
Model.new(:my_backup, 'My Backup') do
database MySQL do |db|
config.each_pair do |key, value|
db.public_send("#{key}=", value)
end
# ...
Use ActiveRecord's own configuration handling:
require 'active_record'
require 'yaml'
Model.new(:my_backup, 'My Backup') do
database MySQL do |db|
config = {
# these are the default values
host: 'localhost'
port: 3306
}.merge(load_configuration(ENV['RAILS_ENV'] || 'development'))
config.each_pair do |key, value|
db.public_send("#{key}=", value)
end
end
# this loads the configuration from file and memoizes it
def load_configuration(env)
#file_config ||= YAML.load(File.read(File.join( "config", "database.yml")))
#configurations ||= ActiveRecord::ConnectionHandling::MergeAndResolveDefaultUrlConfig.new(file_config).resolve
#configurations[env]
end
end
The key advantage here is that it will merge the values from ENV['DATABASE_URL']. Which is very important since you should avoid adding database credentials to config/database.yml.
A good habit is to specify only the connection adapter and base essentials in database.yml. Use ENV['DATABASE_URL'] for usernames, passwords and everything else.
Env vars are easy to change between deploys without changing any
code; unlike config files, there is little chance of them being
checked into the code repo accidentally; and unlike custom config
files, or other config mechanisms such as Java System Properties, they
are a language- and OS-agnostic standard.
- https://12factor.net/config
See:
Configuring Rails Applications

Where should I configure rails gems?

For example, I use mobylette gem and it's documentation says that I can configure it like this.
mobylette_config do |config|
config[:fallback_chains] = { mobile: [:mobile, :html] }
config[:skip_xhr_requests] = false
config[:mobile_user_agents] = proc { %r{iphone}i }
end
Only problem is that, I don't know where to put that code. I tried creating new file config/initializers/mobylette.rb, but I got no method 'mobylette_config' error when starting rails server.
So where should I put these gem configurations and specifically in this case mobylette configuration?
That would be the conventional place to put it -- config/initializers
You can also check that its being loaded by putting in a logger.debug in the initializer
logger.debug 'LOADED mobylette configuration'
You can quickly test if there's another problem by putting the config in your environment.rb file (which is not where I'd leave it)
Both of those should give you some more info to debug
This had me pulling my hair out too. But digging around in the source on github:
https://github.com/tscolari/mobylette/blob/master/lib/mobylette/respond_to_mobile_requests.rb
I found this in the comments:
# Example Usage:
#
# class ApplicationController...
# include Mobylette::RespondToMobileRequests
# ...
# mobylette_config do |config|
# config[:fall_back] = :html
# config[:skip_xhr_requests] = false
# config[:mobile_user_agents] = proc { %r{iphone|android}i }
# config[:devices] = {cool_phone: %r{cool\s+phone} }
# end
# ...
# end

Reload lib files without restart dev server in Rails 3.1

I have some modules inside the lib folder in rails i.e.:
/lib/myapp/lib/**
I am working on them in development, however each time I have to restart server. I have been through a number of different questions on SO but most of them are for not for rails 3.1
I currently have an initializer that does this;
if Rails.env == "development"
lib_reloader = ActiveSupport::FileUpdateChecker.new(Dir["lib/**/*"], true) do
Rails.application.reload_routes! # or do something better here
end
ActionDispatch::Callbacks.to_prepare do
lib_reloader.execute_if_updated
end
end
if Rails.env == "development"
lib_reloader = ActiveSupport::FileUpdateChecker.new(Dir["lib/myapp/lib/*"], true) do
Rails.application.reload_routes! # or do something better here
end
ActionDispatch::Callbacks.to_prepare do
lib_reloader.execute_if_updated
end
end
Is there a generic way to do this? Its very time consuming having to restart the server every single time!
Get rid of the initializer and in your application.rb file put following line:
config.autoload_paths += Dir["#{config.root}/lib/**/"]
One thing to watch out is that your module and class names should follow the naming convention for autoreload to work. for example if you have file lib/myapp/cool.rb, then your constant for class/module declaration in cool.rb should look like this:
Myapp::Cool
If you have file lib/myapp/lib/cool.rb and you want it to use Cool as class/module name instead of Myapp::Lib::Cool then your autoload should look like this:
config.autoload_paths += Dir["#{config.root}/lib/myapp/lib/**/"]
As long as you are running in devmode, rails will automatically reload all classes/modules that are in autoload path and follow naming conventions.
Add to application_controller.rb or your base controller:
before_filter :dev_reload if Rails.env.eql? 'development'
def dev_reload
# add lib files here
["rest_client.rb"].each do |lib_file|
ActiveSupport::Dependencies.load_file lib_file
end
end
Worked for me.

Rails 3 :: config/initializers separate directory per environment

What would be the best way to separate out initializers in a separate subdirectory per environment in Rails 3+?
I heard that all subdirectories of config/initializers will be picked up, so a subdirectory per environment there wouldn't work unless creatively monkey patched.
Example: config/.../initializers/<environment>/*.rb
This answer avoids complexity of getting a Rails::Engine working and is credited to Bosco of SF Ruby on Rails meetup and friends. It also permits overriding via the command-line / shell setup and works with Heroku.
0) Change config/initializers/**/*.rb that look this:
FB_SECRET_KEY = 'kjkjdsfkjalsfyoursecretherelkjsdfljkasdfljdjlf'
To:
FB_SECRET_KEY = ENV['APPNAME_FB_SECRET_KEY']
1) I created a config/secrets.rb which is in .gitignore like this:
ENV['APPNAME_FB_SECRET_KEY'] ||= 'kjkjdsfkjalsfyoursecretherelkjsdfljkasdfljdjlf'
. . .
2) Added this line to start of scripts/rails:
require File.expand_path('../../config/secrets', __FILE__)
3) And the addition to .gitignore
config/secrets.rb
Bonus) Add a config/secrets.rb.example template file which is not in .gitignore.
# config/application.rb
module MyProject
class Application < Rails::Application
# modifies initializers to load as follows:
#
# 1. config/initializers/*.rb
# 2. config/initializers/environments/#{Rails.env}/**/*.rb
#
unless config.paths['config/initializers_environment']
old_initializers_dir = config.paths['config/initializers']
config.paths['config/initializers'] = Dir[File.join(old_initializers_dir, '*.rb')]
config.paths['config/initializers_environment'] = Dir[File.join(old_initializers_dir, 'environments', Rails.env, '**', '*.rb')]
initializer :load_per_environment_initializers do
config.paths['config/initializers_environment'].to_a.sort.each do |initializer|
load(initializer)
end
end
end
# ...
https://gist.github.com/1338248

Understanding rails configuration

So... First, I should make my goal clear. My goal is to have an environment defined constant filled with attributes from a yaml file that my app can reference.
The simplest example I can give is something like this:
#
initalizers/config.rb
CONFIG = YAML.load_file("#{Rails.root}/config/config.yml")[RAILS_ENV]
#
config.yml:
production:
host:
foo.com
development:
host:
localhost
port:
3000
#
config/environments/development.rb
Foo::Application.configure do
#...
config.action_mailer.default_url_options = { :host => CONFIG[:host], :port => CONFIG[:port] }
config.after_initialize do
Rails.application.routes.default_url_options = { :host => CONFIG[:host], :port => CONFIG[:port] }
end
end
My problems are:
The constant "CONFIG" does not exist prior to the after_initialize block (obviously because CONFIG is set as part of the initialize process), so I need to move this into the after_initialize block, but I cannot reference "config.action_mailer" inside the after_initialize block because "config" does not exist in that scope... Which is really confusing to me. Shouldn't "config" be accessible inside the block since it exists outside of it?
And as a side question, I am really really really confused how this config.x business works. The block is not yielding any variables so, how is "config" even valid in the context of Foo::Application.configure ?
I would think for it to work at all that it should be:
Foo::Application.configure do |config|
But that's not the case, so I really am curious how this works..
If you define your configuration loader inside of config/application.rb instead of an initializer you should be able to pre-empt the call to configure and have everything set up in time.
Sometimes it's best to make a sub-class that handles configuration files. A very basic example is this:
class Foo::Configuration
def initialize
# Read in contents of config file
end
def method_missing(name)
#config[name.to_s]
end
end
Then in your application you define a method like this:
class Foo::Application
def self.config
#config ||= Foo::Configuration.new
end
end
This will auto-load as required. It also avoids having a messy constant that shows up in the context of every single object.

Resources