mail_form 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +105 -51
- data/Rakefile +1 -0
- data/lib/mail_form.rb +48 -32
- data/lib/mail_form/base.rb +6 -124
- data/lib/mail_form/delivery.rb +239 -0
- data/lib/mail_form/shim.rb +50 -0
- data/lib/mail_form/version.rb +1 -1
- data/lib/views/mail_form/contact.erb +38 -0
- data/test/mail_form_test.rb +190 -0
- data/test/{base_test.rb → resource_test.rb} +31 -7
- data/test/test_helper.rb +25 -8
- metadata +7 -9
- data/lib/mail_form/dsl.rb +0 -171
- data/lib/mail_form/errors.rb +0 -59
- data/lib/mail_form/notifier.rb +0 -48
- data/test/errors_test.rb +0 -85
- data/test/notifier_test.rb +0 -183
- data/views/mail_form/notifier/contact.erb +0 -30
data/README.rdoc
CHANGED
@@ -1,9 +1,21 @@
|
|
1
1
|
== MailForm
|
2
2
|
|
3
|
+
=== Rails 3
|
4
|
+
|
5
|
+
This gem was built on top of ActiveModel to showcase how you can pull in validations, naming
|
6
|
+
and i18n from Rails to your models without the need to implement it all by yourself.
|
7
|
+
|
8
|
+
In other words, this README refers to the MailForm gem to be used in Rails 3. For instructions
|
9
|
+
on how to use MailForm in Rails 2.x, please check the v1.0 branch:
|
10
|
+
|
11
|
+
http://github.com/plataformatec/mail_form/tree/v1.0
|
12
|
+
|
13
|
+
=== Description
|
14
|
+
|
3
15
|
MailForm allows you to send an e-mail straight from a form. For instance,
|
4
16
|
if you want to make a contact form just the following lines are needed (including the e-mail):
|
5
17
|
|
6
|
-
class ContactForm < MailForm
|
18
|
+
class ContactForm < MailForm::Base
|
7
19
|
subject "My Contact Form"
|
8
20
|
recipients "your.email@your.domain.com"
|
9
21
|
sender{|c| %{"#{c.name}" <#{c.email}>} }
|
@@ -19,61 +31,73 @@ if you want to make a contact form just the following lines are needed (includin
|
|
19
31
|
Then you start a script/console and type:
|
20
32
|
|
21
33
|
c = ContactForm.new(:name => 'José', :email => 'jose@email.com', :message => 'Cool!')
|
22
|
-
c.
|
34
|
+
c.create
|
23
35
|
|
24
|
-
Check your inbox and the e-mail will be there, with the sent fields (assuming
|
25
|
-
|
36
|
+
Check your inbox and the e-mail will be there, with the sent fields (assuming that
|
37
|
+
you configured your mailer delivery method properly).
|
26
38
|
|
27
|
-
MailForm
|
28
|
-
(so it works with custom FormBuilders) and can also send the user request information
|
29
|
-
in the mail.
|
39
|
+
=== MailForm::Base
|
30
40
|
|
31
|
-
|
41
|
+
When you inherit from MailForm::Base, it pulls down a set of stuff from ActiveModel,
|
42
|
+
as ActiveModel::Validation, ActiveModel::Translation and ActiveModel::Naming.
|
32
43
|
|
33
|
-
|
44
|
+
This bring I18n, error messages, validations and attributes handling like in
|
45
|
+
ActiveRecord to MailForm, so MailForm can be used in your controllers and form builders without extra tweaks. This also means that instead of the following:
|
34
46
|
|
35
|
-
|
47
|
+
attribute :email, :validate => /[^@]+@[^\.]+\.[\w\.\-]+/
|
36
48
|
|
37
|
-
|
49
|
+
You could actually do this:
|
38
50
|
|
39
|
-
|
51
|
+
attribute :email
|
52
|
+
validates_format_of :email, :with => /[^@]+@[^\.]+\.[\w\.\-]+/
|
40
53
|
|
41
|
-
|
54
|
+
Choose the one which pleases you the most. For more information on the API, please
|
55
|
+
continue reading below.
|
42
56
|
|
43
|
-
|
57
|
+
=== Playing together ORMs
|
44
58
|
|
45
|
-
MailForm
|
46
|
-
|
59
|
+
MailForm plays nice with ORMs as well. You just need to include MailForm::Delivery
|
60
|
+
in your model and declare which attributes should be sent:
|
61
|
+
|
62
|
+
class User < ActiveRecord::Base
|
63
|
+
include MailForm::Delivery
|
47
64
|
|
48
|
-
class ContactForm < MailForm
|
49
65
|
append :remote_ip, :user_agent, :session
|
50
|
-
|
66
|
+
attributes :name, :email, :created_at
|
67
|
+
subject "New user created"
|
68
|
+
recipients "your.email@your.domain.com"
|
51
69
|
end
|
52
70
|
|
53
|
-
|
71
|
+
The delivery will be triggered in an after_create hook.
|
54
72
|
|
55
|
-
|
56
|
-
@contact_form.request = request
|
73
|
+
== Installation
|
57
74
|
|
58
|
-
|
59
|
-
|
60
|
-
|
75
|
+
Install MailForm is very easy. It is stored in Gemcutter, so just run the following:
|
76
|
+
|
77
|
+
sudo gem install mail_form
|
78
|
+
|
79
|
+
If you want it as plugin, just do:
|
80
|
+
|
81
|
+
script/plugin install git://github.com/plataformatec/mail_form.git
|
61
82
|
|
62
83
|
== API Overview
|
63
84
|
|
64
|
-
|
85
|
+
=== attributes(*attributes)
|
65
86
|
|
66
87
|
Declare your form attributes. All attributes declared here will be appended
|
67
88
|
to the e-mail, except the ones :captcha is true.
|
68
89
|
|
69
90
|
Options:
|
70
91
|
|
71
|
-
* :validate - When true, validates the
|
72
|
-
When a regexp
|
73
|
-
|
92
|
+
* :validate - A hook to validates_*_of. When true is given, validates the
|
93
|
+
presence of the attribute. When a regexp, validates format. When array,
|
94
|
+
validates the inclusion of the attribute in the array.
|
74
95
|
|
75
|
-
Whenever :validate is
|
76
|
-
|
96
|
+
Whenever :validate is given, the presence is automatically checked. Give
|
97
|
+
:allow_blank => true to override.
|
98
|
+
|
99
|
+
Finally, when :validate is a symbol, the method given as symbol will be
|
100
|
+
called. Then you can add validations as you do in ActiveRecord (errors.add).
|
77
101
|
|
78
102
|
* :attachment - When given, expects a file to be sent and attaches
|
79
103
|
it to the e-mail. Don't forget to set your form to multitype.
|
@@ -83,15 +107,18 @@ Options:
|
|
83
107
|
|
84
108
|
Examples:
|
85
109
|
|
86
|
-
class ContactForm < MailForm
|
110
|
+
class ContactForm < MailForm::Base
|
87
111
|
attributes :name, :validate => true
|
88
112
|
attributes :email, :validate => /[^@]+@[^\.]+\.[\w\.\-]+/
|
113
|
+
attributes :type, :validate => ["General", "Interface bug"]
|
89
114
|
attributes :message
|
90
|
-
attributes :screenshot, :attachment => true, :validate => :
|
115
|
+
attributes :screenshot, :attachment => true, :validate => :interface_bug?
|
91
116
|
attributes :nickname, :captcha => true
|
92
117
|
|
93
|
-
def
|
94
|
-
|
118
|
+
def interface_bug?
|
119
|
+
if type == 'Interface bug' && screenshot.nil?
|
120
|
+
self.errors.add(:screenshot, "can't be blank on interface bugs")
|
121
|
+
end
|
95
122
|
end
|
96
123
|
end
|
97
124
|
|
@@ -106,10 +133,9 @@ Examples:
|
|
106
133
|
c.errors.full_messages #=> [ "Name can't be blank", "Email is invalid" ]
|
107
134
|
|
108
135
|
c = ContactForm.new(:name => 'José', :email => 'your@email.com')
|
109
|
-
|
110
|
-
c.save #=> true
|
136
|
+
c.deliver #=> true
|
111
137
|
|
112
|
-
|
138
|
+
=== subject(string_or_symbol_or_block)
|
113
139
|
|
114
140
|
Declares the subject of the contact email. It can be a string or a proc or a symbol.
|
115
141
|
|
@@ -120,7 +146,7 @@ to the class human name.
|
|
120
146
|
subject "My Contact Form"
|
121
147
|
subject { |c| "Contacted by #{c.name}" }
|
122
148
|
|
123
|
-
|
149
|
+
=== sender(string_or_symbol_or_block)
|
124
150
|
|
125
151
|
Declares contact email sender. It can be a string or a proc or a symbol.
|
126
152
|
|
@@ -131,7 +157,7 @@ name as the symbol. As a proc, it receives a mail form instance. By default is:
|
|
131
157
|
|
132
158
|
This requires that your MailForm object have an email attribute.
|
133
159
|
|
134
|
-
|
160
|
+
=== recipients(string_or_array_or_symbol_or_block)
|
135
161
|
|
136
162
|
Who will receive the e-mail. Can be a string or array or a symbol or a proc.
|
137
163
|
|
@@ -140,31 +166,59 @@ name as the symbol. As a proc, it receives a mail form instance.
|
|
140
166
|
|
141
167
|
Both the proc and the symbol must return a string or an array. By default is nil.
|
142
168
|
|
143
|
-
|
169
|
+
=== append(*methods)
|
170
|
+
|
171
|
+
MailForm also makes easy to append request information from client to the sent
|
172
|
+
mail. You just have to do:
|
173
|
+
|
174
|
+
class ContactForm < MailForm::Base
|
175
|
+
append :remote_ip, :user_agent, :session
|
176
|
+
# ...
|
177
|
+
end
|
178
|
+
|
179
|
+
And in your controller:
|
180
|
+
|
181
|
+
@contact_form = ContactForm.new(params[:contact_form])
|
182
|
+
@contact_form.request = request
|
183
|
+
|
184
|
+
The remote ip, user agent and session will be sent in the e-mail in a
|
185
|
+
request information session. You can give to append any method that the
|
186
|
+
request object responds to.
|
187
|
+
|
188
|
+
=== template(string_or_symbol_or_proc)
|
144
189
|
|
145
190
|
Allow you to set the template that is going to rendered. This allows you to have
|
146
191
|
several MailForm instances, using different templates.
|
147
192
|
|
148
|
-
|
193
|
+
=== headers(hash)
|
194
|
+
|
195
|
+
Configure additional headers to your e-mail.
|
196
|
+
|
197
|
+
=== MailForm.template_root
|
198
|
+
|
199
|
+
MailForm by default is configured to use the template which comes with the gem.
|
200
|
+
If you want to use another, you just need to configure MailForm template root
|
201
|
+
to point to your application:
|
149
202
|
|
150
|
-
|
203
|
+
MailForm.template_root = File.join(Rails.root, "app", "views")
|
151
204
|
|
152
205
|
== I18n
|
153
206
|
|
154
|
-
|
155
|
-
|
207
|
+
I18n in MailForm works like in ActiveRecord, so all models, attributes and messages
|
208
|
+
can be used with localized. However, in order to DRY up your yml files, mail_form
|
209
|
+
requires on I18n >= 0.2.0 since it uses the ability to symlink translations. Below
|
210
|
+
is an I18n example file:
|
156
211
|
|
157
212
|
mail_form:
|
158
213
|
models:
|
159
214
|
contact_form: "Your site contact form"
|
160
215
|
attributes:
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
telephone: "must have eight digits"
|
216
|
+
contact_form:
|
217
|
+
email: "E-mail"
|
218
|
+
telephone: "Telephone number"
|
219
|
+
message: "Sent message"
|
220
|
+
errors:
|
221
|
+
messages: :"activerecord.errors.messages"
|
168
222
|
request:
|
169
223
|
title: "Technical information about the user"
|
170
224
|
remote_ip: "IP Address"
|
data/Rakefile
CHANGED
data/lib/mail_form.rb
CHANGED
@@ -1,32 +1,48 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
1
|
+
class MailForm < ActionMailer::Base
|
2
|
+
autoload :Base, 'mail_form/base'
|
3
|
+
autoload :Callbacks, 'mail_form/callbacks'
|
4
|
+
autoload :Delivery, 'mail_form/delivery'
|
5
|
+
autoload :Shim, 'mail_form/shim'
|
6
|
+
|
7
|
+
append_view_path File.expand_path('../views', __FILE__)
|
8
|
+
|
9
|
+
def contact(resource)
|
10
|
+
@from = get_from_class_and_eval(resource, :mail_sender)
|
11
|
+
@subject = get_from_class_and_eval(resource, :mail_subject)
|
12
|
+
@recipients = get_from_class_and_eval(resource, :mail_recipients)
|
13
|
+
@template = get_from_class_and_eval(resource, :mail_template)
|
14
|
+
|
15
|
+
if @recipients.blank?
|
16
|
+
raise ScriptError, "You forgot to setup #{resource.class.name} recipients"
|
17
|
+
end
|
18
|
+
|
19
|
+
if resource.request.nil? && resource.class.mail_appendable.present?
|
20
|
+
raise ScriptError, "You set :append values but forgot to give me the request object"
|
21
|
+
end
|
22
|
+
|
23
|
+
@resource = @form = resource
|
24
|
+
@sent_on = Time.now.utc
|
25
|
+
@headers = resource.class.mail_headers
|
26
|
+
@content_type = 'text/html'
|
27
|
+
|
28
|
+
resource.class.mail_attachments.each do |attribute|
|
29
|
+
value = resource.send(attribute)
|
30
|
+
next unless value.respond_to?(:read)
|
31
|
+
attachments[value.original_filename] = value.read
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
protected
|
36
|
+
|
37
|
+
def get_from_class_and_eval(resource, method)
|
38
|
+
duck = resource.class.send(method)
|
39
|
+
|
40
|
+
if duck.is_a?(Proc)
|
41
|
+
duck.call(resource)
|
42
|
+
elsif duck.is_a?(Symbol)
|
43
|
+
resource.send(duck)
|
44
|
+
else
|
45
|
+
duck
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/mail_form/base.rb
CHANGED
@@ -1,126 +1,8 @@
|
|
1
|
-
class MailForm
|
2
|
-
|
1
|
+
class MailForm::Base
|
2
|
+
include MailForm::Shim
|
3
|
+
include MailForm::Delivery
|
3
4
|
|
4
|
-
|
5
|
-
|
6
|
-
# It also accepts the request object as second parameter which must be sent
|
7
|
-
# whenever :append is called.
|
8
|
-
#
|
9
|
-
def initialize(params={}, request=nil)
|
10
|
-
@request = request
|
11
|
-
params.each_pair do |attr, value|
|
12
|
-
self.send(:"#{attr}=", value)
|
13
|
-
end unless params.blank?
|
5
|
+
def self.lookup_ancestors
|
6
|
+
super - [MailForm::Base]
|
14
7
|
end
|
15
|
-
|
16
|
-
# In development, raises an error if the captcha field is not blank. This is
|
17
|
-
# is good to remember that the field should be hidden with CSS and shown only
|
18
|
-
# to robots.
|
19
|
-
#
|
20
|
-
# In test and in production, it returns true if aall captcha field are blank,
|
21
|
-
# returns false otherwise.
|
22
|
-
#
|
23
|
-
def spam?
|
24
|
-
form_captcha.each do |field|
|
25
|
-
next if send(field).blank?
|
26
|
-
|
27
|
-
if RAILS_ENV == 'development'
|
28
|
-
raise ScriptError, "The captcha field #{field} was supposed to be blank"
|
29
|
-
else
|
30
|
-
return true
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
return false
|
35
|
-
end
|
36
|
-
|
37
|
-
def not_spam?
|
38
|
-
!spam?
|
39
|
-
end
|
40
|
-
|
41
|
-
# To check if the form is valid, we run the validations.
|
42
|
-
#
|
43
|
-
# If the validation is true, we just check if the field is not blank. If it's
|
44
|
-
# a regexp, we check if it is not blank AND if the Regexp matches.
|
45
|
-
#
|
46
|
-
# You can have totally custom validations by sending a symbol. Then the method
|
47
|
-
# given as symbol will be called and then you cann hook your validations there.
|
48
|
-
#
|
49
|
-
def valid?
|
50
|
-
return false unless errors.empty?
|
51
|
-
|
52
|
-
form_validatable.each_pair do |field, validation|
|
53
|
-
next unless validation
|
54
|
-
|
55
|
-
if validation.is_a?(Symbol)
|
56
|
-
send(validation)
|
57
|
-
elsif send(field).blank?
|
58
|
-
errors.add(field, :blank)
|
59
|
-
elsif validation.is_a?(Regexp)
|
60
|
-
errors.add(field, :invalid) unless send(field) =~ validation
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
errors.empty?
|
65
|
-
end
|
66
|
-
|
67
|
-
def invalid?
|
68
|
-
!valid?
|
69
|
-
end
|
70
|
-
|
71
|
-
# Always return true so when using form_for, the default method will be post.
|
72
|
-
#
|
73
|
-
def new_record?
|
74
|
-
true
|
75
|
-
end
|
76
|
-
|
77
|
-
# Always return nil so when using form_for, the default method will be post.
|
78
|
-
#
|
79
|
-
def id
|
80
|
-
nil
|
81
|
-
end
|
82
|
-
|
83
|
-
# If is not spam and the form is valid, we send the e-mail and returns true.
|
84
|
-
# Otherwise returns false.
|
85
|
-
#
|
86
|
-
def deliver(run_validations=true)
|
87
|
-
if !run_validations || (self.not_spam? && self.valid?)
|
88
|
-
MailForm::Notifier.deliver_contact(self)
|
89
|
-
return true
|
90
|
-
else
|
91
|
-
return false
|
92
|
-
end
|
93
|
-
end
|
94
|
-
alias :save :deliver
|
95
|
-
|
96
|
-
# Add a human attribute name interface on top of I18n. If email is received as
|
97
|
-
# attribute, it will look for a translated name on:
|
98
|
-
#
|
99
|
-
# mail_form:
|
100
|
-
# attributes:
|
101
|
-
# email: E-mail
|
102
|
-
#
|
103
|
-
def self.human_attribute_name(attribute, options={})
|
104
|
-
I18n.translate("attributes.#{attribute}", options.merge(:default => attribute.to_s.humanize, :scope => [:mail_form]))
|
105
|
-
end
|
106
|
-
|
107
|
-
# Add a human name interface on top of I18n. If you have a model named
|
108
|
-
# MailForm, it will search for the localized name on:
|
109
|
-
#
|
110
|
-
# mail_form:
|
111
|
-
# models:
|
112
|
-
# contact_form: Contact form
|
113
|
-
#
|
114
|
-
def self.human_name(options={})
|
115
|
-
underscored = self.name.demodulize.underscore
|
116
|
-
I18n.translate("models.#{underscored}", options.merge(:default => underscored.humanize, :scope => [:mail_form]))
|
117
|
-
end
|
118
|
-
|
119
|
-
# Return the errors in this form. The object returned as the same API as the
|
120
|
-
# ActiveRecord one.
|
121
|
-
#
|
122
|
-
def errors
|
123
|
-
@errors ||= MailForm::Errors.new(self)
|
124
|
-
end
|
125
|
-
|
126
|
-
end
|
8
|
+
end
|