Skip to content

Commit

Permalink
Massive refactoring. Instead of relying in class methods, just define…
Browse files Browse the repository at this point in the history
… a headers method which is given straight to the mail method.
  • Loading branch information
josevalim committed Mar 3, 2010
1 parent 664cbeb commit b20b12e
Show file tree
Hide file tree
Showing 8 changed files with 108 additions and 273 deletions.
12 changes: 8 additions & 4 deletions 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

Expand Down
73 changes: 18 additions & 55 deletions README.rdoc
Expand Up @@ -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:
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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/
30 changes: 4 additions & 26 deletions lib/mail_form.rb
Expand Up @@ -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
154 changes: 45 additions & 109 deletions 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
Expand Down Expand Up @@ -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)
Expand All @@ -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:
#
Expand All @@ -203,7 +139,7 @@ def template(new_template)
# end
#
def append(*values)
write_inheritable_array(:mail_appendable, values)
self.mail_appendable += values
end
end

Expand All @@ -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?
Expand Down

0 comments on commit b20b12e

Please sign in to comment.