schemacop 3.0.14 → 3.0.15

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: 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