formeze 4.1.0 → 4.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 797956c11fea269806464fd4a0ea221b74f0a54db569c5d73f3277737e7d6e27
4
- data.tar.gz: b095c29110d1bed615009b469258eba3acb33930bd8d0a434658fc70daf7fd91
3
+ metadata.gz: 39b1043817638b51294f4ca2da81e2e2fa1829f7209eb0026bedffca84c7b430
4
+ data.tar.gz: 0e4d59c83e428005f01522430e740795accb24831dcc226cd1ce30eda28afada
5
5
  SHA512:
6
- metadata.gz: b02c13c677e38ab17d1db9557499d579431b7f33a72c3c3e0f49766bf4fa21daf04af7ff60f2740db92bbcb610e1b11792df90d55cd7a8e1776e3ad2efd6b785
7
- data.tar.gz: 5cdb99967751d382b3e40ee8207c170494fed281c33ea779fa5fcc5b41a090514749f41d004c5bdeb8501e665124c888487cf75c36ab0851645e3c2a529a0458
6
+ metadata.gz: 865f278fd2c8456a1def21e04d2150d6487727eceda9bc6d1ce375cba906f4a177e76cb158eea0cbe40d1cd1c6da785efb512c301647ebbcc8155d2aae8a7061
7
+ data.tar.gz: e20a37f37ebbb28932174a148cedd37fd251f97fa05986b0302ddf4e7f7897638d3f722af071060fca0704d0f97265e273478cd01721a1383b72feabb1279896
data/CHANGES.md CHANGED
@@ -1,3 +1,13 @@
1
+ # 4.2.1
2
+
3
+ * Fixed regression in v4.2.0 when mime-types gem is not loaded
4
+
5
+ # 4.2.0
6
+
7
+ * Fixed file validation for e.g. `.md` files sent as application/octet-stream
8
+
9
+ * Fixed file validation for e.g. `.rtf` files sent as text/rtf
10
+
1
11
  # 4.1.0
2
12
 
3
13
  * Fixed compatibility with rack 3+
data/formeze.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'formeze'
3
- s.version = '4.1.0'
3
+ s.version = '4.2.1'
4
4
  s.license = 'LGPL-3.0'
5
5
  s.platform = Gem::Platform::RUBY
6
6
  s.authors = ['Tim Craft']
