schemacop 3.0.14 → 3.0.15

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: 45a0558e32f2c4a8e4d9db631ac874629ed7e92a378a177266a216b9e35295bd
4
- data.tar.gz: fa2da0867bf598a32ba7728b246343f6bf3849aa1ccdddf9093dcd360df2b53e
3
+ metadata.gz: 2e43238e69ebe95beaac85536a9dadbf3233bd4fa5751c7e019ca6d07aa05860
4
+ data.tar.gz: 8bafd412f0572e0c12a7c163cba48b6f6f69900ec903684dc3c9495b4913cf52
5
5
  SHA512:
6
- metadata.gz: 557da4f2765fc6a9c081b1b894f57b9acb8f3f16ff72c56589064672d060b1879b4d68a498692aeb4764b78ab1091de9b5b0a8dbd4b0ebc0e8bc401875ff5167
7
- data.tar.gz: 54748996bd7ea09dda23163c808e32300e898ceb3910d1decfaa0516d49a268d79afeb9c9292f3a3fbc6d5f128ba8de1470111c66470585772b5903580088cbb
6
+ metadata.gz: 9da291f938673bf76f55f4f4fddbf5a3355fe9c72ae30f8be8c9e224b9bea2f1a89dbcb2da8312ddbaca09dcd25da5511e904963400633d9ef193014da9ee0ec
7
+ data.tar.gz: 21793a6a7f3fc730a123f7ed8ceacde1e930620b77fef63c7e30c919a9e9ebf3372a8a5208dbdf177880e011322f529d04558bd68d363cbfd85b023a1f2ed265
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Change log
2
2
 
3
+ ## 3.0.15 (2021-10-11)
4
+
5
+ * Add support for custom string formatters
6
+
3
7
  ## 3.0.14 (2021-10-11)
4
8
 
5
9
  * Add string format `integer_list`
data/README_V3.md CHANGED
@@ -252,6 +252,24 @@ transformed into various types.
252
252
  * `symbol`
253
253
  The string can be anything and will be casted to a ruby `Symbol` object.
254
254
 
255
+ #### Custom Formats
256
+
257
+ You can also implement your custom formats or override the behavior of the
258
+ standard formats. This can be done in the initializer configuration (in case of
259
+ a Rails appliation):
260
+
261
+ ```ruby
262
+ # config/initializers/schemacop.rb
263
+ Schemacop.register_string_formatter(
264
+ :character_array, # Formatter name
265
+ pattern: /^[a-zA-Z](,[a-zA-Z])*/, # Regex pattern for validation
266
+ handler: ->(value) { value.split(',') } # Casting callback
267
+ )
268
+
269
+ # In your schema
270
+ str! :my_list, format: :character_array
271
+ ```
272
+
255
273
  #### Examples
256
274
 
