How to change Rails application config on a per-spec basis? - ruby-on-rails

Our Rails app has a custom 404 and 500 pages:
match '/500', :to => 'errors#internal_server_error', :via => :all
And we have some specs to make sure it works.
In test.rb:
Rails.application.configure do
config.consider_all_requests_local = false
config.action_dispatch.show_exceptions = true
end
However, during development exceptions are swallowed making it difficult figure out what is happening.
So, for some tests we need the above config, and other specs different config. But the config is set before the test runs so not able to update.
How to update the config for a single spec in a before block?
Rails 6
EDIT: What I've tried:
Shira mentioned mocking, but that does not appear to do anything.
Rails.application.config.consider_all_requests_local = false
Rails.application.config.action_dispatch.show_exceptions = true
This does work, but only before the first request. After the first request it does not do anything.
Rails.application.config.consider_all_requests_local = ->{ ENV['...'] }
This does not work.
It seems that the issue is that these configs are used in the middleware and once the app is configured changes are not reflected.
I tried to find a way to re-initialize the Rails app, but it appears that there is no way to do it.
I guess the only way to do it is to monkey patch the middleware to use a Proc with an ENV...

This does work:
Rails.application.env_config['action_dispatch.show_exceptions'] = true
Rails.application.env_config['action_dispatch.show_detailed_exceptions'] = false
Thanks to Eliot Sykes https://www.eliotsykes.com/realistic-error-responses

I am not sure if it is possible to actually change the configuration but you can mock this configuration instead:
allow(Rails.application.config).to receive(:consider_all_requests_local).and_return(true)

Related

rails 4 email preview in production

I am using rails 4.1.1 and ActionMailer::Preview for previewing emails. In development environment everything is working excellent.
But in production environment the preview routes are not accessible. I store the previews in test/mailers/previews/
Is is possible to enable them for production?
In addition to this:
config.action_mailer.show_previews = true
you will also need to set
config.consider_all_requests_local = true
in your environment for the preview routes to be accessible. This has other implications as well (see https://stackoverflow.com/a/373135/1599045) so you likely don't want to enable this in production. However, if you have a custom environment that's not development, the combination of those two should get things working.
EDITED TO ADD:
The original question was for rails 4.1.1, which doesn't have config.action_mailer.show_previews available. To get ActionMailer previews working in non-development environments in rails 4.1.1, you need to first add some routes to config/routes.rb (in this case, my environment is named custom):
if Rails.env.custom?
get '/rails/mailers' => "rails/mailers#index"
get '/rails/mailers/*path' => "rails/mailers#preview"
end
Then you need to autoload the libraries needed in your environment's config file (in my case, config/environments/custom.rb):
config.action_mailer.preview_path ||= defined?(Rails.root) ? "#{Rails.root}/test/mailers/previews" : nil
config.autoload_paths += [config.action_mailer.preview_path]
This seems to perform the same task as config.action_mailer.show_previews does.
As with 4.2, you will still need to adjust the local request configuration as above depending on whether your custom environment is being used locally or on a server.
To do it without opening a big security hole:
production.rb
MyApp::Application.configure do
config.action_mailer.preview_path ||= defined?(Rails.root) ? "#{Rails.root}/spec/mailer_previews" : nil
config.autoload_paths += [config.action_mailer.preview_path]
routes.append do
get '/rails/mailers' => "rails/mailers#index"
get '/rails/mailers/*path' => "rails/mailers#preview"
end
end
class ::Rails::MailersController
before_filter :authenticate_admin!
def local_request?
true
end
private
def authenticate_admin!
...
end
end
It's possible to enable previews in production by config.action_mailer.show_previews = true as the best answer says.
I just want to add how you can render previews in iframe within your own admin area, eg. in active admin (Rails 5.1)
And also I found out that it is not so hard to write your own email previews administration, and don't use rails standard previews at all. You can then add your own features such as changing preview parameters or Send button to see this email in your phone.
From Rails 4.2 you can use the flag in production.rb (or other custom enviroment):
config.action_mailer.show_previews = true
I haven't found anything similar in Rails 4.1.
Update:
If Rspec used, for example, there will be need to add the path:
config.action_mailer.preview_path = "#{Rails.root}/spec/mailers/previews"
Default path is "#{Rails.root}/test/mailers/previews".
And no need to touch config.consider_all_requests_local
Here's what I did for Rails 5.2:
production.rb
config.action_mailer.show_previews = true
config.action_mailer.preview_path ||= defined?(Rails.root) ? "#{Rails.root}/spec/mailers/previews" : nil
class ::Rails::MailersController
include ::ApplicationController::Authorization
before_action :require_admin
end
Assuming your ApplicationController::Authorization module has the code for require_admin. I preferred this approach rather than rewriting my authorization code. Remembering to include the :: in front was tricky, because saying include ApplicationController::... will look within the Rails::MailersController namespace.

Why don't the config settings in this before filter alter the test environment's settings?

NB this is in the context of Rails 4.0.4
I'm trying to test that Rails correctly delivers custom error pages.
In order for the test environment to process errors like production though I need to alter its configuration. So in line with the guidance from this question and this question I have created the before and after filters that do this:
feature 'Error pages:', focus: true do
include BaseHelper
before do
Rails.application.config.consider_all_requests_local = false
Rails.application.config.action_dispatch.show_exceptions = true
load "application_controller.rb"
end
after do
Rails.application.config.consider_all_requests_local = true
Rails.application.config.action_dispatch.show_exceptions = false
load "application_controller.rb"
end
scenario "system should return the correct 404 page" do
# ...
end
end
However that doesn't seem to affect things. Using only those before filters I can see (using save_and_open_page) that the app does not use my error pages.
I can however alter these settings directly in environments/test.rb
If I put the same settings into test.rb then the tests do correctly use my custom error pages:
# Show full error reports and disable caching
config.consider_all_requests_local = false
config.action_dispatch.show_exceptions = true
When these settings are in place in the config, the test works as expected and returns the custom error pages.
Also, in case it's relevant, in both cases I'm using a custom exceptioins_app handler:
config.exceptions_app = ->(env) { ExceptionController.action(:show).call(env) }
Why aren't the config settings being picked up via the filters?
I don't understand why the exact same changes made in the before filter don't seem to affect the tests.

Rails 3.2.11 suddenly needs a restart to 'acknowledge' any change in a controller?

Title says it all.
Note that this is not about a change in the model or initializers.
I can delete an instance variable in the controller (say, #user) and then reload a view and it will work - until I restart the server, in which case it will complain about the variable being nil.
I was working normally and then switched to work on a totally different set of controllers and views and now it's happening for no reason whatsoever.
The app is in a development environment.
development.rb contents:
Dashboard::Application.configure do
config.cache_classes = false
config.whiny_nils = true
config.consider_all_requests_local = true
config.action_controller.perform_caching = false
config.action_mailer.raise_delivery_errors = false
config.active_support.deprecation = :log
config.action_dispatch.best_standards_support = :builtin
config.assets.compress = false
config.assets.debug = true
end
How can I find out how it's happening and how do I fix it?
Edit:
**It's likely related, but I can't seem to use any paths that exist when running 'rake routes' in a partial, such as dashboards_path**
Plot twist:
Adding
config.reload_classes_only_on_change = false
to development.rb seemed to have ameliorated the issue. I still would like to know why it happened, why it happened out of the blue and why it happened to one controller but not the other.
Rails uses the autoload paths config to determine what files to auto load, and reload:
module YourApp
class Application < Rails::Application
config.autoload_paths += %W( #{config.root}/lib #{config.root}/lib/**/ #{config.root}/app/traits )
...
end
end
As you can see I have added a custom directory, the app/traits directory, where I store some modules that define shared behavior.
If the controller you started working on is in a subdirectory that is not watched by rails or has permissions that stop rails from attaching a file system changed event you get this problem.
The reason that config.reload_classes_only_on_change = false "solves" the problem is that the entire app is reloaded on every request instead of relying on detecting changes to files.
Most likely the controller is not in the watched files list and thats why rails is not reloading it on change. The exact reason why it's not on the list might vary and I need more details about the folder structure and config of the app before I can give a good answer there...

Rails 3.1 set host in test environment

I'm getting http://www.example.com whenever I use root_url in my tests.
It works fine in development, where I have this in config/environments/development.rb:
Rails.application.routes.default_url_options[:host]= 'localhost:3000'
Adding this doesn't work in config/environments/test.rb, though. What should I add to use localhost:3000 as the host in the test environment?
Testing code that depends on default_url_options causes all kinds of problems, see this thread and this issue for examples.
I've solved the problem by patching ActionDispatch::Routing::RouteSet in tests to force rails to include defaults for whatever options I want (in my case locale). See my answer in the github issue linked to above for details.
To override the host option using the same approach:
class ActionDispatch::Routing::RouteSet
def url_for_with_host_fix(options)
url_for_without_host_fix(options.merge(:host => 'localhost:3000'))
end
alias_method_chain :url_for, :host_fix
end
Put this in a file in support, should do the trick.

How to use a route helper method from a file in the lib directory?

I need to use the root_url method from a method defined in a file in the lib folder. Is that possible?
I tried including this line in my class:
include Rails.application.routes.url_helpers
but this gives me the error
Missing host to link to! Please provide :host parameter or set default_url_options[:host]
Edit: I found out that it works if I first initialize the routes:
def initialize_routes
if Rails.env.development? || Rails.env.test?
Rails.application.routes.default_url_options[:host] = 'localhost:3000'
elsif Rails.env.production?
Rails.application.routes.default_url_options[:host] = 'example.com'
end
end
Is there a better way to accomplish this? Maybe setting the routes in a config file?
#JDutil's #3 solution, as mentioned in the comments, is configuring the action mailer and not the router's routes. However, in the configuration you can perform the following:
In config/environments/development.rb and config/environments/test.rb:
MyApp::Application.configure do
# other configuration ...
config.after_initialize do
Rails.application.routes.default_url_options[:host] = 'localhost:3000'
end
end
There should be a couple solutions for this.
1) when using root_url pass in the host param like:
root_url(:host => 'localhost')
That would need to be made to be environment specific though.
2) You should also be able to set the routes default_url_options like:
Rails.application.routes.default_url_options[:host]= 'localhost:3000'
3) Within your environment config files you should set the default_url_options as stated in the error. For example:
In config/environments/development.rb:
config.action_mailer.default_url_options[:host] = 'localhost'
In config/environments/test.rb:
config.action_mailer.default_url_options[:host] = 'localhost'
In config/environments/production.rb:
config.action_mailer.default_url_options[:host] = 'production.com'
If you're relying on url_helpers for something like ActiveModel::Serializers (at least the v0.9.* series) where you'll be generating URLs outside of the main Rails request/response flow, you'll also need to set the Rails.application.routes.default_url_options. I've done it like so,
# config/application.rb
config.after_initialize do
Rails.application.routes.default_url_options = config.action_mailer.default_url_options
end
The url helpers are now available, with a default host, in places like background jobs, etc...
I had this exact same issue and none of the configuration suggestions worked. Thing that left me confused is that url_helpers works just fine from other parts of the application (specifically helpers). So I shouldn't have to insert new configuration to be able to use them elsewhere.
My solution at the end of the day was to do this:
Rails.application.routes.url_helpers.athlete_url(athlete)
What I suspect, though I can't prove, is that including the url helpers manually with:
include Rails.application.routes.url_helpers
actually causes some included block to be executed. Doing so changes the configuration out from under the code trying to use it. By not including, but instead referring to it directly any 'included' blocks are not re-executed.
I'd love to know if anyone can explain if I'm on the right track or just making false assumptions.
Bottom line though, referring to the url_helpers with the full module/class path got the job done.

Resources