How do I store keys for API's in Rails? - ruby-on-rails

I have several api's that I am integrating with and need to call in various parts of my application.
What is the way to store the keys, the user/password, or token information, say, a configuration file and then how do I call them for use in other parts of the application?
Thanks.

Just to keep this question up-to-date, there is a new way to do this in Rails 4.1:
From the Rails guides:
Rails 4.1 generates a new secrets.yml file in the config folder. By default, this file contains the application's secret_key_base, but it could also be used to store other secrets such as access keys for external APIs.

You can store usernames/passwords and similar configuration information in mechanisms that rails already uses; you can either stuff the configuration data right into your environment configuration files (where production, testing, and development are configured), or you could use your own mechanism and:
require "yaml"
config_hash = YAML::load_file("/path/to/your/config.yaml")

Easiest is to store the info as constants in your various environment files. That way you can use different accounts for development, production, etc.
# Eg
# development/environment.rb
....
API_1_USER = "user101"
API_1_PW = "secret!"
Alternative is to create a yaml file, then read it when your app signs in to an api. This is the style used by rails itself with the config/databse.yml file
ADDED
You can also store as a constant using a hash or nested hash.
# Eg
# development/environment.rb
....
API_1 = {"user" => "user101", "pw" => "secret!"}
API_2 = {"user" => "user102", "pw" => "double_secret"}
# or nested hashes
API_KEYS = {
"api_1" => {"user" => "user101", "pw" => "secret!"},
"api_2" => {"user" => "user102", "pw" => "double_secret"}}
# using them in another file:
foo.signin(API_1['user'], API_1['pw'])
# or
foo.signin(API_KEYS["api_1"]['user'], API_KEYS["api_1"]['pw'])
# note, I use string constants instead of symbols to save vm (since the hash is
# not referenced more than once or twice). You could also use
# symbols as the keys, especially if the hash will be referenced often:
API_1 = {:user => "user101", :pw => "secret!"}

Check out Configatron, it's pretty awesome and can be used exactly for this purpose.

Related

when sharing data via memcached between Rails and Sinatra, how fix Sinatra not handling ActiveSupport

Is there any way to cause Sinatra to transparently handle cached data correctly that was written by Rails, eg, to implicitly handle the ActiveSupport class that Rails uses when it stores data?
A Heroku-hosted Rails 4 app and a Sinatra app used a shared Memcachier store to share certain ephemeral data.
If a key/value is created on Sinatra, everything works normally:
# on Sinatra:
session.cache.set("foo", "from sinatra", 100)
will set a key that either the Sinatra app or the Rails app can read, and either app will automatically read nil once it expires in 100 seconds. And both Rails and Sinatra report the class of the data value to be String.
However, if the data is set by the Rails app:
# on Rails:
Rails.cache.write("foo", "from rails", expires_in: 100)
the Rails app read returns a String (as expected), but Sinatra app get returns class ActiveSupport::Cache::Entry
# on Sinatra
d = settings.cache.get("foo")
=> #<ActiveSupport::Cache::Entry:0x7f7ea70 #value="from rails", #created_at=1598330468.8312092, #expires_in=100.0>
and if the Sinatra app fetches the same key after the expiry, the same data is returned (not nil).
It is certainly possible on the Sinatra app to brute-force it with a new method that get's the data, and manually handles expiration and data issues using ActiveSupport::Cache::Entry methods, as outlined below.
But it seems like there ought to be some way to have the ActiveSupport::Cache::Entry code handle those details automatically by telling Sinatra to use ActiveSupport::Cache for both setting and getting data?
# pseudo code for Sinatra app
def new_get(key)
x = settings.cache.get(key)
if x.class == ActiveSupport::Cache::Entry
if x.expired?
x.delete
return nil
else return x.value
else return x
end
Currently the memcache store is configured as per Heroku online documentation:
require 'dalli'
set :cache, Dalli::Client.new(
(ENV["MEMCACHIER_SERVERS"] || "").split(","),
{:username => ENV["MEMCACHIER_USERNAME"],
:password => ENV["MEMCACHIER_PASSWORD"],
:failover => true, # default is true
:socket_timeout => 1.5, # default is 0.5
:socket_failure_delay => 0.2, # default is 0.01
:down_retry_delay => 60 # default is 60
})
EDIT - SOLVED Per accepted answer the key to Sinatra/Rails data interoperability was to explicitly configure the cache store to use ActiveSupport, which still automagically uses the Dalli gem to manage the connection to the memcachier service.
set :cache, ActiveSupport::Cache::MemCacheStore.new(
... # init parameters
)}
which also means using the settings.cache.read/write methods (vs the settings.cache.get/set methods used when you configure using Dalli::Client.new).
ADDITIONAL EDIT:
When using the cache inside a model, you cannot access settings.cache directly, need to use Sinatra::Application.settings.cache.read()
I am not a Sinatra user, Used something like the below in Rails.
cache_store = ActiveSupport::Cache::MemCacheStore.new('localhost')
# For writing the keys:
cache_store.write("foo", "from rails", expires_in: 100)
# For reading the keys
cache_store.read("foo")
EDIT
It seems you are using gem dalli, You can also use the DalliStore instead of MemCacheStore like the below:
cache_store = ActiveSupport::Cache::DalliStore.new('localhost')
# For writing the keys:
cache_store.write("foo", "from rails", expires_in: 100)
# For reading the keys
cache_store.read("foo")

