Permalink
Browse files

Massive refactoring. Instead of relying in class methods, just define…

… a headers method which is given straight to the mail method.
  • Loading branch information...
1 parent 664cbeb commit b20b12e46486b2427b7c89d38ce273cc1d8ce476 @josevalim josevalim committed Mar 3, 2010
Showing with 108 additions and 273 deletions.
  1. +8 −4 CHANGELOG
  2. +18 −55 README.rdoc
  3. +4 −26 lib/mail_form.rb
  4. +45 −109 lib/mail_form/delivery.rb
  5. +10 −1 lib/mail_form/shim.rb
  6. +8 −8 lib/views/mail_form/contact.erb
  7. +4 −52 test/mail_form_test.rb
  8. +11 −18 test/test_helper.rb
View
12 CHANGELOG
@@ -1,12 +1,16 @@
-* Version 1.1
+# Version 1.2
+
+* No more class attributes, just define a headers method
+
+# Version 1.1
* Rails 3 compatibility
-* Version 1.0
+# Version 1.0
-* Rename to mail_form and launch Rails 2.3 branch.
+* Rename to mail_form and launch Rails 2.3 branch
-* Version 0.4
+# Version 0.4
* Added support to template
View
73 README.rdoc
@@ -16,16 +16,22 @@ MailForm allows you to send an e-mail straight from a form. For instance,
if you want to make a contact form just the following lines are needed (including the e-mail):
class ContactForm < MailForm::Base
- subject "My Contact Form"
- recipients "your.email@your.domain.com"
- sender{|c| %{"#{c.name}" <#{c.email}>} }
-
attribute :name, :validate => true
attribute :email, :validate => /[^@]+@[^\.]+\.[\w\.\-]+/
attribute :file, :attachment => true
attribute :message
attribute :nickname, :captcha => true
+
+ # Declare the e-mail headers. It accepts anything the mail method
+ # in ActionMailer accepts.
+ def headers
+ {
+ :subject => "My Contact Form",
+ :to => "your.email@your.domain.com",
+ :from => %("#{name}" <#{email}>)
+ }
+ end
end
Then you start a script/console and type:
@@ -64,8 +70,13 @@ in your model and declare which attributes should be sent:
append :remote_ip, :user_agent, :session
attributes :name, :email, :created_at
- subject "New user created"
- recipients "your.email@your.domain.com"
+
+ def headers
+ {
+ :to => "your.email@your.domain.com",
+ :subject => "User created an account"
+ }
+ end
end
The delivery will be triggered in an after_create hook.
@@ -135,37 +146,6 @@ Examples:
c = ContactForm.new(:name => 'José', :email => 'your@email.com')
c.deliver #=> true
-=== subject(string_or_symbol_or_block)
-
-Declares the subject of the contact email. It can be a string or a proc or a symbol.
-
-When a symbol is given, it will call a method on the form object with the same
-name as the symbol. As a proc, it receives a mail form instance. It defaults
-to the class human name.
-
- subject "My Contact Form"
- subject { |c| "Contacted by #{c.name}" }
-
-=== sender(string_or_symbol_or_block)
-
-Declares contact email sender. It can be a string or a proc or a symbol.
-
-When a symbol is given, it will call a method on the form object with the same
-name as the symbol. As a proc, it receives a mail form instance. By default is:
-
- sender { |c| c.email }
-
-This requires that your MailForm object have an email attribute.
-
-=== recipients(string_or_array_or_symbol_or_block)
-
-Who will receive the e-mail. Can be a string or array or a symbol or a proc.
-
-When a symbol is given, it will call a method on the form object with the same
-name as the symbol. As a proc, it receives a mail form instance.
-
-Both the proc and the symbol must return a string or an array. By default is nil.
-
=== append(*methods)
MailForm also makes easy to append request information from client to the sent
@@ -185,23 +165,6 @@ The remote ip, user agent and session will be sent in the e-mail in a
request information session. You can give to append any method that the
request object responds to.
-=== template(string_or_symbol_or_proc)
-
-Allow you to set the template that is going to rendered. This allows you to have
-several MailForm instances, using different templates.
-
-=== headers(hash)
-
-Configure additional headers to your e-mail.
-
-=== MailForm.template_root
-
-MailForm by default is configured to use the template which comes with the gem.
-If you want to use another, you just need to configure MailForm template root
-to point to your application:
-
- MailForm.template_root = File.join(Rails.root, "app", "views")
-
== I18n
I18n in MailForm works like in ActiveRecord, so all models, attributes and messages
@@ -237,4 +200,4 @@ is an I18n example file:
If you discover any bug, please use github issues tracker.
-Copyright (c) 2009 Plataforma Tec http://blog.plataformatec.com.br/
+Copyright (c) 2010 Plataforma Tec http://blog.plataformatec.com.br/
View
30 lib/mail_form.rb
@@ -7,42 +7,20 @@ class MailForm < ActionMailer::Base
append_view_path File.expand_path('../views', __FILE__)
def contact(resource)
- @from = get_from_class_and_eval(resource, :mail_sender)
- @subject = get_from_class_and_eval(resource, :mail_subject)
- @recipients = get_from_class_and_eval(resource, :mail_recipients)
- @template = get_from_class_and_eval(resource, :mail_template)
-
- if @recipients.blank?
- raise ScriptError, "You forgot to setup #{resource.class.name} recipients"
- end
-
- if resource.request.nil? && resource.class.mail_appendable.present?
+ if resource.request.nil? && resource.class.mail_appendable.any?
raise ScriptError, "You set :append values but forgot to give me the request object"
end
@resource = @form = resource
- @sent_on = Time.now.utc
- @headers = resource.class.mail_headers
- @content_type = 'text/html'
resource.class.mail_attachments.each do |attribute|
value = resource.send(attribute)
next unless value.respond_to?(:read)
attachments[value.original_filename] = value.read
end
- end
- protected
-
- def get_from_class_and_eval(resource, method)
- duck = resource.class.send(method)
-
- if duck.is_a?(Proc)
- duck.call(resource)
- elsif duck.is_a?(Symbol)
- resource.send(duck)
- else
- duck
- end
+ headers = resource.headers
+ headers[:from] ||= resource.email
+ mail(headers)
end
end
View
154 lib/mail_form/delivery.rb
@@ -1,30 +1,55 @@
module MailForm::Delivery
extend ActiveSupport::Concern
- ACCESSORS = [ :mail_attributes, :mail_subject, :mail_captcha,
- :mail_attachments, :mail_recipients, :mail_sender,
- :mail_headers, :mail_template, :mail_appendable ]
-
included do
- class_inheritable_reader *ACCESSORS
- protected *ACCESSORS
+ class_attribute :mail_attributes
+ self.mail_attributes = []
+
+ class_attribute :mail_captcha
+ self.mail_captcha = []
- # Initialize arrays and hashes
- write_inheritable_array :mail_captcha, []
- write_inheritable_array :mail_appendable, []
- write_inheritable_array :mail_attributes, []
- write_inheritable_array :mail_attachments, []
+ class_attribute :mail_attachments
+ self.mail_attachments = []
- headers({})
- sender {|c| c.email }
- subject{|c| c.class.model_name.human }
- template 'contact'
+ class_attribute :mail_appendable
+ self.mail_appendable = []
before_create :not_spam?
after_create :deliver!
attr_accessor :request
alias :deliver :create
+
+ extend Deprecated
+ end
+
+ module Deprecated
+ def subject(duck=nil, &block)
+ ActiveSupport::Deprecation.warn "subject is deprecated. Please define a headers method " <<
+ "in your instance which returns a hash with :subject as key instead.", caller
+ end
+
+ def sender(duck=nil, &block)
+ ActiveSupport::Deprecation.warn "from/sender is deprecated. Please define a headers method " <<
+ "in your instance which returns a hash with :from as key instead.", caller
+ end
+ alias :from :sender
+
+ def recipients(duck=nil, &block)
+ ActiveSupport::Deprecation.warn "to/recipients is deprecated. Please define a headers method " <<
+ "in your instance which returns a hash with :to as key instead.", caller
+ end
+ alias :to :recipients
+
+ def headers(hash)
+ ActiveSupport::Deprecation.warn "to/recipients is deprecated. Please define a headers method " <<
+ "in your instance which returns the desired headers instead.", caller
+ end
+
+ def template(new_template)
+ ActiveSupport::Deprecation.warn "template is deprecated. Please define a headers method " <<
+ "in your instance which returns a hash with :template_name as key instead.", caller
+ end
end
module ClassMethods
@@ -71,11 +96,11 @@ def attribute(*accessors)
attr_accessor *(accessors - instance_methods.map(&:to_sym))
if options[:attachment]
- write_inheritable_array(:mail_attachments, accessors)
+ self.mail_attachments += accessors
elsif options[:captcha]
- write_inheritable_array(:mail_captcha, accessors)
+ self.mail_captcha += accessors
else
- write_inheritable_array(:mail_attributes, accessors)
+ self.mail_attributes += accessors
end
validation = options.delete(:validate)
@@ -99,95 +124,6 @@ def attribute(*accessors)
end
alias :attributes :attribute
- # Declares contact email sender. It can be a string or a proc or a symbol.
- #
- # When a symbol is given, it will call a method on the form object with
- # the same name as the symbol. As a proc, it receives a simple form
- # instance. By default is the class human name.
- #
- # == Examples
- #
- # class ContactForm < MailForm
- # subject "My Contact Form"
- # end
- #
- def subject(duck=nil, &block)
- write_inheritable_attribute(:mail_subject, duck || block)
- end
-
- # Declares contact email sender. It can be a string or a proc or a symbol.
- #
- # When a symbol is given, it will call a method on the form object with
- # the same name as the symbol. As a proc, it receives a simple form
- # instance. By default is:
- #
- # sender{ |c| c.email }
- #
- # This requires that your MailForm object have an email attribute.
- #
- # == Examples
- #
- # class ContactForm < MailForm
- # # Change sender to include also the name
- # sender { |c| %{"#{c.name}" <#{c.email}>} }
- # end
- #
- def sender(duck=nil, &block)
- write_inheritable_attribute(:mail_sender, duck || block)
- end
- alias :from :sender
-
- # Who will receive the e-mail. Can be a string or array or a symbol or a proc.
- #
- # When a symbol is given, it will call a method on the form object with
- # the same name as the symbol. As a proc, it receives a simple form instance.
- #
- # Both the proc and the symbol must return a string or an array. By default
- # is nil.
- #
- # == Examples
- #
- # class ContactForm < MailForm
- # recipients [ "first.manager@domain.com", "second.manager@domain.com" ]
- # end
- #
- def recipients(duck=nil, &block)
- write_inheritable_attribute(:mail_recipients, duck || block)
- end
- alias :to :recipients
-
- # Additional headers to your e-mail.
- #
- # == Examples
- #
- # class ContactForm < MailForm
- # headers { :content_type => 'text/html' }
- # end
- #
- def headers(hash)
- write_inheritable_hash(:mail_headers, hash)
- end
-
- # Customized template for your e-mail, if you don't want to use default
- # 'contact' template or need more than one contact form with different
- # template layouts.
- #
- # When a symbol is given, it will call a method on the form object with
- # the same name as the symbol. As a proc, it receives a simple form
- # instance. Both method and proc must return a string with the template
- # name. Defaults to 'contact'.
- #
- # == Examples
- #
- # class ContactForm < MailForm
- # # look for a template in views/mail_form/notifier/my_template.erb
- # template 'my_template'
- # end
- #
- def template(new_template)
- write_inheritable_attribute(:mail_template, new_template)
- end
-
# Values from request object to be appended to the contact form.
# Whenever used, you have to send the request object when initializing the object:
#
@@ -203,7 +139,7 @@ def template(new_template)
# end
#
def append(*values)
- write_inheritable_array(:mail_appendable, values)
+ self.mail_appendable += values
end
end
@@ -215,7 +151,7 @@ def append(*values)
# returns false otherwise.
#
def spam?
- mail_captcha.each do |field|
+ self.class.mail_captcha.each do |field|
next if send(field).blank?
if defined?(Rails) && Rails.env.development?
View
11 lib/mail_form/shim.rb
@@ -21,13 +21,22 @@ def i18n_scope
end
end
- # Initialize assigning the parameters given as hash (just as in ActiveRecord).
+ # Initialize assigning the parameters given as hash.
def initialize(params={})
params.each_pair do |attr, value|
self.send(:"#{attr}=", value)
end unless params.blank?
end
+ # Returns a hash of attributes, according to the attributes existent in
+ # self.class.mail_attributes.
+ def attributes
+ self.class.mail_attributes.inject({}) do |hash, attr|
+ hash[attr.to_s] = send(attr)
+ hash
+ end
+ end
+
# Always return true so when using form_for, the default method will be post.
def new_record?
true
View
16 lib/views/mail_form/contact.erb
@@ -1,15 +1,14 @@
-<h4 style="text-decoration:underline"><%= mailer.subject %></h4>
+<h4 style="text-decoration:underline"><%= message.subject %></h4>
-<% @resource.class.mail_attributes.each do |attribute|
- value = @resource.send(attribute)
+<% @resource.attributes.each do |attribute, value|
next if value.blank? %>
<p><b><%= @resource.class.human_attribute_name(attribute) %>:</b>
<%= case value
when /\n/
raw(simple_format(h(value)))
when Time, DateTime, Date
- I18n.l value
+ I18n.l(value)
else
value
end
@@ -23,16 +22,17 @@
value = @resource.request.send(attribute)
value = if value.is_a?(Hash) && !value.empty?
- content_tag(:ul, value.to_a.map{|k,v| content_tag(:li, h("#{k}: #{v.inspect}")) }.join("\n"), :style => "list-style:none;")
+ list = value.to_a.map{ |k,v| content_tag(:li, h("#{k}: #{v.inspect}")) }.join("\n")
+ content_tag(:ul, raw(list), :style => "list-style:none;")
elsif value.is_a?(String)
- h(value)
+ value
else
- h(value.inspect)
+ value.inspect
end
%>
<p><b><%= I18n.t attribute, :scope => [ :mail_form, :request ], :default => attribute.to_s.humanize %>:</b>
- <%= value.include?("\n") ? raw(simple_format(value)) : value %></p>
+ <%= value.include?("\n") ? simple_format(value) : value %></p>
<% end %>
<br />
<% end %>
View
56 test/mail_form_test.rb
@@ -13,8 +13,6 @@ def setup
test_file = Rack::Test::UploadedFile.new(File.join(File.dirname(__FILE__), 'test_file.txt'))
@with_file = FileForm.new(:name => 'José', :email => 'my.email@my.domain.com', :message => "Cool", :file => test_file)
- @template = TemplateForm.new(@valid_attributes)
-
ActionMailer::Base.deliveries = []
end
@@ -23,50 +21,14 @@ def test_email_is_sent
assert_equal 1, ActionMailer::Base.deliveries.size
end
- def test_subject_defaults_to_class_human_name
- @form.deliver
- assert_equal 'Contact form', first_delivery.subject
- end
-
- def test_subject_is_a_string
- @advanced.deliver
- assert_equal 'My Advanced Form', first_delivery.subject
- end
-
- def test_sender_defaults_to_form_email
- @form.deliver
- assert_equal ['my.email@my.domain.com'], first_delivery.from
- end
-
- def test_error_is_raised_when_recipients_is_nil
- assert_raise ScriptError do
- NullRecipient.new.deliver
- end
- end
-
- def test_recipients_is_a_string
+ def test_subject_defaults_to_action_mailer_one
@form.deliver
- assert_equal ['my.email@my.domain.com'], first_delivery.to
- end
-
- def test_recipients_is_an_array
- @advanced.deliver
- assert_equal ['my.first@email.com', 'my.second@email.com'], first_delivery.to
- end
-
- def test_recipients_is_a_symbold
- @with_file.deliver
- assert_equal ['contact_file@my.domain.com'], first_delivery.to
- end
-
- def test_headers_is_a_hash
- @advanced.deliver
- assert_equal 'mypath', first_delivery.header['return-path'].to_s
+ assert_equal 'Contact', first_delivery.subject
end
def test_body_contains_subject
@form.deliver
- assert_match /Contact form/, first_delivery.body.to_s
+ assert_match /Contact/, first_delivery.body.to_s
end
def test_body_contains_attributes_values
@@ -142,6 +104,7 @@ def test_request_info_values_are_printed
def test_request_info_hashes_are_print_inside_lis
@request.session = { :my => :session, :user => "data" }
@advanced.deliver
+ assert_match /<ul/, last_delivery.body.to_s
assert_match /<li>my: :session<\/li>/, last_delivery.body.to_s
assert_match /<li>user: &quot;data&quot;<\/li>/, last_delivery.body.to_s
end
@@ -163,17 +126,6 @@ def test_form_with_file_does_not_output_attachment_as_attribute
assert_no_match /File:/, first_delivery.body.to_s
end
- def test_form_with_customized_template_render_correct_template
- begin
- previous_view_path = MailForm.view_paths
- MailForm.prepend_view_path File.join(File.dirname(__FILE__), 'views')
- @template.deliver
- assert_match 'Hello from my custom template!', last_delivery.body.to_s
- ensure
- MailForm.view_paths = previous_view_path
- end
- end
-
protected
def first_delivery
View
29 test/test_helper.rb
@@ -19,8 +19,6 @@
require 'mail_form'
class ContactForm < MailForm::Base
- recipients 'my.email@my.domain.com'
-
attribute :name, :validate => true
attribute :email, :validate => /[^@]+@[^\.]+\.[\w\.\-]+/
attribute :category, :validate => ["Interface bug", "General"], :allow_blank => true
@@ -30,6 +28,10 @@ class ContactForm < MailForm::Base
before_create :set_created_at
+ def headers
+ { :to => 'my.email@my.domain.com' }
+ end
+
def set_created_at
created_at = Date.today
end
@@ -42,10 +44,13 @@ def callback
class AdvancedForm < ContactForm
append :remote_ip, :user_agent, :session
- recipients [ 'my.first@email.com', 'my.second@email.com' ]
- subject 'My Advanced Form'
- sender{|c| %{"#{c.name}" <#{c.email}>} }
- headers 'return-path' => 'mypath'
+ def headers
+ { :to => [ 'my.first@email.com', 'my.second@email.com' ],
+ :subject => "My Advanced Form",
+ :from => %{"#{name}" <#{email}>},
+ "return-path" => "mypath"
+ }
+ end
end
class FileForm < ContactForm
@@ -61,18 +66,6 @@ def set_recipient
end
end
-class NullRecipient < MailForm::Base
- sender 'my.email@my.domain.com'
-end
-
-class TemplateForm < ContactForm
- template 'custom_template'
-end
-
-class WrongForm < ContactForm
- template 'does_not_exist'
-end
-
# Needed to correctly test an uploaded file
class Rack::Test::UploadedFile
def read

0 comments on commit b20b12e

Please sign in to comment.