Skip to content

Fix regression mailer preview path #1388

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jun 5, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ Enhancements:
* Update `rails_helper` generator with a default check to abort the spec run
when the Rails environment is production. (Aaron Kromer, #1383)

Bug Fixes:

* Fix regression with the railtie resulting in undefined method `preview_path=`
on Rails 3.x and 4.0 (Aaron Kromer, #1388)

### 3.2.2 / 2015-06-03
[Full Changelog](http://github.com/rspec/rspec-rails/compare/v3.2.1...v3.2.2)

Expand Down
18 changes: 16 additions & 2 deletions example_app_generator/generate_action_mailer_specs.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,25 @@ def comment_lines(path, flag, *args)

initializer 'action_mailer.rb', <<-CODE
if ENV['DEFAULT_URL']
Rails.application.configure do
config.action_mailer.default_url_options = { :host => ENV['DEFAULT_URL'] }
if ::Rails::VERSION::STRING < '4.1'
ExampleApp::Application.configure do
config.action_mailer.default_url_options = { :host => ENV['DEFAULT_URL'] }
end
else
Rails.application.configure do
config.action_mailer.default_url_options = { :host => ENV['DEFAULT_URL'] }
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious why this change is related to this PR? Shouldn't we always have been doing this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it should always have been there. Rails changed how they provided access to the configuration in 4.1. Incidentally the mailer specs / script never executed with a code path testing a default url option on Rails < 4.1. Which is also partially related to why this issue wasn't noticed sooner.

After fixing the mailer specs to run better coverage for Rails < 4.1, reproducing the issue, more things started failing as this code path was actually run. Thus, a fix is included here. The commit 9522842 has a long explanation of several related things which got fixed in the spec suite.

end
end

if defined?(ActionMailer)
# This will force the loading of ActionMailer settings
ActionMailer::Base.smtp_settings
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the same on all versions of Rails?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From what I can tell.

end
CODE
gsub_file 'config/initializers/action_mailer.rb',
/ExampleApp/,
Rails.application.class.parent.to_s

copy_file 'spec/support/default_preview_path'
chmod 'spec/support/default_preview_path', 0755
Expand Down
5 changes: 5 additions & 0 deletions example_app_generator/spec/support/default_preview_path
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ require_file_stub 'config/environment' do
module ExampleApp
class Application < Rails::Application
config.eager_load = false
config.eager_load_paths.clear

# Don't care if the mailer can't send.
config.action_mailer.raise_delivery_errors = false unless ENV['NO_ACTION_MAILER']
Expand All @@ -58,3 +59,7 @@ if ENV['DEFAULT_URL']
elsif defined?(::ActionMailer::Preview)
puts Rails.application.config.action_mailer.preview_path
end

# This will force the loading of ActionMailer settings to ensure we do not
# accicentally set something we should not
ActionMailer::Base.smtp_settings
70 changes: 64 additions & 6 deletions example_app_generator/spec/verify_mailer_preview_path_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,25 @@ def ==(str)
end
end

def as_commandline(ops)
cmd, ops = ops.reverse
ops ||= {}
cmd_parts = ops.map { |k, v| "#{k}=#{v}" } << cmd << '2>&1'
cmd_parts.join(' ')
end

def capture_exec(*ops)
io = if RUBY_VERSION.to_f < 1.9
IO.popen(ops.join(' '))
IO.popen(as_commandline(ops))
else
ops << { :err => [:child, :out] }
IO.popen(ops)
end
# Necessary to ignore warnings from Rails code base
out = io.readlines.reject { |line|
line =~ /warning: circular argument reference/
}.join.chomp
out = io.readlines.
reject { |line| line =~ /warning: circular argument reference/ }.
join.
chomp
CaptureExec.new(out, $?.exitstatus)
end

Expand Down Expand Up @@ -176,8 +184,58 @@ def have_no_preview
end
end
else
it 'handles no action mailer preview' do
expect(capture_exec(exec_script)).to have_no_preview
context 'in the development environment' do
let(:custom_env) { { 'RAILS_ENV' => rails_env } }
let(:rails_env) { 'development' }

it 'handles no action mailer preview' do
expect(capture_exec(custom_env, exec_script)).to have_no_preview
end

it 'allows initializers to set options' do
expect(
capture_exec(
custom_env.merge('DEFAULT_URL' => 'test-host'),
exec_script
)
).to eq('test-host')
end

it 'handles action mailer not being available' do
expect(
capture_exec(
custom_env.merge('NO_ACTION_MAILER' => 'true'),
exec_script
)
).to have_no_preview
end
end

context 'in a non-development environment' do
let(:custom_env) { { 'RAILS_ENV' => rails_env } }
let(:rails_env) { 'test' }

it 'handles no action mailer preview' do
expect(capture_exec(custom_env, exec_script)).to have_no_preview
end

it 'allows initializers to set options' do
expect(
capture_exec(
custom_env.merge('DEFAULT_URL' => 'test-host'),
exec_script
)
).to eq('test-host')
end

it 'handles action mailer not being available' do
expect(
capture_exec(
custom_env.merge('NO_ACTION_MAILER' => 'true'),
exec_script
)
).to have_no_preview
end
end
end
end
21 changes: 12 additions & 9 deletions lib/rspec-rails.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,12 @@ class Railtie < ::Rails::Railtie
private

def setup_preview_path(app)
# If the action mailer railtie isn't loaded the config will not respond
return unless supports_action_mailer_previews?(app.config)
options = app.config.action_mailer
config_default_preview_path(options) if config_preview_path?(options)
end

def config_preview_path?(options)
# This string version check avoids loading the ActionMailer class, as
# would happen using `defined?`. This is necessary because the
# ActionMailer class only loads it's settings once, at load time. If we
# load the class now any settings declared in a config block in an
# initializer will be ignored.
#
# We cannot use `respond_to?(:show_previews)` here as it will always
# return `true`.
if ::Rails::VERSION::STRING < '4.2'
Expand All @@ -59,8 +52,18 @@ def config_default_preview_path(options)
end

def supports_action_mailer_previews?(config)
config.respond_to?(:action_mailer) &&
config.action_mailer.respond_to?(:preview_path)
# These checks avoid loading `ActionMailer`. Using `defined?` has the
# side-effect of the class getting loaded if it is available. This is
# problematic because loading `ActionMailer::Base` will cause it to
# read the config settings; this is the only time the config is read.
# If the config is loaded now, any settings declared in a config block
# in an initializer will be ignored.
#
# If the action mailer railtie has not been loaded then `config` will
# not respond to the method. However, we cannot use
# `config.action_mailer.respond_to?(:preview_path)` here as it will
# always return `true`.
config.respond_to?(:action_mailer) && ::Rails::VERSION::STRING > '4.1'
end
end
end
Expand Down