| @@ -0,0 +1,23 @@ | ||
| #!/usr/bin/env jruby | ||
| # | ||
| # This file was generated by RubyGems. | ||
| # | ||
| # The application 'puma' is installed as part of a gem, and | ||
| # this file is here to facilitate running it. | ||
| # | ||
|
|
||
| require 'rubygems' | ||
|
|
||
| version = ">= 0" | ||
|
|
||
| if ARGV.first | ||
| str = ARGV.first | ||
| str = str.dup.force_encoding("BINARY") if str.respond_to? :force_encoding | ||
| if str =~ /\A_(.*)_\z/ and Gem::Version.correct?($1) then | ||
| version = $1 | ||
| ARGV.shift | ||
| end | ||
| end | ||
|
|
||
| gem 'puma', version | ||
| load Gem.bin_path('puma', 'puma', version) |
| @@ -0,0 +1,23 @@ | ||
| #!/usr/bin/env jruby | ||
| # | ||
| # This file was generated by RubyGems. | ||
| # | ||
| # The application 'puma' is installed as part of a gem, and | ||
| # this file is here to facilitate running it. | ||
| # | ||
|
|
||
| require 'rubygems' | ||
|
|
||
| version = ">= 0" | ||
|
|
||
| if ARGV.first | ||
| str = ARGV.first | ||
| str = str.dup.force_encoding("BINARY") if str.respond_to? :force_encoding | ||
| if str =~ /\A_(.*)_\z/ and Gem::Version.correct?($1) then | ||
| version = $1 | ||
| ARGV.shift | ||
| end | ||
| end | ||
|
|
||
| gem 'puma', version | ||
| load Gem.bin_path('puma', 'pumactl', version) |
| @@ -0,0 +1,23 @@ | ||
| #!/usr/bin/env jruby | ||
| # | ||
| # This file was generated by RubyGems. | ||
| # | ||
| # The application 'rack' is installed as part of a gem, and | ||
| # this file is here to facilitate running it. | ||
| # | ||
|
|
||
| require 'rubygems' | ||
|
|
||
| version = ">= 0" | ||
|
|
||
| if ARGV.first | ||
| str = ARGV.first | ||
| str = str.dup.force_encoding("BINARY") if str.respond_to? :force_encoding | ||
| if str =~ /\A_(.*)_\z/ and Gem::Version.correct?($1) then | ||
| version = $1 | ||
| ARGV.shift | ||
| end | ||
| end | ||
|
|
||
| gem 'rack', version | ||
| load Gem.bin_path('rack', 'rackup', version) |
| @@ -0,0 +1,23 @@ | ||
| #!/usr/bin/env jruby | ||
| # | ||
| # This file was generated by RubyGems. | ||
| # | ||
| # The application 'railties' is installed as part of a gem, and | ||
| # this file is here to facilitate running it. | ||
| # | ||
|
|
||
| require 'rubygems' | ||
|
|
||
| version = ">= 0" | ||
|
|
||
| if ARGV.first | ||
| str = ARGV.first | ||
| str = str.dup.force_encoding("BINARY") if str.respond_to? :force_encoding | ||
| if str =~ /\A_(.*)_\z/ and Gem::Version.correct?($1) then | ||
| version = $1 | ||
| ARGV.shift | ||
| end | ||
| end | ||
|
|
||
| gem 'railties', version | ||
| load Gem.bin_path('railties', 'rails', version) |
| @@ -0,0 +1,23 @@ | ||
| #!/usr/bin/env jruby | ||
| # | ||
| # This file was generated by RubyGems. | ||
| # | ||
| # The application 'rake' is installed as part of a gem, and | ||
| # this file is here to facilitate running it. | ||
| # | ||
|
|
||
| require 'rubygems' | ||
|
|
||
| version = ">= 0" | ||
|
|
||
| if ARGV.first | ||
| str = ARGV.first | ||
| str = str.dup.force_encoding("BINARY") if str.respond_to? :force_encoding | ||
| if str =~ /\A_(.*)_\z/ and Gem::Version.correct?($1) then | ||
| version = $1 | ||
| ARGV.shift | ||
| end | ||
| end | ||
|
|
||
| gem 'rake', version | ||
| load Gem.bin_path('rake', 'rake', version) |
| @@ -0,0 +1,23 @@ | ||
| #!/usr/bin/env jruby | ||
| # | ||
| # This file was generated by RubyGems. | ||
| # | ||
| # The application 'rest-client' is installed as part of a gem, and | ||
| # this file is here to facilitate running it. | ||
| # | ||
|
|
||
| require 'rubygems' | ||
|
|
||
| version = ">= 0" | ||
|
|
||
| if ARGV.first | ||
| str = ARGV.first | ||
| str = str.dup.force_encoding("BINARY") if str.respond_to? :force_encoding | ||
| if str =~ /\A_(.*)_\z/ and Gem::Version.correct?($1) then | ||
| version = $1 | ||
| ARGV.shift | ||
| end | ||
| end | ||
|
|
||
| gem 'rest-client', version | ||
| load Gem.bin_path('rest-client', 'restclient', version) |
| @@ -0,0 +1,23 @@ | ||
| #!/usr/bin/env jruby | ||
| # | ||
| # This file was generated by RubyGems. | ||
| # | ||
| # The application 'rspec-core' is installed as part of a gem, and | ||
| # this file is here to facilitate running it. | ||
| # | ||
|
|
||
| require 'rubygems' | ||
|
|
||
| version = ">= 0" | ||
|
|
||
| if ARGV.first | ||
| str = ARGV.first | ||
| str = str.dup.force_encoding("BINARY") if str.respond_to? :force_encoding | ||
| if str =~ /\A_(.*)_\z/ and Gem::Version.correct?($1) then | ||
| version = $1 | ||
| ARGV.shift | ||
| end | ||
| end | ||
|
|
||
| gem 'rspec-core', version | ||
| load Gem.bin_path('rspec-core', 'rspec', version) |
| @@ -0,0 +1,23 @@ | ||
| #!/usr/bin/env jruby | ||
| # | ||
| # This file was generated by RubyGems. | ||
| # | ||
| # The application 'rubocop' is installed as part of a gem, and | ||
| # this file is here to facilitate running it. | ||
| # | ||
|
|
||
| require 'rubygems' | ||
|
|
||
| version = ">= 0" | ||
|
|
||
| if ARGV.first | ||
| str = ARGV.first | ||
| str = str.dup.force_encoding("BINARY") if str.respond_to? :force_encoding | ||
| if str =~ /\A_(.*)_\z/ and Gem::Version.correct?($1) then | ||
| version = $1 | ||
| ARGV.shift | ||
| end | ||
| end | ||
|
|
||
| gem 'rubocop', version | ||
| load Gem.bin_path('rubocop', 'rubocop', version) |
| @@ -0,0 +1,23 @@ | ||
| #!/usr/bin/env jruby | ||
| # | ||
| # This file was generated by RubyGems. | ||
| # | ||
| # The application 'parser' is installed as part of a gem, and | ||
| # this file is here to facilitate running it. | ||
| # | ||
|
|
||
| require 'rubygems' | ||
|
|
||
| version = ">= 0" | ||
|
|
||
| if ARGV.first | ||
| str = ARGV.first | ||
| str = str.dup.force_encoding("BINARY") if str.respond_to? :force_encoding | ||
| if str =~ /\A_(.*)_\z/ and Gem::Version.correct?($1) then | ||
| version = $1 | ||
| ARGV.shift | ||
| end | ||
| end | ||
|
|
||
| gem 'parser', version | ||
| load Gem.bin_path('parser', 'ruby-parse', version) |
| @@ -0,0 +1,23 @@ | ||
| #!/usr/bin/env jruby | ||
| # | ||
| # This file was generated by RubyGems. | ||
| # | ||
| # The application 'parser' is installed as part of a gem, and | ||
| # this file is here to facilitate running it. | ||
| # | ||
|
|
||
| require 'rubygems' | ||
|
|
||
| version = ">= 0" | ||
|
|
||
| if ARGV.first | ||
| str = ARGV.first | ||
| str = str.dup.force_encoding("BINARY") if str.respond_to? :force_encoding | ||
| if str =~ /\A_(.*)_\z/ and Gem::Version.correct?($1) then | ||
| version = $1 | ||
| ARGV.shift | ||
| end | ||
| end | ||
|
|
||
| gem 'parser', version | ||
| load Gem.bin_path('parser', 'ruby-rewrite', version) |
| @@ -0,0 +1,25 @@ | ||
| #!/bin/sh | ||
| 'exec' "jruby" '-x' "$0" "$@" | ||
| #!/Users/ma/.rbenv/versions/jruby-9.0.5.0/bin/jruby | ||
| # | ||
| # This file was generated by RubyGems. | ||
| # | ||
| # The application 'ruby_parser' is installed as part of a gem, and | ||
| # this file is here to facilitate running it. | ||
| # | ||
|
|
||
| require 'rubygems' | ||
|
|
||
| version = ">= 0" | ||
|
|
||
| if ARGV.first | ||
| str = ARGV.first | ||
| str = str.dup.force_encoding("BINARY") if str.respond_to? :force_encoding | ||
| if str =~ /\A_(.*)_\z/ and Gem::Version.correct?($1) then | ||
| version = $1 | ||
| ARGV.shift | ||
| end | ||
| end | ||
|
|
||
| gem 'ruby_parser', version | ||
| load Gem.bin_path('ruby_parser', 'ruby_parse', version) |
| @@ -0,0 +1,25 @@ | ||
| #!/bin/sh | ||
| 'exec' "jruby" '-x' "$0" "$@" | ||
| #!/Users/ma/.rbenv/versions/jruby-9.0.5.0/bin/jruby | ||
| # | ||
| # This file was generated by RubyGems. | ||
| # | ||
| # The application 'ruby_parser' is installed as part of a gem, and | ||
| # this file is here to facilitate running it. | ||
| # | ||
|
|
||
| require 'rubygems' | ||
|
|
||
| version = ">= 0" | ||
|
|
||
| if ARGV.first | ||
| str = ARGV.first | ||
| str = str.dup.force_encoding("BINARY") if str.respond_to? :force_encoding | ||
| if str =~ /\A_(.*)_\z/ and Gem::Version.correct?($1) then | ||
| version = $1 | ||
| ARGV.shift | ||
| end | ||
| end | ||
|
|
||
| gem 'ruby_parser', version | ||
| load Gem.bin_path('ruby_parser', 'ruby_parse_extract_error', version) |
| @@ -0,0 +1,23 @@ | ||
| #!/usr/bin/env jruby | ||
| # | ||
| # This file was generated by RubyGems. | ||
| # | ||
| # The application 'sass' is installed as part of a gem, and | ||
| # this file is here to facilitate running it. | ||
| # | ||
|
|
||
| require 'rubygems' | ||
|
|
||
| version = ">= 0" | ||
|
|
||
| if ARGV.first | ||
| str = ARGV.first | ||
| str = str.dup.force_encoding("BINARY") if str.respond_to? :force_encoding | ||
| if str =~ /\A_(.*)_\z/ and Gem::Version.correct?($1) then | ||
| version = $1 | ||
| ARGV.shift | ||
| end | ||
| end | ||
|
|
||
| gem 'sass', version | ||
| load Gem.bin_path('sass', 'sass', version) |
| @@ -0,0 +1,23 @@ | ||
| #!/usr/bin/env jruby | ||
| # | ||
| # This file was generated by RubyGems. | ||
| # | ||
| # The application 'sass' is installed as part of a gem, and | ||
| # this file is here to facilitate running it. | ||
| # | ||
|
|
||
| require 'rubygems' | ||
|
|
||
| version = ">= 0" | ||
|
|
||
| if ARGV.first | ||
| str = ARGV.first | ||
| str = str.dup.force_encoding("BINARY") if str.respond_to? :force_encoding | ||
| if str =~ /\A_(.*)_\z/ and Gem::Version.correct?($1) then | ||
| version = $1 | ||
| ARGV.shift | ||
| end | ||
| end | ||
|
|
||
| gem 'sass', version | ||
| load Gem.bin_path('sass', 'sass-convert', version) |
| @@ -0,0 +1,23 @@ | ||
| #!/usr/bin/env jruby | ||
| # | ||
| # This file was generated by RubyGems. | ||
| # | ||
| # The application 'sass' is installed as part of a gem, and | ||
| # this file is here to facilitate running it. | ||
| # | ||
|
|
||
| require 'rubygems' | ||
|
|
||
| version = ">= 0" | ||
|
|
||
| if ARGV.first | ||
| str = ARGV.first | ||
| str = str.dup.force_encoding("BINARY") if str.respond_to? :force_encoding | ||
| if str =~ /\A_(.*)_\z/ and Gem::Version.correct?($1) then | ||
| version = $1 | ||
| ARGV.shift | ||
| end | ||
| end | ||
|
|
||
| gem 'sass', version | ||
| load Gem.bin_path('sass', 'scss', version) |
| @@ -0,0 +1,23 @@ | ||
| #!/usr/bin/env jruby | ||
| # | ||
| # This file was generated by RubyGems. | ||
| # | ||
| # The application 'sprockets' is installed as part of a gem, and | ||
| # this file is here to facilitate running it. | ||
| # | ||
|
|
||
| require 'rubygems' | ||
|
|
||
| version = ">= 0" | ||
|
|
||
| if ARGV.first | ||
| str = ARGV.first | ||
| str = str.dup.force_encoding("BINARY") if str.respond_to? :force_encoding | ||
| if str =~ /\A_(.*)_\z/ and Gem::Version.correct?($1) then | ||
| version = $1 | ||
| ARGV.shift | ||
| end | ||
| end | ||
|
|
||
| gem 'sprockets', version | ||
| load Gem.bin_path('sprockets', 'sprockets', version) |
| @@ -0,0 +1,23 @@ | ||
| #!/usr/bin/env jruby | ||
| # | ||
| # This file was generated by RubyGems. | ||
| # | ||
| # The application 'thor' is installed as part of a gem, and | ||
| # this file is here to facilitate running it. | ||
| # | ||
|
|
||
| require 'rubygems' | ||
|
|
||
| version = ">= 0" | ||
|
|
||
| if ARGV.first | ||
| str = ARGV.first | ||
| str = str.dup.force_encoding("BINARY") if str.respond_to? :force_encoding | ||
| if str =~ /\A_(.*)_\z/ and Gem::Version.correct?($1) then | ||
| version = $1 | ||
| ARGV.shift | ||
| end | ||
| end | ||
|
|
||
| gem 'thor', version | ||
| load Gem.bin_path('thor', 'thor', version) |
| @@ -0,0 +1,23 @@ | ||
| #!/usr/bin/env jruby | ||
| # | ||
| # This file was generated by RubyGems. | ||
| # | ||
| # The application 'tilt' is installed as part of a gem, and | ||
| # this file is here to facilitate running it. | ||
| # | ||
|
|
||
| require 'rubygems' | ||
|
|
||
| version = ">= 0" | ||
|
|
||
| if ARGV.first | ||
| str = ARGV.first | ||
| str = str.dup.force_encoding("BINARY") if str.respond_to? :force_encoding | ||
| if str =~ /\A_(.*)_\z/ and Gem::Version.correct?($1) then | ||
| version = $1 | ||
| ARGV.shift | ||
| end | ||
| end | ||
|
|
||
| gem 'tilt', version | ||
| load Gem.bin_path('tilt', 'tilt', version) |
| @@ -0,0 +1,23 @@ | ||
| #!/usr/bin/env jruby | ||
| # | ||
| # This file was generated by RubyGems. | ||
| # | ||
| # The application 'zencoder-fetcher' is installed as part of a gem, and | ||
| # this file is here to facilitate running it. | ||
| # | ||
|
|
||
| require 'rubygems' | ||
|
|
||
| version = ">= 0" | ||
|
|
||
| if ARGV.first | ||
| str = ARGV.first | ||
| str = str.dup.force_encoding("BINARY") if str.respond_to? :force_encoding | ||
| if str =~ /\A_(.*)_\z/ and Gem::Version.correct?($1) then | ||
| version = $1 | ||
| ARGV.shift | ||
| end | ||
| end | ||
|
|
||
| gem 'zencoder-fetcher', version | ||
| load Gem.bin_path('zencoder-fetcher', 'zencoder_fetcher', version) |
| @@ -0,0 +1,132 @@ | ||
| ## Rails 4.2.7 (July 12, 2016) ## | ||
|
|
||
| * Removes `-t` from default Sendmail arguments to match the underlying | ||
| `Mail::Sendmail` setting. | ||
|
|
||
| *Clayton Liggitt* | ||
|
|
||
|
|
||
| ## Rails 4.2.6 (March 07, 2016) ## | ||
|
|
||
| * No changes. | ||
|
|
||
|
|
||
| ## Rails 4.2.5.2 (February 26, 2016) ## | ||
|
|
||
| * No changes. | ||
|
|
||
|
|
||
| ## Rails 4.2.5.1 (January 25, 2015) ## | ||
|
|
||
| * No changes. | ||
|
|
||
|
|
||
| ## Rails 4.2.5 (November 12, 2015) ## | ||
|
|
||
| * No changes. | ||
|
|
||
|
|
||
| ## Rails 4.2.4 (August 24, 2015) ## | ||
|
|
||
| * No Changes * | ||
|
|
||
|
|
||
| ## Rails 4.2.3 (June 25, 2015) ## | ||
|
|
||
| * `assert_emails` in block form use the given number as expected value. | ||
| This makes the error message much easier to understand. | ||
|
|
||
| *Yuji Yaginuma* | ||
|
|
||
| * Mailer preview now uses `url_for` to fix links to emails for apps running on | ||
| a subdirectory. | ||
|
|
||
| *Remo Mueller* | ||
|
|
||
| * Mailer previews no longer crash when the `mail` method wasn't called | ||
| (`NullMail`). | ||
|
|
||
| Fixes #19849. | ||
|
|
||
| *Yves Senn* | ||
|
|
||
| * Make sure labels and values line up in mailer previews. | ||
|
|
||
| *Yves Senn* | ||
|
|
||
|
|
||
| ## Rails 4.2.2 (June 16, 2015) ## | ||
|
|
||
| * No Changes * | ||
|
|
||
|
|
||
| ## Rails 4.2.1 (March 19, 2015) ## | ||
|
|
||
| * No Changes * | ||
|
|
||
|
|
||
| ## Rails 4.2.0 (December 20, 2014) ## | ||
|
|
||
| * `MailerGenerator` now generates layouts by default. The HTML mailer layout | ||
| now includes `<html>` and `<body>` tags which improve the spam rating in | ||
| some spam detection engines. Mailers now inherit from `ApplicationMailer` | ||
| which sets the default layout. | ||
|
|
||
| *Andy Jeffries* | ||
|
|
||
| * `link_to` and `url_for` now generate URLs by default in templates. | ||
| Passing `only_path: false` is no longer needed. | ||
|
|
||
| Fixes #16497 and #16589. | ||
|
|
||
| *Xavier Noria*, *Richard Schneeman* | ||
|
|
||
| * Attachments can now be added while rendering the mail template. | ||
|
|
||
| Fixes #16974. | ||
|
|
||
| *Christian Felder* | ||
|
|
||
| * Add `#deliver_later` and `#deliver_now` methods and deprecate `#deliver` in | ||
| favor of `#deliver_now`. `#deliver_later` will enqueue a job to render and | ||
| deliver the mail instead of delivering it immediately. The job is enqueued | ||
| using the new Active Job framework in Rails and will use the queue that you | ||
| have configured in Rails. | ||
|
|
||
| *DHH*, *Abdelkader Boudih*, *Cristian Bica* | ||
|
|
||
| * `ActionMailer::Previews` are now class methods instead of instance methods. | ||
|
|
||
| *Cristian Bica* | ||
|
|
||
| * Deprecate `*_path` helpers in email views. They generated broken links in | ||
| email views and were not the intention of most developers. The `*_url` | ||
| helper is recommended instead. | ||
|
|
||
| *Richard Schneeman* | ||
|
|
||
| * Raise an exception when attachments are added after `mail` is called. | ||
| This is a safeguard to prevent invalid emails. | ||
|
|
||
| Fixes #16163. | ||
|
|
||
| *Yves Senn* | ||
|
|
||
| * Add `config.action_mailer.show_previews` configuration option. | ||
|
|
||
| This configuration option can be used to enable the mail preview in | ||
| environments other than development (such as staging). | ||
|
|
||
| Defaults to `true` in development and `false` elsewhere. | ||
|
|
||
| *Leonard Garvey* | ||
|
|
||
| * Allow preview interceptors to be registered through | ||
| `config.action_mailer.preview_interceptors`. | ||
|
|
||
| See #15739. | ||
|
|
||
| *Yves Senn* | ||
|
|
||
| Please check [4-1-stable](https://github.com/rails/rails/blob/4-1-stable/actionmailer/CHANGELOG.md) | ||
| for previous changes. |
| @@ -0,0 +1,21 @@ | ||
| Copyright (c) 2004-2014 David Heinemeier Hansson | ||
|
|
||
| Permission is hereby granted, free of charge, to any person obtaining | ||
| a copy of this software and associated documentation files (the | ||
| "Software"), to deal in the Software without restriction, including | ||
| without limitation the rights to use, copy, modify, merge, publish, | ||
| distribute, sublicense, and/or sell copies of the Software, and to | ||
| permit persons to whom the Software is furnished to do so, subject to | ||
| the following conditions: | ||
|
|
||
| The above copyright notice and this permission notice shall be | ||
| included in all copies or substantial portions of the Software. | ||
|
|
||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
| EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
| MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
| NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | ||
| LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
| OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
| WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
|
|
| @@ -0,0 +1,176 @@ | ||
| = Action Mailer -- Easy email delivery and testing | ||
|
|
||
| Action Mailer is a framework for designing email service layers. These layers | ||
| are used to consolidate code for sending out forgotten passwords, welcome | ||
| wishes on signup, invoices for billing, and any other use case that requires | ||
| a written notification to either a person or another system. | ||
|
|
||
| Action Mailer is in essence a wrapper around Action Controller and the | ||
| Mail gem. It provides a way to make emails using templates in the same | ||
| way that Action Controller renders views using templates. | ||
|
|
||
| Additionally, an Action Mailer class can be used to process incoming email, | ||
| such as allowing a blog to accept new posts from an email (which could even | ||
| have been sent from a phone). | ||
|
|
||
| == Sending emails | ||
|
|
||
| The framework works by initializing any instance variables you want to be | ||
| available in the email template, followed by a call to +mail+ to deliver | ||
| the email. | ||
|
|
||
| This can be as simple as: | ||
|
|
||
| class Notifier < ActionMailer::Base | ||
| default from: 'system@loudthinking.com' | ||
|
|
||
| def welcome(recipient) | ||
| @recipient = recipient | ||
| mail(to: recipient, | ||
| subject: "[Signed up] Welcome #{recipient}") | ||
| end | ||
| end | ||
|
|
||
| The body of the email is created by using an Action View template (regular | ||
| ERB) that has the instance variables that are declared in the mailer action. | ||
|
|
||
| So the corresponding body template for the method above could look like this: | ||
|
|
||
| Hello there, | ||
|
|
||
| Mr. <%= @recipient %> | ||
|
|
||
| Thank you for signing up! | ||
|
|
||
| If the recipient was given as "david@loudthinking.com", the email | ||
| generated would look like this: | ||
|
|
||
| Date: Mon, 25 Jan 2010 22:48:09 +1100 | ||
| From: system@loudthinking.com | ||
| To: david@loudthinking.com | ||
| Message-ID: <4b5d84f9dd6a5_7380800b81ac29578@void.loudthinking.com.mail> | ||
| Subject: [Signed up] Welcome david@loudthinking.com | ||
| Mime-Version: 1.0 | ||
| Content-Type: text/plain; | ||
| charset="US-ASCII"; | ||
| Content-Transfer-Encoding: 7bit | ||
|
|
||
| Hello there, | ||
|
|
||
| Mr. david@loudthinking.com | ||
|
|
||
| Thank you for signing up! | ||
|
|
||
| In order to send mails, you simply call the method and then call +deliver_now+ on the return value. | ||
|
|
||
| Calling the method returns a Mail Message object: | ||
|
|
||
| message = Notifier.welcome("david@loudthinking.com") # => Returns a Mail::Message object | ||
| message.deliver_now # => delivers the email | ||
|
|
||
| Or you can just chain the methods together like: | ||
|
|
||
| Notifier.welcome("david@loudthinking.com").deliver_now # Creates the email and sends it immediately | ||
|
|
||
| == Setting defaults | ||
|
|
||
| It is possible to set default values that will be used in every method in your | ||
| Action Mailer class. To implement this functionality, you just call the public | ||
| class method +default+ which you get for free from <tt>ActionMailer::Base</tt>. | ||
| This method accepts a Hash as the parameter. You can use any of the headers, | ||
| email messages have, like +:from+ as the key. You can also pass in a string as | ||
| the key, like "Content-Type", but Action Mailer does this out of the box for you, | ||
| so you won't need to worry about that. Finally, it is also possible to pass in a | ||
| Proc that will get evaluated when it is needed. | ||
|
|
||
| Note that every value you set with this method will get overwritten if you use the | ||
| same key in your mailer method. | ||
|
|
||
| Example: | ||
|
|
||
| class AuthenticationMailer < ActionMailer::Base | ||
| default from: "awesome@application.com", subject: Proc.new { "E-mail was generated at #{Time.now}" } | ||
| ..... | ||
| end | ||
|
|
||
| == Receiving emails | ||
|
|
||
| To receive emails, you need to implement a public instance method called | ||
| +receive+ that takes an email object as its single parameter. The Action Mailer | ||
| framework has a corresponding class method, which is also called +receive+, that | ||
| accepts a raw, unprocessed email as a string, which it then turns into the email | ||
| object and calls the receive instance method. | ||
|
|
||
| Example: | ||
|
|
||
| class Mailman < ActionMailer::Base | ||
| def receive(email) | ||
| page = Page.find_by(address: email.to.first) | ||
| page.emails.create( | ||
| subject: email.subject, body: email.body | ||
| ) | ||
|
|
||
| if email.has_attachments? | ||
| email.attachments.each do |attachment| | ||
| page.attachments.create({ | ||
| file: attachment, description: email.subject | ||
| }) | ||
| end | ||
| end | ||
| end | ||
| end | ||
|
|
||
| This Mailman can be the target for Postfix or other MTAs. In Rails, you would use | ||
| the runner in the trivial case like this: | ||
|
|
||
| rails runner 'Mailman.receive(STDIN.read)' | ||
|
|
||
| However, invoking Rails in the runner for each mail to be received is very | ||
| resource intensive. A single instance of Rails should be run within a daemon, if | ||
| it is going to process more than just a limited amount of email. | ||
|
|
||
| == Configuration | ||
|
|
||
| The Base class has the full list of configuration options. Here's an example: | ||
|
|
||
| ActionMailer::Base.smtp_settings = { | ||
| address: 'smtp.yourserver.com', # default: localhost | ||
| port: '25', # default: 25 | ||
| user_name: 'user', | ||
| password: 'pass', | ||
| authentication: :plain # :plain, :login or :cram_md5 | ||
| } | ||
|
|
||
|
|
||
| == Download and installation | ||
|
|
||
| The latest version of Action Mailer can be installed with RubyGems: | ||
|
|
||
| % [sudo] gem install actionmailer | ||
|
|
||
| Source code can be downloaded as part of the Rails project on GitHub | ||
|
|
||
| * https://github.com/rails/rails/tree/4-2-stable/actionmailer | ||
|
|
||
|
|
||
| == License | ||
|
|
||
| Action Mailer is released under the MIT license: | ||
|
|
||
| * http://www.opensource.org/licenses/MIT | ||
|
|
||
|
|
||
| == Support | ||
|
|
||
| API documentation is at | ||
|
|
||
| * http://api.rubyonrails.org | ||
|
|
||
| Bug reports can be filed for the Ruby on Rails project here: | ||
|
|
||
| * https://github.com/rails/rails/issues | ||
|
|
||
| Feature requests should be discussed on the rails-core mailing list here: | ||
|
|
||
| * https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core | ||
|
|
| @@ -0,0 +1,58 @@ | ||
| #-- | ||
| # Copyright (c) 2004-2014 David Heinemeier Hansson | ||
| # | ||
| # Permission is hereby granted, free of charge, to any person obtaining | ||
| # a copy of this software and associated documentation files (the | ||
| # "Software"), to deal in the Software without restriction, including | ||
| # without limitation the rights to use, copy, modify, merge, publish, | ||
| # distribute, sublicense, and/or sell copies of the Software, and to | ||
| # permit persons to whom the Software is furnished to do so, subject to | ||
| # the following conditions: | ||
| # | ||
| # The above copyright notice and this permission notice shall be | ||
| # included in all copies or substantial portions of the Software. | ||
| # | ||
| # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
| # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
| # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
| # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | ||
| # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
| # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
| # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| #++ | ||
|
|
||
| require 'abstract_controller' | ||
| require 'action_mailer/version' | ||
|
|
||
| # Common Active Support usage in Action Mailer | ||
| require 'active_support/rails' | ||
| require 'active_support/core_ext/class' | ||
| require 'active_support/core_ext/module/attr_internal' | ||
| require 'active_support/core_ext/string/inflections' | ||
| require 'active_support/lazy_load_hooks' | ||
|
|
||
| module ActionMailer | ||
| extend ::ActiveSupport::Autoload | ||
|
|
||
| eager_autoload do | ||
| autoload :Collector | ||
| end | ||
|
|
||
| autoload :Base | ||
| autoload :DeliveryMethods | ||
| autoload :InlinePreviewInterceptor | ||
| autoload :MailHelper | ||
| autoload :Preview | ||
| autoload :Previews, 'action_mailer/preview' | ||
| autoload :TestCase | ||
| autoload :TestHelper | ||
| autoload :MessageDelivery | ||
| autoload :DeliveryJob | ||
| end | ||
|
|
||
| autoload :Mime, 'action_dispatch/http/mime_type' | ||
|
|
||
| ActiveSupport.on_load(:action_view) do | ||
| ActionView::Base.default_formats ||= Mime::SET.symbols | ||
| ActionView::Template::Types.delegate_to Mime | ||
| end |
| @@ -0,0 +1,30 @@ | ||
| require 'abstract_controller/collector' | ||
| require 'active_support/core_ext/hash/reverse_merge' | ||
| require 'active_support/core_ext/array/extract_options' | ||
|
|
||
| module ActionMailer | ||
| class Collector | ||
| include AbstractController::Collector | ||
| attr_reader :responses | ||
|
|
||
| def initialize(context, &block) | ||
| @context = context | ||
| @responses = [] | ||
| @default_render = block | ||
| end | ||
|
|
||
| def any(*args, &block) | ||
| options = args.extract_options! | ||
| raise ArgumentError, "You have to supply at least one format" if args.empty? | ||
| args.each { |type| send(type, options.dup, &block) } | ||
| end | ||
| alias :all :any | ||
|
|
||
| def custom(mime, options = {}) | ||
| options.reverse_merge!(content_type: mime.to_s) | ||
| @context.formats = [mime.to_sym] | ||
| options[:body] = block_given? ? yield : @default_render.call | ||
| @responses << options | ||
| end | ||
| end | ||
| end |
| @@ -0,0 +1,13 @@ | ||
| require 'active_job' | ||
|
|
||
| module ActionMailer | ||
| # The <tt>ActionMailer::DeliveryJob</tt> class is used when you | ||
| # want to send emails outside of the request-response cycle. | ||
| class DeliveryJob < ActiveJob::Base # :nodoc: | ||
| queue_as :mailers | ||
|
|
||
| def perform(mailer, mail_method, delivery_method, *args) # :nodoc: | ||
| mailer.constantize.public_send(mail_method, *args).send(delivery_method) | ||
| end | ||
| end | ||
| end |
| @@ -0,0 +1,84 @@ | ||
| require 'tmpdir' | ||
|
|
||
| module ActionMailer | ||
| # This module handles everything related to mail delivery, from registering | ||
| # new delivery methods to configuring the mail object to be sent. | ||
| module DeliveryMethods | ||
| extend ActiveSupport::Concern | ||
|
|
||
| included do | ||
| class_attribute :delivery_methods, :delivery_method | ||
|
|
||
| # Do not make this inheritable, because we always want it to propagate | ||
| cattr_accessor :raise_delivery_errors | ||
| self.raise_delivery_errors = true | ||
|
|
||
| cattr_accessor :perform_deliveries | ||
| self.perform_deliveries = true | ||
|
|
||
| self.delivery_methods = {}.freeze | ||
| self.delivery_method = :smtp | ||
|
|
||
| add_delivery_method :smtp, Mail::SMTP, | ||
| address: "localhost", | ||
| port: 25, | ||
| domain: 'localhost.localdomain', | ||
| user_name: nil, | ||
| password: nil, | ||
| authentication: nil, | ||
| enable_starttls_auto: true | ||
|
|
||
| add_delivery_method :file, Mail::FileDelivery, | ||
| location: defined?(Rails.root) ? "#{Rails.root}/tmp/mails" : "#{Dir.tmpdir}/mails" | ||
|
|
||
| add_delivery_method :sendmail, Mail::Sendmail, | ||
| location: '/usr/sbin/sendmail', | ||
| arguments: '-i' | ||
|
|
||
| add_delivery_method :test, Mail::TestMailer | ||
| end | ||
|
|
||
| # Helpers for creating and wrapping delivery behavior, used by DeliveryMethods. | ||
| module ClassMethods | ||
| # Provides a list of emails that have been delivered by Mail::TestMailer | ||
| delegate :deliveries, :deliveries=, to: Mail::TestMailer | ||
|
|
||
| # Adds a new delivery method through the given class using the given | ||
| # symbol as alias and the default options supplied. | ||
| # | ||
| # add_delivery_method :sendmail, Mail::Sendmail, | ||
| # location: '/usr/sbin/sendmail', | ||
| # arguments: '-i -t' | ||
| def add_delivery_method(symbol, klass, default_options={}) | ||
| class_attribute(:"#{symbol}_settings") unless respond_to?(:"#{symbol}_settings") | ||
| send(:"#{symbol}_settings=", default_options) | ||
| self.delivery_methods = delivery_methods.merge(symbol.to_sym => klass).freeze | ||
| end | ||
|
|
||
| def wrap_delivery_behavior(mail, method=nil, options=nil) # :nodoc: | ||
| method ||= self.delivery_method | ||
| mail.delivery_handler = self | ||
|
|
||
| case method | ||
| when NilClass | ||
| raise "Delivery method cannot be nil" | ||
| when Symbol | ||
| if klass = delivery_methods[method] | ||
| mail.delivery_method(klass, (send(:"#{method}_settings") || {}).merge(options || {})) | ||
| else | ||
| raise "Invalid delivery method #{method.inspect}" | ||
| end | ||
| else | ||
| mail.delivery_method(method) | ||
| end | ||
|
|
||
| mail.perform_deliveries = perform_deliveries | ||
| mail.raise_delivery_errors = raise_delivery_errors | ||
| end | ||
| end | ||
|
|
||
| def wrap_delivery_behavior!(*args) # :nodoc: | ||
| self.class.wrap_delivery_behavior(message, *args) | ||
| end | ||
| end | ||
| end |
| @@ -0,0 +1,15 @@ | ||
| module ActionMailer | ||
| # Returns the version of the currently loaded Action Mailer as a <tt>Gem::Version</tt> | ||
| def self.gem_version | ||
| Gem::Version.new VERSION::STRING | ||
| end | ||
|
|
||
| module VERSION | ||
| MAJOR = 4 | ||
| MINOR = 2 | ||
| TINY = 7 | ||
| PRE = "1" | ||
|
|
||
| STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".") | ||
| end | ||
| end |
| @@ -0,0 +1,61 @@ | ||
| require 'base64' | ||
|
|
||
| module ActionMailer | ||
| # Implements a mailer preview interceptor that converts image tag src attributes | ||
| # that use inline cid: style urls to data: style urls so that they are visible | ||
| # when previewing a HTML email in a web browser. | ||
| # | ||
| # This interceptor is not enabled by default, to use it just register it like any | ||
| # other mailer preview interceptor: | ||
| # | ||
| # ActionMailer::Base.register_preview_interceptor(ActionMailer::InlinePreviewInterceptor) | ||
| # | ||
| class InlinePreviewInterceptor | ||
| PATTERN = /src=(?:"cid:[^"]+"|'cid:[^']+')/i | ||
|
|
||
| include Base64 | ||
|
|
||
| def self.previewing_email(message) #:nodoc: | ||
| new(message).transform! | ||
| end | ||
|
|
||
| def initialize(message) #:nodoc: | ||
| @message = message | ||
| end | ||
|
|
||
| def transform! #:nodoc: | ||
| return message if html_part.blank? | ||
|
|
||
| html_source.gsub!(PATTERN) do |match| | ||
| if part = find_part(match[9..-2]) | ||
| %[src="#{data_url(part)}"] | ||
| else | ||
| match | ||
| end | ||
| end | ||
|
|
||
| message | ||
| end | ||
|
|
||
| private | ||
| def message | ||
| @message | ||
| end | ||
|
|
||
| def html_part | ||
| @html_part ||= message.html_part | ||
| end | ||
|
|
||
| def html_source | ||
| html_part.body.raw_source | ||
| end | ||
|
|
||
| def data_url(part) | ||
| "data:#{part.mime_type};base64,#{strict_encode64(part.body.raw_source)}" | ||
| end | ||
|
|
||
| def find_part(cid) | ||
| message.all_parts.find{ |p| p.attachment? && p.cid == cid } | ||
| end | ||
| end | ||
| end |
| @@ -0,0 +1,39 @@ | ||
| require 'active_support/log_subscriber' | ||
|
|
||
| module ActionMailer | ||
| # Implements the ActiveSupport::LogSubscriber for logging notifications when | ||
| # email is delivered and received. | ||
| class LogSubscriber < ActiveSupport::LogSubscriber | ||
| # An email was delivered. | ||
| def deliver(event) | ||
| info do | ||
| recipients = Array(event.payload[:to]).join(', ') | ||
| "\nSent mail to #{recipients} (#{event.duration.round(1)}ms)" | ||
| end | ||
|
|
||
| debug { event.payload[:mail] } | ||
| end | ||
|
|
||
| # An email was received. | ||
| def receive(event) | ||
| info { "\nReceived mail (#{event.duration.round(1)}ms)" } | ||
| debug { event.payload[:mail] } | ||
| end | ||
|
|
||
| # An email was generated. | ||
| def process(event) | ||
| debug do | ||
| mailer = event.payload[:mailer] | ||
| action = event.payload[:action] | ||
| "\n#{mailer}##{action}: processed outbound mail in #{event.duration.round(1)}ms" | ||
| end | ||
| end | ||
|
|
||
| # Use the logger configured for ActionMailer::Base | ||
| def logger | ||
| ActionMailer::Base.logger | ||
| end | ||
| end | ||
| end | ||
|
|
||
| ActionMailer::LogSubscriber.attach_to :action_mailer |
| @@ -0,0 +1,58 @@ | ||
| module ActionMailer | ||
| # Provides helper methods for ActionMailer::Base that can be used for easily | ||
| # formatting messages, accessing mailer or message instances, and the | ||
| # attachments list. | ||
| module MailHelper | ||
| # Take the text and format it, indented two spaces for each line, and | ||
| # wrapped at 72 columns. | ||
| def block_format(text) | ||
| formatted = text.split(/\n\r?\n/).collect { |paragraph| | ||
| format_paragraph(paragraph) | ||
| }.join("\n\n") | ||
|
|
||
| # Make list points stand on their own line | ||
| formatted.gsub!(/[ ]*([*]+) ([^*]*)/) { " #{$1} #{$2.strip}\n" } | ||
| formatted.gsub!(/[ ]*([#]+) ([^#]*)/) { " #{$1} #{$2.strip}\n" } | ||
|
|
||
| formatted | ||
| end | ||
|
|
||
| # Access the mailer instance. | ||
| def mailer | ||
| @_controller | ||
| end | ||
|
|
||
| # Access the message instance. | ||
| def message | ||
| @_message | ||
| end | ||
|
|
||
| # Access the message attachments list. | ||
| def attachments | ||
| mailer.attachments | ||
| end | ||
|
|
||
| # Returns +text+ wrapped at +len+ columns and indented +indent+ spaces. | ||
| # | ||
| # my_text = 'Here is a sample text with more than 40 characters' | ||
| # | ||
| # format_paragraph(my_text, 25, 4) | ||
| # # => " Here is a sample text with\n more than 40 characters" | ||
| def format_paragraph(text, len = 72, indent = 2) | ||
| sentences = [[]] | ||
|
|
||
| text.split.each do |word| | ||
| if sentences.first.present? && (sentences.last + [word]).join(' ').length > len | ||
| sentences << [word] | ||
| else | ||
| sentences.last << word | ||
| end | ||
| end | ||
|
|
||
| indentation = " " * indent | ||
| sentences.map! { |sentence| | ||
| "#{indentation}#{sentence.join(' ')}" | ||
| }.join "\n" | ||
| end | ||
| end | ||
| end |
| @@ -0,0 +1,115 @@ | ||
| require 'delegate' | ||
| require 'active_support/core_ext/string/filters' | ||
|
|
||
| module ActionMailer | ||
|
|
||
| # The <tt>ActionMailer::MessageDelivery</tt> class is used by | ||
| # <tt>ActionMailer::Base</tt> when creating a new mailer. | ||
| # <tt>MessageDelivery</tt> is a wrapper (+Delegator+ subclass) around a lazy | ||
| # created <tt>Mail::Message</tt>. You can get direct access to the | ||
| # <tt>Mail::Message</tt>, deliver the email or schedule the email to be sent | ||
| # through Active Job. | ||
| # | ||
| # Notifier.welcome(User.first) # an ActionMailer::MessageDelivery object | ||
| # Notifier.welcome(User.first).deliver_now # sends the email | ||
| # Notifier.welcome(User.first).deliver_later # enqueue email delivery as a job through Active Job | ||
| # Notifier.welcome(User.first).message # a Mail::Message object | ||
| class MessageDelivery < Delegator | ||
| def initialize(mailer, mail_method, *args) #:nodoc: | ||
| @mailer = mailer | ||
| @mail_method = mail_method | ||
| @args = args | ||
| end | ||
|
|
||
| def __getobj__ #:nodoc: | ||
| @obj ||= @mailer.send(:new, @mail_method, *@args).message | ||
| end | ||
|
|
||
| def __setobj__(obj) #:nodoc: | ||
| @obj = obj | ||
| end | ||
|
|
||
| # Returns the Mail::Message object | ||
| def message | ||
| __getobj__ | ||
| end | ||
|
|
||
| # Enqueues the email to be delivered through Active Job. When the | ||
| # job runs it will send the email using +deliver_now!+. That means | ||
| # that the message will be sent bypassing checking +perform_deliveries+ | ||
| # and +raise_delivery_errors+, so use with caution. | ||
| # | ||
| # Notifier.welcome(User.first).deliver_later! | ||
| # Notifier.welcome(User.first).deliver_later!(wait: 1.hour) | ||
| # Notifier.welcome(User.first).deliver_later!(wait_until: 10.hours.from_now) | ||
| # | ||
| # Options: | ||
| # | ||
| # * <tt>:wait</tt> - Enqueue the email to be delivered with a delay | ||
| # * <tt>:wait_until</tt> - Enqueue the email to be delivered at (after) a specific date / time | ||
| # * <tt>:queue</tt> - Enqueue the email on the specified queue | ||
| def deliver_later!(options={}) | ||
| enqueue_delivery :deliver_now!, options | ||
| end | ||
|
|
||
| # Enqueues the email to be delivered through Active Job. When the | ||
| # job runs it will send the email using +deliver_now+. | ||
| # | ||
| # Notifier.welcome(User.first).deliver_later | ||
| # Notifier.welcome(User.first).deliver_later(wait: 1.hour) | ||
| # Notifier.welcome(User.first).deliver_later(wait_until: 10.hours.from_now) | ||
| # | ||
| # Options: | ||
| # | ||
| # * <tt>:wait</tt> - Enqueue the email to be delivered with a delay | ||
| # * <tt>:wait_until</tt> - Enqueue the email to be delivered at (after) a specific date / time | ||
| # * <tt>:queue</tt> - Enqueue the email on the specified queue | ||
| def deliver_later(options={}) | ||
| enqueue_delivery :deliver_now, options | ||
| end | ||
|
|
||
| # Delivers an email without checking +perform_deliveries+ and +raise_delivery_errors+, | ||
| # so use with caution. | ||
| # | ||
| # Notifier.welcome(User.first).deliver_now! | ||
| # | ||
| def deliver_now! | ||
| message.deliver! | ||
| end | ||
|
|
||
| # Delivers an email: | ||
| # | ||
| # Notifier.welcome(User.first).deliver_now | ||
| # | ||
| def deliver_now | ||
| message.deliver | ||
| end | ||
|
|
||
| def deliver! #:nodoc: | ||
| ActiveSupport::Deprecation.warn(<<-MSG.squish) | ||
| `#deliver!` is deprecated and will be removed in Rails 5. Use | ||
| `#deliver_now!` to deliver immediately or `#deliver_later!` to | ||
| deliver through Active Job. | ||
| MSG | ||
|
|
||
| deliver_now! | ||
| end | ||
|
|
||
| def deliver #:nodoc: | ||
| ActiveSupport::Deprecation.warn(<<-MSG.squish) | ||
| `#deliver` is deprecated and will be removed in Rails 5. Use | ||
| `#deliver_now` to deliver immediately or `#deliver_later` to | ||
| deliver through Active Job. | ||
| MSG | ||
|
|
||
| deliver_now | ||
| end | ||
|
|
||
| private | ||
|
|
||
| def enqueue_delivery(delivery_method, options={}) | ||
| args = @mailer.name, @mail_method.to_s, delivery_method.to_s, *@args | ||
| ActionMailer::DeliveryJob.set(options).perform_later(*args) | ||
| end | ||
| end | ||
| end |
| @@ -0,0 +1,118 @@ | ||
| 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" | ||
| # | ||
| mattr_accessor :preview_path, instance_writer: false | ||
|
|
||
| # Enable or disable mailer previews through app configuration: | ||
| # | ||
| # config.action_mailer.show_previews = true | ||
| # | ||
| # Defaults to true for development environment | ||
| # | ||
| mattr_accessor :show_previews, instance_writer: false | ||
|
|
||
| # :nodoc: | ||
| mattr_accessor :preview_interceptors, instance_writer: false | ||
| self.preview_interceptors = [] | ||
| end | ||
|
|
||
| module ClassMethods | ||
| # Register one or more Interceptors which will be called before mail is previewed. | ||
| def register_preview_interceptors(*interceptors) | ||
| interceptors.flatten.compact.each { |interceptor| register_preview_interceptor(interceptor) } | ||
| end | ||
|
|
||
| # Register an Interceptor which will be called before mail is previewed. | ||
| # Either a class or a string can be passed in as the Interceptor. If a | ||
| # string is passed in it will be <tt>constantize</tt>d. | ||
| def register_preview_interceptor(interceptor) | ||
| preview_interceptor = case interceptor | ||
| when String, Symbol | ||
| interceptor.to_s.camelize.constantize | ||
| else | ||
| interceptor | ||
| end | ||
|
|
||
| unless preview_interceptors.include?(preview_interceptor) | ||
| preview_interceptors << preview_interceptor | ||
| end | ||
| end | ||
| 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. The registered preview | ||
| # interceptors will be informed so that they can transform the message | ||
| # as they would if the mail was actually being delivered. | ||
| def call(email) | ||
| preview = self.new | ||
| message = preview.public_send(email) | ||
| inform_preview_interceptors(message) | ||
| message | ||
| 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: | ||
| if preview_path | ||
| Dir["#{preview_path}/**/*_preview.rb"].each{ |file| require_dependency file } | ||
| end | ||
| end | ||
|
|
||
| def preview_path #:nodoc: | ||
| Base.preview_path | ||
| end | ||
|
|
||
| def show_previews #:nodoc: | ||
| Base.show_previews | ||
| end | ||
|
|
||
| def inform_preview_interceptors(message) #:nodoc: | ||
| Base.preview_interceptors.each do |interceptor| | ||
| interceptor.previewing_email(message) | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end |
| @@ -0,0 +1,64 @@ | ||
| require 'active_job/railtie' | ||
| require "action_mailer" | ||
| require "rails" | ||
| require "abstract_controller/railties/routes_helpers" | ||
|
|
||
| module ActionMailer | ||
| class Railtie < Rails::Railtie # :nodoc: | ||
| config.action_mailer = ActiveSupport::OrderedOptions.new | ||
| config.eager_load_namespaces << ActionMailer | ||
|
|
||
| initializer "action_mailer.logger" do | ||
| ActiveSupport.on_load(:action_mailer) { self.logger ||= Rails.logger } | ||
| end | ||
|
|
||
| initializer "action_mailer.set_configs" do |app| | ||
| paths = app.config.paths | ||
| options = app.config.action_mailer | ||
|
|
||
| options.assets_dir ||= paths["public"].first | ||
| options.javascripts_dir ||= paths["public/javascripts"].first | ||
| options.stylesheets_dir ||= paths["public/stylesheets"].first | ||
| options.show_previews = Rails.env.development? if options.show_previews.nil? | ||
|
|
||
| if options.show_previews | ||
| options.preview_path ||= defined?(Rails.root) ? "#{Rails.root}/test/mailers/previews" : nil | ||
| end | ||
|
|
||
| # make sure readers methods get compiled | ||
| options.asset_host ||= app.config.asset_host | ||
| options.relative_url_root ||= app.config.relative_url_root | ||
|
|
||
| ActiveSupport.on_load(:action_mailer) do | ||
| include AbstractController::UrlFor | ||
| extend ::AbstractController::Railties::RoutesHelpers.with(app.routes, false) | ||
| include app.routes.mounted_helpers | ||
|
|
||
| register_interceptors(options.delete(:interceptors)) | ||
| register_preview_interceptors(options.delete(:preview_interceptors)) | ||
| register_observers(options.delete(:observers)) | ||
|
|
||
| options.each { |k,v| send("#{k}=", v) } | ||
|
|
||
| if options.show_previews | ||
| app.routes.append do | ||
| get '/rails/mailers' => "rails/mailers#index" | ||
| get '/rails/mailers/*path' => "rails/mailers#preview" | ||
| end | ||
| end | ||
| end | ||
| end | ||
|
|
||
| initializer "action_mailer.compile_config_methods" do | ||
| ActiveSupport.on_load(:action_mailer) do | ||
| config.compile_methods! if config.respond_to?(:compile_methods!) | ||
| end | ||
| end | ||
|
|
||
| config.after_initialize do | ||
| if ActionMailer::Base.preview_path | ||
| ActiveSupport::Dependencies.autoload_paths << ActionMailer::Base.preview_path | ||
| end | ||
| end | ||
| end | ||
| end |
| @@ -0,0 +1,105 @@ | ||
| require 'active_support/test_case' | ||
| require 'rails-dom-testing' | ||
|
|
||
| module ActionMailer | ||
| class NonInferrableMailerError < ::StandardError | ||
| def initialize(name) | ||
| super "Unable to determine the mailer to test from #{name}. " + | ||
| "You'll need to specify it using tests YourMailer in your " + | ||
| "test case definition" | ||
| end | ||
| end | ||
|
|
||
| class TestCase < ActiveSupport::TestCase | ||
| module Behavior | ||
| extend ActiveSupport::Concern | ||
|
|
||
| include ActiveSupport::Testing::ConstantLookup | ||
| include TestHelper | ||
| include Rails::Dom::Testing::Assertions::SelectorAssertions | ||
| include Rails::Dom::Testing::Assertions::DomAssertions | ||
|
|
||
| included do | ||
| class_attribute :_mailer_class | ||
| setup :initialize_test_deliveries | ||
| setup :set_expected_mail | ||
| teardown :restore_test_deliveries | ||
| end | ||
|
|
||
| module ClassMethods | ||
| def tests(mailer) | ||
| case mailer | ||
| when String, Symbol | ||
| self._mailer_class = mailer.to_s.camelize.constantize | ||
| when Module | ||
| self._mailer_class = mailer | ||
| else | ||
| raise NonInferrableMailerError.new(mailer) | ||
| end | ||
| end | ||
|
|
||
| def mailer_class | ||
| if mailer = self._mailer_class | ||
| mailer | ||
| else | ||
| tests determine_default_mailer(name) | ||
| end | ||
| end | ||
|
|
||
| def determine_default_mailer(name) | ||
| mailer = determine_constant_from_test_name(name) do |constant| | ||
| Class === constant && constant < ActionMailer::Base | ||
| end | ||
| raise NonInferrableMailerError.new(name) if mailer.nil? | ||
| mailer | ||
| end | ||
| end | ||
|
|
||
| protected | ||
|
|
||
| def initialize_test_deliveries | ||
| set_delivery_method :test | ||
| @old_perform_deliveries = ActionMailer::Base.perform_deliveries | ||
| ActionMailer::Base.perform_deliveries = true | ||
| ActionMailer::Base.deliveries.clear | ||
| end | ||
|
|
||
| def restore_test_deliveries | ||
| restore_delivery_method | ||
| ActionMailer::Base.perform_deliveries = @old_perform_deliveries | ||
| end | ||
|
|
||
| def set_delivery_method(method) | ||
| @old_delivery_method = ActionMailer::Base.delivery_method | ||
| ActionMailer::Base.delivery_method = method | ||
| end | ||
|
|
||
| def restore_delivery_method | ||
| ActionMailer::Base.deliveries.clear | ||
| ActionMailer::Base.delivery_method = @old_delivery_method | ||
| end | ||
|
|
||
| def set_expected_mail | ||
| @expected = Mail.new | ||
| @expected.content_type ["text", "plain", { "charset" => charset }] | ||
| @expected.mime_version = '1.0' | ||
| end | ||
|
|
||
| private | ||
|
|
||
| def charset | ||
| "UTF-8" | ||
| end | ||
|
|
||
| def encode(subject) | ||
| Mail::Encodings.q_value_encode(subject, charset) | ||
| end | ||
|
|
||
| def read_fixture(action) | ||
| IO.readlines(File.join(Rails.root, 'test', 'fixtures', self.class.mailer_class.name.underscore, action)) | ||
| end | ||
| end | ||
|
|
||
| include Behavior | ||
| end | ||
| end |
| @@ -0,0 +1,62 @@ | ||
| module ActionMailer | ||
| # Provides helper methods for testing Action Mailer, including #assert_emails | ||
| # and #assert_no_emails | ||
| module TestHelper | ||
| # Asserts that the number of emails sent matches the given number. | ||
| # | ||
| # def test_emails | ||
| # assert_emails 0 | ||
| # ContactMailer.welcome.deliver_now | ||
| # assert_emails 1 | ||
| # ContactMailer.welcome.deliver_now | ||
| # assert_emails 2 | ||
| # end | ||
| # | ||
| # If a block is passed, that block should cause the specified number of | ||
| # emails to be sent. | ||
| # | ||
| # def test_emails_again | ||
| # assert_emails 1 do | ||
| # ContactMailer.welcome.deliver_now | ||
| # end | ||
| # | ||
| # assert_emails 2 do | ||
| # ContactMailer.welcome.deliver_now | ||
| # ContactMailer.welcome.deliver_now | ||
| # end | ||
| # end | ||
| def assert_emails(number) | ||
| if block_given? | ||
| original_count = ActionMailer::Base.deliveries.size | ||
| yield | ||
| new_count = ActionMailer::Base.deliveries.size | ||
| assert_equal number, new_count - original_count, "#{number} emails expected, but #{new_count - original_count} were sent" | ||
| else | ||
| assert_equal number, ActionMailer::Base.deliveries.size | ||
| end | ||
| end | ||
|
|
||
| # Assert that no emails have been sent. | ||
| # | ||
| # def test_emails | ||
| # assert_no_emails | ||
| # ContactMailer.welcome.deliver_now | ||
| # assert_emails 1 | ||
| # end | ||
| # | ||
| # If a block is passed, that block should not cause any emails to be sent. | ||
| # | ||
| # def test_emails_again | ||
| # assert_no_emails do | ||
| # # No emails should be sent from this block | ||
| # end | ||
| # end | ||
| # | ||
| # Note: This assertion is simply a shortcut for: | ||
| # | ||
| # assert_emails 0 | ||
| def assert_no_emails(&block) | ||
| assert_emails 0, &block | ||
| end | ||
| end | ||
| end |
| @@ -0,0 +1,9 @@ | ||
| require_relative 'gem_version' | ||
|
|
||
| module ActionMailer | ||
| # Returns the version of the currently loaded Action Mailer as a | ||
| # <tt>Gem::Version</tt>. | ||
| def self.version | ||
| gem_version | ||
| end | ||
| end |
| @@ -0,0 +1,17 @@ | ||
| Description: | ||
| ============ | ||
| Stubs out a new mailer and its views. Passes the mailer name, either | ||
| CamelCased or under_scored, and an optional list of emails as arguments. | ||
|
|
||
| This generates a mailer class in app/mailers and invokes your template | ||
| engine and test framework generators. | ||
|
|
||
| Example: | ||
| ======== | ||
| rails generate mailer Notifications signup forgot_password invoice | ||
|
|
||
| creates a Notifications mailer class, views, and test: | ||
| Mailer: app/mailers/notifications.rb | ||
| Views: app/views/notifications/signup.text.erb [...] | ||
| Test: test/mailers/notifications_test.rb | ||
|
|
| @@ -0,0 +1,19 @@ | ||
| module Rails | ||
| module Generators | ||
| class MailerGenerator < NamedBase | ||
| source_root File.expand_path("../templates", __FILE__) | ||
|
|
||
| argument :actions, type: :array, default: [], banner: "method method" | ||
| check_class_collision | ||
|
|
||
| def create_mailer_file | ||
| template "mailer.rb", File.join('app/mailers', class_path, "#{file_name}.rb") | ||
| if self.behavior == :invoke | ||
| template "application_mailer.rb", 'app/mailers/application_mailer.rb' | ||
| end | ||
| end | ||
|
|
||
| hook_for :template_engine, :test_framework | ||
| end | ||
| end | ||
| end |
| @@ -0,0 +1,4 @@ | ||
| class ApplicationMailer < ActionMailer::Base | ||
| default from: "from@example.com" | ||
| layout 'mailer' | ||
| end |
| @@ -0,0 +1,17 @@ | ||
| <% module_namespacing do -%> | ||
| class <%= class_name %> < ApplicationMailer | ||
| <% actions.each do |action| -%> | ||
| # Subject can be set in your I18n file at config/locales/en.yml | ||
| # with the following lookup: | ||
| # | ||
| # en.<%= file_path.tr("/",".") %>.<%= action %>.subject | ||
| # | ||
| def <%= action %> | ||
| @greeting = "Hi" | ||
| mail to: "to@example.org" | ||
| end | ||
| <% end -%> | ||
| end | ||
| <% end -%> |
| @@ -0,0 +1,21 @@ | ||
| Copyright (c) 2004-2014 David Heinemeier Hansson | ||
|
|
||
| Permission is hereby granted, free of charge, to any person obtaining | ||
| a copy of this software and associated documentation files (the | ||
| "Software"), to deal in the Software without restriction, including | ||
| without limitation the rights to use, copy, modify, merge, publish, | ||
| distribute, sublicense, and/or sell copies of the Software, and to | ||
| permit persons to whom the Software is furnished to do so, subject to | ||
| the following conditions: | ||
|
|
||
| The above copyright notice and this permission notice shall be | ||
| included in all copies or substantial portions of the Software. | ||
|
|
||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
| EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
| MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | ||
| NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE | ||
| LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION | ||
| OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | ||
| WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
|
|
| @@ -0,0 +1,58 @@ | ||
| = Action Pack -- From request to response | ||
|
|
||
| Action Pack is a framework for handling and responding to web requests. It | ||
| provides mechanisms for *routing* (mapping request URLs to actions), defining | ||
| *controllers* that implement actions, and generating responses by rendering | ||
| *views*, which are templates of various formats. In short, Action Pack | ||
| provides the view and controller layers in the MVC paradigm. | ||
|
|
||
| It consists of several modules: | ||
|
|
||
| * Action Dispatch, which parses information about the web request, handles | ||
| routing as defined by the user, and does advanced processing related to HTTP | ||
| such as MIME-type negotiation, decoding parameters in POST, PATCH, or PUT bodies, | ||
| handling HTTP caching logic, cookies and sessions. | ||
|
|
||
| * Action Controller, which provides a base controller class that can be | ||
| subclassed to implement filters and actions to handle requests. The result | ||
| of an action is typically content generated from views. | ||
|
|
||
| With the Ruby on Rails framework, users only directly interface with the | ||
| Action Controller module. Necessary Action Dispatch functionality is activated | ||
| by default and Action View rendering is implicitly triggered by Action | ||
| Controller. However, these modules are designed to function on their own and | ||
| can be used outside of Rails. | ||
|
|
||
|
|
||
| == Download and installation | ||
|
|
||
| The latest version of Action Pack can be installed with RubyGems: | ||
|
|
||
| % [sudo] gem install actionpack | ||
|
|
||
| Source code can be downloaded as part of the Rails project on GitHub | ||
|
|
||
| * https://github.com/rails/rails/tree/4-2-stable/actionpack | ||
|
|
||
|
|
||
| == License | ||
|
|
||
| Action Pack is released under the MIT license: | ||
|
|
||
| * http://www.opensource.org/licenses/MIT | ||
|
|
||
|
|
||
| == Support | ||
|
|
||
| API documentation is at | ||
|
|
||
| * http://api.rubyonrails.org | ||
|
|
||
| Bug reports can be filed for the Ruby on Rails project here: | ||
|
|
||
| * https://github.com/rails/rails/issues | ||
|
|
||
| Feature requests should be discussed on the rails-core mailing list here: | ||
|
|
||
| * https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core | ||
|
|
| @@ -0,0 +1,20 @@ | ||
| require 'action_pack' | ||
| require 'active_support/rails' | ||
| require 'active_support/core_ext/module/attr_internal' | ||
| require 'active_support/core_ext/module/anonymous' | ||
| require 'active_support/i18n' | ||
|
|
||
| module AbstractController | ||
| extend ActiveSupport::Autoload | ||
|
|
||
| autoload :Base | ||
| autoload :Callbacks | ||
| autoload :Collector | ||
| autoload :DoubleRenderError, "abstract_controller/rendering" | ||
| autoload :Helpers | ||
| autoload :Logger | ||
| autoload :Rendering | ||
| autoload :Translation | ||
| autoload :AssetPaths | ||
| autoload :UrlFor | ||
| end |
| @@ -0,0 +1,10 @@ | ||
| module AbstractController | ||
| module AssetPaths #:nodoc: | ||
| extend ActiveSupport::Concern | ||
|
|
||
| included do | ||
| config_accessor :asset_host, :assets_dir, :javascripts_dir, | ||
| :stylesheets_dir, :default_asset_host_protocol, :relative_url_root | ||
| end | ||
| end | ||
| end |
| @@ -0,0 +1,269 @@ | ||
| require 'erubis' | ||
| require 'set' | ||
| require 'active_support/configurable' | ||
| require 'active_support/descendants_tracker' | ||
| require 'active_support/core_ext/module/anonymous' | ||
|
|
||
| module AbstractController | ||
| class Error < StandardError #:nodoc: | ||
| end | ||
|
|
||
| # Raised when a non-existing controller action is triggered. | ||
| class ActionNotFound < StandardError | ||
| end | ||
|
|
||
| # <tt>AbstractController::Base</tt> is a low-level API. Nobody should be | ||
| # using it directly, and subclasses (like ActionController::Base) are | ||
| # expected to provide their own +render+ method, since rendering means | ||
| # different things depending on the context. | ||
| class Base | ||
| attr_internal :response_body | ||
| attr_internal :action_name | ||
| attr_internal :formats | ||
|
|
||
| include ActiveSupport::Configurable | ||
| extend ActiveSupport::DescendantsTracker | ||
|
|
||
| undef_method :not_implemented | ||
| class << self | ||
| attr_reader :abstract | ||
| alias_method :abstract?, :abstract | ||
|
|
||
| # Define a controller as abstract. See internal_methods for more | ||
| # details. | ||
| def abstract! | ||
| @abstract = true | ||
| end | ||
|
|
||
| def inherited(klass) # :nodoc: | ||
| # Define the abstract ivar on subclasses so that we don't get | ||
| # uninitialized ivar warnings | ||
| unless klass.instance_variable_defined?(:@abstract) | ||
| klass.instance_variable_set(:@abstract, false) | ||
| end | ||
| super | ||
| end | ||
|
|
||
| # A list of all internal methods for a controller. This finds the first | ||
| # abstract superclass of a controller, and gets a list of all public | ||
| # instance methods on that abstract class. Public instance methods of | ||
| # a controller would normally be considered action methods, so methods | ||
| # declared on abstract classes are being removed. | ||
| # (ActionController::Metal and ActionController::Base are defined as abstract) | ||
| def internal_methods | ||
| controller = self | ||
|
|
||
| controller = controller.superclass until controller.abstract? | ||
| controller.public_instance_methods(true) | ||
| end | ||
|
|
||
| # The list of hidden actions. Defaults to an empty array. | ||
| # This can be modified by other modules or subclasses | ||
| # to specify particular actions as hidden. | ||
| # | ||
| # ==== Returns | ||
| # * <tt>Array</tt> - An array of method names that should not be considered actions. | ||
| def hidden_actions | ||
| [] | ||
| end | ||
|
|
||
| # A list of method names that should be considered actions. This | ||
| # includes all public instance methods on a controller, less | ||
| # any internal methods (see #internal_methods), adding back in | ||
| # any methods that are internal, but still exist on the class | ||
| # itself. Finally, #hidden_actions are removed. | ||
| # | ||
| # ==== Returns | ||
| # * <tt>Set</tt> - A set of all methods that should be considered actions. | ||
| def action_methods | ||
| @action_methods ||= begin | ||
| # All public instance methods of this class, including ancestors | ||
| methods = (public_instance_methods(true) - | ||
| # Except for public instance methods of Base and its ancestors | ||
| internal_methods + | ||
| # Be sure to include shadowed public instance methods of this class | ||
| public_instance_methods(false)).uniq.map { |x| x.to_s } - | ||
| # And always exclude explicitly hidden actions | ||
| hidden_actions.to_a | ||
|
|
||
| # Clear out AS callback method pollution | ||
| Set.new(methods.reject { |method| method =~ /_one_time_conditions/ }) | ||
| end | ||
| end | ||
|
|
||
| # action_methods are cached and there is sometimes need to refresh | ||
| # them. clear_action_methods! allows you to do that, so next time | ||
| # you run action_methods, they will be recalculated | ||
| def clear_action_methods! | ||
| @action_methods = nil | ||
| end | ||
|
|
||
| # Returns the full controller name, underscored, without the ending Controller. | ||
| # For instance, MyApp::MyPostsController would return "my_app/my_posts" for | ||
| # controller_path. | ||
| # | ||
| # ==== Returns | ||
| # * <tt>String</tt> | ||
| def controller_path | ||
| @controller_path ||= name.sub(/Controller$/, '').underscore unless anonymous? | ||
| end | ||
|
|
||
| # Refresh the cached action_methods when a new action_method is added. | ||
| def method_added(name) | ||
| super | ||
| clear_action_methods! | ||
| end | ||
| end | ||
|
|
||
| abstract! | ||
|
|
||
| # Calls the action going through the entire action dispatch stack. | ||
| # | ||
| # The actual method that is called is determined by calling | ||
| # #method_for_action. If no method can handle the action, then an | ||
| # AbstractController::ActionNotFound error is raised. | ||
| # | ||
| # ==== Returns | ||
| # * <tt>self</tt> | ||
| def process(action, *args) | ||
| @_action_name = action.to_s | ||
|
|
||
| unless action_name = _find_action_name(@_action_name) | ||
| raise ActionNotFound, "The action '#{action}' could not be found for #{self.class.name}" | ||
| end | ||
|
|
||
| @_response_body = nil | ||
|
|
||
| process_action(action_name, *args) | ||
| end | ||
|
|
||
| # Delegates to the class' #controller_path | ||
| def controller_path | ||
| self.class.controller_path | ||
| end | ||
|
|
||
| # Delegates to the class' #action_methods | ||
| def action_methods | ||
| self.class.action_methods | ||
| end | ||
|
|
||
| # Returns true if a method for the action is available and | ||
| # can be dispatched, false otherwise. | ||
| # | ||
| # Notice that <tt>action_methods.include?("foo")</tt> may return | ||
| # false and <tt>available_action?("foo")</tt> returns true because | ||
| # this method considers actions that are also available | ||
| # through other means, for example, implicit render ones. | ||
| # | ||
| # ==== Parameters | ||
| # * <tt>action_name</tt> - The name of an action to be tested | ||
| # | ||
| # ==== Returns | ||
| # * <tt>TrueClass</tt>, <tt>FalseClass</tt> | ||
| def available_action?(action_name) | ||
| _find_action_name(action_name).present? | ||
| end | ||
|
|
||
| # Returns true if the given controller is capable of rendering | ||
| # a path. A subclass of +AbstractController::Base+ | ||
| # may return false. An Email controller for example does not | ||
| # support paths, only full URLs. | ||
| def self.supports_path? | ||
| true | ||
| end | ||
|
|
||
| private | ||
|
|
||
| # Returns true if the name can be considered an action because | ||
| # it has a method defined in the controller. | ||
| # | ||
| # ==== Parameters | ||
| # * <tt>name</tt> - The name of an action to be tested | ||
| # | ||
| # ==== Returns | ||
| # * <tt>TrueClass</tt>, <tt>FalseClass</tt> | ||
| # | ||
| # :api: private | ||
| def action_method?(name) | ||
| self.class.action_methods.include?(name) | ||
| end | ||
|
|
||
| # Call the action. Override this in a subclass to modify the | ||
| # behavior around processing an action. This, and not #process, | ||
| # is the intended way to override action dispatching. | ||
| # | ||
| # Notice that the first argument is the method to be dispatched | ||
| # which is *not* necessarily the same as the action name. | ||
| def process_action(method_name, *args) | ||
| send_action(method_name, *args) | ||
| end | ||
|
|
||
| # Actually call the method associated with the action. Override | ||
| # this method if you wish to change how action methods are called, | ||
| # not to add additional behavior around it. For example, you would | ||
| # override #send_action if you want to inject arguments into the | ||
| # method. | ||
| alias send_action send | ||
|
|
||
| # If the action name was not found, but a method called "action_missing" | ||
| # was found, #method_for_action will return "_handle_action_missing". | ||
| # This method calls #action_missing with the current action name. | ||
| def _handle_action_missing(*args) | ||
| action_missing(@_action_name, *args) | ||
| end | ||
|
|
||
| # Takes an action name and returns the name of the method that will | ||
| # handle the action. | ||
| # | ||
| # It checks if the action name is valid and returns false otherwise. | ||
| # | ||
| # See method_for_action for more information. | ||
| # | ||
| # ==== Parameters | ||
| # * <tt>action_name</tt> - An action name to find a method name for | ||
| # | ||
| # ==== Returns | ||
| # * <tt>string</tt> - The name of the method that handles the action | ||
| # * false - No valid method name could be found. | ||
| # Raise AbstractController::ActionNotFound. | ||
| def _find_action_name(action_name) | ||
| _valid_action_name?(action_name) && method_for_action(action_name) | ||
| end | ||
|
|
||
| # Takes an action name and returns the name of the method that will | ||
| # handle the action. In normal cases, this method returns the same | ||
| # name as it receives. By default, if #method_for_action receives | ||
| # a name that is not an action, it will look for an #action_missing | ||
| # method and return "_handle_action_missing" if one is found. | ||
| # | ||
| # Subclasses may override this method to add additional conditions | ||
| # that should be considered an action. For instance, an HTTP controller | ||
| # with a template matching the action name is considered to exist. | ||
| # | ||
| # If you override this method to handle additional cases, you may | ||
| # also provide a method (like _handle_method_missing) to handle | ||
| # the case. | ||
| # | ||
| # If none of these conditions are true, and method_for_action | ||
| # returns nil, an AbstractController::ActionNotFound exception will be raised. | ||
| # | ||
| # ==== Parameters | ||
| # * <tt>action_name</tt> - An action name to find a method name for | ||
| # | ||
| # ==== Returns | ||
| # * <tt>string</tt> - The name of the method that handles the action | ||
| # * <tt>nil</tt> - No method name could be found. | ||
| def method_for_action(action_name) | ||
| if action_method?(action_name) | ||
| action_name | ||
| elsif respond_to?(:action_missing, true) | ||
| "_handle_action_missing" | ||
| end | ||
| end | ||
|
|
||
| # Checks if the action name is valid and returns false otherwise. | ||
| def _valid_action_name?(action_name) | ||
| !action_name.to_s.include? File::SEPARATOR | ||
| end | ||
| end | ||
| end |
| @@ -0,0 +1,196 @@ | ||
| module AbstractController | ||
| module Callbacks | ||
| extend ActiveSupport::Concern | ||
|
|
||
| # Uses ActiveSupport::Callbacks as the base functionality. For | ||
| # more details on the whole callback system, read the documentation | ||
| # for ActiveSupport::Callbacks. | ||
| include ActiveSupport::Callbacks | ||
|
|
||
| included do | ||
| define_callbacks :process_action, | ||
| terminator: ->(controller,_) { controller.response_body }, | ||
| skip_after_callbacks_if_terminated: true | ||
| end | ||
|
|
||
| # Override AbstractController::Base's process_action to run the | ||
| # process_action callbacks around the normal behavior. | ||
| def process_action(*args) | ||
| run_callbacks(:process_action) do | ||
| super | ||
| end | ||
| end | ||
|
|
||
| module ClassMethods | ||
| # If :only or :except are used, convert the options into the | ||
| # :unless and :if options of ActiveSupport::Callbacks. | ||
| # The basic idea is that :only => :index gets converted to | ||
| # :if => proc {|c| c.action_name == "index" }. | ||
| # | ||
| # ==== Options | ||
| # * <tt>only</tt> - The callback should be run only for this action | ||
| # * <tt>except</tt> - The callback should be run for all actions except this action | ||
| def _normalize_callback_options(options) | ||
| _normalize_callback_option(options, :only, :if) | ||
| _normalize_callback_option(options, :except, :unless) | ||
| end | ||
|
|
||
| def _normalize_callback_option(options, from, to) # :nodoc: | ||
| if from = options[from] | ||
| from = Array(from).map {|o| "action_name == '#{o}'"}.join(" || ") | ||
| options[to] = Array(options[to]).unshift(from) | ||
| end | ||
| end | ||
|
|
||
| # Skip before, after, and around action callbacks matching any of the names. | ||
| # | ||
| # ==== Parameters | ||
| # * <tt>names</tt> - A list of valid names that could be used for | ||
| # callbacks. Note that skipping uses Ruby equality, so it's | ||
| # impossible to skip a callback defined using an anonymous proc | ||
| # using #skip_action_callback | ||
| def skip_action_callback(*names) | ||
| skip_before_action(*names) | ||
| skip_after_action(*names) | ||
| skip_around_action(*names) | ||
| end | ||
| alias_method :skip_filter, :skip_action_callback | ||
|
|
||
| # Take callback names and an optional callback proc, normalize them, | ||
| # then call the block with each callback. This allows us to abstract | ||
| # the normalization across several methods that use it. | ||
| # | ||
| # ==== Parameters | ||
| # * <tt>callbacks</tt> - An array of callbacks, with an optional | ||
| # options hash as the last parameter. | ||
| # * <tt>block</tt> - A proc that should be added to the callbacks. | ||
| # | ||
| # ==== Block Parameters | ||
| # * <tt>name</tt> - The callback to be added | ||
| # * <tt>options</tt> - A hash of options to be used when adding the callback | ||
| def _insert_callbacks(callbacks, block = nil) | ||
| options = callbacks.extract_options! | ||
| _normalize_callback_options(options) | ||
| callbacks.push(block) if block | ||
| callbacks.each do |callback| | ||
| yield callback, options | ||
| end | ||
| end | ||
|
|
||
| ## | ||
| # :method: before_action | ||
| # | ||
| # :call-seq: before_action(names, block) | ||
| # | ||
| # Append a callback before actions. See _insert_callbacks for parameter details. | ||
|
|
||
| ## | ||
| # :method: prepend_before_action | ||
| # | ||
| # :call-seq: prepend_before_action(names, block) | ||
| # | ||
| # Prepend a callback before actions. See _insert_callbacks for parameter details. | ||
|
|
||
| ## | ||
| # :method: skip_before_action | ||
| # | ||
| # :call-seq: skip_before_action(names) | ||
| # | ||
| # Skip a callback before actions. See _insert_callbacks for parameter details. | ||
|
|
||
| ## | ||
| # :method: append_before_action | ||
| # | ||
| # :call-seq: append_before_action(names, block) | ||
| # | ||
| # Append a callback before actions. See _insert_callbacks for parameter details. | ||
|
|
||
| ## | ||
| # :method: after_action | ||
| # | ||
| # :call-seq: after_action(names, block) | ||
| # | ||
| # Append a callback after actions. See _insert_callbacks for parameter details. | ||
|
|
||
| ## | ||
| # :method: prepend_after_action | ||
| # | ||
| # :call-seq: prepend_after_action(names, block) | ||
| # | ||
| # Prepend a callback after actions. See _insert_callbacks for parameter details. | ||
|
|
||
| ## | ||
| # :method: skip_after_action | ||
| # | ||
| # :call-seq: skip_after_action(names) | ||
| # | ||
| # Skip a callback after actions. See _insert_callbacks for parameter details. | ||
|
|
||
| ## | ||
| # :method: append_after_action | ||
| # | ||
| # :call-seq: append_after_action(names, block) | ||
| # | ||
| # Append a callback after actions. See _insert_callbacks for parameter details. | ||
|
|
||
| ## | ||
| # :method: around_action | ||
| # | ||
| # :call-seq: around_action(names, block) | ||
| # | ||
| # Append a callback around actions. See _insert_callbacks for parameter details. | ||
|
|
||
| ## | ||
| # :method: prepend_around_action | ||
| # | ||
| # :call-seq: prepend_around_action(names, block) | ||
| # | ||
| # Prepend a callback around actions. See _insert_callbacks for parameter details. | ||
|
|
||
| ## | ||
| # :method: skip_around_action | ||
| # | ||
| # :call-seq: skip_around_action(names) | ||
| # | ||
| # Skip a callback around actions. See _insert_callbacks for parameter details. | ||
|
|
||
| ## | ||
| # :method: append_around_action | ||
| # | ||
| # :call-seq: append_around_action(names, block) | ||
| # | ||
| # Append a callback around actions. See _insert_callbacks for parameter details. | ||
|
|
||
| # set up before_action, prepend_before_action, skip_before_action, etc. | ||
| # for each of before, after, and around. | ||
| [:before, :after, :around].each do |callback| | ||
| define_method "#{callback}_action" do |*names, &blk| | ||
| _insert_callbacks(names, blk) do |name, options| | ||
| set_callback(:process_action, callback, name, options) | ||
| end | ||
| end | ||
| alias_method :"#{callback}_filter", :"#{callback}_action" | ||
|
|
||
| define_method "prepend_#{callback}_action" do |*names, &blk| | ||
| _insert_callbacks(names, blk) do |name, options| | ||
| set_callback(:process_action, callback, name, options.merge(:prepend => true)) | ||
| end | ||
| end | ||
| alias_method :"prepend_#{callback}_filter", :"prepend_#{callback}_action" | ||
|
|
||
| # Skip a before, after or around callback. See _insert_callbacks | ||
| # for details on the allowed parameters. | ||
| define_method "skip_#{callback}_action" do |*names| | ||
| _insert_callbacks(names) do |name, options| | ||
| skip_callback(:process_action, callback, name, options) | ||
| end | ||
| end | ||
| alias_method :"skip_#{callback}_filter", :"skip_#{callback}_action" | ||
|
|
||
| # *_action is the same as append_*_action | ||
| alias_method :"append_#{callback}_action", :"#{callback}_action" | ||
| alias_method :"append_#{callback}_filter", :"#{callback}_action" | ||
| end | ||
| end | ||
| end | ||
| end |
| @@ -0,0 +1,46 @@ | ||
| require "action_dispatch/http/mime_type" | ||
|
|
||
| module AbstractController | ||
| module Collector | ||
| def self.generate_method_for_mime(mime) | ||
| sym = mime.is_a?(Symbol) ? mime : mime.to_sym | ||
| const = sym.upcase | ||
| class_eval <<-RUBY, __FILE__, __LINE__ + 1 | ||
| def #{sym}(*args, &block) # def html(*args, &block) | ||
| custom(Mime::#{const}, *args, &block) # custom(Mime::HTML, *args, &block) | ||
| end # end | ||
| RUBY | ||
| end | ||
|
|
||
| Mime::SET.each do |mime| | ||
| generate_method_for_mime(mime) | ||
| end | ||
|
|
||
| Mime::Type.register_callback do |mime| | ||
| generate_method_for_mime(mime) unless self.instance_methods.include?(mime.to_sym) | ||
| end | ||
|
|
||
| protected | ||
|
|
||
| def method_missing(symbol, &block) | ||
| const_name = symbol.upcase | ||
|
|
||
| unless Mime.const_defined?(const_name) | ||
| raise NoMethodError, "To respond to a custom format, register it as a MIME type first: " \ | ||
| "http://guides.rubyonrails.org/action_controller_overview.html#restful-downloads. " \ | ||
| "If you meant to respond to a variant like :tablet or :phone, not a custom format, " \ | ||
| "be sure to nest your variant response within a format response: " \ | ||
| "format.html { |html| html.tablet { ... } }" | ||
| end | ||
|
|
||
| mime_constant = Mime.const_get(const_name) | ||
|
|
||
| if Mime::SET.include?(mime_constant) | ||
| AbstractController::Collector.generate_method_for_mime(mime_constant) | ||
| send(symbol, &block) | ||
| else | ||
| super | ||
| end | ||
| end | ||
| end | ||
| end |
| @@ -0,0 +1,194 @@ | ||
| require 'active_support/dependencies' | ||
|
|
||
| module AbstractController | ||
| module Helpers | ||
| extend ActiveSupport::Concern | ||
|
|
||
| included do | ||
| class_attribute :_helpers | ||
| self._helpers = Module.new | ||
|
|
||
| class_attribute :_helper_methods | ||
| self._helper_methods = Array.new | ||
| end | ||
|
|
||
| class MissingHelperError < LoadError | ||
| def initialize(error, path) | ||
| @error = error | ||
| @path = "helpers/#{path}.rb" | ||
| set_backtrace error.backtrace | ||
|
|
||
| if error.path =~ /^#{path}(\.rb)?$/ | ||
| super("Missing helper file helpers/%s.rb" % path) | ||
| else | ||
| raise error | ||
| end | ||
| end | ||
| end | ||
|
|
||
| module ClassMethods | ||
| # When a class is inherited, wrap its helper module in a new module. | ||
| # This ensures that the parent class's module can be changed | ||
| # independently of the child class's. | ||
| def inherited(klass) | ||
| helpers = _helpers | ||
| klass._helpers = Module.new { include helpers } | ||
| klass.class_eval { default_helper_module! } unless klass.anonymous? | ||
| super | ||
| end | ||
|
|
||
| # Declare a controller method as a helper. For example, the following | ||
| # makes the +current_user+ controller method available to the view: | ||
| # class ApplicationController < ActionController::Base | ||
| # helper_method :current_user, :logged_in? | ||
| # | ||
| # def current_user | ||
| # @current_user ||= User.find_by(id: session[:user]) | ||
| # end | ||
| # | ||
| # def logged_in? | ||
| # current_user != nil | ||
| # end | ||
| # end | ||
| # | ||
| # In a view: | ||
| # <% if logged_in? -%>Welcome, <%= current_user.name %><% end -%> | ||
| # | ||
| # ==== Parameters | ||
| # * <tt>method[, method]</tt> - A name or names of a method on the controller | ||
| # to be made available on the view. | ||
| def helper_method(*meths) | ||
| meths.flatten! | ||
| self._helper_methods += meths | ||
|
|
||
| meths.each do |meth| | ||
| _helpers.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1 | ||
| def #{meth}(*args, &blk) # def current_user(*args, &blk) | ||
| controller.send(%(#{meth}), *args, &blk) # controller.send(:current_user, *args, &blk) | ||
| end # end | ||
| ruby_eval | ||
| end | ||
| end | ||
|
|
||
| # The +helper+ class method can take a series of helper module names, a block, or both. | ||
| # | ||
| # ==== Options | ||
| # * <tt>*args</tt> - Module, Symbol, String | ||
| # * <tt>block</tt> - A block defining helper methods | ||
| # | ||
| # When the argument is a module it will be included directly in the template class. | ||
| # helper FooHelper # => includes FooHelper | ||
| # | ||
| # When the argument is a string or symbol, the method will provide the "_helper" suffix, require the file | ||
| # and include the module in the template class. The second form illustrates how to include custom helpers | ||
| # when working with namespaced controllers, or other cases where the file containing the helper definition is not | ||
| # in one of Rails' standard load paths: | ||
| # helper :foo # => requires 'foo_helper' and includes FooHelper | ||
| # helper 'resources/foo' # => requires 'resources/foo_helper' and includes Resources::FooHelper | ||
| # | ||
| # Additionally, the +helper+ class method can receive and evaluate a block, making the methods defined available | ||
| # to the template. | ||
| # | ||
| # # One line | ||
| # helper { def hello() "Hello, world!" end } | ||
| # | ||
| # # Multi-line | ||
| # helper do | ||
| # def foo(bar) | ||
| # "#{bar} is the very best" | ||
| # end | ||
| # end | ||
| # | ||
| # Finally, all the above styles can be mixed together, and the +helper+ method can be invoked with a mix of | ||
| # +symbols+, +strings+, +modules+ and blocks. | ||
| # | ||
| # helper(:three, BlindHelper) { def mice() 'mice' end } | ||
| # | ||
| def helper(*args, &block) | ||
| modules_for_helpers(args).each do |mod| | ||
| add_template_helper(mod) | ||
| end | ||
|
|
||
| _helpers.module_eval(&block) if block_given? | ||
| end | ||
|
|
||
| # Clears up all existing helpers in this class, only keeping the helper | ||
| # with the same name as this class. | ||
| def clear_helpers | ||
| inherited_helper_methods = _helper_methods | ||
| self._helpers = Module.new | ||
| self._helper_methods = Array.new | ||
|
|
||
| inherited_helper_methods.each { |meth| helper_method meth } | ||
| default_helper_module! unless anonymous? | ||
| end | ||
|
|
||
| # Returns a list of modules, normalized from the acceptable kinds of | ||
| # helpers with the following behavior: | ||
| # | ||
| # String or Symbol:: :FooBar or "FooBar" becomes "foo_bar_helper", | ||
| # and "foo_bar_helper.rb" is loaded using require_dependency. | ||
| # | ||
| # Module:: No further processing | ||
| # | ||
| # After loading the appropriate files, the corresponding modules | ||
| # are returned. | ||
| # | ||
| # ==== Parameters | ||
| # * <tt>args</tt> - An array of helpers | ||
| # | ||
| # ==== Returns | ||
| # * <tt>Array</tt> - A normalized list of modules for the list of | ||
| # helpers provided. | ||
| def modules_for_helpers(args) | ||
| args.flatten.map! do |arg| | ||
| case arg | ||
| when String, Symbol | ||
| file_name = "#{arg.to_s.underscore}_helper" | ||
| begin | ||
| require_dependency(file_name) | ||
| rescue LoadError => e | ||
| raise AbstractController::Helpers::MissingHelperError.new(e, file_name) | ||
| end | ||
|
|
||
| mod_name = file_name.camelize | ||
| begin | ||
| mod_name.constantize | ||
| rescue LoadError | ||
| # dependencies.rb gives a similar error message but its wording is | ||
| # not as clear because it mentions autoloading. To the user all it | ||
| # matters is that a helper module couldn't be loaded, autoloading | ||
| # is an internal mechanism that should not leak. | ||
| raise NameError, "Couldn't find #{mod_name}, expected it to be defined in helpers/#{file_name}.rb" | ||
| end | ||
| when Module | ||
| arg | ||
| else | ||
| raise ArgumentError, "helper must be a String, Symbol, or Module" | ||
| end | ||
| end | ||
| end | ||
|
|
||
| private | ||
| # Makes all the (instance) methods in the helper module available to templates | ||
| # rendered through this controller. | ||
| # | ||
| # ==== Parameters | ||
| # * <tt>module</tt> - The module to include into the current helper module | ||
| # for the class | ||
| def add_template_helper(mod) | ||
| _helpers.module_eval { include mod } | ||
| end | ||
|
|
||
| def default_helper_module! | ||
| module_name = name.sub(/Controller$/, '') | ||
| module_path = module_name.underscore | ||
| helper module_path | ||
| rescue MissingSourceFile => e | ||
| raise e unless e.is_missing? "helpers/#{module_path}_helper" | ||
| rescue NameError => e | ||
| raise e unless e.missing_name? "#{module_name}Helper" | ||
| end | ||
| end | ||
| end | ||
| end |
| @@ -0,0 +1,12 @@ | ||
| require "active_support/benchmarkable" | ||
|
|
||
| module AbstractController | ||
| module Logger #:nodoc: | ||
| extend ActiveSupport::Concern | ||
|
|
||
| included do | ||
| config_accessor :logger | ||
| include ActiveSupport::Benchmarkable | ||
| end | ||
| end | ||
| end |
| @@ -0,0 +1,18 @@ | ||
| module AbstractController | ||
| module Railties | ||
| module RoutesHelpers | ||
| def self.with(routes, include_path_helpers = true) | ||
| Module.new do | ||
| define_method(:inherited) do |klass| | ||
| super(klass) | ||
| if namespace = klass.parents.detect { |m| m.respond_to?(:railtie_routes_url_helpers) } | ||
| klass.send(:include, namespace.railtie_routes_url_helpers(include_path_helpers)) | ||
| else | ||
| klass.send(:include, routes.url_helpers(include_path_helpers)) | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end |
| @@ -0,0 +1,126 @@ | ||
| require 'active_support/concern' | ||
| require 'active_support/core_ext/class/attribute' | ||
| require 'action_view' | ||
| require 'action_view/view_paths' | ||
| require 'set' | ||
|
|
||
| module AbstractController | ||
| class DoubleRenderError < Error | ||
| DEFAULT_MESSAGE = "Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like \"redirect_to(...) and return\"." | ||
|
|
||
| def initialize(message = nil) | ||
| super(message || DEFAULT_MESSAGE) | ||
| end | ||
| end | ||
|
|
||
| module Rendering | ||
| extend ActiveSupport::Concern | ||
| include ActionView::ViewPaths | ||
|
|
||
| # Normalize arguments, options and then delegates render_to_body and | ||
| # sticks the result in self.response_body. | ||
| # :api: public | ||
| def render(*args, &block) | ||
| options = _normalize_render(*args, &block) | ||
| self.response_body = render_to_body(options) | ||
| _process_format(rendered_format, options) if rendered_format | ||
| self.response_body | ||
| end | ||
|
|
||
| # Raw rendering of a template to a string. | ||
| # | ||
| # It is similar to render, except that it does not | ||
| # set the response_body and it should be guaranteed | ||
| # to always return a string. | ||
| # | ||
| # If a component extends the semantics of response_body | ||
| # (as Action Controller extends it to be anything that | ||
| # responds to the method each), this method needs to be | ||
| # overridden in order to still return a string. | ||
| # :api: plugin | ||
| def render_to_string(*args, &block) | ||
| options = _normalize_render(*args, &block) | ||
| render_to_body(options) | ||
| end | ||
|
|
||
| # Performs the actual template rendering. | ||
| # :api: public | ||
| def render_to_body(options = {}) | ||
| end | ||
|
|
||
| # Returns Content-Type of rendered content | ||
| # :api: public | ||
| def rendered_format | ||
| Mime::TEXT | ||
| end | ||
|
|
||
| DEFAULT_PROTECTED_INSTANCE_VARIABLES = Set.new %w( | ||
| @_action_name @_response_body @_formats @_prefixes @_config | ||
| @_view_context_class @_view_renderer @_lookup_context | ||
| @_routes @_db_runtime | ||
| ).map(&:to_sym) | ||
|
|
||
| # This method should return a hash with assigns. | ||
| # You can overwrite this configuration per controller. | ||
| # :api: public | ||
| def view_assigns | ||
| protected_vars = _protected_ivars | ||
| variables = instance_variables | ||
|
|
||
| variables.reject! { |s| protected_vars.include? s } | ||
| variables.each_with_object({}) { |name, hash| | ||
| hash[name.slice(1, name.length)] = instance_variable_get(name) | ||
| } | ||
| end | ||
|
|
||
| # Normalize args by converting render "foo" to render :action => "foo" and | ||
| # render "foo/bar" to render :file => "foo/bar". | ||
| # :api: plugin | ||
| def _normalize_args(action=nil, options={}) | ||
| if action.respond_to?(:permitted?) | ||
| if action.permitted? | ||
| action | ||
| else | ||
| raise ArgumentError, "render parameters are not permitted" | ||
| end | ||
| elsif action.is_a?(Hash) | ||
| action | ||
| else | ||
| options | ||
| end | ||
| end | ||
|
|
||
| # Normalize options. | ||
| # :api: plugin | ||
| def _normalize_options(options) | ||
| options | ||
| end | ||
|
|
||
| # Process extra options. | ||
| # :api: plugin | ||
| def _process_options(options) | ||
| options | ||
| end | ||
|
|
||
| # Process the rendered format. | ||
| # :api: private | ||
| def _process_format(format, options = {}) | ||
| end | ||
|
|
||
| # Normalize args and options. | ||
| # :api: private | ||
| def _normalize_render(*args, &block) | ||
| options = _normalize_args(*args, &block) | ||
| #TODO: remove defined? when we restore AP <=> AV dependency | ||
| if defined?(request) && request && request.variant.present? | ||
| options[:variant] = request.variant | ||
| end | ||
| _normalize_options(options) | ||
| options | ||
| end | ||
|
|
||
| def _protected_ivars # :nodoc: | ||
| DEFAULT_PROTECTED_INSTANCE_VARIABLES | ||
| end | ||
| end | ||
| end |
| @@ -0,0 +1,28 @@ | ||
| module AbstractController | ||
| module Translation | ||
| # Delegates to <tt>I18n.translate</tt>. Also aliased as <tt>t</tt>. | ||
| # | ||
| # When the given key starts with a period, it will be scoped by the current | ||
| # controller and action. So if you call <tt>translate(".foo")</tt> from | ||
| # <tt>PeopleController#index</tt>, it will convert the call to | ||
| # <tt>I18n.translate("people.index.foo")</tt>. This makes it less repetitive | ||
| # to translate many keys within the same controller / action and gives you a | ||
| # simple framework for scoping them consistently. | ||
| def translate(*args) | ||
| key = args.first | ||
| if key.is_a?(String) && (key[0] == '.') | ||
| key = "#{ controller_path.tr('/', '.') }.#{ action_name }#{ key }" | ||
| args[0] = key | ||
| end | ||
|
|
||
| I18n.translate(*args) | ||
| end | ||
| alias :t :translate | ||
|
|
||
| # Delegates to <tt>I18n.localize</tt>. Also aliased as <tt>l</tt>. | ||
| def localize(*args) | ||
| I18n.localize(*args) | ||
| end | ||
| alias :l :localize | ||
| end | ||
| end |
| @@ -0,0 +1,33 @@ | ||
| module AbstractController | ||
| # Includes +url_for+ into the host class (e.g. an abstract controller or mailer). The class | ||
| # has to provide a +RouteSet+ by implementing the <tt>_routes</tt> methods. Otherwise, an | ||
| # exception will be raised. | ||
| # | ||
| # Note that this module is completely decoupled from HTTP - the only requirement is a valid | ||
| # <tt>_routes</tt> implementation. | ||
| module UrlFor | ||
| extend ActiveSupport::Concern | ||
| include ActionDispatch::Routing::UrlFor | ||
|
|
||
| def _routes | ||
| raise "In order to use #url_for, you must include routing helpers explicitly. " \ | ||
| "For instance, `include Rails.application.routes.url_helpers`." | ||
| end | ||
|
|
||
| module ClassMethods | ||
| def _routes | ||
| nil | ||
| end | ||
|
|
||
| def action_methods | ||
| @action_methods ||= begin | ||
| if _routes | ||
| super - _routes.named_routes.helper_names | ||
| else | ||
| super | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end | ||
| end |
| @@ -0,0 +1,58 @@ | ||
| require 'active_support/rails' | ||
| require 'abstract_controller' | ||
| require 'action_dispatch' | ||
| require 'action_controller/metal/live' | ||
| require 'action_controller/metal/strong_parameters' | ||
|
|
||
| module ActionController | ||
| extend ActiveSupport::Autoload | ||
|
|
||
| autoload :Base | ||
| autoload :Caching | ||
| autoload :Metal | ||
| autoload :Middleware | ||
|
|
||
| autoload_under "metal" do | ||
| autoload :Compatibility | ||
| autoload :ConditionalGet | ||
| autoload :Cookies | ||
| autoload :DataStreaming | ||
| autoload :EtagWithTemplateDigest | ||
| autoload :Flash | ||
| autoload :ForceSSL | ||
| autoload :Head | ||
| autoload :Helpers | ||
| autoload :HideActions | ||
| autoload :HttpAuthentication | ||
| autoload :ImplicitRender | ||
| autoload :Instrumentation | ||
| autoload :MimeResponds | ||
| autoload :ParamsWrapper | ||
| autoload :RackDelegation | ||
| autoload :Redirecting | ||
| autoload :Renderers | ||
| autoload :Rendering | ||
| autoload :RequestForgeryProtection | ||
| autoload :Rescue | ||
| autoload :Streaming | ||
| autoload :StrongParameters | ||
| autoload :Testing | ||
| autoload :UrlFor | ||
| end | ||
|
|
||
| autoload :TestCase, 'action_controller/test_case' | ||
| autoload :TemplateAssertions, 'action_controller/test_case' | ||
|
|
||
| def self.eager_load! | ||
| super | ||
| ActionController::Caching.eager_load! | ||
| end | ||
| end | ||
|
|
||
| # Common Active Support usage in Action Controller | ||
| require 'active_support/core_ext/module/attribute_accessors' | ||
| require 'active_support/core_ext/load_error' | ||
| require 'active_support/core_ext/module/attr_internal' | ||
| require 'active_support/core_ext/name_error' | ||
| require 'active_support/core_ext/uri' | ||
| require 'active_support/inflector' |