RoR, Fog: Getting Started with Fog

I just installed the gem asset_sync and I am trying to get set up with my AWS account. When I run bundle exec rake assets:precompile I get the following errror:
AssetSync::Config::Invalid: Fog provider can't be blank, Fog directory can't be blank
I understand the simply reason that I am getting this error, namely that I havent pushed the Fog provider or directory to heroku. What I am stumped about is where to put the Following code (Taken from the Fog README). In config/initializers/fog.rb? Is this all I need to do to start using fog, other than installing the gem?
require 'rubygems'
require 'fog'
# create a connection
connection = Fog::Storage.new({
:provider => 'AWS',
:aws_access_key_id => YOUR_AWS_ACCESS_KEY_ID,
:aws_secret_access_key => YOUR_AWS_SECRET_ACCESS_KEY
})
# First, a place to contain the glorious details
directory = connection.directories.create(
:key => "fog-demo-#{Time.now.to_i}", # globally unique name
:public => true
)
not a problem, getting started tends to be the hardest part.
The answer is, it depends. I'd actually venture to say it would be best to put this in your environment based initializers, ie config/init/development or config/init/production, etc. Relatedly, you probably will not want to generate a new directory every time you start your app (there is an account level limit of 100 total I believe). So you might want to either set a key for each environment for that create or simply create the directory somewhere outside the initializers (and within the initializer you can assume it exists).
If you want to use that directory directly, you'll still need to create a reference, but you can create a local reference without making any api calls with #new like this:
directory = connection.directories.new(:key => ...)
As for asset_sync, it needs those keys and a reference to the directory key, which you will probably want to provide via ENV vars (to avoid checking your credentials into version control). You can find details on which keys and how to set them here: https://github.com/rumblelabs/asset_sync#built-in-initializer-environment-variables (the readme also describes how to do it via initializers, but that probably isn't the best plan).
Hope that helps!

How do I reference a tiny_tds connection in ruby/sinatra via database.yml (or other)?

I'm trying to figure out how to pull data from a database without the need to place a connection string at the top of each ruby file.
I'm learning the basics of ruby via a little Sinatra app I'm putting together which pulls data from a MSSQL database.
So far I've managed to create various simple erb pages that display data from the MSSQL database by using the following code structure at the top of each file:-
<% client = TinyTds::Client.new(:username => 'name', :password => 'password', :dataserver => 'hostname', :database => 'database') %>
<% data = client.execute("SELECT * from tablename") %>
From the books, guides and online tutorials I've found based on lots of configs to do with PostgreSQL or MySQL databases it seems to me I need to be creating a central file to store my connection data (such as a database.yml file) and then referencing that somewhere/somehow in my app.
Would that be correct, and should I be doing that in my main.rb file so that each of my .erb files do not require the connection string or do I have to still refer to the database in each .erb file also?
I've noted references to creating database config variables such as:-
db_config = YAML.load(File.Open("/path_to_file/database.yml")) [ENV['RAILS_ENV']]
but that clearly seems suited to Rails apps.
Could I do something similar for my sinatra driven 'app'?
Thanks.
This should work:
require "sinatra"
require "sinatra/config_file"
config_file 'path/to/config.yml'
DB = TinyTds::Client.new(
:username => settings.name,
:password => settings.password,
:dataserver => settings.hostname,
:database => settings.database
)
get '/' do
#result = DB.do_stuff
haml :index
end
What I would suggest though, is that you look for an ORM that supports TinyTDS and use that to set up the database connection and run queries. I use Sequel a lot and I know it supports TinyTDS, but I'm sure others do too.
I'd also suggest not putting things like database settings in a file that gets checked in to source control, as it's sensitive information. I prefer to put this kind of thing into environment variables for production, and use a config file to load the environment variables in development (for convenience). Using the above example:
DB = TinyTds::Client.new(
:username => ENV["name"],
:password => ENV["password"],
:dataserver => ENV["hostname"],
:database => ENV["database"]
)
Those env variables are loaded into the production server's memory, which makes them a little bit more secure. For development, I load YAML files before starting up Sinatra, adding each value to an ENV var. I'm not suggesting this is a standard way, but it's how I do it.

How to define custom configuration variables in Rails?

