josevalim-simple_form 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,5 @@
1
+ * Added support to :attachments (thanks to @andrewtimberlake)
2
+
1
3
  # Version 0.1
2
4
 
3
5
  * First release
data/README CHANGED
@@ -17,12 +17,12 @@ to have a contact form (including the e-mail):
17
17
  recipients "your.email@your.domain.com"
18
18
  sender{|c| %{"#{c.name}" <#{c.email}>} }
19
19
 
20
- attribute :name, :validate => true
21
- attribute :email, :validate => /[^@]+@[^\.]+\.[\w\.\-]+/
22
- attribute :company_name
23
- attribute :telephone
24
- attribute :message, :validate => true
25
- attribute :nickname, :captcha => true
20
+ attribute :name, :validate => true
21
+ attribute :email, :validate => /[^@]+@[^\.]+\.[\w\.\-]+/
22
+ attribute :file, :attachment => true
23
+
24
+ attribute :message
25
+ attribute :nickname, :captcha => true
26
26
  end
27
27
 
28
28
  Then you start a script/console and type:
@@ -32,8 +32,11 @@ Then you start a script/console and type:
32
32
 
33
33
  Check your inbox and the e-mail will be there, with the sent fields.
34
34
 
35
- SimpleForm is tested and compatible with Rails 2.2.x and Rails 2.3.x. It also
36
- supports I18n and error messages, just as an ActiveRecord model does.
35
+ SimpleForm also support attachments, I18n, error messages like in ActiveRecord
36
+ (so it works with custom FormBuilders) and can also send the user request information
37
+ in the contact mail.
38
+
39
+ It's tested and compatible with Rails 2.2.x and Rails 2.3.x.
37
40
 
38
41
  Installation
39
42
  ------------
@@ -47,6 +50,25 @@ If you want it as plugin, just do:
47
50
 
48
51
  script/plugin install git://github.com/josevalim/simple_form.git
49
52
 
53
+ Request
54
+ -------
55
+
56
+ SimpleForm makes easy to append user information to the contact mail. You just
57
+ have to do:
58
+
59
+ class ContactForm < SimpleForm
60
+ append :remote_ip, :user_agent, :session
61
+ # ...
62
+ end
63
+
64
+ And in your controller:
65
+
66
+ @contact_form = ContactForm.new(params[:contact_form], request)
67
+
68
+ And the remote ip, user agent and session will be sent in the e-mail in a
69
+ request information session. You can send to append any method that the
70
+ request object responds to.
71
+
50
72
  API Overview
51
73
  ------------
52
74
 
@@ -61,6 +83,9 @@ Options:
61
83
  When a regexp is given, check if the attribute matches is not blank and
62
84
  then if it matches the regexp.
63
85
 
86
+ * :attachment - When given, expects a file to be sent and attaches
87
+ it to the e-mail. Don't forget to set your form to multitype.
88
+
64
89
  * :captcha - When true, validates the attributes must be blank.
65
90
  This is a simple way to avoid spam and the input should be hidden with CSS.
66
91
 
@@ -109,9 +134,9 @@ This requires that your SimpleForm object have at least an email attribute.
109
134
 
110
135
  Additional headers to your e-mail.
111
136
 
112
- == recipients(string_or_array)
137
+ == recipients(string_or_array_or_proc)
113
138
 
114
- Who will receive the e-mail. Can be a string or array.
139
+ Who will receive the e-mail. Can be a string or array, or a proc that returns one of them.
115
140
 
116
141
  I18n
117
142
  ----
@@ -121,17 +146,24 @@ Below is an I18n example file:
121
146
 
122
147
  simple_form:
123
148
  models:
124
- contact_form: Formulário de contato
149
+ contact_form: "Your site contact form"
125
150
  attributes:
126
- name: Nome
127
- email: E-mail
128
- company_name: Empresa
129
- telephone: Telefone
130
- message: Mensagem
151
+ email: "E-mail"
152
+ telephone: "Telephone number"
153
+ message: "Sent message"
131
154
  messages:
132
- blank: "não pode ficar em branco"
133
- invalid: "não é válido"
134
- telephone: "deve haver oito dígitos"
155
+ blank: "can not be blank"
156
+ invalid: "is not valid"
157
+ telephone: "must have eight digits"
158
+ request:
159
+ title: "Technical information about the user"
160
+ remote_ip: "IP Address"
161
+ user_agent: "Browser"
162
+
163
+ Contributors
164
+ ------------
165
+
166
+ * "Andrew Timberlake":http://github.com/andrewtimberlake
135
167
 
136
168
  Bugs and Feedback
137
169
  -----------------
@@ -1,8 +1,13 @@
1
1
  class SimpleForm
2
+ attr_accessor :request
2
3
 
3
4
  # Initialize assigning the parameters given as hash (just as in ActiveRecord).
4
5
  #
5
- def initialize(params={})
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
6
11
  params.each_pair do |attr, value|
7
12
  self.send(:"#{attr}=", value)
8
13
  end unless params.blank?
@@ -70,8 +75,8 @@ class SimpleForm
70
75
  # If is not spam and the form is valid, we send the e-mail and returns true.
71
76
  # Otherwise returns false.
72
77
  #
73
- def deliver
74
- if self.not_spam? && self.valid?
78
+ def deliver(run_validations=true)
79
+ if !run_validations || (self.not_spam? && self.valid?)
75
80
  SimpleForm::Notifier.deliver_contact(self)
76
81
  return true
77
82
  else
@@ -11,6 +11,10 @@ class SimpleForm
11
11
  # * <tt>:validate</tt> - When true, validates the attributes can't be blank.
12
12
  # When a regexp is given, check if the attribute matches is not blank and
13
13
  # then if it matches the regexp.
14
+ #
15
+ # * <tt>:attachment</tt> - When given, expects a file to be sent and attaches
16
+ # it to the e-mail. Don't forget to set your form to multitype.
17
+ #
14
18
  # * <tt>:captcha</tt> - When true, validates the attributes must be blank
15
19
  # This is a simple way to avoid spam
16
20
  #
@@ -20,6 +24,7 @@ class SimpleForm
20
24
  # attributes :name, :validate => true
21
25
  # attributes :email, :validate => /^([^@]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i
22
26
  # attributes :message
27
+ # attributes :file, :attachment => true
23
28
  # attributes :nickname, :captcha => true
24
29
  # end
25
30
  #
@@ -28,7 +33,9 @@ class SimpleForm
28
33
 
29
34
  attr_accessor *accessors
30
35
 
31
- if options[:captcha]
36
+ if options[:attachment]
37
+ write_inheritable_array(:form_attachments, accessors)
38
+ elsif options[:captcha]
32
39
  write_inheritable_array(:form_captcha, accessors)
33
40
  else
34
41
  write_inheritable_array(:form_attributes, accessors)
@@ -96,10 +103,28 @@ class SimpleForm
96
103
  # recipients "jose.valim@gmail.com"
97
104
  # end
98
105
  #
99
- def recipients(string_or_array)
100
- write_inheritable_array(:form_recipients, [*string_or_array])
106
+ def recipients(string_or_array_or_proc)
107
+ write_inheritable_attribute(:form_recipients, string_or_array_or_proc)
101
108
  end
102
109
  alias :to :recipients
103
110
 
111
+ # Values from request object to be appended to the contact form.
112
+ # Whenever used, you have to send the request object when initializing the object:
113
+ #
114
+ # @contact_form = ContactForm.new(params[:contact_form], request)
115
+ #
116
+ # You can get the values to be appended from the AbstractRequest
117
+ # documentation (http://api.rubyonrails.org/classes/ActionController/AbstractRequest.html)
118
+ #
119
+ # == Examples
120
+ #
121
+ # class ContactForm < SimpleForm
122
+ # append :remote_ip, :user_agent, :session, :cookies
123
+ # end
124
+ #
125
+ def append(*values)
126
+ write_inheritable_array(:form_appendable, values)
127
+ end
128
+
104
129
  end
105
130
  end
@@ -2,16 +2,14 @@
2
2
  #
3
3
  class SimpleForm
4
4
  class Notifier < ActionMailer::Base
5
- def contact(form)
6
- @subject = form.class.form_subject
7
- @subject = @subject.call(form) if @subject.is_a?(Proc)
8
-
9
- @from = form.class.form_sender
10
- @from = @from.call(form) if @from.is_a?(Proc)
11
5
 
12
- @recipients = form.class.form_recipients
6
+ def contact(form)
7
+ @from = get_from_class_and_eval(form, :form_sender)
8
+ @subject = get_from_class_and_eval(form, :form_subject)
9
+ @recipients = get_from_class_and_eval(form, :form_recipients)
13
10
 
14
11
  raise ScriptError, "You forgot to setup #{form.class.name} recipients" if @recipients.blank?
12
+ raise ScriptError, "You set :append values but forgot to give me the request object" if form.request.nil? && !form.class.form_appendable.blank?
15
13
 
16
14
  @body['form'] = form
17
15
  @body['subject'] = @subject
@@ -19,6 +17,29 @@ class SimpleForm
19
17
  @sent_on = Time.now.utc
20
18
  @headers = form.class.form_headers
21
19
  @content_type = 'text/html'
20
+
21
+ form.class.form_attachments.each do |attribute|
22
+ value = form.send(attribute)
23
+ if value.respond_to?(:read)
24
+ attachment value.content_type.to_s do |att|
25
+ att.filename = value.original_filename
26
+ att.body = value.read
27
+ end
28
+ end
29
+ end
22
30
  end
31
+
32
+ protected
33
+
34
+ def get_from_class_and_eval(form, method)
35
+ duck = form.class.send(method)
36
+
37
+ if duck.is_a?(Proc)
38
+ duck.call(form)
39
+ else
40
+ duck
41
+ end
42
+ end
43
+
23
44
  end
24
45
  end
data/lib/simple_form.rb CHANGED
@@ -7,22 +7,24 @@ require File.join(dir, 'simple_form', 'notifier')
7
7
  class SimpleForm
8
8
  extend SimpleForm::DSL
9
9
 
10
- ACCESSORS = [ :form_attributes, :form_validatable, :form_subject,
11
- :form_recipients, :form_sender, :form_captcha, :form_headers ]
10
+ ACCESSORS = [ :form_attributes, :form_validatable, :form_subject, :form_attachments,
11
+ :form_recipients, :form_sender, :form_captcha, :form_headers, :form_appendable ]
12
12
 
13
13
  DEFAULT_MESSAGES = { :blank => "can't be blank", :invalid => "is invalid" }
14
14
 
15
15
  class_inheritable_reader *ACCESSORS
16
16
  protected *ACCESSORS
17
17
 
18
- # Configure default values
18
+ # Initialize arrays and hashes
19
19
  #
20
- attribute :captcha => true
21
- attribute :validate => true
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, {}
22
25
 
23
26
  headers({})
24
- recipients([])
25
- sender{|c| c.email }
27
+ sender {|c| c.email }
26
28
  subject{|c| c.class.human_name }
27
29
  end
28
30
 
data/test/errors_test.rb CHANGED
@@ -47,7 +47,8 @@ class SimpleFormErrorsTest < ActiveSupport::TestCase
47
47
  form = ContactForm.new(:email => 'not_valid')
48
48
  form.valid?
49
49
 
50
- assert_equal ["Name can't be blank", "Email is invalid"], form.errors.full_messages
50
+ assert form.errors.full_messages.include?("Name can't be blank")
51
+ assert form.errors.full_messages.include?("Email is invalid")
51
52
  end
52
53
 
53
54
  def test_full_localized_messages
@@ -56,7 +57,8 @@ class SimpleFormErrorsTest < ActiveSupport::TestCase
56
57
  form = ContactForm.new(:email => 'not_valid')
57
58
  form.valid?
58
59
 
59
- assert_equal ["Name should be filled", "E-mail is not valid"], form.errors.full_messages
60
+ assert form.errors.full_messages.include?("Name should be filled")
61
+ assert form.errors.full_messages.include?("E-mail is not valid")
60
62
  end
61
63
 
62
64
  def teardown
@@ -3,8 +3,15 @@ require File.dirname(__FILE__) + '/test_helper'
3
3
  class SimpleFormNotifierTest < ActiveSupport::TestCase
4
4
 
5
5
  def setup
6
- @form = ContactForm.new(:name => 'José', :email => 'my.email@my.domain.com', :message => 'Cool')
7
- @advanced = AdvancedForm.new(:name => 'José', :email => 'my.email@my.domain.com', :message => "Cool\nno?")
6
+ @form = ContactForm.new(:name => 'José', :email => 'my.email@my.domain.com', :message => 'Cool')
7
+
8
+ @request = ActionController::TestRequest.new
9
+ @valid_attributes = { :name => 'José', :email => 'my.email@my.domain.com', :message => "Cool\nno?" }
10
+ @advanced = AdvancedForm.new(@valid_attributes, @request)
11
+
12
+ test_file = ActionController::TestUploadedFile.new(File.join(File.dirname(__FILE__), 'test-file.txt'))
13
+ @with_file = FileForm.new(:name => 'José', :email => 'my.email@my.domain.com', :message => "Cool", :file => test_file)
14
+
8
15
  ActionMailer::Base.deliveries = []
9
16
  end
10
17
 
@@ -41,7 +48,7 @@ class SimpleFormNotifierTest < ActiveSupport::TestCase
41
48
 
42
49
  def test_recipients_is_an_array
43
50
  @advanced.deliver
44
- assert_equal ['my.email@my.domain.com', 'my.first@email.com', 'my.second@email.com'], ActionMailer::Base.deliveries.first.to
51
+ assert_equal ['my.first@email.com', 'my.second@email.com'], ActionMailer::Base.deliveries.first.to
45
52
  end
46
53
 
47
54
  def test_headers_is_a_hash
@@ -69,10 +76,10 @@ class SimpleFormNotifierTest < ActiveSupport::TestCase
69
76
  end
70
77
 
71
78
  def test_body_contains_localized_attributes_names
72
- I18n.backend.store_translations(:en, :simple_form => { :attributes => { :email => 'E-mail', :message => 'Sent message' } })
79
+ I18n.backend.store_translations(:en, :simple_form => { :attributes => { :message => 'Sent message' } })
73
80
  @form.deliver
74
- assert_match /E\-mail:/, ActionMailer::Base.deliveries.first.body
75
81
  assert_match /Sent message:/, ActionMailer::Base.deliveries.first.body
82
+ assert_no_match /Message:/, ActionMailer::Base.deliveries.first.body
76
83
  end
77
84
 
78
85
  def test_body_simple_format_messages_with_break_line
@@ -83,6 +90,69 @@ class SimpleFormNotifierTest < ActiveSupport::TestCase
83
90
  assert_match /<p>Cool/, ActionMailer::Base.deliveries.last.body
84
91
  end
85
92
 
93
+ def test_body_does_not_append_request_if_append_is_not_called
94
+ @form.deliver
95
+ assert_no_match /Request information/, ActionMailer::Base.deliveries.first.body
96
+ end
97
+
98
+ def test_body_does_append_request_if_append_is_called
99
+ @advanced.deliver
100
+ assert_match /Request information/, ActionMailer::Base.deliveries.last.body
101
+ end
102
+
103
+ def test_request_title_is_localized
104
+ I18n.backend.store_translations(:en, :simple_form => { :request => { :title => 'Information about the request' } })
105
+ @advanced.deliver
106
+ assert_no_match /Request information/, ActionMailer::Base.deliveries.last.body
107
+ assert_match /Information about the request/, ActionMailer::Base.deliveries.last.body
108
+ end
109
+
110
+ def test_request_info_attributes_are_printed
111
+ @advanced.deliver
112
+ assert_match /Remote ip/, ActionMailer::Base.deliveries.last.body
113
+ assert_match /User agent/, ActionMailer::Base.deliveries.last.body
114
+ end
115
+
116
+ def test_request_info_attributes_are_localized
117
+ I18n.backend.store_translations(:en, :simple_form => { :request => { :remote_ip => 'IP Address' } })
118
+ @advanced.deliver
119
+ assert_match /IP Address/, ActionMailer::Base.deliveries.last.body
120
+ assert_no_match /Remote ip/, ActionMailer::Base.deliveries.last.body
121
+ end
122
+
123
+ def test_request_info_values_are_printed
124
+ @advanced.deliver
125
+ assert_match /0\.0\.0\.0/, ActionMailer::Base.deliveries.last.body
126
+ assert_match /Rails Testing/, ActionMailer::Base.deliveries.last.body
127
+ end
128
+
129
+ def test_request_info_hashes_are_print_inside_lis
130
+ @request.session = { :my => :session, :user => :data }
131
+ @advanced.deliver
132
+ assert_match /<li>my: session<\/li>/, ActionMailer::Base.deliveries.last.body
133
+ assert_match /<li>user: data<\/li>/, ActionMailer::Base.deliveries.last.body
134
+ end
135
+
136
+ def test_error_is_raised_when_append_is_given_but_no_request_is_given
137
+ assert_raise ScriptError do
138
+ @advanced.request = nil
139
+ @advanced.deliver
140
+ end
141
+ end
142
+
143
+ def test_form_with_file_includes_an_attachment
144
+ @with_file.deliver
145
+
146
+ #For some reason I need to encode the mail before the attachments array returns values
147
+ ActionMailer::Base.deliveries.first.to_s
148
+ assert_equal 1, ActionMailer::Base.deliveries.first.attachments.size
149
+ end
150
+
151
+ def test_form_with_file_does_not_output_attachment_as_attribute
152
+ @with_file.deliver
153
+ assert_no_match /File/, ActionMailer::Base.deliveries.first.body
154
+ end
155
+
86
156
  def teardown
87
157
  I18n.reload!
88
158
  end
data/test/test_helper.rb CHANGED
@@ -6,6 +6,7 @@ RAILS_ENV = ENV["RAILS_ENV"] = "test"
6
6
  require 'active_support'
7
7
  require 'active_support/test_case'
8
8
  require 'action_mailer'
9
+ require 'action_controller/test_case'
9
10
 
10
11
  ActionMailer::Base.delivery_method = :test
11
12
 
@@ -22,12 +23,25 @@ class ContactForm < SimpleForm
22
23
  end
23
24
 
24
25
  class AdvancedForm < ContactForm
26
+ append :remote_ip, :user_agent, :session
27
+
25
28
  recipients [ 'my.first@email.com', 'my.second@email.com' ]
26
29
  subject 'My Advanced Form'
27
30
  sender{|c| %{"#{c.name}" <#{c.email}>} }
28
31
  headers 'return-path' => 'mypath'
29
32
  end
30
33
 
34
+ class FileForm < ContactForm
35
+ attribute :file, :attachment => true, :validate => true
36
+ end
37
+
31
38
  class NullRecipient < SimpleForm
32
39
  sender 'my.email@my.domain.com'
33
40
  end
41
+
42
+ #Needed to correctly test an uploaded file
43
+ class ActionController::TestUploadedFile
44
+ def read
45
+ @tempfile.read
46
+ end
47
+ end
@@ -1,4 +1,4 @@
1
- <p><%=h @subject %></p>
1
+ <h4 style="text-decoration:underline"><%=h @subject %></h4>
2
2
 
3
3
  <% @form.class.form_attributes.each do |attribute|
4
4
  value = @form.send(attribute)
@@ -7,3 +7,24 @@
7
7
  <p><b><%= @form.class.human_attribute_name(attribute) %>:</b>
8
8
  <%= value.include?("\n") ? simple_format(h(value)) : h(value) %></p>
9
9
  <% end %>
10
+
11
+ <% unless @form.class.form_appendable.blank? %>
12
+ <br /><h4 style="text-decoration:underline"><%= I18n.t :title, :scope => [ :simple_form, :request ], :default => 'Request information' %></h4>
13
+
14
+ <% @form.class.form_appendable.each do |attribute|
15
+ value = @form.request.send(attribute)
16
+
17
+ value = if value.is_a?(Hash) && !value.empty?
18
+ content_tag(:ul, value.to_a.map{|k,v| content_tag(:li, h("#{k}: #{v.inspect}")) }.join("\n"), :style => "list-style:none;")
19
+ elsif value.is_a?(String)
20
+ h(value)
21
+ else
22
+ h(value.inspect)
23
+ end
24
+ %>
25
+
26
+ <p><b><%= I18n.t attribute, :scope => [ :simple_form, :request ], :default => attribute.to_s.humanize %>:</b>
27
+ <%= value.include?("\n") ? simple_format(value) : value %></p>
28
+ <% end %>
29
+ <br />
30
+ <% end %>
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: josevalim-simple_form
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - "Jos\xC3\xA9 Valim"