@@ -0,0 +1,11 @@
1
+ module Formeze::Errors
2
+ SCOPE = [:formeze, :errors].freeze
3
+
4
+ def self.translate(error, default)
5
+ if defined?(I18n)
6
+ return I18n.translate(error, scope: SCOPE, default: default)
7
+ end
8
+
9
+ default
10
+ end
11
+ end
@@ -0,0 +1,153 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Formeze::Field
4
+ include Formeze::Presence
5
+
6
+ attr_reader :name
7
+
8
+ def initialize(name, **options)
9
+ @name, @options = name, options
10
+ end
11
+
12
+ def validate_all(values, form)
13
+ size = 0
14
+
15
+ values.each do |value|
16
+ if String === value
17
+ validate(value, form)
18
+ else
19
+ validate_file(value, form)
20
+
21
+ size += value.size
22
+ end
23
+ end
24
+
25
+ form.add_error(self, :too_large, 'is too large') if maxsize? && size > maxsize
26
+ end
27
+
28
+ def validate(value, form)
29
+ value = Formeze.scrub(value, @options[:scrub])
30
+
31
+ if blank?(value)
32
+ form.add_error(self, :required, 'is required') if required?
33
+
34
+ value = blank_value if blank_value?
35
+ else
36
+ form.add_error(self, :not_multiline, 'cannot contain newlines') if !multiline? && value.lines.count > 1
37
+
38
+ form.add_error(self, :too_long, 'is too long') if too_long?(value)
39
+
40
+ form.add_error(self, :too_short, 'is too short') if too_short?(value)
41
+
42
+ form.add_error(self, :no_match, 'is invalid') if no_match?(value)
43
+
44
+ form.add_error(self, :bad_value, 'is invalid') if values? && !values.include?(value)
45
+ end
46
+
47
+ value = Array(form.send(name)).push(value) if multiple?
48
+
49
+ form.send(:"#{name}=", value)
50
+ end
51
+
52
+ def validate_file(object, form)
53
+ unless acceptable_file?(object)
54
+ form.add_error(self, :not_accepted, 'is not an accepted file type')
55
+ end
56
+
57
+ object = Array(form.send(name)).push(object) if multiple?
58
+
59
+ form.send(:"#{name}=", object)
60
+ end
61
+
62
+ def acceptable_file?(object)
63
+ types = MIME::Types.type_for(object.original_filename)
64
+
65
+ if object.content_type == 'application/octet-stream'
66
+ types.any? { |type| accept.include?(type) }
67
+ else
68
+ type = MIME::Types[object.content_type].first
69
+
70
+ accept.include?(type) && types.include?(type)
71
+ end
72
+ end
73
+
74
+ def key
75
+ @key ||= @name.to_s
76
+ end
77
+
78
+ def key_required?
79
+ @options.fetch(:key_required) { true }
80
+ end
81
+
82
+ def label
83
+ @options.fetch(:label) { Formeze::Labels.translate(name) }
84
+ end
85
+
86
+ def required?
87
+ @options.fetch(:required) { true }
88
+ end
89
+
90
+ def multiline?
91
+ @options.fetch(:multiline) { false }
92
+ end
93
+
94
+ def multiple?
95
+ @options.fetch(:multiple) { false }
96
+ end
97
+
98
+ def maxsize?
99
+ @options.key?(:maxsize)
100
+ end
101
+
102
+ def maxsize
103
+ @options.fetch(:maxsize)
104
+ end
105
+
106
+ def accept
107
+ @accept ||= @options.fetch(:accept).split(',').flat_map { |type| MIME::Types[type] }
108
+ end
109
+
110
+ def too_long?(value)
111
+ value.chars.count > @options.fetch(:maxlength) { 64 }
112
+ end
113
+
114
+ def too_short?(value)
115
+ @options.key?(:minlength) && value.chars.count < @options.fetch(:minlength)
116
+ end
117
+
118
+ def no_match?(value)
119
+ @options.key?(:pattern) && value !~ @options[:pattern]
120
+ end
121
+
122
+ def blank_value?
123
+ @options.key?(:blank)
124
+ end
125
+
126
+ def blank_value
127
+ @options.fetch(:blank)
128
+ end
129
+
130
+ def values?
131
+ @options.key?(:values)
132
+ end
133
+
134
+ def values
135
+ @options.fetch(:values)
136
+ end
137
+
138
+ def defined_if?
139
+ @options.key?(:defined_if)
140
+ end
141
+
142
+ def defined_if
143
+ @options.fetch(:defined_if)
144
+ end
145
+
146
+ def defined_unless?
147
+ @options.key?(:defined_unless)
148
+ end
149
+
150
+ def defined_unless
151
+ @options.fetch(:defined_unless)
152
+ end
153
+ end
@@ -0,0 +1,5 @@
1
+ class Formeze::Form
2
+ def self.inherited(subclass)
3
+ Formeze.setup(subclass)
4
+ end
5
+ end
@@ -0,0 +1,23 @@
1
+ require 'cgi'
2
+
3
+ module Formeze::FormData
4
+ class CGI < ::CGI
5
+ def env_table
6
+ @options[:request].env
7
+ end
8
+
9
+ def stdinput
10
+ @options[:request].body.tap do |body|
11
+ body.rewind if body.respond_to?(:rewind)
12
+ end
13
+ end
14
+ end
15
+
16
+ def self.parse(input)
17
+ if input.is_a?(String)
18
+ CGI.parse(input)
19
+ else
20
+ CGI.new(request: input).params
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,13 @@
1
+ module Formeze::Labels
2
+ SCOPE = [:formeze, :labels].freeze
3
+
4
+ def self.translate(name)
5
+ default = Formeze.label(name)
6
+
7
+ if defined?(I18n)
8
+ return I18n.translate(name, scope: SCOPE, default: default)
9
+ end
10
+
11
+ default
12
+ end
13
+ end
@@ -0,0 +1,11 @@
1
+ module Formeze::Presence
2
+ REGEXP = /\S/
3
+
4
+ def present?(string)
5
+ string =~ REGEXP
6
+ end
7
+
8
+ def blank?(string)
9
+ string !~ REGEXP
10
+ end
11
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Formeze::Validation
4
+ include Formeze::Presence
5
+
6
+ def initialize(field, **kwargs, &block)
7
+ @field = field
8
+
9
+ @error = kwargs[:error] || :invalid
10
+
11
+ @precondition = kwargs[:if]
12
+
13
+ @block = block
14
+ end
15
+
16
+ def validates?(form)
17
+ @precondition ? form.instance_eval(&@precondition) : true
18
+ end
19
+
20
+ def field_value?(form)
21
+ present?(form.send(@field.name))
22
+ end
23
+
24
+ def field_errors?(form)
25
+ form.errors_on?(@field.name)
26
+ end
27
+
28
+ def validate(form)
29
+ if validates?(form) && field_value?(form) && !field_errors?(form)
30
+ return_value = if @block.arity == 1
31
+ @block.call(form.send(@field.name))
32
+ else
33
+ form.instance_eval(&@block)
34
+ end
35
+
36
+ form.add_error(@field, @error, 'is invalid') unless return_value
37
+ end
38
+ end
39
+ end
data/lib/formeze.rb CHANGED
@@ -1,210 +1,13 @@
1
1
  # frozen_string_literal: true