I was wondering how to add custom configuration variables to a Rails application and how to access them in the controller?
Secondly, I was planning to have S3 support for uploads in my application, if I wanted to add a yaml file with the S3 access, secret key, how do I initialize it in my Rails App and how do I access the values that I have defined in that config file.
In Rails 3, Application specific custom configuration data can be placed in the application configuration object. The configuration can be assigned in the initialization files or the environment files -- say for a given application MyApp:
MyApp::Application.config.custom_config_variable = :my_config_setting
or
Rails.configuration.custom_config_variable = :my_config_setting
To read the setting, simply call the configuration variable without setting it:
Rails.configuration.custom_config_variable
=> :my_config_setting
UPDATE Rails 4
In Rails 4 there a new way for this => http://guides.rubyonrails.org/configuring.html#custom-configuration
Update 1
Very recommended: I'm going with Rails Config gem nowadays for the fine grained control it provides.
Update2
If you want a quick solution, then check Jack Pratt's answer below.
Although my original answer below still works, this answer is now outdated. I recommend looking at updates 1 and 2.
Original Answer:
For a quick solution, watching the "YAML Configuration File" screen cast by Ryan Bates should be very helpful.
In summary:
# config/initializers/load_config.rb
APP_CONFIG = YAML.load_file("#{Rails.root}/config/config.yml")[Rails.env]
# application.rb
if APP_CONFIG['perform_authentication']
# Do stuff
end
In Rails 3.0.5, the following approach worked for me:
In config/environments/development.rb, write
config.custom_config_key = :config_value
The value custom_config_key can then be referenced from other files using
Rails.application.config.custom_config_key
In Rails 4
Assuming you put your custom variables into a yaml file:
# config/acme.yml
development:
:api_user: 'joe'
:api_pass: 's4cret'
:timeout: 20
Create an initializer to load them:
# config/initializers/acme.rb
acme_config = Rails.application.config_for :acme
Rails.application.configure do
config.acme = ActiveSupport::OrderedOptions.new
config.acme.api_user = acme_config[:api_user]
config.acme.api_pass = acme_config[:api_pass]
config.acme.timeout = acme_config[:timeout]
end
Now anywhere in your app you can access these values like so:
Rails.configuration.acme.api_user
It is convenient that Rails.application.config_for :acme will load your acme.yml and use the correct environment.
This works in rails 3.1:
in config/environment.rb (or in config/environments/.. to target a specific environment) :
YourApp::Application.config.yourKey = 'foo'
This will be accessible in controller or views like this:
YourApp::Application.config.yourKey
(YourApp should be replaced by your application name.)
Note: It's Ruby code, so if you have a lot of config keys, you can do this :
in config/environment.rb :
YourApp::Application.configure do
config.something = foo
config.....
config....
.
config....
end
Since Rails 4.2, without additional gems, you can load config/hi.yml simply by using Rails.application.config_for :hi.
For example:
touch config/passwords.yml
#config/passwords.yml
development:
username: 'a'
password: 'b'
production:
username: 'aa'
password: 'bb'
touch config/initializers/constants.rb
#config/initializers/constants.rb
AUTHENTICATION = Rails.application.config_for :passwords
and now you can use AUTHENTICATION constant everywhere in your application:
#rails c production
:001> AUTHENTICATION['username'] => 'aa'
then add passwords.yml to .gitignore: echo /config/passwords.yml >> .gitignore, create an example file for your comfort cp /config/passwords.yml /config/passwords.example.yml and then just edit your example file in your production console with actual production values.
Rails 6 and 7
Many outdated answers, so adding one that is specific to Rails 6.
Application specific configuration goes in initializer files. Details are here: edge guides
Example:
config/initializers/foo.rb
module MyApp
class Application < Rails::Application
config.test_val = 'foo'
end
end
Alternatively:
Rails.application.config.test_val = 'foo'
This can now be accessed as:
Rails.configuration.test_val
Many more possibilities.
edge guides #custom-configuration
ex, you can also set up nested namespace configurations:
config.x.payment_processing.schedule = :daily
config.x.payment_processing.retries = 3
config.super_debugger = true
or use config_for to load entire custom config files:
config/payment.yml
production:
environment: production
merchant_id: production_merchant_id
public_key: production_public_key
private_key: production_private_key
development:
environment: sandbox
merchant_id: development_merchant_id
public_key: development_public_key
private_key: development_private_key
Then load it with:
config/initializers/load_payment.rb
module MyApp
class Application < Rails::Application
config.payment = config_for(:payment)
end
end
I just wanted to update this for the latest cool stuff in Rails 4.2, you can now do this inside any of your config/**/*.rb files:
config.x.whatever.you.want = 42
...and this will be available in your app as:
Rails.configuration.x.whatever.you.want
See more here: http://guides.rubyonrails.org/configuring.html#custom-configuration
Check out this neat gem doing exactly that:
https://github.com/mislav/choices
This way your sensitive data won't be exposed in open source projects
I created a simple plugin for YAML settings: Yettings
It works in a similar fashion to the code in khelll's answer, but you only need to add this YAML configuration file:
app/config/yetting.yml
The plugin dynamically creates a class that allows you to access the YML settings as class methods in your app like so:
Yetting.your_setting
Also, if you want to use multiple settings files with unique names, you can place them in a subdirectory inside app/config like this:
app/config/yettings/first.yml
app/config/yettings/second.yml
Then you can access the values like this:
FirstYetting.your_setting
SecondYetting.your_setting
It also provides you with default settings that can be overridden per environment. You can also use erb inside the yml file.
I really like the settingslogic gem. Very easy to set up and use.
https://github.com/binarylogic/settingslogic
If you use Heroku or otherwise have need to keep your application settings as environment variables, the figaro gem is very helpful.
I like to use rails-settings for global configuration values that need to be changeable via web interface.
Something we've starting doing at work is the ActiveSupport Ordered Hash
Which allows you to define your configuration cleanly inside the environment files e.g.
config.service = ActiveSupport::OrderedOptions.new
config.service.api_key = ENV['SERVICE_API_KEY']
config.service.shared_secret = ENV['SERVICE_SHARED_SECRET']
I would suggest good approach how to deal with configuration in your application at all. There are three basic rules:
change your configuration not a code;
use configurations over conditions;
write code that means something.
To have more detailed overview follow this link: Rails configuration in the proper way

