I need some advice on configuring mail in production Ruby-on-Rails sites.
I deploy my Rails app on EngineYard. I have a couple of sites, like demo.mydomain.com or staging.mydomain.com - how can I configure Devise so that at deploy time I can make sure confirmation mails come from demo.mydomain.com or staging.mydomain.com automatically? ie, I want the same GitHub codebase, and want to fill the configuration in dynamically.
Currently in config/environments/production.rb I have the line:
config.action_mailer.default_url_options = { :host => 'demo.mydomain.com' }
But that's incorrect when the same code is deployed to staging.mydomain.com as they both run in RAILS_ENV=production
Any ideas?
Thanks,
Dave
Update: For now, to be practical, I've added specific environments to hardcode the mailer domain. So now demo.mydomain.com runs on environments/demo.rb, and www.mydomain.com runs on environments/productions.rb. What I don't like about this is the duplication between the files, it's not clear to me how to DRY them up as I have with, eg, database.yml
in your devise configuration, usually config/initializers/devise.rb you can configure the mail-sender for devise. this configuration takes a proc, so that it's possible to evaluate something at runtime.
Devise.setup do |config|
config.mailer_sender = Proc.new { your_magic_here }
end
Ideally staging & production servers should run on different rails environment. Still if you wanted to have production env running on both staging & production servers with different action mailer urls then it should be done at deployment level. You can always write environment file while deployment.
First of all, I think you should separate the environments of your application. Check this guide to learn how you can do it.
Then, try something like this in your devise configuration:
Devise.setup do |config|
if Rails.env.production?
config.mailer_sender = "no-reply#domain.com"
elsif Rails.env.staging?
config.mailer_sender = "no-reply#staging.domain.com"
else
config.mailer_sender = "no-reply#domain.com"
end
...
Check this guide to understand more about Proc object.
Related
I have a project configured to use Rails encrypted secrets. Everything works fine until I try to access a secret within the production.rb environment file.
I found that if I try to access something like Rails.application.secrets.smtp_user_name within the configure block it wipes out all of the encrypted secrets (I'm only left with things in secrets.yml ... which I'm not using). Example:
Loading production environment (Rails 5.1.2)
irb(main):001:0> Rails.application.secrets
=> {:secret_key_base=>nil, :secret_token=>nil}
If I remove the attempt to access the secrets it works correctly:
irb(main):001:0> Rails.application.secrets
=> {:secret_key_base=>"...", :smtp_user_name=>"...", :smtp_password=>"...", :secret_token=>nil}
I'm currently working around it by using two configure blocks in production.rb as follows:
# This is hacky, it needs to come before the second configure block where
# the encrypted secrets are used.
Rails.application.configure do
config.read_encrypted_secrets = true
end
Rails.application.configure do
... stuff that uses Rails.application.secrets, like ActionMailer
end
Anybody else faced this and possibly have a more correct way to work around it?
It makes sense why this is happening (Rails doesn't know to load the encrypted secrets because we haven't told it to you), but I'm thinking there must be a better way to deal with it.
Update
This nailed me again 9 months later. To be clear, if you reference Rails.application.secrets BEFORE calling config.read_encrypted_secrets = true you will cache empty secrets and not be able to access any of the values in secrets.yml.enc!
In my case I had tried to configure Paperclip S3 credentials in application.rb while my config.read_encrypted_secrets = true was set in production.rb. Result was devise.rb blowing up trying to read a secret for the key base, all because in application.rb I had effectively cached nil secrets.
here is bug report related to your issue:
https://github.com/rails/rails/issues/30362#issuecomment-326821656
In general even if you have all things set up properly you need check also order how your application loads secrets. If your application ask first for Rails.application.secrets and then set proper flag... Rails.application.secrets will cache version without secrets... and Secrets from secret.yml.enc will not be merge.
How do we create custom environment variables in Rails 3.1?
For example, in my mailer I might want to send an email from emailA#domain.com during development mode but I want to send an email from emailB#domain.com during production.
I tried something like this but got an error saying the variable was not initialized.
Thanks :)
In your environment files, add a variable
app/config/environments/development.rb
YourApp::Application.configure do
# other stuff...
config.admin_email = "admin_development#gmail.com"
end
app/config/environments/production.rb
YourApp::Application.configure do
# other stuff...
config.admin_email = "admin_production#gmail.com"
end
Depending on the environment, YourApp::Application.config.admin_email will contain either admin_development#gmail.com or admin_production#gmail.com
Alternatively, if the emails only differ by the environment name, I would suggest doing something like this...
admin_email = "admin_#{Rails.env}#gmail.com"
in my config/application.rb file I have a line:
config.action_mailer.default_url_options = { :host => 'example.com' }
How can I configure the host attribute for default url options automaticaly from the domain, where my Rails app is hosted ?
It has to be explicitly specified somewhere because how else would rails know what the domain is?.. A request through the browser (or curl, or whatever) will have a host which rails knows about, but if you load up rails console, what would the domain be?
If you're using something like capistrano, you could write a task to create an initializer which sets the mailer url depending on the host(s) you've set in your deploy recipe.
I'm storing configuration variables for different environments in the production.rb and development.rb
production.rb
ENV['my_variable'] = 'val1'
development.rb
ENV['my_variable'] = 'val2'
Maybe exists another way to store variables for different environments. What is the best way?
I've been pointing people towards the settingsgem lately. Settings Gem
I'm trying to work out what the best way to secure my staging environment would be. Currently I'm running both staging and production on the same server.
The two options I can think of would be to:
Use rails digest authentication
I could put something like this in the application_controller.rb
# Password protection for staging environment
if RAILS_ENV == 'staging'
before_filter :authenticate_for_staging
end
def authenticate_for_staging
success = authenticate_or_request_with_http_digest("Staging") do |username|
if username == "staging"
"staging_password"
end
end
unless success
request_http_digest_authentication("Admin", "Authentication failed")
end
end
This was ripped from Ryan Daigle's blog. I'm running on the latest Rails 2.3 so I should be free from the security problem they had with this.
Use web server authentication
I could also achieve this using .htaccess or apache permissions, however it makes my server provisioning slightly more complex (I'm using Chef, and would require different apache configs for staging/production).
For now I have the first one implemented and working, do you see ay problems with it? Have I missed something obvious? Thanks in advance!
bumping this to help others, like myself as I read this before settling on an similar, but cleaner solution.
# config/environments/staging.rb
MyApp::Application.configure do
config.middleware.insert_after(::Rack::Lock, "::Rack::Auth::Basic", "Staging") do |u, p|
[u, p] == ['username', 'password']
end
#... other config
end
I wrote a short blog post about it.
If you are deploying with multi-staging environments and so you have production environment and staging environment, you only need to add these lines to config/environments/staging.rb
MyApp::Application.configure do
# RESTRICTING ACCESS TO THE STAGE ENVIRONMENT
config.middleware.insert_before(::Rack::Runtime, "::Rack::Auth::Basic", "Staging") do |u, p|
u == 'tester' && p == 'secret'
end
...
end
By doing so, you don't need to configure Apache.
I am using Ruby 2 with Rails 4 and it works like a charm!
I would go with the http basic authentication, I see no inherent problems with it.