mail_form 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
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.deliver
34
+ c.create
23
35
 
24
- Check your inbox and the e-mail will be there, with the sent fields (assuming
25
- that you configured your delivery method properly).
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 also support attachments, I18n, error messages like in ActiveRecord
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
- It's tested and compatible with Rails 2.2.x and Rails 2.3.x.
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
- == Installation
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
- Install MailForm is very easy. It is stored in Gemcutter, so just run the following:
47
+ attribute :email, :validate => /[^@]+@[^\.]+\.[\w\.\-]+/
36
48
 
37
- sudo gem install mail_form --version=1.0.0
49
+ You could actually do this:
38
50
 
39
- If you want it as plugin, just do:
51
+ attribute :email
52
+ validates_format_of :email, :with => /[^@]+@[^\.]+\.[\w\.\-]+/
40
53
 
41
- script/plugin install git://github.com/plataformatec/mail_form.git
54
+ Choose the one which pleases you the most. For more information on the API, please
55
+ continue reading below.
42
56
 
43
- == Request
57
+ === Playing together ORMs
44
58
 
45
- MailForm makes easy to append user information to the contact mail. You just
46
- have to do:
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
- And in your controller:
71
+ The delivery will be triggered in an after_create hook.
54
72
 
55
- @contact_form = ContactForm.new(params[:contact_form])
56
- @contact_form.request = request
73
+ == Installation
57
74
 
58
- The remote ip, user agent and session will be sent in the e-mail in a
59
- request information session. You can give to append any method that the
60
- request object responds to.
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
- ==== attributes(*attributes)
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 attributes can't be blank.
72
- When a regexp is given, check if the attribute matches is not blank and
73
- then if it matches the regexp.
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 a symbol, the method given as symbol will be
76
- called. You can then add validations as you do in ActiveRecord (errors.add).
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 => :screenshot_required?
115
+ attributes :screenshot, :attachment => true, :validate => :interface_bug?
91
116
  attributes :nickname, :captcha => true
92
117
 
93
- def screenshot_required?
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
- # save is an alias to deliver
110
- c.save #=> true
136
+ c.deliver #=> true
111
137
 
112
- ==== subject(string_or_symbol_or_block)
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
- ==== sender(string_or_symbol_or_block)
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
- ==== recipients(string_or_array_or_symbol_or_block)
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
- ==== template(string_or_symbol_or_proc)
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
- ==== headers(hash)
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
- Additional headers to your e-mail.
203
+ MailForm.template_root = File.join(Rails.root, "app", "views")
151
204
 
152
205
  == I18n
153
206
 
154
- All models, attributes and messages in MailForm can be used with localized.
155
- Below is an I18n example file:
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
- email: "E-mail"
162
- telephone: "Telephone number"
163
- message: "Sent message"
164
- messages:
165
- blank: "can not be blank"
166
- invalid: "is not valid"
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
@@ -5,6 +5,7 @@ require File.join(File.dirname(__FILE__), "lib", "mail_form", "version")
5
5
 
6
6
  desc 'Run tests for MailForm.'
7
7
  Rake::TestTask.new(:test) do |t|
8
+ t.libs << 'test'
8
9
  t.pattern = 'test/**/*_test.rb'
9
10
  t.verbose = true
10
11
  end
data/lib/mail_form.rb CHANGED
@@ -1,32 +1,48 @@
1
- require 'mail_form/base'
2
- require 'mail_form/dsl'
3
- require 'mail_form/errors'
4
- require 'mail_form/notifier'
5
-
6
- class MailForm
7
- extend MailForm::DSL
8
-
9
- ACCESSORS = [ :form_attributes, :form_validatable, :form_subject,
10
- :form_attachments, :form_recipients, :form_sender,
11
- :form_captcha, :form_headers, :form_template, :form_appendable ]
12
-
13
- DEFAULT_MESSAGES = { :blank => "can't be blank", :invalid => "is invalid" }
14
-
15
- class_inheritable_reader *ACCESSORS
16
- protected *ACCESSORS
17
-
18
- # Initialize arrays and hashes
19
- #
20
- write_inheritable_array :form_captcha, []
21
- write_inheritable_array :form_appendable, []
22
- write_inheritable_array :form_attributes, []
23
- write_inheritable_array :form_attachments, []
24
- write_inheritable_hash :form_validatable, {}
25
-
26
- headers({})
27
- sender {|c| c.email }
28
- subject{|c| c.class.human_name }
29
- template 'contact'
30
- end
31
-
32
- MailForm::Notifier.template_root = File.join(File.dirname(__FILE__), '..', 'views')
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
@@ -1,126 +1,8 @@
1
- class MailForm
2
- attr_accessor :request
1
+ class MailForm::Base
2
+ include MailForm::Shim
3
+ include MailForm::Delivery
3
4
 
4
- # Initialize assigning the parameters given as hash (just as in ActiveRecord).
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