formeze 4.1.0 → 4.2.0

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: 959c1584612e08ca4e490f77a71bef8e2f2be9c1db827dc2903335a7f8a340b2
4
+ data.tar.gz: 60d9b150269c308824e72282e5ea55b5bdb369f04cfe7120dec8f9a7764157bd
5
5
  SHA512:
6
- metadata.gz: b02c13c677e38ab17d1db9557499d579431b7f33a72c3c3e0f49766bf4fa21daf04af7ff60f2740db92bbcb610e1b11792df90d55cd7a8e1776e3ad2efd6b785
7
- data.tar.gz: 5cdb99967751d382b3e40ee8207c170494fed281c33ea779fa5fcc5b41a090514749f41d004c5bdeb8501e665124c888487cf75c36ab0851645e3c2a529a0458
6
+ metadata.gz: d19f682f89a2222e8043d9a4a3569ef8e977cf33070723e655a4e6268b2bce45e5b410608db0a8648731a4be55774fe7d89ca15d63da2ac7c7247bdba23c4e9d
7
+ data.tar.gz: fe373bbd667d964054c625e7d188721222f02d384de318184296a3fef6ed85da96443c1834649349792f33a6320e69ac29ad933b2b6e3d1a013233f6a067b30d
data/CHANGES.md CHANGED
@@ -1,3 +1,9 @@
1
+ # 4.2.0
2
+
3
+ * Fixed file validation for e.g. `.md` files sent as application/octet-stream
4
+
5
+ * Fixed file validation for e.g. `.rtf` files sent as text/rtf
6
+
1
7
  # 4.1.0
2
8
 
3
9
  * 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.0'
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,155 @@
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
+ BINARY = MIME::Types['application/octet-stream'].first
63
+
64
+ def acceptable_file?(object)
65
+ type = MIME::Types[object.content_type].first
66
+
67
+ types = MIME::Types.type_for(object.original_filename)
68
+
69
+ if type == BINARY
70
+ types.any? { |type| accept.include?(type) }
71
+ else
72
+ accept.include?(type) && types.include?(type)
73
+ end
74
+ end
75
+
76
+ def key
77
+ @key ||= @name.to_s
78
+ end
79
+
80
+ def key_required?
81
+ @options.fetch(:key_required) { true }
82
+ end
83
+
84
+ def label
85
+ @options.fetch(:label) { Formeze::Labels.translate(name) }
86
+ end
87
+
88
+ def required?
89
+ @options.fetch(:required) { true }
90
+ end
91
+
92
+ def multiline?
93
+ @options.fetch(:multiline) { false }
94
+ end
95
+
96
+ def multiple?
97
+ @options.fetch(:multiple) { false }
98
+ end
99
+
100
+ def maxsize?
101
+ @options.key?(:maxsize)
102
+ end
103
+
104
+ def maxsize
105
+ @options.fetch(:maxsize)
106
+ end
107
+
108
+ def accept
109
+ @accept ||= @options.fetch(:accept).split(',').flat_map { |type| MIME::Types[type] }
110
+ end
111
+
112
+ def too_long?(value)
113
+ value.chars.count > @options.fetch(:maxlength) { 64 }
114
+ end
115
+
116
+ def too_short?(value)
117
+ @options.key?(:minlength) && value.chars.count < @options.fetch(:minlength)
118
+ end
119
+
120
+ def no_match?(value)
121
+ @options.key?(:pattern) && value !~ @options[:pattern]
122
+ end
123
+
124
+ def blank_value?
125
+ @options.key?(:blank)
126
+ end
127
+
128
+ def blank_value
129
+ @options.fetch(:blank)
130
+ end
131
+
132
+ def values?
133
+ @options.key?(:values)
134
+ end
135
+
136
+ def values
137
+ @options.fetch(:values)
138
+ end
139
+
140
+ def defined_if?
141
+ @options.key?(:defined_if)
142
+ end
143
+
144
+ def defined_if
145
+ @options.fetch(:defined_if)
146
+ end
147
+
148
+ def defined_unless?
149
+ @options.key?(:defined_unless)
150
+ end
151
+
152
+ def defined_unless
153
+ @options.fetch(:defined_unless)
154
+ end
155
+ 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.0
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-07 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