2
- require 'cgi'
3
2
 
4
3
  module Formeze
5
- module Presence
6
- REGEXP = /\S/
7
-
8
- def present?(string)
9
- string =~ REGEXP
10
- end
11
-
12
- def blank?(string)
13
- string !~ REGEXP
14
- end
15
- end
16
-
17
- private_constant :Presence
18
-
19
- class Field
20
- include Presence
21
-
22
- attr_reader :name
23
-
24
- def initialize(name, **options)
25
- @name, @options = name, options
26
- end
27
-
28
- def validate_all(values, form)
29
- size = 0
30
-
31
- values.each do |value|
32
- if String === value
33
- validate(value, form)
34
- else
35
- validate_file(value, form)
36
-
37
- size += value.size
38
- end
39
- end
40
-
41
- form.add_error(self, error(:too_large, 'is too large')) if maxsize? && size > maxsize
42
- end
43
-
44
- def validate(value, form)
45
- value = Formeze.scrub(value, @options[:scrub])
46
-
47
- if blank?(value)
48
- form.add_error(self, error(:required, 'is required')) if required?
49
-
50
- value = blank_value if blank_value?
51
- else
52
- form.add_error(self, error(:not_multiline, 'cannot contain newlines')) if !multiline? && value.lines.count > 1
53
-
54
- form.add_error(self, error(:too_long, 'is too long')) if too_long?(value)
55
-
56
- form.add_error(self, error(:too_short, 'is too short')) if too_short?(value)
57
-
58
- form.add_error(self, error(:no_match, 'is invalid')) if no_match?(value)
59
-
60
- form.add_error(self, error(:bad_value, 'is invalid')) if values? && !values.include?(value)
61
- end
62
-
63
- value = Array(form.send(name)).push(value) if multiple?
64
-
65
- form.send(:"#{name}=", value)
66
- end
67
-
68
- def validate_file(object, form)
69
- type = MIME::Types[object.content_type].first
70
-
71
- filename_type = MIME::Types.type_for(object.original_filename).first
72
-
73
- if type.nil? || type != filename_type || !accept.include?(type)
74
- form.add_error(self, error(:not_accepted, 'is not an accepted file type'))
75
- end
76
-
77
- object = Array(form.send(name)).push(object) if multiple?
78
-
79
- form.send(:"#{name}=", object)
80
- end
81
-
82
- def error(key, default)
83
- Formeze.translate(key, scope: ERRORS_SCOPE, default: default)
84
- end
85
-
86
- def key
87
- @key ||= @name.to_s
88
- end
89
-
90
- def key_required?
91
- @options.fetch(:key_required) { true }
92
- end
93
-
94
- def label
95
- @options.fetch(:label) { Formeze.translate(name, scope: LABELS_SCOPE, default: Formeze.label(name)) }
96
- end
97
-
98
- def required?
99
- @options.fetch(:required) { true }
100
- end
101
-
102
- def multiline?
103
- @options.fetch(:multiline) { false }
104
- end
105
-
106
- def multiple?
107
- @options.fetch(:multiple) { false }
108
- end
109
-
110
- def maxsize?
111
- @options.key?(:maxsize)
112
- end
113
-
114
- def maxsize
115
- @options.fetch(:maxsize)
116
- end
117
-
118
- def accept
119
- @accept ||= @options.fetch(:accept).split(',').flat_map { |type| MIME::Types[type] }
120
- end
121
-
122
- def too_long?(value)
123
- value.chars.count > @options.fetch(:maxlength) { 64 }
124
- end
125
-
126
- def too_short?(value)
127
- @options.key?(:minlength) && value.chars.count < @options.fetch(:minlength)
128
- end
129
-
130
- def no_match?(value)
131
- @options.key?(:pattern) && value !~ @options[:pattern]
132
- end
133
-
134
- def blank_value?
135
- @options.key?(:blank)
136
- end
137
-
138
- def blank_value
139
- @options.fetch(:blank)
140
- end
141
-
142
- def values?
143
- @options.key?(:values)
144
- end
145
-
146
- def values
147
- @options.fetch(:values)
148
- end
149
-
150
- def defined_if?
151
- @options.key?(:defined_if)
152
- end
153
-
154
- def defined_if
155
- @options.fetch(:defined_if)
156
- end
157
-
158
- def defined_unless?
159
- @options.key?(:defined_unless)
160
- end
161
-
162
- def defined_unless
163
- @options.fetch(:defined_unless)
164
- end
165
- end
166
-
167
- class Validation
168
- include Presence
169
-
170
- def initialize(field, **kwargs, &block)
171
- @field = field
172
-
173
- @error = kwargs[:error] || :invalid
174
-
175
- @precondition = kwargs[:if]
176
-
177
- @block = block
178
- end
179
-
180
- def error_message
181
- Formeze.translate(@error, scope: ERRORS_SCOPE, default: 'is invalid')
182
- end
183
-
184
- def validates?(form)
185
- @precondition ? form.instance_eval(&@precondition) : true
186
- end
187
-
188
- def field_value?(form)
189
- present?(form.send(@field.name))
190
- end
191
-
192
- def field_errors?(form)
193
- form.errors_on?(@field.name)
194
- end
195
-
196
- def validate(form)
197
- if validates?(form) && field_value?(form) && !field_errors?(form)
198
- return_value = if @block.arity == 1
199
- @block.call(form.send(@field.name))
200
- else
201
- form.instance_eval(&@block)
202
- end
203
-
204
- form.add_error(@field, error_message) unless return_value
205
- end
206
- end
207
- end
4
+ autoload :Errors, 'formeze/errors'
5
+ autoload :Field, 'formeze/field'
6
+ autoload :Form, 'formeze/form'
7
+ autoload :FormData, 'formeze/form_data'
8
+ autoload :Labels, 'formeze/labels'
9
+ autoload :Presence, 'formeze/presence'
10
+ autoload :Validation, 'formeze/validation'
208
11
 
