formeze 4.2.1 → 5.0.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: 39b1043817638b51294f4ca2da81e2e2fa1829f7209eb0026bedffca84c7b430
4
- data.tar.gz: 0e4d59c83e428005f01522430e740795accb24831dcc226cd1ce30eda28afada
3
+ metadata.gz: bd2160c23942cd4a127e7f84993aceb1a416a687060fb4f907181d295d5b26d5
4
+ data.tar.gz: 7406001697348315beeeb47c7400756e4bf8fb8acb60b8d872004f8de2966382
5
5
  SHA512:
6
- metadata.gz: 865f278fd2c8456a1def21e04d2150d6487727eceda9bc6d1ce375cba906f4a177e76cb158eea0cbe40d1cd1c6da785efb512c301647ebbcc8155d2aae8a7061
7
- data.tar.gz: e20a37f37ebbb28932174a148cedd37fd251f97fa05986b0302ddf4e7f7897638d3f722af071060fca0704d0f97265e273478cd01721a1383b72feabb1279896
6
+ metadata.gz: 83a0278250e4a4d9a1e7adae850e34cc4d44bdfa0e71638cf15ec7042abce347889495a4c6466d189ca30a9eb7b35e23472db91237f85f11841ef83fee2e01f8
7
+ data.tar.gz: b4218dd2e97436668378a40863a652ef1e7f7e329c0fa903e44197846b935ba8af21c7d2e34f804e483c9fda385deb39529b8a2049f69e4130b2238ef1ef9c2b
data/CHANGES.md CHANGED
@@ -1,3 +1,39 @@
1
+ # 5.0.0
2
+
3
+ * Added a `fill` field option to specify how to fill a field. For example:
4
+
5
+ class ExampleForm < Formeze::Form
6
+ field :year, required: false, fill: ->{ _1.date&.year }
7
+ end
8
+
9
+ * Added functionality for defining field specific error messages via i18n.
10
+
11
+ Add translations to your locale files like this:
12
+
13
+ ExampleForm:
14
+ errors:
15
+ comments:
16
+ required: 'are required'
17
+
18
+ * Removed support for older rubies. **Required ruby version is now 3.0.0**
19
+
20
+ * Changed from cgi to rack for parsing form data, adding support for parsing
21
+ requests with different methods e.g. PUT and PATCH instead of just POST.
22
+
23
+ Whilst this should largely be backwards compatible there are some differences,
24
+ for example uploaded files will be instances of `Rack::Multipart::UploadedFile`
25
+ instead of `StringIO` instances as they were before.
26
+
27
+ * Changed the default blank value to `nil`
28
+
29
+ # 4.3.0
30
+
31
+ * Added dependency on cgi gem
32
+
33
+ * Added support for lambda conditions without arguments
34
+
35
+ * Fixed custom validation for fields with multiple values
36
+
1
37
  # 4.2.1
2
38
 
3
39
  * Fixed regression in v4.2.0 when mime-types gem is not loaded
