formeze 4.0.1 → 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: a68f63365be4c93159701308fd264f143e508111ec6d1d860ba62bba8cfacd41
4
- data.tar.gz: f52e50d471f7df325dbe7a6e5400375d06b988b2411888c1a2c357aec2ec24be
3
+ metadata.gz: 959c1584612e08ca4e490f77a71bef8e2f2be9c1db827dc2903335a7f8a340b2
4
+ data.tar.gz: 60d9b150269c308824e72282e5ea55b5bdb369f04cfe7120dec8f9a7764157bd
5
5
  SHA512:
6
- metadata.gz: a08a90d2595aeeecb185c58a80b6a1fb588bee98a74002e6646a6be5d97e18effb47cf0513734de1f077a9e47501c8402fc92e1f400f2438b9f49d437ffc214c
7
- data.tar.gz: a15f47a7e423b78c67104376f7e4ebd41bff3dc168f2703c65347bd795c0196202c1cb079b0f82d9cc8b4e68e5f8ab4374dbfb8e7d58fc9fc8ba8bb2905e7420
6
+ metadata.gz: d19f682f89a2222e8043d9a4a3569ef8e977cf33070723e655a4e6268b2bce45e5b410608db0a8648731a4be55774fe7d89ca15d63da2ac7c7247bdba23c4e9d
7
+ data.tar.gz: fe373bbd667d964054c625e7d188721222f02d384de318184296a3fef6ed85da96443c1834649349792f33a6320e69ac29ad933b2b6e3d1a013233f6a067b30d
data/CHANGES.md CHANGED
@@ -1,3 +1,13 @@
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
+
7
+ # 4.1.0
8
+
9
+ * Fixed compatibility with rack 3+
10
+
1
11
  # 4.0.1
2
12
 
3
13
  * Fixed outdated changelog_uri
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012-2020 TIMCRAFT
1
+ Copyright (c) 2012-2024 TIMCRAFT
2
2
 
3
3
  This is an Open Source project licensed under the terms of the LGPLv3 license.
4
4
  Please see <http://www.gnu.org/licenses/lgpl-3.0.html> for license text.
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # formeze
2
2
 
3
3
  ![Gem Version](https://badge.fury.io/rb/formeze.svg)
4
- ![Build Status](https://github.com/readysteady/formeze/workflows/Test/badge.svg)
4
+ ![Test Status](https://github.com/readysteady/formeze/actions/workflows/test.yml/badge.svg)
5
5
 
6
6
  Ruby gem for validating form data.
7
7
 
data/formeze.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'formeze'
3
- s.version = '4.0.1'
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,18 +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
244
- end
245
- end
246
-
247
- private_constant :RequestCGI
248
-
249
40
  RAILS_FORM_KEYS = %w[utf8 authenticity_token commit]
250
41
 
251
42
  private_constant :RAILS_FORM_KEYS
@@ -264,11 +55,7 @@ module Formeze
264
55
  end
265
56
 
266
57
  def parse(input)
267
- form_data = if String === input
268
- CGI.parse(input)
269
- else
270
- RequestCGI.new(request: input).params
271
- end
58
+ form_data = FormData.parse(input)
272
59
 
273
60
  self.class.fields.each_value do |field|
274
61
  next unless field_defined?(field)
@@ -305,7 +92,9 @@ module Formeze
305
92
  return self
306
93
  end
307
94
 
308
- def add_error(field, message)
95
+ def add_error(field, message, default = nil)
96
+ message = Formeze::Errors.translate(message, default) unless default.nil?
97
+
309
98
  error = ValidationError.new("#{field.label} #{message}")
310
99
 
311
100
  errors << error
@@ -378,27 +167,9 @@ module Formeze
378
167
  end
379
168
  end
380
169
 
381
- ERRORS_SCOPE = [:formeze, :errors].freeze
382
-
383
- private_constant :ERRORS_SCOPE
384
-
385
- LABELS_SCOPE = [:formeze, :labels].freeze
386
-
387
- private_constant :LABELS_SCOPE
388
-
389
- def self.translate(key, **options)
390
- defined?(I18n) ? I18n.translate(key, **options) : options.fetch(:default)
391
- end
392
-
393
170
  def self.setup(form)
394
171
  form.send :include, InstanceMethods
395
172
 
396
173
  form.extend ClassMethods
397
174
  end
398
-
399
- class Form
400
- def self.inherited(subclass)
401
- Formeze.setup(subclass)
402
- end
403
- end
404
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.0.1
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: 2020-11-03 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.1.4
55
+ rubygems_version: 3.5.3
49
56
  signing_key:
50
57
  specification_version: 4
51
58
  summary: See description