Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

convert to a rails plugin structure

  • Loading branch information...
commit 5650248e705bec5cb0f98c6abf21ea4b31710c4c 1 parent 4832ee8
@AnthonyCaliendo authored
View
20 MIT-LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2009 Anthony Caliendo
+
+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.
View
29 README
@@ -1,21 +1,20 @@
= ActionMailer Callbacks
-Version 1.0
+ActionMailer callbacks provides basic callback support for ActionMailer. It providers similar functionality to before and after filters available in ActionController.
== Installation
-Drop "action_mailer_callbacks.rb" into either your "lib" directory.
-Drop "action_mailer_callbacks_test.rb" into your "test/unit" directory.
+Install as a plugin in your vendor/plugins directory.
-In the future, this will be made a proper plugin to make installation easier...
+If your rails version supports it, you can do:
+
+script/plugin install git://github.com/AnthonyCaliendo/action_mailer_callbacks.git
-== What Is It?
-ActionMailer callbacks provides basic callback support for ActionMailer. It providers similar functionality to before and after filters available in ActionController.
== How Do I Use It?
-There are 2 main ways to define a callback. In each case, the callback method is passed the mail as the only argument.
+There are 2 main ways to define a callback. In each case, the callback method/block is passed the mail object as the only argument.
You may define a callback using a block:
class FooMailer < ActionMailer::Base
@@ -46,6 +45,8 @@ class FooMailer < ActionMailer::Base
...
end
+
+
== What About Halting the Chain?
You can halt the chain in either a before or after callback. In order to do this, just call +halt_callback_chain+ in the block (or +self.class.halt_callback_chain+ in an instance method).
@@ -64,18 +65,20 @@ class FooMailer < ActionMailer::Base
end
end
+
+
== Anything Planned?
First, I would like to clean up the code a bit (extract most of the logic into a separate module, etc.). I apologize for the messiness of the code, at the time I wrote it I just wanted something "that worked" and I was pressed for time.
In terms of features, I would like to do at least three more things with this:
-1) Make it a proper plugin
-2) Add an around_deliver callback which would allow around advice.
-3) Add ability to skip filters
-4) Add ability to prepend filters in the chain
+1) Add an around_deliver callback which would allow around advice
+2) Add ability to skip filters
+3) Add ability to prepend filters in the chain
Feedback is welcomed.
-== License
-ActionMailer Callbacks is available under an MIT-style license.
+
+
+Copyright (c) 2009 Anthony Caliendo, released under the MIT license
View
22 Rakefile
@@ -0,0 +1,22 @@
+require 'rake'
+require 'rake/testtask'
+require 'rake/rdoctask'
+
+desc 'Default: run unit tests.'
+task :default => :test
+
+desc 'Test the action_mailer_callbacks plugin.'
+Rake::TestTask.new(:test) do |t|
+ t.libs << 'lib'
+ t.pattern = 'test/**/*_test.rb'
+ t.verbose = true
+end
+
+desc 'Generate documentation for the action_mailer_callbacks plugin.'
+Rake::RDocTask.new(:rdoc) do |rdoc|
+ rdoc.rdoc_dir = 'rdoc'
+ rdoc.title = 'ActionMailerCallbacks'
+ rdoc.options << '--line-numbers' << '--inline-source'
+ rdoc.rdoc_files.include('README')
+ rdoc.rdoc_files.include('lib/**/*.rb')
+end
View
1  init.rb
@@ -0,0 +1 @@
+require File.join(File.dirname(__FILE__), 'lib', 'action_mailer_callbacks')
View
79 lib/action_mailer_callbacks.rb
@@ -0,0 +1,79 @@
+class ActionMailer::Base
+ cattr_accessor :callback_chain_halted
+
+ def self.before_deliver(*args, &block)
+ delivery_callback :before, *args, &block
+ end
+
+ def self.after_deliver(*args, &block)
+ delivery_callback :after, *args, &block
+ end
+
+ def self.delivery_callback(type, *args, &block)
+ args = args.dup
+
+ method = args.first
+ method = nil if method.is_a?(Hash)
+
+ options = args.pop
+ options = {} unless options.is_a?(Hash)
+
+ callable = block || method
+ raise 'Specify a block or method to invoke in "before_deliver"' unless callable
+
+ callbacks = class_variable_defined?("@@#{type}_deliver_callbacks") ? class_variable_get("@@#{type}_deliver_callbacks") : ActiveSupport::OrderedHash.new
+ callbacks[callable] = options.symbolize_keys
+ class_variable_set("@@#{type}_deliver_callbacks", callbacks)
+ end
+
+ private
+ def create_mail_with_method_stored
+ mail = create_mail_without_method_stored
+ mail.instance_variable_set '@method_name', @method_name
+ mail
+ end
+ alias_method_chain :create_mail, :method_stored
+
+ def create_with_method_stored!(method_name, *parameters)
+ @method_name = method_name
+ create_without_method_stored!(method_name, *parameters)
+ end
+ alias_method_chain :create!, :method_stored
+
+ def deliver_with_callbacks!(mail = @mail)
+ self.class.callback_chain_halted = false
+ if invoke_delivery_callbacks(:before, mail)
+ deliver_without_callbacks!
+ invoke_delivery_callbacks(:after, mail)
+ end
+ end
+ alias_method_chain :deliver!, :callbacks
+
+ def invoke_delivery_callbacks(type, mail)
+ callbacks = self.class.class_variable_defined?("@@#{type}_deliver_callbacks") ? self.class.send(:class_variable_get, "@@#{type}_deliver_callbacks") : {}
+ method = mail.instance_variable_get '@method_name'
+
+ callbacks.each do |callable, options|
+ break if callback_chain_halted
+
+ only = [].push(*options[:only]) if options[:only] && method
+ next if only && !(only.include?(method.to_s) || only.include?(method.to_sym))
+
+ except = [].push(*options[:except]) if options[:except] && method
+ next if except && (except.include?(method.to_s) || except.include?(method.to_sym))
+
+ if callable.is_a? Proc
+ callable.call(mail)
+ else
+ send(callable, mail)
+ end
+ end
+
+ return !callback_chain_halted
+ end
+
+ def self.halt_callback_chain
+ self.callback_chain_halted = true
+ end
+
+end
View
184 test/action_mailer_callbacks_test.rb
@@ -0,0 +1,184 @@
+require File.expand_path(File.join(File.dirname(__FILE__), '../../../../test/test_helper'))
+require 'test/unit'
+
+class ActionMailerCallbacksTest < Test::Unit::TestCase
+
+ class TestMailer < ActionMailer::Base
+ cattr_accessor :befores, :afters
+ cattr_accessor :before_deliveries, :after_deliveries
+
+ def always; end #no-op
+ before_deliver do |mail|
+ TestMailer.befores << :before_block
+ end
+ before_deliver 'before_from_string'
+ def before_from_string(mail)
+ TestMailer.befores << :before_string
+ end
+ before_deliver :before_from_symbol
+ def before_from_symbol(mail)
+ TestMailer.befores << :before_symbol
+ end
+ after_deliver do |mail|
+ TestMailer.afters << :after_block
+ end
+ after_deliver 'after_from_string'
+ def after_from_string(mail)
+ TestMailer.afters << :after_string
+ end
+ after_deliver :after_from_symbol
+ def after_from_symbol(mail)
+ TestMailer.afters << :after_symbol
+ end
+
+ def in_except; end # no-op
+ def not_in_except; end #no-op
+ before_deliver :except => :in_except do |mail|
+ TestMailer.befores << :before_except
+ end
+ after_deliver :except => :in_except do |mail|
+ TestMailer.afters << :after_except
+ end
+
+ def in_only; end #no-op
+ def not_in_only; end #no-op
+ before_deliver :only => :in_only do |mail|
+ TestMailer.befores << :before_only
+ end
+ after_deliver :only => :in_only do |mail|
+ TestMailer.afters << :after_only
+ end
+
+ def halts; end
+ before_deliver :only => :halts do |mail|
+ TestMailer.befores << :halts_before
+ halt_callback_chain
+ end
+
+ after_deliver :only => :halts do |mail|
+ TestMailer.afters << :halts_after
+ raise 'this should never happen if the chain is halted...'
+ end
+
+ # we can test this by checking the deliveries in the before and after callbacks
+ def ensure_before_and_after; end
+ before_deliver :only => :ensure_before_and_after do |mail|
+ TestMailer.befores << :ensure_before
+ TestMailer.before_deliveries = ActionMailer::Base.deliveries.dup
+ end
+ after_deliver :only => :ensure_before_and_after do |mail|
+ TestMailer.afters << :ensure_before
+ TestMailer.after_deliveries = ActionMailer::Base.deliveries.dup
+ end
+
+ def template_path
+ "#{RAILS_ROOT}/test/fixtures/test_mailer"
+ end
+
+ def render(opts)
+ # no-op
+ end
+ end
+
+ setup do
+ TestMailer.befores = []
+ TestMailer.afters = []
+ TestMailer.before_deliveries = []
+ TestMailer.after_deliveries = []
+ ActionMailer::Base.deliveries = []
+ end
+
+ def test_invoke_before_with_block
+ TestMailer.deliver_always
+ assert TestMailer.befores.include?(:before_block)
+ end
+
+ def test_invoke_before_with_symbol
+ TestMailer.deliver_always
+ assert TestMailer.befores.include?(:before_symbol)
+ end
+
+ def test_invoke_before_with_string
+ TestMailer.deliver_always
+ assert TestMailer.befores.include?(:before_string)
+ end
+
+ def test_invoke_before_when_in_only_option
+ TestMailer.deliver_in_only
+ assert TestMailer.befores.include?(:before_only)
+ end
+
+ def test_invoke_before_when_not_in_except_option
+ TestMailer.deliver_not_in_except
+ assert TestMailer.befores.include?(:before_except)
+ end
+
+ def test_not_invoke_before_when_not_in_only_option
+ TestMailer.deliver_not_in_only
+ assert !TestMailer.befores.include?(:before_only)
+ end
+
+ def not_invoke_before_when_in_except_option
+ TestMailer.deliver_in_except
+ assert !TestMailer.befores.include?(:before_except)
+ end
+
+ def test_invoke_after_with_block
+ TestMailer.deliver_always
+ assert TestMailer.afters.include?(:after_block)
+ end
+
+ def test_invoke_after_with_symbol
+ TestMailer.deliver_always
+ assert TestMailer.afters.include?(:after_symbol)
+ end
+
+ def test_invoke_after_with_string
+ TestMailer.deliver_always
+ assert TestMailer.afters.include?(:after_string)
+ end
+
+ def test_invoke_after_when_in_only_option
+ TestMailer.deliver_in_only
+ assert TestMailer.afters.include?(:after_only)
+ end
+
+ def test_invoke_after_when_not_in_except_option
+ TestMailer.deliver_not_in_except
+ assert TestMailer.afters.include?(:after_except)
+ end
+
+ def test_not_invoke_after_when_not_in_only_option
+ TestMailer.deliver_not_in_only
+ assert !TestMailer.afters.include?(:after_only)
+ end
+
+ def test_not_invoke_after_when_in_except_option
+ TestMailer.deliver_in_except
+ assert !TestMailer.afters.include?(:after_except)
+ end
+
+ def test_should_halt_chain_when_callback_halts
+ TestMailer.deliver_halts
+ assert TestMailer.befores.include?(:halts_before)
+ assert !TestMailer.afters.include?(:halts_after)
+ end
+
+ def test_should_not_halt_chain_when_previous_chain_halted
+ TestMailer.deliver_halts
+ assert !TestMailer.afters.include?(:after_block)
+
+ TestMailer.deliver_always
+ assert TestMailer.afters.include?(:after_block)
+ end
+
+ def test_ensure_before_filter_called_before_method
+ TestMailer.deliver_ensure_before_and_after
+ assert TestMailer.before_deliveries.empty?
+ end
+
+ def test_ensure_after_filter_called_after_method
+ TestMailer.deliver_ensure_before_and_after
+ assert !TestMailer.after_deliveries.empty?
+ end
+end
Please sign in to comment.
Something went wrong with that request. Please try again.