What is the best way to keep web app config?

If I have some config for web admin to set e.g. number of post per page, some enum showing choice. How should I keep this settings in db ? Should I serialize it and save as blob.
Thanks,
I using rails and I want it to dynamically change this setting through web interface, so I think environment.rb would not fit this situation. So I should have a extra table with two tuples as name, value ?
Most languages/frameworks have a config file of sorts. such as the web.config in ASP or the environment.rb files in RoR. You could use one of these.
Or failing that have a key value pair table in your database.
If you're wanting to do this dynamically through the website I would definitely go for the key value pair table.
For the dynamic config values, you should create a model called Configuration with keys and values. I generally have multiple value columns (for number, string, and date) and then call the appropriate method for the configuration.
For "enums" you should create lookup tables with foreign key relationships to where they attach. For example if you have a Post model and you want an enumeration of Category, you should make the Post belong_to :category and Category has_many :posts.
Use a YAML file. YAML is way simpler than XML.
Make a file called "config.yml" in "config" directory. And load the file using YAML::load(). You can make a setting for each environment by naming the first level as environment (e.g., production, development, test).
See this episode of RailsCasts for details.
If you are using asp.net you can use the Web.Config file.
See Asp .net Web.config Configuration File
You could to create a single table in your database to store key-value pairs.
This is what I use. Got the idea from elsewhere, but the implementation is mine. Pulled from a production project of mine:
class AppConfig
# Loads a YAML configuration file from RAILS_ROOT/config/. The default file
# it looks for is 'application.yml', although if this doesn't match your
# application, you can pass in an alternative value as an argument
# to AppConfig.load.
# After the file has been loaded, any inline ERB is evaluated and unserialized
# into a hash. For each key-value pair in the hash, class getter and setter methods
# are defined i.e., AppConfig.key => "value"
# This allows you to store your application configuration information e.g., API keys and
# authentication credentials in a convenient manner, external to your application source
#
# application.yml example
#
# :defaults: &defaults
# :app_name: Platform
# :app_domain: dev.example.com
# :admin_email: admin#example.com
# :development:
# <<: *defaults
# :test:
# <<: *defaults
# :production:
# <<: *defaults
# :app_domain: example.com
#
# For example will result in AppConfig.app_domain => "dev.example.com"
# when Rails.env == "development"
#
class << self
def load(file='application.yml')
configuration_file = File.join Rails.root, 'config', file
File.open(configuration_file) do |configuration|
configuration = ERB.new(configuration.read).result
configuration = YAML.load(configuration)[Rails.env.to_sym]
configuration.each do |key, value|
cattr_accessor key
send "#{key}=", value
end
end if File.exists? configuration_file
end
end
end
AppConfig.load
Create config/initializers/app_config.rb and paste the above code into it. I'm going to make this into a gem. I figure other people will find it useful.
EDIT: Just saw you wish to edit the config as the app runs via a web based interface. You could do this with this method as both getter and setter methods are defined for each attribute.
in your controller:
def update
params[:configuration].each { |k,v| AppConfig.send "#{k}=", v }
…
end
I don't find a model is the right solution here. Forget about the DB overheard, the idea of being able to instantiate something that controls app configuration doesn't make sense. What's more how you implement it? An instance for each tuple?! It should be a singleton class.

Resources