data/README.md CHANGED
@@ -3,19 +3,19 @@
3
3
  ![Gem Version](https://badge.fury.io/rb/formeze.svg)
4
4
  ![Test Status](https://github.com/readysteady/formeze/actions/workflows/test.yml/badge.svg)
5
5
 
6
- Ruby gem for validating form data.
6
+ Ruby gem for parsing and validating form data.
7
7
 
8
8
 
9
9
  ## Motivation
10
10
 
11
- Most web apps built for end users will need to process url-encoded form data.
11
+ Most web applications built for end users will need to process form data.
12
12
  Registration forms, profile forms, checkout forms, contact forms, and forms
13
13
  for adding/editing application specific data.
14
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.
15
+ With formeze you can define form objects that explicitly define what your
16
+ application expects as input. This is more secure, and leads to much better
17
+ separation of responsibilities, and also allows for implementing different
18
+ validation rules in different contexts.
19
19
 
20
20
 
21
21
  ## Install
@@ -53,7 +53,8 @@ else
53
53
  end
54
54
  ```
55
55
 
56
- Formeze will automatically ignore the Rails "utf8" and "authenticity_token" parameters.
56
+ Formeze will automatically ignore the Rails "authenticity_token", "commit",
57
+ and "utf8" parameters.
57
58
 
58
59
  If you prefer not to inherit from the `Formeze::Form` class then you can
59
60
  instead call the `Formeze.setup` method on your classes like this:
@@ -89,9 +90,9 @@ messages specific to a single field.
89
90
 
90
91
  ## Field options
91
92
 
92
- By default fields cannot be blank, they are limited to 64 characters,
93
- and they cannot contain newlines. These restrictions can be overridden
94
- by setting various field options.
93
+ By default fields are required (i.e. they cannot be blank), they are limited
94
+ to 64 characters, and they cannot contain newlines. These restrictions can be
95
+ overridden by setting various field options.
95
96
 
96
97
  Defining a field without any options works well for a simple text input.
97
98
  If the default length limit is too big or too small you can override it
@@ -101,11 +102,14 @@ by setting the `maxlength` option. For example:
101
102
  field :title, maxlength: 200
102
103
  ```
103
104
 
104
- Similarly there is a `minlength` option for validating fields that should
105
- have a minimum number of characters (e.g. passwords).
105
+ Similarly there is a `minlength` option for defining a minimum length:
106
+
107
+ ```ruby
108
+ field :password, minlength: 8
109
+ ```
106
110
 
107
111
  Fields are required by default. Specify the `required` option if the field
108
- is not required, i.e. the value of the field can be blank/empty. For example:
112
+ is optional. For example:
109
113
 
110
114
  ```ruby
111
115
  field :title, required: false
@@ -174,7 +178,7 @@ the `defined_if` or `defined_unless` options with a proc. Here's an example
174
178
  of using the defined_if option:
175
179
 
176
180
  ```ruby
177
- field :business_name, defined_if: proc { @account.business? }
181
+ field :business_name, defined_if: ->{ @account.business? }
178
182
  ```
179
183
 
180
184
  In this example the `business_name` field will only be defined and validated
@@ -185,7 +189,7 @@ Here's an example of using the defined_unless option:
185
189
  ```ruby
186
190
  field :same_address, values: %w(true), key_required: false
187
191
 
188
- field :billing_address_line_one, defined_unless: proc { same_address? }
192
+ field :billing_address_line_one, defined_unless: ->{ same_address? }
189
193
 
190
194
  def same_address?
191
195
  same_address == 'true'
@@ -308,11 +312,11 @@ would include the value of the `formeze.errors.does_not_match` I18n key.
308
312
 
309
313
  ## I18n integration
310
314
 
311
- Formeze integrates with [I18n](http://edgeguides.rubyonrails.org/i18n.html)
315
+ Formeze integrates with the [i18n gem](https://rubygems.org/gems/i18n)
312
316
  so that you can define custom error messages and field labels within your
313
317
  locales (useful both for localization, and when working with designers).
314
- For example, here is how you would change the "required" error message
315
- (which defaults to "is required"):
318
+
319
+ Here is an example of how you would change the "required" error message:
316
320
 
317
321
  ```yaml
318
322
  # config/locales/en.yml
@@ -322,8 +326,20 @@ en:
322
326
  required: "cannot be blank"
323
327
  ```
324
328
 
325
- And here is an example of how you would set a custom label for fields named
326
- "first_name" (for which the default label would be "First name"):
329
+ Error messages defined in this way apply globally to all Formeze forms.
330
+
331
+ You can also change error messages on a per field basis, for example:
332
+
333
+ ```yaml
334
+ # config/locales/en.yml
335
+ en:
336
+ ExampleForm:
337
+ errors:
338
+ comments:
339
+ required: 'are required'
340
+ ```
341
+
342
+ Here is an example of how to define a custom label for "first_name" fields:
327
343
 
328
344
  ```yaml
329
345
  # config/locales/en.yml
data/formeze.gemspec CHANGED
@@ -1,15 +1,15 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'formeze'
3
- s.version = '4.2.1'
3
+ s.version = '5.0.0'
4
4
  s.license = 'LGPL-3.0'
5
5
  s.platform = Gem::Platform::RUBY
6
6
  s.authors = ['Tim Craft']
7
7
  s.email = ['mail@timcraft.com']
8
8
  s.homepage = 'https://github.com/readysteady/formeze'
9
- s.description = 'Ruby gem for validating form data'
9
+ s.description = 'Ruby gem for parsing and validating form data'
10
10
  s.summary = 'See description'
11
11
  s.files = Dir.glob('lib/**/*.rb') + %w(CHANGES.md LICENSE.txt README.md formeze.gemspec)
12
- s.required_ruby_version = '>= 2.4.0'
12
+ s.required_ruby_version = '>= 3.0.0'
13
13
  s.require_path = 'lib'
14
14
  s.metadata = {
15
15
  'homepage' => 'https://github.com/readysteady/formeze',
@@ -17,4 +17,5 @@ Gem::Specification.new do |s|
17
17
  'bug_tracker_uri' => 'https://github.com/readysteady/formeze/issues',
18
18
  'changelog_uri' => 'https://github.com/readysteady/formeze/blob/main/CHANGES.md'
19
19
  }
20
+ s.add_dependency 'rack', '~> 3'
20
21
  end
@@ -0,0 +1,11 @@
1
+ module Formeze::Block
2
+ def self.evaluate(instance, block)
3
+ block = block.to_proc
4
+
5
+ if block.arity.zero?
6
+ instance.instance_exec(&block)
7
+ else
8
+ instance.instance_eval(&block)
9
+ end
10
+ end
11
+ end
@@ -1,11 +1,26 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Formeze::Errors
2
4
  SCOPE = [:formeze, :errors].freeze
3
5
 
4
- def self.translate(error, default)
5
- if defined?(I18n)
6
- return I18n.translate(error, scope: SCOPE, default: default)
7
- end
6
+ DEFAULT = {
7
+ bad_value: 'is invalid',
8
+ not_accepted: 'is not an accepted file type',
9
+ not_multiline: 'cannot contain newlines',
10
+ no_match: 'is invalid',
11
+ required: 'is required',
12
+ too_large: 'is too large',
13
+ too_long: 'is too long',
14
+ too_short: 'is too short',
15
+ }
16
+
17
+ def self.translate(error, scope:)
18
+ default = DEFAULT[error] || 'is invalid'
19
+
20
+ return default unless defined?(I18n)
21
+
22
+ message = I18n.translate(error, scope: scope, default: nil)
8
23
 
9
- default
24
+ message || I18n.translate(error, scope: SCOPE, default: default)
10
25
  end
11
26
  end
data/lib/formeze/field.rb CHANGED
@@ -14,49 +14,41 @@ class Formeze::Field
14
14
 
15
15
  values.each do |value|
16
16
  if String === value
17
- validate(value, form)
17
+ value = validate(value, form)
18
18
  else
19
- validate_file(value, form)
19
+ form.add_error(self, :not_accepted) unless acceptable_file?(value)
20
20
 
21
21
  size += value.size
22
22
  end
23
+
24
+ value = Array(form.send(name)).push(value) if multiple?
25
+
26
+ form.send(:"#{name}=", value)
23
27
  end
24
28
 
25
- form.add_error(self, :too_large, 'is too large') if maxsize? && size > maxsize
29
+ form.add_error(self, :too_large) if maxsize? && size > maxsize
26
30
  end
27
31
 
28
32
  def validate(value, form)
29
33
  value = Formeze.scrub(value, @options[:scrub])
30
34
 
31
35
  if blank?(value)
32
- form.add_error(self, :required, 'is required') if required?
36
+ form.add_error(self, :required) if required?
33
37
 
34
- value = blank_value if blank_value?
38
+ value = blank_value
35
39
  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)
40
+ form.add_error(self, :not_multiline) if !multiline? && value.lines.count > 1
43
41
 
44
- form.add_error(self, :bad_value, 'is invalid') if values? && !values.include?(value)
45
- end
42
+ form.add_error(self, :too_long) if too_long?(value)
46
43
 
47
- value = Array(form.send(name)).push(value) if multiple?
44
+ form.add_error(self, :too_short) if too_short?(value)
48
45
 
49
- form.send(:"#{name}=", value)
50
- end
46
+ form.add_error(self, :no_match) if no_match?(value)
51
47
 
52
- def validate_file(object, form)
53
- unless acceptable_file?(object)
54
- form.add_error(self, :not_accepted, 'is not an accepted file type')
48
+ form.add_error(self, :bad_value) if values? && !values.include?(value)
55
49
  end
56
50
 
57
- object = Array(form.send(name)).push(object) if multiple?
58
-
59
- form.send(:"#{name}=", object)
51
+ value
60
52
  end
61
53
 
62
54
  def acceptable_file?(object)
@@ -103,6 +95,10 @@ class Formeze::Field
103
95
  @options.fetch(:maxsize)
104
96
  end
105
97
 
98
+ def accept?
99
+ @options.key?(:accept)
100
+ end
101
+
106
102
  def accept
107
103
  @accept ||= @options.fetch(:accept).split(',').flat_map { |type| MIME::Types[type] }
108
104
  end
@@ -119,12 +115,8 @@ class Formeze::Field
119
115
  @options.key?(:pattern) && value !~ @options[:pattern]
120
116
  end
121
117
 
122
- def blank_value?
123
- @options.key?(:blank)
124
- end
125
-
126
118
  def blank_value
127
- @options.fetch(:blank)
119
+ @options[:blank]
128
120
  end
129
121
 
130
122
  def values?
@@ -150,4 +142,22 @@ class Formeze::Field
150
142
  def defined_unless
151
143
  @options.fetch(:defined_unless)
152
144
  end
145
+
146
+ def undefined?(form)
147
+ if defined_if?
148
+ !Formeze::Block.evaluate(form, defined_if)
149
+ elsif defined_unless?
150
+ Formeze::Block.evaluate(form, defined_unless)
151
+ else
152
+ false
153
+ end
154
+ end
155
+
156
+ def fill_proc?
157
+ @options.key?(:fill)
158
+ end
159
+
160
+ def fill_proc
161
+ @options[:fill]
162
+ end
153
163
  end
@@ -1,23 +1,53 @@
1
- require 'cgi'
1
+ # frozen_string_literal: true
2
+ require 'rack'
2
3
 
3
4
  module Formeze::FormData
4
- class CGI < ::CGI
5
- def env_table
6
- @options[:request].env
5
+ def self.parse(input)
6
+ if input.is_a?(String)
7
+ query_parser.parse_query(input)
8
+ elsif input.respond_to?(:env)
9
+ body = input.body
10
+ body.rewind if body.respond_to?(:rewind)
11
+ case input.media_type
12
+ when 'multipart/form-data'
13
+ Rack::Multipart.parse_multipart(input.env, Params)
14
+ when 'application/x-www-form-urlencoded'
15
+ query_parser.parse_query(body.read)
16
+ else
17
+ raise ArgumentError, "can't parse #{input.media_type.inspect} form data"
18
+ end
19
+ else
20
+ raise ArgumentError, "can't parse #{input.class} form data"
21
+ end
22
+ end
23
+
24
+ module Params
25
+ def self.make_params
26
+ ParamsHash.new { |h, k| h[k] = Array.new }
7
27
  end
8
28
 
9
- def stdinput
10
- @options[:request].body.tap do |body|
11
- body.rewind if body.respond_to?(:rewind)
29
+ def self.normalize_params(params, key, value)
30
+ if value.is_a?(Hash)
31
+ value = Rack::Multipart::UploadedFile.new(io: value[:tempfile], filename: value[:filename], content_type: value[:type])
12
32
  end
33
+
34
+ params[key] << value
13
35
  end
14
36
  end
15
37
 
16
- def self.parse(input)
17
- if input.is_a?(String)
18
- CGI.parse(input)
19
- else
20
- CGI.new(request: input).params
38
+ class ParamsHash < ::Hash
39
+ alias_method :to_params_hash, :to_h
40
+ end
41
+
42
+ class QueryParser < Rack::QueryParser
43
+ def make_params
44
+ Hash.new { |h, k| h[k] = Array.new }
21
45
  end
22
46
  end
47
+
48
+ def self.query_parser
49
+ @query_parser ||= QueryParser.new(nil, 0)
50
+ end
51
+
52
+ private_class_method :query_parser
23
53
  end
@@ -1,11 +1,14 @@
1
1
  module Formeze::Presence
2
2
  REGEXP = /\S/
3
3
 
4
- def present?(string)
5
- string =~ REGEXP
4
+ def present?(value)
5
+ return false if value.nil?
6
+ return false if value.respond_to?(:empty?) && value.empty?
7
+ return false if value.is_a?(String) && value !~ REGEXP
8
+ return true
6
9
  end
7
10
 
8
- def blank?(string)
9
- string !~ REGEXP
11
+ def blank?(value)
12
+ !present?(value)
10
13
  end
11
14
  end
@@ -1,5 +1,3 @@
1
- # frozen_string_literal: true
2
-
3
1
  class Formeze::Validation
4
2
  include Formeze::Presence
5
3
 
@@ -14,7 +12,7 @@ class Formeze::Validation
14
12
  end
15
13
 
16
14
  def validates?(form)
17
- @precondition ? form.instance_eval(&@precondition) : true
15
+ @precondition ? Formeze::Block.evaluate(form, @precondition) : true
18
16
  end
19
17
 
20
18
  def field_value?(form)
@@ -33,7 +31,7 @@ class Formeze::Validation
33
31
  form.instance_eval(&@block)
34
32
  end
35
33
 
36
- form.add_error(@field, @error, 'is invalid') unless return_value
34
+ form.add_error(@field, @error) unless return_value
37
35
  end
38
36
  end
39
37
  end
data/lib/formeze.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Formeze
4
+ autoload :Block, 'formeze/block'
4
5
  autoload :Errors, 'formeze/errors'
5
6
  autoload :Field, 'formeze/field'
6
7
  autoload :Form, 'formeze/form'
@@ -35,16 +36,24 @@ module Formeze
35
36
 
36
37
  class ValueError < StandardError; end
37
38
 
38
- class ValidationError < StandardError; end
39
+ class ValidationError < StandardError
40
+ def initialize(field, message)
41
+ @field = field
39
42
 
40
- RAILS_FORM_KEYS = %w[utf8 authenticity_token commit]
43
+ super("#{field.label} #{message}")
44
+ end
41
45
 
42
- private_constant :RAILS_FORM_KEYS
46
+ def field_name
47
+ @field.name
48
+ end
49
+ end
43
50
 
44
51
  module InstanceMethods
45
52
  def fill(object)
46
53
  self.class.fields.each_value do |field|
47
- if Hash === object && object.key?(field.name)
54
+ if field.fill_proc?
55
+ send(:"#{field.name}=", Formeze::Block.evaluate(object, field.fill_proc))
56
+ elsif Hash === object && object.key?(field.name)
48
57
  send(:"#{field.name}=", object[field.name])
49
58
  elsif object.respond_to?(field.name)
50
59
  send(:"#{field.name}=", object.send(field.name))
@@ -58,27 +67,31 @@ module Formeze
58
67
  form_data = FormData.parse(input)
59
68
 
60
69
  self.class.fields.each_value do |field|
61
- next unless field_defined?(field)
70
+ next if field.undefined?(self)
62
71
 
63
72
  unless form_data.key?(field.key)
64
73
  next if field.multiple? || !field.key_required?
65
74
 
66
- raise KeyError, "missing form key: #{field.key}"
75
+ raise KeyError, "missing form key: #{field.key}" unless field.accept?
67
76
  end
68
77
 
69
78
  values = form_data.delete(field.key)
70
79
 
71
- if values.length > 1
72
- raise ValueError unless field.multiple?
73
- end
80
+ if values.is_a?(Array)
81
+ if values.length > 1
82
+ raise ValueError, "multiple values for #{field.key} field" unless field.multiple?
83
+ end
74
84
 
75
- field.validate_all(values, self)
85
+ field.validate_all(values, self)
86
+ else
87
+ field.validate(values, self)
88
+ end
76
89
  end
77
90
 
78
91
  if defined?(Rails)
79
- RAILS_FORM_KEYS.each do |key|
80
- form_data.delete(key)
81
- end
92
+ form_data.delete('authenticity_token')
93
+ form_data.delete('commit')
94
+ form_data.delete('utf8')
82
95
  end
83
96
 
84
97
  unless form_data.empty?
@@ -92,14 +105,10 @@ module Formeze
92
105
  return self
93
106
  end
94
107
 
95
- def add_error(field, message, default = nil)
96
- message = Formeze::Errors.translate(message, default) unless default.nil?
97
-
98
- error = ValidationError.new("#{field.label} #{message}")
108
+ def add_error(field, error)
109
+ message = Formeze::Errors.translate(error, scope: "#{self.class.name}.errors.#{field.name}")
99
110
 
100
- errors << error
101
-
102
- field_errors[field.name] << error
111
+ errors << ValidationError.new(field, message)
103
112
  end
104
113
 
105
114
  def valid?
@@ -115,11 +124,11 @@ module Formeze
115
124
  end
116
125
 
117
126
  def errors_on?(field_name)
118
- field_errors[field_name].size > 0
127
+ errors.any? { |error| error.field_name == field_name }
119
128
  end
120
129
 
121
130
  def errors_on(field_name)
122
- field_errors[field_name]
131
+ errors.select { |error| error.field_name == field_name }
123
132
  end
124
133
 
125
134
  def to_h
@@ -129,22 +138,6 @@ module Formeze
129
138
  end
130
139
 
131
140
  alias_method :to_hash, :to_h
132
-
133
- private
134
-
135
- def field_defined?(field)
136
- if field.defined_if?
137
- instance_eval(&field.defined_if)
138
- elsif field.defined_unless?
139
- !instance_eval(&field.defined_unless)
140
- else
141
- true
142
- end
143
- end
144
-
145
- def field_errors
146
- @field_errors ||= Hash.new { |h, k| h[k] = [] }
147
- end
148
141
  end
149
142
 
150
143
  def self.label(field_name)
@@ -153,11 +146,11 @@ module Formeze
153
146
 
154
147
  def self.scrub_methods
155
148
  @scrub_methods ||= {
156
- :strip => :strip.to_proc,
157
- :upcase => :upcase.to_proc,
158
- :downcase => :downcase.to_proc,
159
- :squeeze => proc { |string| string.squeeze(' ') },
160
- :squeeze_lines => proc { |string| string.gsub(/(\r?\n)(\r?\n)(\r?\n)+/, '\\1\\2') }
149
+ strip: :strip.to_proc,
150
+ upcase: :upcase.to_proc,
151
+ downcase: :downcase.to_proc,
152
+ squeeze: proc { |string| string.squeeze(' ') },
153
+ squeeze_lines: proc { |string| string.gsub(/(\r?\n)(\r?\n)(\r?\n)+/, '\\1\\2') }
161
154
  }
162
155
  end
163
156
 
metadata CHANGED
@@ -1,16 +1,30 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: formeze
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.2.1
4
+ version: 5.0.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-03-08 00:00:00.000000000 Z
12
- dependencies: []
13
- description: Ruby gem for validating form data
11
+ date: 2024-07-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rack
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3'
27
+ description: Ruby gem for parsing and validating form data
14
28
  email:
15
29
  - mail@timcraft.com
16
30
  executables: []
@@ -22,6 +36,7 @@ files:
22
36
  - README.md
23
37
  - formeze.gemspec
24
38
  - lib/formeze.rb
39
+ - lib/formeze/block.rb
25
40
  - lib/formeze/errors.rb
26
41
  - lib/formeze/field.rb
27
42
  - lib/formeze/form.rb
@@ -45,14 +60,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
45
60
  requirements:
46
61
  - - ">="
47
62
  - !ruby/object:Gem::Version
48
- version: 2.4.0
63
+ version: 3.0.0
49
64
  required_rubygems_version: !ruby/object:Gem::Requirement
50
65
  requirements:
51
66
  - - ">="
52
67
  - !ruby/object:Gem::Version
53
68
  version: '0'
54
69
  requirements: []
55
- rubygems_version: 3.5.3
70
+ rubygems_version: 3.5.11
56
71
  signing_key:
57
72
  specification_version: 4
58
73
  summary: See description