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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c4fef2991f9d46da2fdbe6447c7e128042e8f60d6696a562f29b38e7cab2755d
4
- data.tar.gz: 4bd93645f7a6efbbaccfe2fd3c2049b0f6b4310cd959a9f75fc6c4d7a23d8e25
3
+ metadata.gz: 12ff37dae5fd328aaa0299e09ff2fe4827b98a3f59e0b4b6545a4b1176f834cf
4
+ data.tar.gz: 95609b587580e37a859ca906dc9a7c767dc589a32a58a3a86f9c2b9e70a2c27c
5
5
  SHA512:
6
- metadata.gz: cc3b421c2c645c7ca205efa89104d16805320c3a9407ad3aa34330545d62e6037f713998bb483da8bf59934b3e2b090e5add1254fe19f04f888457d4ac12aa38
7
- data.tar.gz: ee0734400bd7df20adadbbed1e565419995dca36f66e4f7d65664dac5c9ffb0a03041dc658842e307d3520825464cae6019b0e29fa843d01641e99eb35fc3bb2
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, _options = {}|
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 = {}|
@@ -4,7 +4,7 @@ module Subroutine
4
4
 
5
5
  MAJOR = 4
6
6
  MINOR = 1
7
- PATCH = 3
7
+ PATCH = 5
8
8
  PRE = nil
9
9
 
10
10
  VERSION = [MAJOR, MINOR, PATCH, PRE].compact.join(".")
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 test_time_inputs
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 123456, op.time_input.usec
310
+ assert_equal 0, op.time_input.usec
298
311
 
299
- time = Time.at(1678741605.123456)
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 time, op.time_input
302
- assert_equal time.object_id, op.time_input.object_id
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:24Z', op.iso_time_input
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:24Z')
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:24Z', op.iso_time_input
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.3
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-05 00:00:00.000000000 Z
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.4.19
215
+ rubygems_version: 3.5.23
216
216
  signing_key:
217
217
  specification_version: 4
218
218
  summary: Feature-driven operation objects.