formeze 2.2.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b7bf0599d8a5997c77dafb5da086aa3cbd8f7f72
4
+ data.tar.gz: 72ce1f27ad4d9d694eb95cd1b2b7837d1e33d86a
5
+ SHA512:
6
+ metadata.gz: a42a9f27f6ba27c3aa42a7640bd703294d594e87dff8d6d6750d9c024ef5f15c28d1bc8d96e6d16181f904f78d24bc31a0f4d6af9a048f9de454964896ff5496
7
+ data.tar.gz: 39846f79aa0a5e5f5319d0e8c9ded0b50fc308e031ed988263ebcbff24e38e3b79a7778bfdf1549a1bbcb107a820285cc15142a30fc8e050ca9d69b08367c60a
@@ -0,0 +1,9 @@
1
+ Copyright (c) TIMCRAFT <http://timcraft.com>
2
+
3
+ This is an Open Source project licensed under the terms of the LGPLv3 license.
4
+ Please see <http://www.gnu.org/licenses/lgpl-3.0.html> for license text.
5
+
6
+ This code is distributed in the hope that it will be useful,
7
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
8
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9
+ GNU Lesser General Public License for more details.
data/README.md CHANGED
@@ -2,7 +2,7 @@ formeze
2
2
  =======
3
3
 
4
4
 
5
- A little library for handling form data/input.
5
+ A little Ruby library for handling form data/input.
6
6
 
7
7
 
8
8
  Motivation
@@ -10,11 +10,12 @@ Motivation
10
10
 
11
11
  Most web apps built for end users will need to process url-encoded form data.
12
12
  Registration forms, profile forms, checkout forms, contact forms, and forms
13
- for adding/editing application specific data. As developers we would like to
14
- process this data safely, to minimise the possibility of security holes
15
- within our application that could be exploited. Formeze adopts the approach
16
- of being "strict by default", forcing the application code to be explicit in
17
- what it accepts as input.
13
+ for adding/editing application specific data.
14
+
15
+ As developers we would like to process this data safely, to minimise the
16
+ possibility of security holes within our application that could be exploited.
17
+ Formeze adopts the approach of being "strict by default", forcing the application
18
+ code to be explicit in what it accepts as input.
18
19
 
19
20
 
20
21
  Installation
@@ -31,23 +32,29 @@ Example usage
31
32
  Here is a minimal example, which defines a form with a single field:
32
33
 
33
34
  ```ruby
35
+ require 'formeze'
36
+
34
37
  class ExampleForm < Formeze::Form
35
38
  field :title
36
39
  end
37
40
  ```
38
41
 
39
- This form can then be used to parse and validate input data like this:
42
+ You can then parse and validate form data in Rails or Sinatra like this:
40
43
 
41
44
  ```ruby
42
- form = ExampleForm.new
45
+ form = ExampleForm.new.parse(request)
43
46
 
44
- form.parse('title=Title')
45
-
46
- form.title # => "Title"
47
+ if form.valid?
48
+ # do something with form data
49
+ else
50
+ # display form.errors to user
51
+ end
47
52
  ```
48
53
 
54
+ Formeze will automatically ignore the Rails "utf8" and "authenticity_token" parameters.
55
+
49
56
  If you prefer not to inherit from the `Formeze::Form` class then you can
50
- instead call the `Formeze.setup` method like this:
57
+ instead call the `Formeze.setup` method on your classes like this:
51
58
 
