Rails 3.1 set host in test environment - ruby-on-rails

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.

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.

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.

Rails testing default request host

I would like all my unit tests to use www.test.host instead of the default test.host.
I tried setting ENV['HTTP_HOST'] in config/environments/test.rb, but that didn't get it.
My purpose is to avoid a redirect in my controller test, the output of inspecting the response object in my test is:
#<ActionController::TestResponse:0x000001059ed378, ..., #header={"Location"=>"http://www.test.host", ... , #status=301, #body=["<html><body>You are being redirected.</body></html>"], ... , "REQUEST_METHOD"=>"GET", "SERVER_NAME"=>"example.org", "SERVER_PORT"=>"80",... , "HTTP_HOST"=>"test.host", "REMOTE_ADDR"=>"0.0.0.0" ...>>
If it makes a difference, I'm using Rails3 and RSPEC2
You do it by setting request.host in your test. You can do it globally by adding this to your test_helper.rb (in Rails 3.1 anyway, not sure about previous versions, but I think it's similar):
class ActionController::TestCase
setup do
request.host = "www.test.host"
end
end

How should I test request-related logic in Rails development?

I have several before_filters defined in my application controller to handle requests I don't like. One representative example is:
before_filter :reject_www
private
def reject_www
if request.subdomains.include? 'www'
redirect_to 'http://example.com' + request.path, :status => 301
false
end
end
(Returning false skips any following before_filters and simply returns the redirection immediately)
So, two questions:
One, how should I test this functionality? The only testing framework I've used so far is Cucumber + Webrat, which isn't really set up to handle this kind of thing. Is there another framework I should also use to fake requests like this?
Two, is there any way I can try out this functionality myself in my development environment? Since I'm simply browsing the site at localhost:3000, I can't ensure that the above code works in my browser - I'd have to push it to production, hope it works and hope it doesn't mess up anything for anyone in the meantime, which makes me nervous. Is there an alternative?
In a functional test, you can explicitly set the request host. I'm not sure what testing framework you prefer, so here is an example in good ole' Test::Unit.
def test_should_redirect_to_non_www
#request.host = 'www.mydomain.com'
get :index
assert_redirected_to 'http://mydomain.com/'
end
To address #2
You can add entries in your hosts file so that www.mydomain.com points to your local host and then test the logic in your development environment .
You can try hosting it using passenger so that it works on apache .
Hope that helps .

Rails: filter defined in lib file required in environment.rb disappears from filter_chain in production environment. Why?

In my rails application, I have a file in lib that, among other things, sets up a filter that runs on all controllers.
When running under development environment, everything runs fine. However, under production the filter goes missing. Funny thing is, by inspecting the filter_chain, I noticed other filters remain, eg. those defined in plugins, or later in the specific controller class.
I've tested this with both rails edge and v2.3.0.
Testing update:
I've now tested with older rails and found the issue to be present back to v2.1.0, but not in v2.0.5, I've bisect them and found the 986aec5 rails commit to be guilty.
I've isolated the behavior to the following tiny test case:
# app/controllers/foo_controller.rb
class FooController < ApplicationController
def index
render :text => 'not filtered'
end
end
# lib/foobar.rb
ActionController::Base.class_eval do
before_filter :foobar
def foobar
render :text => 'hi from foobar filter'
end
end
# config/environment.rb (at end of file)
require 'foobar'
Here's the output I get when running under the development environment:
$ script/server &
$ curl localhost:3000/foo
> hi from foobar filter
And here's the output for the production environment:
$ script/server -e production &
$ curl localhost:3000/foo
> not filtered
As alluded to before, it works fine for any environment when I do the same thing via plugin. All I need is to put what's under lib/foobar.rb in the plugin's init.rb file.
So in a way I already have a workaround, but I'd like to understand what's going on and what's causing the filter to go missing when in production.
I conjecture it's something in the different ways Rails handles loading in the different environments, but I need to dig deeper.
update
Indeed, I've now narrowed it down to the following config line:
config.cache_classes = false
If, in production.rb, config.cache_classes is changed from true to false, the test application works properly.
I still wonder why class reloading is causing such thing.
Frederick Cheung on the Rails list had the answer:
Because cache_classes does a little more than just that. The way
before filters work, if Foo < Bar then only those filters defined in
Bar at that point will be inherited by Foo - adding them to Bar at a
later date will not do anythingn
In development mode, the app starts, your file is required, the filter
added to ActionController::Base. Later the first request comes along,
the controller is loaded and it inherits that filter.
When cache_classes is true then all of your application classes are
loaded ahead of time. This happens before your file is required, so
all of your controllers already exist when that file is run and so it
has no effect. You could solve this by requiring this file from an
initializer (ensuring it runs before app classes are loaded), but
really why wouldn;t you just put this in application.rb ?
Fred
My real case was actually way more involved, and this is the way I found to solve the issue:
config.after_initialize do
require 'foobar'
end
The after_initialize block runs after the framework has been initialized but before it loads the application files, hence, it'll affect ActionPack::Base after it's been loaded, but before the application controllers are.
I guess that's the generally safe way to deal with all the preloading that goes on in production.
Drop a ruby script in
RAILS_ROOT\config\initializers
that contains
require "foobar.rb"
This invokes the before_filter for me.

Resources