I have the following configuration for both my production and development environments.
config.action_mailer.smtp_settings = {
:address => "smtp.mandrillapp.com",
:port => 587,
:user_name => ENV["MANDRILL_USERNAME"],
:password => ENV["MANDRILL_PASSWORD"]
}
I googled this for a while but I didn't find an answer that made me understand. How do I set the ENV variables both for development and for production for my rails project?
For development, you'll want to use a configuration file that is ignored in version control. It could be YAML, JSON, ruby, bash... it's a trivial choice. You'll then use an initializer file to make sure these variables are loaded when the app boots. For production, you can copy the config file (it may even have different settings) into the right place as part of your deploy process; or if you're using heroku, you can set these from command line using heroku config:add YADA=yada.
Here's a basic example with settings coming from a YAML file:
# config/settings.yml
development:
MANDRILL_USERNAME=secret_username
MANDRILL_PASSWORD=secret_password
ANOTHER_SECRET_SETTING=the_list_goes_on_and_on
production:
MANDRILL_USERNAME=different_username
MANDRILL_PASSWORD=another_password
ANOTHER_SECRET_SETTING=get_the_idea?
# .gitigore
# ...
config/settings.yml
# config/initializers/environment_settings.rb
environment_settings = YAML.load_file('./config/settings.yml')[Rails.env]
environment_settings.each do |key, value|
ENV[key] ||= value
end
They're environment variables; you don't set them for development and production.
You can set application config variables in a variety of ways, or you can set them in the shell before running, or you can use environment-specific names, or...
Related
I am trying to set production and dev profiles in Eclipse in order to deploy my compojure/ring app on Heroku. However, the Counterclockwise plug-in doesn't load up environment variables from the profile. I have added the variables in the environment tab and have restarted the REPL but the variables are still not available. This is how I added them: environment variables.
I have also tried adding the variables to profiles.clj but to no avail:
`:profiles
{:production
{:ring
{:open-browser? false,
:stacktraces? false,
:auto-reload? false}
:env {:port 3000
:db-url "//localhost/login"
:db-user "test"
:db-pass "test"
:galleries-path "test"}}
:dev
{:dependencies [[ring-mock "0.1.5"]
[ring/ring-devel "1.2.0"]]
:env {:port 3000
:db-url "//localhost/gallery"
:db-user "test"
:db-pass "testProd"
:galleries-path "galleries"}}}`
When you use environ, it automatically coerces "DB_URL" into the more idiomatic :db-url. Looking at environ's coercion code, it doesn't seem like it should matter, but I would try uppercasing and underscoring all of the environment variables you have set in the Environment tab.
tl;dr How can I get a single Sinatra app to start up very differently on different servers via customizations to config.ru?
Background
I have a single web application written using Sinatra that's run on different servers. Currently the codebase for these servers is forked because there are some non-trivial differences in the way (discrete) parts of them work. For example:
one server authenticates users via an intranet LDAP server, while another server uses a simpler local database table lookup.
one server uses an external cron job to periodically update some statistics, while another (Windows-based) server uses an internal sleepy Thread.
one server stores certain metadata in a local table, while another server pulls the metadata from an external Wiki via screen scraping (!).
…and so on.
I'd like to get these code bases completely shared (single Git repo). I envision that each server would have one slightly-differing configuration file that causes the app to be started up differently.
Abandoned Solutions
I could change the behavior of the app based on environment variables. As there are a not-tiny number of variations in behavior, I'd rather not hide the settings in environment variables.
I could create my own "server-settings.rb" file that is unique to each machine, require it in my app.rb, and then change the configuration there. However, this seems to possibly be re-inventing the wheel. I already have a file named config.ru for each server. Shouldn't I be using this?
The Current Code
My config.ru for the app currently is simply:
require ::File.join( ::File.dirname(__FILE__), 'app' )
run MyApp.new
And the app.rb that it requires is, in essence:
require 'sinatra'
require_relative 'helpers/login' # customized for LDAP lookup on this server
class MyApp < Sinatra::Application
use Rack::Session::Cookie, key:'foo.bar', path:'/', secret:'ohnoes'
set :protection, except: [:path_traversal, :session_hijacking]
configure :production do
# run various code that depends on server settings, e.g.
Snapshotter.start # there is no cron on this machine, so we do it ourselves
end
configure :development do
# run various code that depends on server settings
end
end
The Question
I'd like to make config.ru live up to its name, and have it look something like this:
require ::File.join( ::File.dirname(__FILE__), 'app' )
run MyApp.new( auth: :ldap, snapshot:false, metadata: :remote_wiki, … )
How can I modify my application to change its configuration behavior based on settings supplied via config.ru? Or is this an abuse of config.ru, trying to use it for totally the wrong thing?
As soon as I started reading the question the first answer to pop into my head was "environment variable" but you scotched that straight away :)
I'll go with a mixture of one of your coulds and the desired outcome code, as it's how I structure things…
Because I want to be able to test my applications more easily, I take most of the Ruby out of the config.ru and into a separate config.rb file and leave config.ru to be a bootstrap file. So my standard skel is:
config.ru
# encoding: UTF-8
require 'rubygems'
require 'bundler'
Bundler.setup
root = File.expand_path File.dirname(__FILE__)
require File.join( root , "./app/config.rb" )
# everything was moved into a separate module/file to make it easier to set up tests
map "/" do
run APP_NAME.app
end
app/config.rb
# encoding: utf-8
require_relative File.expand_path(File.join File.dirname(__FILE__), "../lib/ext/warn.rb")
require_relative "./init.rb" # config
require_relative "./main.rb" # routes and helpers
require 'encrypted_cookie'
# standard cookie settings
COOKIE_SETTINGS = {
:key => 'usr',
:path => "/",
:expire_after => 86400, # In seconds, 1 day
:secret => ENV["LLAVE"],
:httponly => true
}
module APP_NAME # overall name of the app
require 'rack/ssl' # force SSL
require 'rack/csrf'
if ENV["RACK_ENV"] == "development"
require 'pry'
require 'pry-nav'
end
# from http://devcenter.heroku.com/articles/ruby#logging
$stdout.sync = true
ONE_MONTH = 60 * 60 * 24 * 30
def self.app
Rack::Builder.app do
cookie_settings = COOKIE_SETTINGS
# more security if in production
cookie_settings.merge!( :secure => true ) if ENV["RACK_ENV"] == "production"
# AES encryption of cookies
use Rack::Session::EncryptedCookie, cookie_settings
if ENV["RACK_ENV"] == "production"
use Rack::SSL, :hsts => {:expires => ONE_MONTH}
end
# to stop XSS
use Rack::Csrf, :raise => true unless ENV["RACK_ENV"] == "test"
run App # the main Sinatra app
end
end # self.app
end # APP_NAME
The initial reason I did this was making it easy to run the app in specs:
shared_context "All routes" do
include Rack::Test::Methods
let(:app){ APP_NAME.app }
end
but it makes sense to me to keep this code with the rest of the application code, so to speak, as I can bundle things together, run other apps etc. I've used this to conditionally load different examples into the specs in a few projects (it helps cut down on duplicated effort and check the examples really work), so I don't see why you couldn't use it to conditionally load configurations.
This way you get to choose to use a conditional in the config.ru as to which config.rb file you would use, or use an env var in the config.rb as to which definiton of self.app to use , or pass in an options hash to self.app…
With your set up I'd rename the APP_NAME module to MyApp, and the Sinatra class to App (because quite often I'll have an website that runs a front end and an API, so the Sinatra classes get named by their function (App, API etc) and wrapped in a module named after the site) and end up with:
config.ru
map "/" do
run MyApp.app( auth: :ldap, snapshot:false, metadata: :remote_wiki )
end
config.rb
def self.app( opts={} )
opts = DEFAULT_OPTIONS.merge opts
# …
run App
end
It'll be interesting to see how other people tackle this.
In the layout file of haml I would like to determine whether we are in our development and build environments. We're using Middleman.
I would like to do something like this:
- if environment == 'development'
/ Development Code
= javascript_include_tag "Dev.js"
I tried to access Ruby's environment variable, as well as define a custom variable in the config.rb file with no success.
You’ve almost got it right – you need to check against a symbol rather than a string:
- if environment == :development
/ Development Code
= javascript_include_tag "Dev.js"
Middleman also adds the development? and build? methods which may be easier to use:
- if development?
/ Development Code
= javascript_include_tag "Dev.js"
This works with ERB too:
<% if development? %>
<!-- Development Code -->
<%= javascript_include_tag "Dev.js" %>
<% end %>
First, if possible, you should separate the logic from the data. Determine your environment in your controller, and toggle the data being sent to the view (HAML layout file).
Typically you'd set a flag in your environment variables and access it in your code from ENV. For instance, Sinatra makes the development/test/production setting available inside the code using their RACK_ENV symbol:
:environment - configuration/deployment environment A symbol
specifying the deployment environment; typically set to one of
:development, :test, or :production. The :environment defaults to the
value of the RACK_ENV environment variable (ENV['RACK_ENV']), or
:development when no RACK_ENV environment variable is set.
The environment can be set explicitly:
set :environment, :production
If you have to roll your own, that's a good way to go about it.
Use the :environment symbol that middleman creates by default:
http://rubydoc.info/github/middleman/middleman/Middleman/Application#environment-instance_method
combined with haml - you can do something like:
= javascript_include_tag "Dev.js" unless :environment == "developement"
note that middlemans build process changes the :environment value to "build"
you can also use developement? to test whether you're on dev or not: http://rubydoc.info/github/middleman/middleman/Middleman/Application#development%3F-instance_method
All the above applies to middleman 3.0.6 and might not work on lesser versions (won't work on 2.x for sure)
I have a Sinatra application that uses sinatra/config_file to parse a YAML configuration file. The file has entries for various environments such as development, test, production.
class MyApp < Sinatra::Base
register Sinatra::ConfigFile
config_file 'config.yml'
##client = Mysql2::EM::Client.new(
:host => settings.host,
:username => settings.username,
:password => settings.password,
:database => settings.database
)
Currently when I run the application, the settings are retrieved from the development section. And the only way that I found to make it use a different one was to set
ENV['RACK_ENV'] = 'production'
in my myapp.rb file.
I do not like editing source, how do I set the environment the proper way?
According to the Sinatra documentation, you can run your app in a different environment by using an argument at the command line, as such:
ruby my_app.rb -e [ENVIRONMENT].
this is probably a newbie ruby question. I have several libraries and apps that I need to deploy to several different hosts. All of the apps and libs will share some common settings for those hosts-- e.g. host name, database server/user/pass, etc.
My goal is to do something like:
cap host1 stage deploy
cap host2 stage deploy
cap host1 prod deploy
# ...
My question is how do you include these common settings in all of your deploy.rb files? More specifically, I want to create a an rb file that I can include that has some common settings and several host specific task definitions:
set :use_sudo, false
# set some other options
task :host1 do
role :app, "host1.example.com"
role :web, "host1.example.com"
role :db, "host1.example.com", :primary => true
set :rodb_host, "dbhost"
set :rodb_user, "user"
set :rodb_pass, "pass"
set :rodb_name, "db"
end
task :host2 do
#...
end
deploy.task :carsala do
transaction do
setup
update_code
symlink
end
end
And then "include" this file in all of my deploy.rb files where I define stage, prod, etc and overwrite any "common" configuration parameters as necessary. Any suggestions would be appreciated. I've tried a few different things, but I get errors from cap for all of them.
Edit: I've tried
require 'my_module'
But I get errors complaining about an undefined task object.
I just experimented with it a little more and what I discovered is that you have to:
load 'config/my_module'
I can put all of my common definitions here and just load it into my deploy.rb.
It appears from the docs that load loads and executes the file. Alternatively, require attempts to load the library specified. I'm not totally sure about real difference, but it appears that there is some separation between the current app symbol space and the library require'd (hence the errors about the undefined task object) that isn't a problem when you do a load.
require 'my_extension'
Save your extensions in my_extension.rb
Jon has it right, that's the simplest way to go, just save it in a separate file and use require 'filename'. You could also use something fancy like Webistrano for deployment which also supports this in the form of Capistrano 'Recipes'. I've been using it for a while on a few projects and have come to love it.
I'm not sure how complex your needs are, but this works well for me for deployment:
set :application, "app"
set :scm, :subversion
# ... set all your common variables
task :staging do
set :repository, "http://app/repository/trunk/"
# ... set other uncommon variables in task
end
task :production do
set :repository, "http://app/repository/production/"
# ...
end
Deployment is just
cap staging deploy
or
cap production deploy