52
59
  ```ruby
53
60
  class ExampleForm
@@ -62,22 +69,20 @@ methods but will otherwise leave the object untouched (i.e. you can define
62
69
  your own initialization logic).
63
70
 
64
71
 
65
- Detecting errors
66
- ----------------
72
+ Validation errors
73
+ -----------------
67
74
 
68
75
  Formeze distinguishes between validation errors (which are expected in the
69
76
  normal running of your application), and key/value errors (which most likely
70
- indicate either developer error, or form tampering).
71
-
72
- For the latter case, the `parse` method that formeze provides will raise a
73
- `Formeze::KeyError` or a `Formeze::ValueError` exception if the structure of
74
- the form data does not match the field definitions.
77
+ indicate either developer error, or form tampering). For the latter case,
78
+ the `parse` method that formeze provides will raise a `Formeze::KeyError`
79
+ or a `Formeze::ValueError` exception if the structure of the form data
80
+ does not match the field definitions.
75
81
 
76
82
  After calling `parse` you can check that the form is valid by calling the
77
- `#valid?` method. If it isn't you can call the `errors` method which will
78
- return an array of error messages to display to the end user.
79
-
80
- You can also use `errors_on?` and `errors_on` to check for and select error
83
+ `valid?` method. If it isn't you can call the `errors` method which will
84
+ return an array of error messages to display to the end user. You can also
85
+ use the `errors_on?` and `errors_on` methods to check for and select error
81
86
  messages specific to a single field.
82
87
 
83
88
 
@@ -106,9 +111,9 @@ is not required, i.e. the value of the field can be blank/empty. For example:
106
111
  field :title, required: false
107
112
  ```
108
113
 
109
- To make it easy to integrate with your application you might want to return
110
- a different value for blank fields, such as nil, zero, or a "null" object.
111
- Use the `blank` option to specify this behaviour. For example:
114
+ You might want to return a different value for blank fields, such as nil,
115
+ zero, or a "null" object. Use the `blank` option to specify this behaviour.
116
+ For example:
112
117
 
113
118
  ```ruby
114
119
  field :title, required: false, blank: nil
@@ -154,18 +159,14 @@ option to handle the case where the checkbox is unchecked. For example:
154
159
  field :accept_terms, values: %w(true), key_required: false
155
160
  ```
156
161
 
157
- Sometimes you'll have a field with multiple values. A multiple select input,
158
- a set of checkboxes. For this case you can specify the `multiple` option to
159
- allow multiple values. For example:
162
+ Sometimes you'll have a field with multiple values, such as a multiple select
163
+ input, or a set of checkboxes. For this case you can specify the `multiple`
164
+ option, for example:
160
165
 
161
166
  ```ruby
162
167
  field :colour, multiple: true, values: Colour.keys
