subroutine 4.1.3 → 4.1.5
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 +4 -4
- data/CHANGELOG.MD +10 -0
- data/lib/subroutine/type_caster.rb +13 -4
- data/lib/subroutine/version.rb +1 -1
- data/lib/subroutine.rb +11 -0
- data/test/subroutine/type_caster_test.rb +139 -8
- data/test/support/ops.rb +1 -0
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 12ff37dae5fd328aaa0299e09ff2fe4827b98a3f59e0b4b6545a4b1176f834cf
|
4
|
+
data.tar.gz: 95609b587580e37a859ca906dc9a7c767dc589a32a58a3a86f9c2b9e70a2c27c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8d29b8e4f5cb1511974c339f28552f85da427bd146a273783abab7951a059b402d96e3fb53c52f8aa64d50bc1f174b103f5c183b4b35f94fc7f570de8e2330ba
|
7
|
+
data.tar.gz: c144a73f6ff5dd51ad70ae3b1bc3d11aee020e9bc78a69805c40f1809e4597a9d51470d6974ef3259ac5975cde33d1c2beb5437985be12c116493d135b1e23a6
|
data/CHANGELOG.MD
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
## Subroutine 4.1.4
|
2
|
+
|
3
|
+
Fields using the time/timestamp/datetime caster will now default back to the old behavior, and use a `precision:` option to opt-in to the new behavior introduced in `v4.1.1`.
|
4
|
+
|
5
|
+
`precision: :seconds` will retain the old behavior of always parsing to a new Time object
|
6
|
+
with floored sub-second precision, but applied more forcefully than before as it would have parsed whatever you passed to it. (This is the default, now.)
|
7
|
+
|
8
|
+
`precision: :high` will now use the new functionality of re-using Time objects when they
|
9
|
+
are passed in, or if not parsing exactly the provided string as to a Time object.
|
10
|
+
|
1
11
|
## Subroutine 4.1.1
|
2
12
|
|
3
13
|
Fields using the time/timestamp/datetime caster will now return exactly the passed in value
|
@@ -4,11 +4,15 @@ require 'date'
|
|
4
4
|
require 'time'
|
5
5
|
require 'bigdecimal'
|
6
6
|
require 'securerandom'
|
7
|
+
require 'active_support/json'
|
8
|
+
require 'active_support/core_ext/date_time/acts_like'
|
9
|
+
require 'active_support/core_ext/date_time/calculations'
|
7
10
|
require 'active_support/core_ext/object/acts_like'
|
8
11
|
require 'active_support/core_ext/object/blank'
|
9
12
|
require 'active_support/core_ext/object/try'
|
10
13
|
require 'active_support/core_ext/array/wrap'
|
11
14
|
require 'active_support/core_ext/time/acts_like'
|
15
|
+
require 'active_support/core_ext/time/calculations'
|
12
16
|
|
13
17
|
module Subroutine
|
14
18
|
module TypeCaster
|
@@ -108,7 +112,7 @@ end
|
|
108
112
|
t ||= value if value.is_a?(::Time)
|
109
113
|
t ||= value if value.try(:acts_like?, :time)
|
110
114
|
t ||= ::Time.parse(String(value))
|
111
|
-
t.utc.iso8601
|
115
|
+
t.utc.iso8601(::ActiveSupport::JSON::Encoding.time_precision)
|
112
116
|
end
|
113
117
|
|
114
118
|
::Subroutine::TypeCaster.register :date do |value, _options = {}|
|
@@ -117,14 +121,19 @@ end
|
|
117
121
|
::Date.parse(String(value))
|
118
122
|
end
|
119
123
|
|
120
|
-
::Subroutine::TypeCaster.register :time, :timestamp, :datetime do |value,
|
124
|
+
::Subroutine::TypeCaster.register :time, :timestamp, :datetime do |value, options = {}|
|
121
125
|
next nil unless value.present?
|
122
126
|
|
123
|
-
if value.try(:acts_like?, :time)
|
124
|
-
value
|
127
|
+
value = if value.try(:acts_like?, :time)
|
128
|
+
value.to_time
|
125
129
|
else
|
126
130
|
::Time.parse(String(value))
|
127
131
|
end
|
132
|
+
|
133
|
+
# High precision must be opted into. The original implementation is to set usec:0
|
134
|
+
next value if options[:precision] == :high || ::Subroutine.preserve_time_precision?
|
135
|
+
|
136
|
+
value.change(usec: 0)
|
128
137
|
end
|
129
138
|
|
130
139
|
::Subroutine::TypeCaster.register :hash, :object, :hashmap, :dict do |value, _options = {}|
|
data/lib/subroutine/version.rb
CHANGED
data/lib/subroutine.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_model"
|
4
|
+
require "active_support"
|
4
5
|
require "active_support/concern"
|
5
6
|
require "active_support/core_ext/hash/indifferent_access"
|
6
7
|
require "active_support/core_ext/module/redefine_method"
|
@@ -33,4 +34,14 @@ module Subroutine
|
|
33
34
|
@inheritable_field_options ||= %i[mass_assignable field_reader field_writer groups aka]
|
34
35
|
end
|
35
36
|
|
37
|
+
def self.preserve_time_precision=(bool)
|
38
|
+
@preserve_time_precision = !!bool
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.preserve_time_precision?
|
42
|
+
return !!@preserve_time_precision if defined?(@preserve_time_precision)
|
43
|
+
|
44
|
+
false
|
45
|
+
end
|
46
|
+
|
36
47
|
end
|
@@ -269,7 +269,7 @@ module Subroutine
|
|
269
269
|
assert_nil op.date_input
|
270
270
|
end
|
271
271
|
|
272
|
-
def
|
272
|
+
def test_time_inputs__with_seconds_precision
|
273
273
|
op.time_input = nil
|
274
274
|
assert_nil op.time_input
|
275
275
|
|
@@ -284,22 +284,153 @@ module Subroutine
|
|
284
284
|
assert_equal 0, op.time_input.min
|
285
285
|
assert_equal 0, op.time_input.sec
|
286
286
|
|
287
|
+
op.time_input = ::DateTime.new(2022, 12, 22)
|
288
|
+
assert_equal ::Time, op.time_input.class
|
289
|
+
refute_equal ::DateTime, op.time_input.class
|
290
|
+
|
291
|
+
assert_equal 0, op.time_input.utc_offset
|
292
|
+
assert_equal 2022, op.time_input.year
|
293
|
+
assert_equal 12, op.time_input.month
|
294
|
+
assert_equal 22, op.time_input.day
|
295
|
+
assert_equal 0, op.time_input.hour
|
296
|
+
assert_equal 0, op.time_input.min
|
297
|
+
assert_equal 0, op.time_input.sec
|
298
|
+
|
287
299
|
op.time_input = '2023-05-05T10:00:30.123456Z'
|
288
300
|
assert_equal ::Time, op.time_input.class
|
289
301
|
refute_equal ::DateTime, op.time_input.class
|
290
302
|
|
303
|
+
assert_equal 0, op.time_input.utc_offset
|
291
304
|
assert_equal 2023, op.time_input.year
|
292
305
|
assert_equal 5, op.time_input.month
|
293
306
|
assert_equal 5, op.time_input.day
|
294
307
|
assert_equal 10, op.time_input.hour
|
295
308
|
assert_equal 0, op.time_input.min
|
296
309
|
assert_equal 30, op.time_input.sec
|
297
|
-
assert_equal
|
310
|
+
assert_equal 0, op.time_input.usec
|
298
311
|
|
299
|
-
|
312
|
+
op.time_input = '2023-05-05T10:00:30Z'
|
313
|
+
assert_equal ::Time, op.time_input.class
|
314
|
+
assert_equal 0, op.time_input.utc_offset
|
315
|
+
assert_equal 2023, op.time_input.year
|
316
|
+
assert_equal 5, op.time_input.month
|
317
|
+
assert_equal 5, op.time_input.day
|
318
|
+
assert_equal 10, op.time_input.hour
|
319
|
+
assert_equal 0, op.time_input.min
|
320
|
+
assert_equal 30, op.time_input.sec
|
321
|
+
assert_equal 0, op.time_input.usec
|
322
|
+
|
323
|
+
op.time_input = '2024-11-11T16:42:23.246+0100'
|
324
|
+
assert_equal ::Time, op.time_input.class
|
325
|
+
assert_equal 3600, op.time_input.utc_offset
|
326
|
+
assert_equal 2024, op.time_input.year
|
327
|
+
assert_equal 11, op.time_input.month
|
328
|
+
assert_equal 11, op.time_input.day
|
329
|
+
assert_equal 16, op.time_input.hour
|
330
|
+
assert_equal 42, op.time_input.min
|
331
|
+
assert_equal 23, op.time_input.sec
|
332
|
+
assert_equal 0, op.time_input.usec
|
333
|
+
|
334
|
+
time = Time.at(1678741605.123456).utc
|
335
|
+
op.time_input = time
|
336
|
+
refute_equal time, op.time_input
|
337
|
+
refute_equal time.object_id, op.time_input.object_id
|
338
|
+
assert_equal 2023, op.time_input.year
|
339
|
+
assert_equal 3, op.time_input.month
|
340
|
+
assert_equal 13, op.time_input.day
|
341
|
+
assert_equal 21, op.time_input.hour
|
342
|
+
assert_equal 6, op.time_input.min
|
343
|
+
assert_equal 45, op.time_input.sec
|
344
|
+
assert_equal 0, op.time_input.usec
|
345
|
+
end
|
346
|
+
|
347
|
+
def test_time_inputs__with_preserve_time_precision
|
348
|
+
Subroutine.stubs(:preserve_time_precision?).returns(true)
|
349
|
+
|
350
|
+
time = Time.at(1678741605.123456).utc
|
300
351
|
op.time_input = time
|
301
|
-
assert_equal
|
302
|
-
assert_equal
|
352
|
+
assert_equal 2023, op.time_input.year
|
353
|
+
assert_equal 3, op.time_input.month
|
354
|
+
assert_equal 13, op.time_input.day
|
355
|
+
assert_equal 21, op.time_input.hour
|
356
|
+
assert_equal 6, op.time_input.min
|
357
|
+
assert_equal 45, op.time_input.sec
|
358
|
+
assert_equal 123456, op.time_input.usec
|
359
|
+
end
|
360
|
+
|
361
|
+
def test_time_inputs__with_high_precision
|
362
|
+
op.precise_time_input = nil
|
363
|
+
assert_nil op.precise_time_input
|
364
|
+
|
365
|
+
op.precise_time_input = '2022-12-22'
|
366
|
+
assert_equal ::Time, op.precise_time_input.class
|
367
|
+
refute_equal ::DateTime, op.precise_time_input.class
|
368
|
+
|
369
|
+
assert_equal 2022, op.precise_time_input.year
|
370
|
+
assert_equal 12, op.precise_time_input.month
|
371
|
+
assert_equal 22, op.precise_time_input.day
|
372
|
+
assert_equal 0, op.precise_time_input.hour
|
373
|
+
assert_equal 0, op.precise_time_input.min
|
374
|
+
assert_equal 0, op.precise_time_input.sec
|
375
|
+
|
376
|
+
op.precise_time_input = ::DateTime.new(2022, 12, 22)
|
377
|
+
assert_equal ::Time, op.precise_time_input.class
|
378
|
+
refute_equal ::DateTime, op.precise_time_input.class
|
379
|
+
|
380
|
+
assert_equal 0, op.precise_time_input.utc_offset
|
381
|
+
assert_equal 2022, op.precise_time_input.year
|
382
|
+
assert_equal 12, op.precise_time_input.month
|
383
|
+
assert_equal 22, op.precise_time_input.day
|
384
|
+
assert_equal 0, op.precise_time_input.hour
|
385
|
+
assert_equal 0, op.precise_time_input.min
|
386
|
+
assert_equal 0, op.precise_time_input.sec
|
387
|
+
|
388
|
+
op.precise_time_input = '2023-05-05T10:00:30.123456Z'
|
389
|
+
assert_equal ::Time, op.precise_time_input.class
|
390
|
+
refute_equal ::DateTime, op.precise_time_input.class
|
391
|
+
|
392
|
+
assert_equal 0, op.precise_time_input.utc_offset
|
393
|
+
assert_equal 2023, op.precise_time_input.year
|
394
|
+
assert_equal 5, op.precise_time_input.month
|
395
|
+
assert_equal 5, op.precise_time_input.day
|
396
|
+
assert_equal 10, op.precise_time_input.hour
|
397
|
+
assert_equal 0, op.precise_time_input.min
|
398
|
+
assert_equal 30, op.precise_time_input.sec
|
399
|
+
assert_equal 123456, op.precise_time_input.usec
|
400
|
+
|
401
|
+
op.precise_time_input = '2023-05-05T10:00:30Z'
|
402
|
+
assert_equal ::Time, op.precise_time_input.class
|
403
|
+
assert_equal 0, op.precise_time_input.utc_offset
|
404
|
+
assert_equal 2023, op.precise_time_input.year
|
405
|
+
assert_equal 5, op.precise_time_input.month
|
406
|
+
assert_equal 5, op.precise_time_input.day
|
407
|
+
assert_equal 10, op.precise_time_input.hour
|
408
|
+
assert_equal 0, op.precise_time_input.min
|
409
|
+
assert_equal 30, op.precise_time_input.sec
|
410
|
+
assert_equal 0, op.precise_time_input.usec
|
411
|
+
|
412
|
+
op.precise_time_input = '2024-11-11T16:42:23.246+0100'
|
413
|
+
assert_equal ::Time, op.precise_time_input.class
|
414
|
+
assert_equal 3600, op.precise_time_input.utc_offset
|
415
|
+
assert_equal 2024, op.precise_time_input.year
|
416
|
+
assert_equal 11, op.precise_time_input.month
|
417
|
+
assert_equal 11, op.precise_time_input.day
|
418
|
+
assert_equal 16, op.precise_time_input.hour
|
419
|
+
assert_equal 42, op.precise_time_input.min
|
420
|
+
assert_equal 23, op.precise_time_input.sec
|
421
|
+
assert_equal 246000, op.precise_time_input.usec
|
422
|
+
|
423
|
+
time = Time.at(1678741605.123456).utc
|
424
|
+
op.precise_time_input = time
|
425
|
+
assert_equal time, op.precise_time_input
|
426
|
+
assert_equal time.object_id, op.precise_time_input.object_id
|
427
|
+
assert_equal 2023, op.precise_time_input.year
|
428
|
+
assert_equal 3, op.precise_time_input.month
|
429
|
+
assert_equal 13, op.precise_time_input.day
|
430
|
+
assert_equal 21, op.precise_time_input.hour
|
431
|
+
assert_equal 6, op.precise_time_input.min
|
432
|
+
assert_equal 45, op.precise_time_input.sec
|
433
|
+
assert_equal 123456, op.precise_time_input.usec
|
303
434
|
end
|
304
435
|
|
305
436
|
def test_iso_date_inputs
|
@@ -321,11 +452,11 @@ module Subroutine
|
|
321
452
|
|
322
453
|
op.iso_time_input = '2022-12-22T10:30:24Z'
|
323
454
|
assert_equal ::String, op.iso_time_input.class
|
324
|
-
assert_equal '2022-12-22T10:30:
|
455
|
+
assert_equal '2022-12-22T10:30:24.000Z', op.iso_time_input
|
325
456
|
|
326
|
-
op.iso_time_input = Time.parse('2022-12-22T10:30:
|
457
|
+
op.iso_time_input = Time.parse('2022-12-22T10:30:24.123456Z')
|
327
458
|
assert_equal ::String, op.iso_time_input.class
|
328
|
-
assert_equal '2022-12-22T10:30:
|
459
|
+
assert_equal '2022-12-22T10:30:24.123Z', op.iso_time_input
|
329
460
|
end
|
330
461
|
|
331
462
|
def test_file_inputs
|
data/test/support/ops.rb
CHANGED
@@ -173,6 +173,7 @@ class TypeCastOp < ::Subroutine::Op
|
|
173
173
|
boolean :boolean_input
|
174
174
|
date :date_input
|
175
175
|
time :time_input, default: -> { Time.now }
|
176
|
+
time :precise_time_input, precision: :high
|
176
177
|
iso_date :iso_date_input
|
177
178
|
iso_time :iso_time_input
|
178
179
|
object :object_input
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: subroutine
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.1.
|
4
|
+
version: 4.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Nelson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-11-
|
11
|
+
date: 2024-11-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|
@@ -212,7 +212,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
212
212
|
- !ruby/object:Gem::Version
|
213
213
|
version: '0'
|
214
214
|
requirements: []
|
215
|
-
rubygems_version: 3.
|
215
|
+
rubygems_version: 3.5.23
|
216
216
|
signing_key:
|
217
217
|
specification_version: 4
|
218
218
|
summary: Feature-driven operation objects.
|