209
12
  module ClassMethods
210
13
  def fields
@@ -234,20 +37,6 @@ module Formeze
234
37
 
235
38
  class ValidationError < StandardError; end
236
39
 
237
- class RequestCGI < CGI
238
- def env_table
239
- @options[:request].env
240
- end
241
-
242
- def stdinput
243
- @options[:request].body.tap do |body|
244
- body.rewind if body.respond_to?(:rewind)
245
- end
246
- end
247
- end
248
-
249
- private_constant :RequestCGI
250
-
251
40
  RAILS_FORM_KEYS = %w[utf8 authenticity_token commit]
252
41
 
253
42
  private_constant :RAILS_FORM_KEYS
@@ -266,11 +55,7 @@ module Formeze
266
55
  end
267
56
 
268
57
  def parse(input)
269
- form_data = if String === input
270
- CGI.parse(input)
271
- else
272
- RequestCGI.new(request: input).params
273
- end
58
+ form_data = FormData.parse(input)
274
59
 
275
60
  self.class.fields.each_value do |field|
276
61
  next unless field_defined?(field)
@@ -307,7 +92,9 @@ module Formeze
307
92
  return self
308
93
  end
309
94
 
310
- def add_error(field, message)
95
+ def add_error(field, message, default = nil)
96
+ message = Formeze::Errors.translate(message, default) unless default.nil?
97
+
311
98
  error = ValidationError.new("#{field.label} #{message}")
312
99
 
313
100
  errors << error
@@ -380,27 +167,9 @@ module Formeze
380
167
  end
381
168
  end
382
169
 
383
- ERRORS_SCOPE = [:formeze, :errors].freeze
384
-
385
- private_constant :ERRORS_SCOPE
386
-
387
- LABELS_SCOPE = [:formeze, :labels].freeze
388
-
389
- private_constant :LABELS_SCOPE
390
-
391
- def self.translate(key, **options)
392
- defined?(I18n) ? I18n.translate(key, **options) : options.fetch(:default)
393
- end
394
-
395
170
  def self.setup(form)
396
171
  form.send :include, InstanceMethods
397
172
 
398
173
  form.extend ClassMethods
399
174
  end
400
-
401
- class Form
402
- def self.inherited(subclass)
403
- Formeze.setup(subclass)
404
- end
405
- end
406
175
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: formeze
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.1.0
4
+ version: 4.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tim Craft
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-01-11 00:00:00.000000000 Z
11
+ date: 2024-03-08 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Ruby gem for validating form data
14
14
  email:
@@ -22,6 +22,13 @@ files:
22
22
  - README.md
23
23
  - formeze.gemspec
24
24
  - lib/formeze.rb
25
+ - lib/formeze/errors.rb
26
+ - lib/formeze/field.rb
27
+ - lib/formeze/form.rb
28
+ - lib/formeze/form_data.rb
29
+ - lib/formeze/labels.rb
30
+ - lib/formeze/presence.rb
31
+ - lib/formeze/validation.rb
25
32
  homepage: https://github.com/readysteady/formeze
26
33
  licenses:
27
34
  - LGPL-3.0
@@ -45,7 +52,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
45
52
  - !ruby/object:Gem::Version
46
53
  version: '0'
47
54
  requirements: []
48
- rubygems_version: 3.4.10
55
+ rubygems_version: 3.5.3
49
56
  signing_key:
50
57
  specification_version: 4
51
58
  summary: See description