formeze 2.2.0 → 3.0.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.
- checksums.yaml +7 -0
- data/LICENSE.txt +9 -0
- data/README.md +52 -72
- data/formeze.gemspec +9 -6
- data/lib/formeze.rb +69 -71
- data/spec/formeze_spec.rb +231 -30
- metadata +50 -27
checksums.yaml
ADDED
@@ -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
|
data/LICENSE.txt
ADDED
@@ -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.
|
14
|
-
|
15
|
-
|
16
|
-
of
|
17
|
-
|
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
|
-
|
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.
|
45
|
-
|
46
|
-
|
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
|
-
|
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
|
-
|
73
|
-
|
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
|
-
|
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
|
-
|
110
|
-
|
111
|
-
|
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
|
158
|
-
a set of checkboxes. For this case you can specify the `multiple`
|
159
|
-
|
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
|
-
|
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
|
|
data/formeze.gemspec
CHANGED
@@ -1,15 +1,18 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'formeze'
|
3
|
-
s.version = '
|
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.
|
12
|
-
s.add_development_dependency('
|
13
|
-
s.add_development_dependency('
|
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
|
data/lib/formeze.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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 ||=
|
190
|
+
@fields ||= {}
|
194
191
|
end
|
195
192
|
|
196
193
|
def field(*args)
|
197
194
|
field = Field.new(*args)
|
198
195
|
|
199
|
-
fields
|
196
|
+
fields[field.name] = field
|
200
197
|
|
201
|
-
|
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.
|
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(
|
243
|
-
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.
|
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
|
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.
|
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
|
|
data/spec/formeze_spec.rb
CHANGED
@@ -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.
|
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
|
-
|
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
|
321
|
-
@form.colour.
|
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.
|
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
|
384
|
-
@form.colour.
|
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
|
-
|
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:
|
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:
|
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:
|
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:
|
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
|
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
|
46
|
-
|
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
|
-
-
|
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:
|
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:
|
102
|
+
rubygems_version: 2.2.2
|
79
103
|
signing_key:
|
80
|
-
specification_version:
|
104
|
+
specification_version: 4
|
81
105
|
summary: See description
|
82
106
|
test_files: []
|
83
|
-
has_rdoc:
|