257
275
  ```ruby
data/VERSION CHANGED
@@ -1 +1 @@
1
- 3.0.14
1
+ 3.0.15
@@ -7,21 +7,6 @@ module Schemacop
7
7
  format
8
8
  ].freeze
9
9
 
10
- # rubocop:disable Layout/LineLength
11
- FORMAT_PATTERNS = {
12
- date: /^([0-9]{4})-?(1[0-2]|0[1-9])-?(3[01]|0[1-9]|[12][0-9])$/,
13
- 'date-time': /^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(\.[0-9]+)?(Z|[+-](?:2[0-3]|[01][0-9]):[0-5][0-9])?$/,
14
- time: /^(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(\.[0-9]+)?(Z|[+-](?:2[0-3]|[01][0-9]):[0-5][0-9])?$/,
15
- email: URI::MailTo::EMAIL_REGEXP,
16
- boolean: /^(true|false|0|1)$/,
17
- binary: nil,
18
- symbol: nil,
19
- integer: /^-?[0-9]+$/,
20
- number: /^-?[0-9]+(\.[0-9]+)?$/,
21
- 'integer-list': /^(-?[0-9]+)(,-?[0-9]+)*$/
22
- }.freeze
23
- # rubocop:enable Layout/LineLength
24
-
25
10
  def self.allowed_options
26
11
  super + ATTRIBUTES + %i[format_options pattern allow_blank]
27
12
  end
@@ -71,8 +56,9 @@ module Schemacop
71
56
  end
72
57
 
73
58
  # Validate format #
74
- if options[:format] && FORMAT_PATTERNS.include?(options[:format])
75
- pattern = FORMAT_PATTERNS[options[:format]]
59
+ if options[:format] && Schemacop.string_formatters.include?(options[:format])
60
+ pattern = Schemacop.string_formatters[options[:format]][:pattern]
61
+
76
62
  if pattern && !super_data.match?(pattern)
77
63
  result.error "String does not match format #{options[:format].to_s.inspect}."
78
64
  elsif options[:format_options] && Node.resolve_class(options[:format])
@@ -91,23 +77,8 @@ module Schemacop
91
77
  return nil
92
78
  end
93
79
 
94
- case options[:format]
95
- when :boolean
96
- %w[true 1].include?(to_cast)
97
- when :date
98
- return Date.parse(to_cast)
99
- when :'date-time'
100
- return DateTime.parse(to_cast)
101
- when :time
102
- Time.parse(to_cast)
103
- when :integer
104
- return Integer(to_cast)
105
- when :number
106
- return Float(to_cast)
107
- when :'integer-list'
108
- return to_cast.split(',').map(&:to_i)
109
- when :symbol
110
- return to_cast.to_sym
80
+ if (handler = Schemacop.string_formatters.dig(options[:format], :handler))
81
+ return handler.call(to_cast)
111
82
  else
112
83
  return to_cast
113
84
  end
@@ -122,7 +93,7 @@ module Schemacop
122
93
  end
123
94
 
124
95
  def validate_self
125
- if options.include?(:format) && !FORMAT_PATTERNS.include?(options[:format])
96
+ if options.include?(:format) && !Schemacop.string_formatters.include?(options[:format])
126
97
  fail "Format #{options[:format].to_s.inspect} is not supported."
127
98
  end
128
99
 
data/lib/schemacop.rb CHANGED
@@ -13,6 +13,80 @@ module Schemacop
13
13
  mattr_accessor :default_schema_version
14
14
  self.default_schema_version = 3
15
15
 
16
+ mattr_accessor :string_formatters
17
+ self.string_formatters = {}
18
+
19
+ def self.register_string_formatter(name, pattern:, handler:)
20
+ name = name.to_s.dasherize.to_sym
21
+
22
+ string_formatters[name] = {
23
+ pattern: pattern,
24
+ handler: handler
25
+ }
26
+ end
27
+
28
+ register_string_formatter(
29
+ :date,
30
+ pattern: /^([0-9]{4})-?(1[0-2]|0[1-9])-?(3[01]|0[1-9]|[12][0-9])$/,
31
+ handler: ->(value) { Date.parse(value) }
32
+ )
33
+
34
+ # rubocop: disable Layout/LineLength
35
+ register_string_formatter(
36
+ :'date-time',
37
+ pattern: /^(-?(?:[1-9][0-9]*)?[0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(\.[0-9]+)?(Z|[+-](?:2[0-3]|[01][0-9]):[0-5][0-9])?$/,
38
+ handler: ->(value) { DateTime.parse(value) }
39
+ )
40
+ # rubocop: enable Layout/LineLength
41
+
42
+ register_string_formatter(
43
+ :time,
44
+ pattern: /^(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])(\.[0-9]+)?(Z|[+-](?:2[0-3]|[01][0-9]):[0-5][0-9])?$/,
45
+ handler: ->(value) { Time.parse(value) }
46
+ )
47
+
48
+ register_string_formatter(
49
+ :email,
50
+ pattern: URI::MailTo::EMAIL_REGEXP,
51
+ handler: ->(value) { value }
52
+ )
53
+
54
+ register_string_formatter(
55
+ :boolean,
56
+ pattern: /^(true|false|0|1)$/,
57
+ handler: ->(value) { %w[true 1].include?(value) }
58
+ )
59
+
60
+ register_string_formatter(
61
+ :binary,
62
+ pattern: nil,
63
+ handler: ->(value) { value }
64
+ )
65
+
66
+ register_string_formatter(
67
+ :symbol,
68
+ pattern: nil,
69
+ handler: ->(value) { value.to_sym }
70
+ )
71
+
72
+ register_string_formatter(
73
+ :integer,
74
+ pattern: /^-?[0-9]+$/,
75
+ handler: ->(value) { Integer(value) }
76
+ )
77
+
78
+ register_string_formatter(
79
+ :number,
80
+ pattern: /^-?[0-9]+(\.[0-9]+)?$/,
81
+ handler: ->(value) { Float(value) }
82
+ )
83
+
84
+ register_string_formatter(
85
+ :'integer-list',
86
+ pattern: /^(-?[0-9]+)(,-?[0-9]+)*$/,
87
+ handler: ->(value) { value.split(',').map(&:to_i) }
88
+ )
89
+
16
90
  def self.with_context(context)
17
91
  prev_context = Thread.current[CONTEXT_THREAD_KEY]
18
92
  Thread.current[CONTEXT_THREAD_KEY] = context
data/schemacop.gemspec CHANGED
@@ -1,9 +1,9 @@
1
1
  # -*- encoding: utf-8 -*-
2
- # stub: schemacop 3.0.14 ruby lib
2
+ # stub: schemacop 3.0.15 ruby lib
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "schemacop".freeze
6
- s.version = "3.0.14"
6
+ s.version = "3.0.15"
7
7
 
8
8
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
9
9
  s.require_paths = ["lib".freeze]
@@ -158,6 +158,14 @@ module Schemacop
158
158
  assert_cast('2018-11-13T20:20:39+00:00', DateTime.new(2018, 11, 13, 20, 20, 39))
159
159
  end
160
160
 
161
+ def test_format_time
162
+ schema :string, format: :time
163
+ assert_json(type: :string, format: :time)
164
+ assert_cast '20:30:39+00:00', Time.strptime('20:30:39+00:00', '%H:%M:%S%z')
165
+
166
+ assert_cast nil, nil
167
+ end
168
+
161
169
  def test_format_email
162
170
  schema :string, format: :email
163
171
 
@@ -183,6 +191,17 @@ module Schemacop
183
191
  assert_cast('john.doe@example.com', 'john.doe@example.com')
184
192
  end
185
193
 
194
+ def test_format_boolean
195
+ schema :string, format: :boolean
196
+
197
+ assert_json(type: :string, format: :boolean)
198
+
199
+ assert_cast 'true', true
200
+ assert_cast 'false', false
201
+
202
+ assert_cast nil, nil
203
+ end
204
+
186
205
  def test_format_symbol
187
206
  schema :string, format: :symbol
188
207
 
@@ -200,6 +219,28 @@ module Schemacop
200
219
  assert_cast('039n23$g- sfk3/', :'039n23$g- sfk3/')
201
220
  end
202
221
 
222
+ def test_format_integer
223
+ schema :string, format: :integer
224
+
225
+ assert_json(type: :string, format: :integer)
226
+
227
+ assert_validation '23425'
228
+ assert_validation '-23425'
229
+
230
+ assert_validation 12_312 do
231
+ error '/', StringNodeTest.invalid_type_error(Integer)
232
+ end
233
+
234
+ assert_validation '24.32' do
235
+ error '/', 'String does not match format "integer".'
236
+ end
237
+
238
+ assert_cast(nil, nil)
239
+ assert_cast('2234', 2234)
240
+ assert_cast('-1', -1)
241
+ assert_cast('-0', 0)
242
+ end
243
+
203
244
  def test_format_integer_list
204
245
  schema :string, format: :integer_list
205
246
 
@@ -224,6 +265,34 @@ module Schemacop
224
265
  assert_cast '-1', [-1]
225
266
  end
226
267
 
268
+ def test_format_custom
269
+ Schemacop.register_string_formatter(
270
+ :integer_tuple_list,
271
+ pattern: /^(-?[0-9]+):(-?[0-9]+)(,(-?[0-9]+):(-?[0-9]+))*$/,
272
+ handler: proc do |value|
273
+ value.split(',').map { |t| t.split(':').map(&:to_i) }
274
+ end
275
+ )
276
+
277
+ schema :string, format: :integer_tuple_list
278
+
279
+ assert_json(type: :string, format: :'integer-tuple-list')
280
+
281
+ assert_validation '1:5,4:2,-4:4,4:-1,0:0'
282
+ assert_validation '-1:5'
283
+
284
+ assert_validation 234 do
285
+ error '/', StringNodeTest.invalid_type_error(Integer)
286
+ end
287
+
288
+ assert_validation 'sd sfdij soidf' do
289
+ error '/', 'String does not match format "integer-tuple-list".'
290
+ end
291
+
292
+ assert_cast nil, nil
293
+ assert_cast '1:2,3:4,5:-6', [[1, 2], [3, 4], [5, -6]]
294
+ end
295
+
227
296
  def test_enum
228
297
  schema :string, enum: ['foo', 'some string', 'some other string', 42]
229
298
 
@@ -248,33 +317,6 @@ module Schemacop
248
317
  end
249
318
  end
250
319
 
251
- def test_boolean_casting
252
- schema :string, format: :boolean
253
-
254
- assert_json(type: :string, format: :boolean)
255
-
256
- assert_cast 'true', true
257
- assert_cast 'false', false
258
-
259
- assert_cast nil, nil
260
- end
261
-
262
- def test_time_casting
263
- schema :string, format: :time
264
- assert_json(type: :string, format: :time)
265
- assert_cast '20:30:39+00:00', Time.strptime('20:30:39+00:00', '%H:%M:%S%z')
266
-
267
- assert_cast nil, nil
268
- end
269
-
270
- def test_date_casting
271
- schema :string, format: :date
272
- assert_json(type: :string, format: :date)
273
- assert_cast '2018-11-13', Date.new(2018, 11, 13)
274
-
275
- assert_cast nil, nil
276
- end
277
-
278
320
  def test_date_time_casting
279
321
  schema :string, format: :date_time
280
322
  assert_json(type: :string, format: :'date-time')
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: schemacop
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.14
4
+ version: 3.0.15
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sitrox