Skip to content
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

WIP: Integration of mail_view gem #13332

Merged
merged 3 commits into from
Dec 17, 2013
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
4 changes: 4 additions & 0 deletions actionmailer/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
* Add mailer previews feature based on 37 Signals mail_view gem

*Andrew White*

* Calling `mail()` without arguments serves as getter for the current mail
message and keeps previously set headers.

Expand Down
2 changes: 2 additions & 0 deletions actionmailer/lib/action_mailer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ module ActionMailer
autoload :Base
autoload :DeliveryMethods
autoload :MailHelper
autoload :Preview
autoload :Previews, 'action_mailer/preview'
autoload :TestCase
autoload :TestHelper
end
20 changes: 20 additions & 0 deletions actionmailer/lib/action_mailer/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,25 @@ module ActionMailer
# Note that unless you have a specific reason to do so, you should prefer using before_action
# rather than after_action in your ActionMailer classes so that headers are parsed properly.
#
# = Previewing emails
#
# You can preview your email templates visually by adding a mailer preview file to the
# <tt>ActionMailer::Base.preview_path</tt>. Since most emails do something interesting
# with database data, you'll need to write some scenarios to load messages with fake data:
#
# class NotifierPreview < ActionMailer::Preview
# def welcome
# Notifier.welcome(User.first)
# end
# end
#
# Methods must return a Mail::Message object which can be generated by calling the mailer
# method without the additional <tt>deliver</tt>. The location of the mailer previews
# directory can be configured using the <tt>preview_path</tt> option which has a default
# of <tt>test/mailers/previews</tt>:
#
# config.action_mailer.preview_path = "#{Rails.root}/lib/mailer_previews"
#
# = Configuration options
#
# These options are specified on the class level, like
Expand Down Expand Up @@ -362,6 +381,7 @@ module ActionMailer
# <tt>delivery_method :test</tt>. Most useful for unit and functional testing.
class Base < AbstractController::Base
include DeliveryMethods
include Previews

abstract!

Expand Down
67 changes: 67 additions & 0 deletions actionmailer/lib/action_mailer/preview.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
require 'active_support/descendants_tracker'

module ActionMailer
module Previews #:nodoc:
extend ActiveSupport::Concern

included do
# Set the location of mailer previews through app configuration:
#
# config.action_mailer.preview_path = "#{Rails.root}/lib/mailer_previews"
#
class_attribute :preview_path, instance_writer: false
end
end

class Preview
extend ActiveSupport::DescendantsTracker

class << self
# Returns all mailer preview classes
def all
load_previews if descendants.empty?
descendants
end

# Returns the mail object for the given email name
def call(email)
preview = self.new
preview.public_send(email)
end

# Returns all of the available email previews
def emails
public_instance_methods(false).map(&:to_s).sort
end

# Returns true if the email exists
def email_exists?(email)
emails.include?(email)
end

# Returns true if the preview exists
def exists?(preview)
all.any?{ |p| p.preview_name == preview }
end

# Find a mailer preview by its underscored class name
def find(preview)
all.find{ |p| p.preview_name == preview }
end

# Returns the underscored name of the mailer preview without the suffix
def preview_name
name.sub(/Preview$/, '').underscore
end

protected
def load_previews #:nodoc:
Dir["#{preview_path}/**/*_preview.rb"].each{ |file| require_dependency file }
end

def preview_path #:nodoc:
Base.preview_path
end
end
end
end
8 changes: 8 additions & 0 deletions actionmailer/lib/action_mailer/railtie.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,13 @@ class Railtie < Rails::Railtie # :nodoc:
config.compile_methods! if config.respond_to?(:compile_methods!)
end
end

initializer "action_mailer.configure_mailer_previews", before: :set_autoload_paths do |app|
if Rails.env.development?
options = app.config.action_mailer
options.preview_path ||= defined?(Rails.root) ? "#{Rails.root}/test/mailers/previews" : nil
app.config.autoload_paths << options.preview_path
end
end
end
end
2 changes: 1 addition & 1 deletion actionpack/lib/action_dispatch/routing/inspector.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ def action
end

def internal?
controller.to_s =~ %r{\Arails/(info|welcome)} || path =~ %r{\A#{Rails.application.config.assets.prefix}}
controller.to_s =~ %r{\Arails/(info|mailers|welcome)} || path =~ %r{\A#{Rails.application.config.assets.prefix}}
end

def engine?
Expand Down
1 change: 1 addition & 0 deletions railties/lib/rails.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ module Rails

autoload :Info
autoload :InfoController
autoload :MailersController
autoload :WelcomeController

class << self
Expand Down
2 changes: 2 additions & 0 deletions railties/lib/rails/application/finisher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ module Finisher
initializer :add_builtin_route do |app|
if Rails.env.development?
app.routes.append do
get '/rails/mailers' => "rails/mailers#index"
get '/rails/mailers/*path' => "rails/mailers#preview"
get '/rails/info/properties' => "rails/info#properties"
get '/rails/info/routes' => "rails/info#routes"
get '/rails/info' => "rails/info#index"
Expand Down
16 changes: 16 additions & 0 deletions railties/lib/rails/application_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
class Rails::ApplicationController < ActionController::Base # :nodoc:
self.view_paths = File.expand_path('../templates', __FILE__)
layout 'application'

protected

def require_local!
unless local_request?
render text: '<p>For security purposes, this information is only available to local requests.</p>', status: :forbidden
end
end

def local_request?
Rails.application.config.consider_all_requests_local || request.local?
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,18 @@ module TestUnit # :nodoc:
module Generators # :nodoc:
class MailerGenerator < Base # :nodoc:
argument :actions, type: :array, default: [], banner: "method method"
check_class_collision suffix: "Test"

def check_class_collision
class_collisions "#{class_name}Test", "#{class_name}Preview"
end

def create_test_files
template "functional_test.rb", File.join('test/mailers', class_path, "#{file_name}_test.rb")
end

def create_preview_files
template "preview.rb", File.join('test/mailers/previews', class_path, "#{file_name}_preview.rb")
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<% module_namespacing do -%>
class <%= class_name %>Preview < ActionMailer::Preview
<% actions.each do |action| -%>

def <%= action %>
<%= class_name %>.<%= action %>
end
<% end -%>

end
<% end -%>
18 changes: 3 additions & 15 deletions railties/lib/rails/info_controller.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
require 'rails/application_controller'
require 'action_dispatch/routing/inspector'

class Rails::InfoController < ActionController::Base # :nodoc:
self.view_paths = File.expand_path('../templates', __FILE__)
class Rails::InfoController < Rails::ApplicationController # :nodoc:
prepend_view_path ActionDispatch::DebugExceptions::RESCUES_TEMPLATE_PATH
layout -> { request.xhr? ? nil : 'application' }
layout -> { request.xhr? ? false : 'application' }

before_filter :require_local!

Expand All @@ -20,16 +20,4 @@ def routes
@routes_inspector = ActionDispatch::Routing::RoutesInspector.new(_routes.routes)
@page_title = 'Routes'
end

protected

def require_local!
unless local_request?
render text: '<p>For security purposes, this information is only available to local requests.</p>', status: :forbidden
end
end

def local_request?
Rails.application.config.consider_all_requests_local || request.local?
end
end
73 changes: 73 additions & 0 deletions railties/lib/rails/mailers_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
require 'rails/application_controller'

class Rails::MailersController < Rails::ApplicationController # :nodoc:
prepend_view_path ActionDispatch::DebugExceptions::RESCUES_TEMPLATE_PATH

before_filter :require_local!
before_filter :find_preview, only: :preview

def index
@previews = ActionMailer::Preview.all
@page_title = "Mailer Previews"
end

def preview
if params[:path] == @preview.preview_name
@page_title = "Mailer Previews for #{@preview.preview_name}"
render action: 'mailer'
else
email = File.basename(params[:path])

if @preview.email_exists?(email)
@email = @preview.call(email)

if params[:part]
part_type = Mime::Type.lookup(params[:part])

if part = find_part(part_type)
response.content_type = part_type
render text: part.respond_to?(:decoded) ? part.decoded : part
else
raise AbstractController::ActionNotFound, "Email part '#{part_type}' not found in #{@preview.name}##{email}"
end
else
@part = find_preferred_part(request.format, Mime::HTML, Mime::TEXT)
render action: 'email', layout: false, formats: %w[html]
end
else
raise AbstractController::ActionNotFound, "Email '#{email}' not found in #{@preview.name}"
end
end
end

protected
def find_preview
candidates = []
params[:path].to_s.scan(%r{/|$}){ candidates << $` }
preview = candidates.detect{ |candidate| ActionMailer::Preview.exists?(candidate) }

if preview
@preview = ActionMailer::Preview.find(preview)
else
raise AbstractController::ActionNotFound, "Mailer preview '#{params[:path]}' not found"
end
end

def find_preferred_part(*formats)
if @email.multipart?
formats.each do |format|
return find_part(format) if @email.parts.any?{ |p| p.mime_type == format }
end
else
@email
end
end

def find_part(format)
if @email.multipart?
@email.parts.find{ |p| p.mime_type == format }
elsif @email.mime_type == format
@email
end
end
end
7 changes: 6 additions & 1 deletion railties/lib/rails/templates/layouts/application.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,12 @@
</style>
</head>
<body>
<h2>Your App: <%= link_to 'properties', '/rails/info/properties' %> | <%= link_to 'routes', '/rails/info/routes' %></h2>
<h2>
Your App:
<%= link_to 'mailers', '/rails/mailers' %> |
<%= link_to 'properties', '/rails/info/properties' %> |
<%= link_to 'routes', '/rails/info/routes' %>
</h2>
<%= yield %>

</body>
Expand Down
Loading