163
168
  ```
164
169
 
165
- Note that unlike all the other examples so far, reading the attribute
166
- that corresponds to this field will return an array of strings instead
167
- of a single string.
168
-
169
170
  Sometimes you'll only want the field to be defined if some condition is true.
170
171
  The condition may depend on the state of other form fields, or some external
171
172
  state accessible from the form object. You can do this by specifying either
@@ -210,6 +211,22 @@ Custom scrub methods can be defined by adding a symbol/proc entry to the
210
211
  `Formeze.scrub_methods` hash.
211
212
 
212
213
 
214
+ Multipart form data
215
+ -------------------
216
+
217
+ For file fields you can specify the `accept` and `maxsize` options, for example:
218
+
219
+ ```ruby
220
+ class ExampleForm < Formeze::Form
221
+ field :image, accept: 'image/jpg,image/png', maxsize: 1000
222
+ end
223
+ ```
224
+
225
+ For this to work you need to make sure your application includes the
226
+ [mime-types gem](https://rubygems.org/gems/mime-types), and that the
227
+ form is submitted with the multipart/form-data mime type.
228
+
229
+
213
230
  Custom validation
214
231
  -----------------
215
232
 
@@ -291,49 +308,12 @@ key does not exist. The error for the password_confirmation field validation
291
308
  would include the value of the `formeze.errors.does_not_match` I18n key.
292
309
 
293
310
 
294
- Rails usage
295
- -----------
296
-
297
- This is the basic pattern for using a formeze form in a Rails controller:
298
-
299
- ```ruby
300
- form = SomeForm.parse(request.raw_post)
301
-
302
- if form.valid?
303
- # do something with form data
304
- else
305
- # display form.errors to user
306
- end
307
- ```
308
-
309
- Formeze will automatically ignore the "utf8" and "authenticity_token"
310
- parameters that Rails uses.
311
-
312
-
313
- Sinatra usage
314
- -------------
315
-
316
- Using formeze with sinatra is similar, the only difference is that there is
317
- no raw_post method on the request object so the body has to be read directly:
318
-
319
- ```ruby
320
- form = SomeForm.parse(request.body.read)
321
-
322
- if form.valid?
323
- # do something with form data
324
- else
325
- # display form.errors to user
326
- end
327
- ```
328
-
329
-
330
- Integration with I18n
331
- ---------------------
311
+ I18n integration
312
+ ----------------
332
313
 
333
314
  Formeze integrates with [I18n](http://edgeguides.rubyonrails.org/i18n.html)
334
315
  so that you can define custom error messages and field labels within your
335
316
  locales (useful both for localization, and when working with designers).
336
-
337
317
  For example, here is how you would change the "required" error message
338
318
  (which defaults to "is required"):
339
319
 
@@ -1,15 +1,18 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'formeze'
3
- s.version = '2.2.0'
3
+ s.version = '3.0.0'
4
+ s.license = 'LGPL-3.0'
4
5
  s.platform = Gem::Platform::RUBY
5
6
  s.authors = ['Tim Craft']
6
7
  s.email = ['mail@timcraft.com']
7
8
  s.homepage = 'http://github.com/timcraft/formeze'
8
- s.description = 'A little library for handling form data/input'
9
+ s.description = 'A little Ruby library for handling form data/input'
9
10
  s.summary = 'See description'
10
- s.files = Dir.glob('{lib,spec}/**/*') + %w(README.md Rakefile.rb formeze.gemspec)
11
- s.add_development_dependency('rake', ['>= 0.9.3'])
12
- s.add_development_dependency('i18n', ['~> 0.6.0'])
13
- s.add_development_dependency('minitest', ['>= 4.2.0']) if RUBY_VERSION == '1.8.7'
11
+ s.files = Dir.glob('{lib,spec}/**/*') + %w(LICENSE.txt README.md Rakefile.rb formeze.gemspec)
12
+ s.required_ruby_version = '>= 1.9.3'
13
+ s.add_development_dependency('rake', '~> 10')
14
+ s.add_development_dependency('i18n', '~> 0.6')
15
+ s.add_development_dependency('minitest', '~> 5')
16
+ s.add_development_dependency('mime-types', '~> 2')
14
17
  s.require_path = 'lib'
15
18
  end
@@ -8,13 +8,29 @@ module Formeze
8
8
  @name, @options = name, options
9
9
  end
10
10
 
11
+ def validate_all(values, form)
12
+ size = 0
13
+
14
+ values.each do |value|
15
+ if String === value
16
+ validate(value, form)
17
+ else
18
+ validate_file(value, form)
19
+
20
+ size += value.size
21
+ end
22
+ end
23
+
24
+ form.add_error(self, error(:too_large, 'is too large')) if maxsize? && size > maxsize
25
+ end
26
+
11
27
  def validate(value, form)
12
28
  value = Formeze.scrub(value, @options[:scrub])
13
29
 
14
30
  if value !~ /\S/
15
31
  form.add_error(self, error(:required, 'is required')) if required?
16
32
 
17
- form.send(:"#{name}=", blank_value? ? blank_value : value)
33
+ value = blank_value if blank_value?
18
34
  else
19
35
  form.add_error(self, error(:not_multiline, 'cannot contain newlines')) if !multiline? && value.lines.count > 1
20
36
 
@@ -25,9 +41,25 @@ module Formeze
25
41
  form.add_error(self, error(:no_match, 'is invalid')) if no_match?(value)
26
42
 
27
43
  form.add_error(self, error(:bad_value, 'is invalid')) if values? && !values.include?(value)
44
+ end
45
+
46
+ value = Array(form.send(name)).push(value) if multiple?
47
+
48
+ form.send(:"#{name}=", value)
49
+ end
28
50
 
29
- form.send(:"#{name}=", value)
51
+ def validate_file(object, form)
52
+ type = MIME::Types[object.content_type].first
53
+
54
+ filename_type = MIME::Types.type_for(object.original_filename).first
55
+
56
+ if type.nil? || type != filename_type || !accept.include?(type)
57
+ form.add_error(self, error(:not_accepted, 'is not an accepted file type'))
30
58
  end
59
+
60
+ object = Array(form.send(name)).push(object) if multiple?
61
+
62
+ form.send(:"#{name}=", object)
31
63
  end
32
64
 
33
65
  def error(key, default)
@@ -58,6 +90,18 @@ module Formeze
58
90
  @options.fetch(:multiple) { false }
59
91
  end
60
92
 
93
+ def maxsize?
94
+ @options.has_key?(:maxsize)
95
+ end
96
+
97
+ def maxsize
98
+ @options.fetch(:maxsize)
99
+ end
100
+
101
+ def accept
102
+ @accept ||= @options.fetch(:accept).split(',').flat_map { |type| MIME::Types[type] }
103
+ end
104
+
61
105
  def too_long?(value)
62
106
  value.chars.count > @options.fetch(:maxlength) { 64 }
63
107
  end
@@ -103,28 +147,6 @@ module Formeze
103
147
  end
104
148
  end
105
149
 
106
- class FieldSet
107
- include Enumerable
108
-
109
- def initialize
110
- @fields, @index = [], {}
111
- end
112
-
113
- def each(&block)
114
- @fields.each(&block)
115
- end
116
-
117
- def <<(field)
118
- @fields << field
119
-
120
- @index[field.name] = field
121
- end
122
-
123
- def [](field_name)
124
- @index.fetch(field_name)
125
- end
126
- end
127
-
128
150
  class Validation
129
151
  def initialize(field, options, &block)
130
152
  @field, @options, @block = field, options, block
@@ -163,46 +185,17 @@ module Formeze
163
185
  end
164
186
  end
165
187
 
166
- module ArrayAttrAccessor
167
- def array_attr_reader(name)
168
- define_method(name) do
169
- ivar = :"@#{name}"
170
-
171
- instance_variable_defined?(ivar) ? Array(instance_variable_get(ivar)) : []
172
- end
173
- end
174
-
175
- def array_attr_writer(name)
176
- define_method(:"#{name}=") do |value|
177
- ivar = :"@#{name}"
178
-
179
- instance_variable_set(ivar, send(name) + [value])
180
- end
181
- end
182
-
183
- def array_attr_accessor(name)
184
- array_attr_reader(name)
185
- array_attr_writer(name)
186
- end
187
- end
188
-
189
188
  module ClassMethods
190
- include ArrayAttrAccessor
191
-
192
189
  def fields
193
- @fields ||= FieldSet.new
190
+ @fields ||= {}
194
191
  end
195
192
 
196
193
  def field(*args)
197
194
  field = Field.new(*args)
198
195
 
199
- fields << field
196
+ fields[field.name] = field
200
197
 
201
- if field.multiple?
202
- array_attr_accessor field.name
203
- else
204
- attr_accessor field.name
205
- end
198
+ attr_accessor field.name
206
199
  end
207
200
 
208
201
  def validations
@@ -212,12 +205,6 @@ module Formeze
212
205
  def validates(field_name, options = {}, &block)
213
206
  validations << Validation.new(fields[field_name], options, &block)
214
207
  end
215
-
216
- def parse(encoded_form_data)
217
- Kernel.warn '[formeze] the parse class method is deprecated and will be removed, please use the instance method instead'
218
-
219
- new.tap { |form| form.parse(encoded_form_data) }
220
- end
221
208
  end
222
209
 
223
210
  class KeyError < StandardError; end
@@ -226,9 +213,19 @@ module Formeze
226
213
 
227
214
  class ValidationError < StandardError; end
228
215
 
216
+ class RequestCGI < CGI
217
+ def env_table
218
+ @options[:request].env
219
+ end
220
+
221
+ def stdinput
222
+ @options[:request].body
223
+ end
224
+ end
225
+
229
226
  module InstanceMethods
230
227
  def fill(object)
231
- self.class.fields.each do |field|
228
+ self.class.fields.each_value do |field|
232
229
  if Hash === object && object.has_key?(field.name)
233
230
  send(:"#{field.name}=", object[field.name])
234
231
  elsif object.respond_to?(field.name)
@@ -239,10 +236,14 @@ module Formeze
239
236
  return self
240
237
  end
241
238
 
242
- def parse(encoded_form_data)
243
- form_data = CGI.parse(encoded_form_data)
239
+ def parse(input)
240
+ form_data = if String === input
241
+ CGI.parse(input)
242
+ else
243
+ RequestCGI.new(request: input).params
244
+ end
244
245
 
245
- self.class.fields.each do |field|
246
+ self.class.fields.each_value do |field|
246
247
  next unless field_defined?(field)
247
248
 
248
249
  unless form_data.has_key?(field.key)
@@ -257,9 +258,7 @@ module Formeze
257
258
  raise ValueError unless field.multiple?
258
259
  end
259
260
 
260
- values.each do |value|
261
- field.validate(value, self)
262
- end
261
+ field.validate_all(values, self)
263
262
  end
264
263
 
265
264
  if defined?(Rails)
@@ -308,9 +307,8 @@ module Formeze
308
307
  end
309
308
 
310
309
  def to_h
311
- self.class.fields.inject({}) do |hash, field|
310
+ self.class.fields.values.each_with_object({}) do |field, hash|
312
311
  hash[field.name] = send(field.name)
313
- hash
314
312
  end
315
313
  end
316
314
 
@@ -1,6 +1,9 @@
1
1
  require 'minitest/autorun'
2
2
  require 'formeze'
3
3
  require 'i18n'
4
+ require 'mime-types'
5
+
6
+ I18n.available_locales = [:en]
4
7
 
5
8
  class FormWithField < Formeze::Form
6
9
  field :title
@@ -96,8 +99,7 @@ describe 'FormWithField after parsing valid input' do
96
99
 
97
100
  describe 'errors method' do
98
101
  it 'returns an empty array' do
99
- @form.errors.must_be_instance_of(Array)
100
- @form.errors.must_be_empty
102
+ @form.errors.must_equal([])
101
103
  end
102
104
  end
103
105
 
@@ -109,9 +111,7 @@ describe 'FormWithField after parsing valid input' do
109
111
 
110
112
  describe 'errors_on method' do
111
113
  it 'returns an empty array when given the title field name' do
112
- errors = @form.errors_on(:title)
113
- errors.must_be_instance_of(Array)
114
- errors.must_be_empty
114
+ @form.errors_on(:title).must_equal([])
115
115
  end
116
116
  end
117
117
 
@@ -183,15 +183,6 @@ describe 'FormWithField after parsing input containing newlines' do
183
183
  end
184
184
  end
185
185
 
186
- describe 'FormWithField parse class method' do
187
- it 'creates a new instance of the class and calls the parse instance method' do
188
- form = FormWithField.parse('title=Untitled')
189
- form.must_be_instance_of(FormWithField)
190
- form.valid?.must_equal(true)
191
- form.title.must_equal('Untitled')
192
- end
193
- end
194
-
195
186
  class FormWithOptionalField < Formeze::Form
196
187
  field :title, :required => false
197
188
  end
@@ -317,9 +308,8 @@ describe 'FormWithFieldThatCanHaveMultipleValues' do
317
308
  end
318
309
 
319
310
  describe 'colour method' do
320
- it 'returns an empty array' do
321
- @form.colour.must_be_instance_of(Array)
322
- @form.colour.must_be_empty
311
+ it 'returns nil' do
312
+ @form.colour.must_be_nil
323
313
  end
324
314
  end
325
315
 
@@ -354,9 +344,7 @@ describe 'FormWithFieldThatCanHaveMultipleValues after parsing input with multip
354
344
 
355
345
  describe 'colour method' do
356
346
  it 'returns an array containing the values' do
357
- @form.colour.must_be_instance_of(Array)
358
- @form.colour.must_include('black')
359
- @form.colour.must_include('white')
347
+ @form.colour.must_equal(['black', 'white'])
360
348
  end
361
349
  end
362
350
 
@@ -380,9 +368,8 @@ describe 'FormWithFieldThatCanHaveMultipleValues after parsing input with no val
380
368
  end
381
369
 
382
370
  describe 'colour method' do
383
- it 'returns an empty array' do
384
- @form.colour.must_be_instance_of(Array)
385
- @form.colour.must_be_empty
371
+ it 'returns nil' do
372
+ @form.colour.must_be_nil
386
373
  end
387
374
  end
388
375
 
@@ -706,6 +693,226 @@ describe 'FormWithOptionalFieldAndCustomValidation after parsing blank input' do
706
693
  end
707
694
  end
708
695
 
696
+ class FormWithFileField < Formeze::Form
697
+ field :file, :maxsize => 42, :accept => 'text/plain'
698
+ end
699
+
700
+ describe 'FormWithFileField after parsing multipart input' do
701
+ before do
702
+ @form = FormWithFileField.new
703
+
704
+ body = <<-EOS.gsub(/\n/, "\r\n")
705
+ --AaB03x
706
+ Content-Disposition: form-data; name="file"; filename="example.txt"
707
+ Content-Type: text/plain
708
+
709
+ contents
710
+ --AaB03x--
711
+ EOS
712
+
713
+ request = Struct.new(:body, :env).new(StringIO.new(body), {
714
+ 'REQUEST_METHOD' => 'POST',
715
+ 'CONTENT_TYPE' => 'multipart/form-data; boundary=AaB03x',
716
+ 'CONTENT_LENGTH' => body.bytesize
717
+ })
718
+
719
+ @form.parse(request)
720
+ end
721
+
722
+ describe 'file method' do
723
+ it 'returns the value of the field' do
724
+ @form.file.must_be_instance_of(StringIO)
725
+ @form.file.original_filename.must_equal('example.txt')
726
+ end
727
+ end
728
+
729
+ describe 'valid query method' do
730
+ it 'returns true' do
731
+ @form.valid?.must_equal(true)
732
+ end
733
+ end
734
+
735
+ describe 'errors query method' do
736
+ it 'returns false' do
737
+ @form.errors?.must_equal(false)
738
+ end
739
+ end
740
+
741
+ describe 'errors method' do
742
+ it 'returns an empty array' do
743
+ @form.errors.must_equal([])
744
+ end
745
+ end
746
+
747
+ describe 'errors_on query method' do
748
+ it 'returns false when given the file field name' do
749
+ @form.errors_on?(:file).must_equal(false)
750
+ end
751
+ end
752
+
753
+ describe 'errors_on method' do
754
+ it 'returns an empty array when given the file field name' do
755
+ @form.errors_on(:file).must_equal([])
756
+ end
757
+ end
758
+ end
759
+
760
+ describe 'FormWithFileField after parsing blank multipart input' do
761
+ before do
762
+ @form = FormWithFileField.new
763
+
764
+ body = <<-EOS.gsub(/\n/, "\r\n")
765
+ --AaB03x
766
+ Content-Disposition: form-data; name="file"; filename=""
767
+ Content-Type: application/octet-stream
768
+
769
+
770
+ --AaB03x--
771
+ EOS
772
+
773
+ request = Struct.new(:body, :env).new(StringIO.new(body), {
774
+ 'REQUEST_METHOD' => 'POST',
775
+ 'CONTENT_TYPE' => 'multipart/form-data; boundary=AaB03x',
776
+ 'CONTENT_LENGTH' => body.bytesize
777
+ })
778
+
779
+ @form.parse(request)
780
+ end
781
+
782
+ describe 'errors query method' do
783
+ it 'returns true' do
784
+ @form.errors?.must_equal(true)
785
+ end
786
+ end
787
+
788
+ describe 'errors method' do
789
+ it 'returns an array containing a single error message' do
790
+ @form.errors.must_be_instance_of(Array)
791
+ @form.errors.length.must_equal(1)
792
+ @form.errors.first.to_s.must_equal('File is required')
793
+ end
794
+ end
795
+
796
+ describe 'errors_on query method' do
797
+ it 'returns true when given the file field name' do
798
+ @form.errors_on?(:file).must_equal(true)
799
+ end
800
+ end
801
+
802
+ describe 'errors_on method' do
803
+ it 'returns an array containing a single error message when given the file field name' do
804
+ errors = @form.errors_on(:file)
805
+ errors.must_be_instance_of(Array)
806
+ errors.length.must_equal(1)
807
+ errors.first.to_s.must_equal('File is required')
808
+ end
809
+ end
810
+ end
811
+
812
+ describe 'FormWithFileField after parsing multipart input with too much data' do
813
+ before do
814
+ @form = FormWithFileField.new
815
+
816
+ body = <<-EOS.gsub(/\n/, "\r\n")
817
+ --AaB03x
818
+ Content-Disposition: form-data; name="file"; filename="example.txt"
819
+ Content-Type: text/plain
820
+
821
+ The quick brown fox jumps over the lazy dog.
822
+ --AaB03x--
823
+ EOS
824
+
825
+ request = Struct.new(:body, :env).new(StringIO.new(body), {
826
+ 'REQUEST_METHOD' => 'POST',
827
+ 'CONTENT_TYPE' => 'multipart/form-data; boundary=AaB03x',
828
+ 'CONTENT_LENGTH' => body.bytesize
829
+ })
830
+
831
+ @form.parse(request)
832
+ end
833
+
834
+ describe 'errors query method' do
835
+ it 'returns true' do
836
+ @form.errors?.must_equal(true)
837
+ end
838
+ end
839
+
840
+ describe 'errors method' do
841
+ it 'returns an array containing a single error message' do
842
+ @form.errors.must_be_instance_of(Array)
843
+ @form.errors.length.must_equal(1)
844
+ @form.errors.first.to_s.must_equal('File is too large')
845
+ end
846
+ end
847
+
848
+ describe 'errors_on query method' do
849
+ it 'returns true when given the file field name' do
850
+ @form.errors_on?(:file).must_equal(true)
851
+ end
852
+ end
853
+
854
+ describe 'errors_on method' do
855
+ it 'returns an array containing a single error message when given the file field name' do
856
+ errors = @form.errors_on(:file)
857
+ errors.must_be_instance_of(Array)
858
+ errors.length.must_equal(1)
859
+ errors.first.to_s.must_equal('File is too large')
860
+ end
861
+ end
862
+ end
863
+
864
+ describe 'FormWithFileField after parsing multipart input with an unacceptable content type' do
865
+ before do
866
+ @form = FormWithFileField.new
867
+
868
+ body = <<-EOS.gsub(/\n/, "\r\n")
869
+ --AaB03x
870
+ Content-Disposition: form-data; name="file"; filename="example.html"
871
+ Content-Type: text/html
872
+
873
+ <!DOCTYPE html>
874
+ --AaB03x--
875
+ EOS
876
+
877
+ request = Struct.new(:body, :env).new(StringIO.new(body), {
878
+ 'REQUEST_METHOD' => 'POST',
879
+ 'CONTENT_TYPE' => 'multipart/form-data; boundary=AaB03x',
880
+ 'CONTENT_LENGTH' => body.bytesize
881
+ })
882
+
883
+ @form.parse(request)
884
+ end
885
+
886
+ describe 'errors query method' do
887
+ it 'returns true' do
888
+ @form.errors?.must_equal(true)
889
+ end
890
+ end
891
+
892
+ describe 'errors method' do
893
+ it 'returns an array containing a single error message' do
894
+ @form.errors.must_be_instance_of(Array)
895
+ @form.errors.length.must_equal(1)
896
+ @form.errors.first.to_s.must_equal('File is not an accepted file type')
897
+ end
898
+ end
899
+
900
+ describe 'errors_on query method' do
901
+ it 'returns true when given the file field name' do
902
+ @form.errors_on?(:file).must_equal(true)
903
+ end
904
+ end
905
+
906
+ describe 'errors_on method' do
907
+ it 'returns an array containing a single error message when given the file field name' do
908
+ errors = @form.errors_on(:file)
909
+ errors.must_be_instance_of(Array)
910
+ errors.length.must_equal(1)
911
+ errors.first.to_s.must_equal('File is not an accepted file type')
912
+ end
913
+ end
914
+ end
915
+
709
916
  describe 'FormWithField on Rails' do
710
917
  before do
711
918
  @form = FormWithField.new
@@ -804,13 +1011,7 @@ describe 'FormClassWithExplicitSetupCall' do
804
1011
  end
805
1012
 
806
1013
  it 'includes the formeze class methods and instance methods' do
807
- singleton_class = if @form_class.respond_to?(:singleton_class)
808
- @form_class.singleton_class
809
- else
810
- (class << @form_class; self; end)
811
- end
812
-
813
- singleton_class.must_include(Formeze::ClassMethods)
1014
+ @form_class.singleton_class.must_include(Formeze::ClassMethods)
814
1015
 
815
1016
  @form_class.must_include(Formeze::InstanceMethods)
816
1017
  end
metadata CHANGED
@@ -1,83 +1,106 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: formeze
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.0
5
- prerelease:
4
+ version: 3.0.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Tim Craft
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-09-02 00:00:00.000000000 Z
11
+ date: 2015-06-21 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: rake
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>='
17
+ - - "~>"
20
18
  - !ruby/object:Gem::Version
21
- version: 0.9.3
19
+ version: '10'
22
20
  type: :development
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ! '>='
24
+ - - "~>"
28
25
  - !ruby/object:Gem::Version
29
- version: 0.9.3
26
+ version: '10'
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: i18n
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
- - - ~>
31
+ - - "~>"
36
32
  - !ruby/object:Gem::Version
37
- version: 0.6.0
33
+ version: '0.6'
38
34
  type: :development
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
- - - ~>
38
+ - - "~>"
44
39
  - !ruby/object:Gem::Version
45
- version: 0.6.0
46
- description: A little library for handling form data/input
40
+ version: '0.6'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5'
55
+ - !ruby/object:Gem::Dependency
56
+ name: mime-types
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '2'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '2'
69
+ description: A little Ruby library for handling form data/input
47
70
  email:
48
71
  - mail@timcraft.com
49
72
  executables: []
50
73
  extensions: []
51
74
  extra_rdoc_files: []
52
75
  files:
53
- - lib/formeze.rb
54
- - spec/formeze_spec.rb
76
+ - LICENSE.txt
55
77
  - README.md
56
78
  - Rakefile.rb
57
79
  - formeze.gemspec
80
+ - lib/formeze.rb
81
+ - spec/formeze_spec.rb
58
82
  homepage: http://github.com/timcraft/formeze
59
- licenses: []
83
+ licenses:
84
+ - LGPL-3.0
85
+ metadata: {}
60
86
  post_install_message:
61
87
  rdoc_options: []
62
88
  require_paths:
63
89
  - lib
64
90
  required_ruby_version: !ruby/object:Gem::Requirement
65
- none: false
66
91
  requirements:
67
- - - ! '>='
92
+ - - ">="
68
93
  - !ruby/object:Gem::Version
69
- version: '0'
94
+ version: 1.9.3
70
95
  required_rubygems_version: !ruby/object:Gem::Requirement
71
- none: false
72
96
  requirements:
73
- - - ! '>='
97
+ - - ">="
74
98
  - !ruby/object:Gem::Version
75
99
  version: '0'
76
100
  requirements: []
77
101
  rubyforge_project:
78
- rubygems_version: 1.8.23
102
+ rubygems_version: 2.2.2
79
103
  signing_key:
80
- specification_version: 3
104
+ specification_version: 4
81
105
  summary: See description
82
106
  test_files: []
83
- has_rdoc: