formeze 4.1.0 → 4.2.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 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