dexkit 0.8.0 → 0.9.0

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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -0
  3. data/README.md +6 -2
  4. data/gemfiles/mongoid_no_ar.gemfile +10 -0
  5. data/gemfiles/mongoid_no_ar.gemfile.lock +232 -0
  6. data/guides/llm/EVENT.md +17 -4
  7. data/guides/llm/FORM.md +2 -2
  8. data/guides/llm/OPERATION.md +22 -17
  9. data/guides/llm/QUERY.md +2 -2
  10. data/lib/dex/event/bus.rb +7 -0
  11. data/lib/dex/event/test_helpers.rb +88 -0
  12. data/lib/dex/event_test_helpers.rb +1 -86
  13. data/lib/dex/form/uniqueness_validator.rb +17 -1
  14. data/lib/dex/operation/async_proxy.rb +1 -0
  15. data/lib/dex/operation/explain.rb +11 -7
  16. data/lib/dex/operation/lock_wrapper.rb +15 -2
  17. data/lib/dex/operation/once_wrapper.rb +23 -15
  18. data/lib/dex/operation/record_backend.rb +13 -0
  19. data/lib/dex/operation/record_wrapper.rb +29 -4
  20. data/lib/dex/operation/test_helpers/assertions.rb +335 -0
  21. data/lib/dex/operation/test_helpers/execution.rb +30 -0
  22. data/lib/dex/operation/test_helpers/stubbing.rb +61 -0
  23. data/lib/dex/operation/test_helpers.rb +150 -0
  24. data/lib/dex/operation/transaction_adapter.rb +29 -68
  25. data/lib/dex/operation/transaction_wrapper.rb +10 -16
  26. data/lib/dex/query/backend.rb +13 -0
  27. data/lib/dex/query.rb +9 -5
  28. data/lib/dex/ref_type.rb +4 -0
  29. data/lib/dex/test_helpers.rb +4 -139
  30. data/lib/dex/type_coercion.rb +4 -1
  31. data/lib/dex/version.rb +1 -1
  32. data/lib/dexkit.rb +6 -5
  33. metadata +9 -5
  34. data/lib/dex/test_helpers/assertions.rb +0 -333
  35. data/lib/dex/test_helpers/execution.rb +0 -28
  36. data/lib/dex/test_helpers/stubbing.rb +0 -59
  37. /data/lib/dex/{event_test_helpers → event/test_helpers}/assertions.rb +0 -0
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dex
4
+ class Operation
5
+ module TestHelpers
6
+ def call_operation(*args, **params)
7
+ klass = _dex_resolve_subject(args)
8
+ klass.new(**params).safe.call
9
+ end
10
+
11
+ def call_operation!(*args, **params)
12
+ klass = _dex_resolve_subject(args)
13
+ klass.new(**params).call
14
+ end
15
+
16
+ private
17
+
18
+ def _dex_resolve_subject(args)
19
+ if args.first.is_a?(Class) && args.first < Dex::Operation
20
+ args.first
21
+ elsif _dex_test_subject
22
+ _dex_test_subject
23
+ else
24
+ raise ArgumentError,
25
+ "No operation class specified. Pass it as the first argument or use `testing MyOperation` in your test class."
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dex
4
+ class Operation
5
+ module TestHelpers
6
+ def stub_operation(klass, returns: nil, error: nil, &block)
7
+ raise ArgumentError, "stub_operation requires a block" unless block
8
+
9
+ opts = if error
10
+ { error: error }
11
+ else
12
+ { returns: returns }
13
+ end
14
+
15
+ Dex::Operation::TestWrapper.register_stub(klass, **opts)
16
+ yield
17
+ ensure
18
+ Dex::Operation::TestWrapper.clear_stub(klass)
19
+ end
20
+
21
+ def spy_on_operation(klass, &block)
22
+ spy = Spy.new(klass)
23
+ yield spy
24
+ spy
25
+ end
26
+
27
+ class Spy
28
+ def initialize(klass)
29
+ @klass = klass
30
+ @started_at = Dex::TestLog.size
31
+ end
32
+
33
+ def calls
34
+ Dex::TestLog.calls[@started_at..].select { |e| e.operation_class == @klass }
35
+ end
36
+
37
+ def called?
38
+ calls.any?
39
+ end
40
+
41
+ def called_once?
42
+ calls.size == 1
43
+ end
44
+
45
+ def call_count
46
+ calls.size
47
+ end
48
+
49
+ def last_result
50
+ calls.last&.result
51
+ end
52
+
53
+ def called_with?(**params)
54
+ calls.any? do |entry|
55
+ params.all? { |k, v| entry.params[k] == v }
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,150 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../test_log"
4
+
5
+ module Dex
6
+ class Operation
7
+ module TestWrapper
8
+ @_installed = false
9
+
10
+ class << self
11
+ def install!
12
+ return if @_installed
13
+
14
+ Dex::Operation.prepend(self)
15
+ @_installed = true
16
+ end
17
+
18
+ def installed?
19
+ @_installed
20
+ end
21
+
22
+ # Stub registry
23
+
24
+ def stubs
25
+ @_stubs ||= {}
26
+ end
27
+
28
+ def find_stub(klass)
29
+ stubs[klass]
30
+ end
31
+
32
+ def register_stub(klass, **options)
33
+ stubs[klass] = options
34
+ end
35
+
36
+ def clear_stub(klass)
37
+ stubs.delete(klass)
38
+ end
39
+
40
+ def clear_all_stubs!
41
+ stubs.clear
42
+ end
43
+ end
44
+
45
+ def call
46
+ stub = Dex::Operation::TestWrapper.find_stub(self.class)
47
+ return _test_apply_stub(stub) if stub
48
+
49
+ started_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
50
+ result = nil
51
+ err = nil
52
+
53
+ begin
54
+ result = super
55
+ rescue Exception => e # rubocop:disable Lint/RescueException
56
+ err = e
57
+ raise
58
+ ensure
59
+ duration = Process.clock_gettime(Process::CLOCK_MONOTONIC) - started_at
60
+ _test_record_to_log(result, err, duration)
61
+ end
62
+
63
+ result
64
+ end
65
+
66
+ private
67
+
68
+ def _test_apply_stub(stub)
69
+ if stub[:error]
70
+ err_opts = stub[:error]
71
+ case err_opts
72
+ when Symbol
73
+ raise Dex::Error.new(err_opts)
74
+ when Hash
75
+ raise Dex::Error.new(err_opts[:code], err_opts[:message], details: err_opts[:details])
76
+ end
77
+ else
78
+ stub[:returns]
79
+ end
80
+ end
81
+
82
+ def _test_safe_params
83
+ respond_to?(:to_h) ? to_h : {}
84
+ rescue
85
+ {}
86
+ end
87
+
88
+ def _test_record_to_log(result, err, duration)
89
+ safe_result = if err
90
+ dex_err = if err.is_a?(Dex::Error)
91
+ err
92
+ else
93
+ Dex::Error.new(:exception, err.message, details: { exception_class: err.class.name })
94
+ end
95
+ Dex::Operation::Err.new(dex_err)
96
+ else
97
+ Dex::Operation::Ok.new(result)
98
+ end
99
+
100
+ entry = Dex::TestLog::Entry.new(
101
+ type: "Operation",
102
+ name: self.class.name || self.class.to_s,
103
+ operation_class: self.class,
104
+ params: _test_safe_params,
105
+ result: safe_result,
106
+ duration: duration,
107
+ caller_location: caller_locations(4, 1)&.first
108
+ )
109
+ Dex::TestLog.record(entry)
110
+ end
111
+ end
112
+
113
+ module TestHelpers
114
+ extend Dex::Concern
115
+
116
+ def self.included(base)
117
+ Dex::Operation::TestWrapper.install!
118
+ super
119
+ end
120
+
121
+ def setup
122
+ super
123
+ Dex::TestLog.clear!
124
+ Dex::Operation::TestWrapper.clear_all_stubs!
125
+ end
126
+
127
+ module ClassMethods
128
+ def testing(klass)
129
+ @_dex_test_subject = klass
130
+ end
131
+
132
+ def _dex_test_subject
133
+ return @_dex_test_subject if defined?(@_dex_test_subject) && @_dex_test_subject
134
+
135
+ superclass._dex_test_subject if superclass.respond_to?(:_dex_test_subject)
136
+ end
137
+ end
138
+
139
+ private
140
+
141
+ def _dex_test_subject
142
+ self.class._dex_test_subject
143
+ end
144
+ end
145
+ end
146
+ end
147
+
148
+ require_relative "test_helpers/execution"
149
+ require_relative "test_helpers/assertions"
150
+ require_relative "test_helpers/stubbing"
@@ -3,25 +3,44 @@
3
3
  module Dex
4
4
  class Operation
5
5
  module TransactionAdapter
6
+ KNOWN_ADAPTERS = %i[active_record].freeze
7
+
6
8
  def self.for(adapter_name)
7
- case adapter_name&.to_sym
9
+ case normalize_name(adapter_name)
8
10
  when :active_record
9
11
  ActiveRecordAdapter
10
- when :mongoid
11
- MongoidAdapter
12
12
  when nil
13
13
  detect
14
- else
15
- raise ArgumentError, "Unknown transaction adapter: #{adapter_name}"
16
14
  end
17
15
  end
18
16
 
17
+ def self.known_adapters
18
+ KNOWN_ADAPTERS
19
+ end
20
+
21
+ def self.normalize_name(adapter_name)
22
+ return nil if adapter_name.nil?
23
+
24
+ normalized = adapter_name.to_sym
25
+ return normalized if KNOWN_ADAPTERS.include?(normalized)
26
+
27
+ raise ArgumentError,
28
+ "unknown transaction adapter: #{adapter_name.inspect}. " \
29
+ "Known: #{KNOWN_ADAPTERS.map(&:inspect).join(", ")}"
30
+ end
31
+
19
32
  def self.detect
20
- if defined?(ActiveRecord::Base)
21
- ActiveRecordAdapter
22
- elsif defined?(Mongoid)
23
- MongoidAdapter
24
- end
33
+ return unless defined?(ActiveRecord::Base)
34
+ return unless active_record_pool?
35
+
36
+ ActiveRecordAdapter
37
+ end
38
+
39
+ def self.active_record_pool?
40
+ !ActiveRecord::Base.connection_handler.retrieve_connection_pool(
41
+ ActiveRecord::Base.connection_specification_name,
42
+ strict: false
43
+ ).nil?
25
44
  end
26
45
 
27
46
  module ActiveRecordAdapter
@@ -44,64 +63,6 @@ module Dex
44
63
  ActiveRecord::Rollback
45
64
  end
46
65
  end
47
-
48
- module MongoidAdapter
49
- AFTER_COMMIT_KEY = :_dex_mongoid_after_commit
50
-
51
- def self.wrap(&block)
52
- unless defined?(Mongoid)
53
- raise LoadError, "Mongoid is required for transactions"
54
- end
55
-
56
- callbacks = Thread.current[AFTER_COMMIT_KEY]
57
- outermost = callbacks.nil?
58
-
59
- unless outermost
60
- snapshot = callbacks.length
61
- begin
62
- return block.call
63
- rescue rollback_exception_class
64
- callbacks.slice!(snapshot..)
65
- return nil
66
- rescue StandardError # rubocop:disable Style/RescueStandardError
67
- callbacks.slice!(snapshot..)
68
- raise
69
- end
70
- end
71
-
72
- Thread.current[AFTER_COMMIT_KEY] = []
73
- block_completed = false
74
- result = Mongoid.transaction do
75
- value = block.call
76
- block_completed = true
77
- value
78
- end
79
-
80
- if block_completed
81
- Thread.current[AFTER_COMMIT_KEY].each(&:call)
82
- end
83
-
84
- result
85
- ensure
86
- Thread.current[AFTER_COMMIT_KEY] = nil if outermost
87
- end
88
-
89
- # NOTE: Only detects transactions opened via MongoidAdapter.wrap (i.e. Dex operations).
90
- # Ambient Mongoid.transaction blocks opened outside Dex are invisible here —
91
- # the callback will fire immediately instead of deferring to the outer commit.
92
- def self.after_commit(&block)
93
- callbacks = Thread.current[AFTER_COMMIT_KEY]
94
- if callbacks
95
- callbacks << block
96
- else
97
- block.call
98
- end
99
- end
100
-
101
- def self.rollback_exception_class
102
- Mongoid::Errors::Rollback
103
- end
104
- end
105
66
  end
106
67
  end
107
68
  end
@@ -7,10 +7,10 @@ module Dex
7
7
  DEFERRED_CALLBACKS_KEY = :_dex_after_commit_queue
8
8
 
9
9
  def _transaction_wrap
10
- deferred = Thread.current[DEFERRED_CALLBACKS_KEY]
10
+ deferred = Fiber[DEFERRED_CALLBACKS_KEY]
11
11
  outermost = deferred.nil?
12
- Thread.current[DEFERRED_CALLBACKS_KEY] = [] if outermost
13
- snapshot = Thread.current[DEFERRED_CALLBACKS_KEY].length
12
+ Fiber[DEFERRED_CALLBACKS_KEY] = [] if outermost
13
+ snapshot = Fiber[DEFERRED_CALLBACKS_KEY].length
14
14
 
15
15
  result, interceptor = if _transaction_enabled?
16
16
  _transaction_run_adapter(snapshot) { yield }
@@ -23,16 +23,16 @@ module Dex
23
23
  interceptor&.rethrow!
24
24
  result
25
25
  rescue # rubocop:disable Style/RescueStandardError -- explicit for clarity
26
- Thread.current[DEFERRED_CALLBACKS_KEY]&.slice!(snapshot..)
26
+ Fiber[DEFERRED_CALLBACKS_KEY]&.slice!(snapshot..)
27
27
  raise
28
28
  ensure
29
- Thread.current[DEFERRED_CALLBACKS_KEY] = nil if outermost
29
+ Fiber[DEFERRED_CALLBACKS_KEY] = nil if outermost
30
30
  end
31
31
 
32
32
  def after_commit(&block)
33
33
  raise ArgumentError, "after_commit requires a block" unless block
34
34
 
35
- deferred = Thread.current[DEFERRED_CALLBACKS_KEY]
35
+ deferred = Fiber[DEFERRED_CALLBACKS_KEY]
36
36
  if deferred
37
37
  deferred << block
38
38
  else
@@ -40,8 +40,6 @@ module Dex
40
40
  end
41
41
  end
42
42
 
43
- TRANSACTION_KNOWN_ADAPTERS = %i[active_record mongoid].freeze
44
-
45
43
  module ClassMethods
46
44
  def transaction(enabled_or_options = nil, **options)
47
45
  validate_options!(options, %i[adapter], :transaction)
@@ -66,11 +64,7 @@ module Dex
66
64
  def _transaction_validate_adapter!(adapter)
67
65
  return if adapter.nil?
68
66
 
69
- unless TransactionWrapper::TRANSACTION_KNOWN_ADAPTERS.include?(adapter.to_sym)
70
- raise ArgumentError,
71
- "unknown transaction adapter: #{adapter.inspect}. " \
72
- "Known: #{TransactionWrapper::TRANSACTION_KNOWN_ADAPTERS.map(&:inspect).join(", ")}"
73
- end
67
+ Operation::TransactionAdapter.normalize_name(adapter)
74
68
  end
75
69
  end
76
70
 
@@ -85,7 +79,7 @@ module Dex
85
79
  end
86
80
 
87
81
  if interceptor&.error?
88
- Thread.current[DEFERRED_CALLBACKS_KEY]&.slice!(snapshot..)
82
+ Fiber[DEFERRED_CALLBACKS_KEY]&.slice!(snapshot..)
89
83
  interceptor.rethrow!
90
84
  end
91
85
 
@@ -96,7 +90,7 @@ module Dex
96
90
  interceptor = Operation::HaltInterceptor.new { yield }
97
91
 
98
92
  if interceptor.error?
99
- Thread.current[DEFERRED_CALLBACKS_KEY]&.slice!(snapshot..)
93
+ Fiber[DEFERRED_CALLBACKS_KEY]&.slice!(snapshot..)
100
94
  interceptor.rethrow!
101
95
  end
102
96
 
@@ -117,7 +111,7 @@ module Dex
117
111
  end
118
112
 
119
113
  def _transaction_flush_deferred
120
- callbacks = Thread.current[DEFERRED_CALLBACKS_KEY]
114
+ callbacks = Fiber[DEFERRED_CALLBACKS_KEY]
121
115
  return if callbacks.empty?
122
116
 
123
117
  flush = -> { callbacks.each(&:call) }
@@ -76,16 +76,29 @@ module Dex
76
76
  module_function
77
77
 
78
78
  def apply_strategy(scope, strategy, column, value)
79
+ scope = normalize_scope(scope)
79
80
  adapter_for(scope).apply(scope, strategy, column, value)
80
81
  end
81
82
 
82
83
  def adapter_for(scope)
84
+ scope = normalize_scope(scope)
85
+
83
86
  if defined?(Mongoid::Criteria) && scope.is_a?(Mongoid::Criteria)
84
87
  MongoidAdapter
85
88
  else
86
89
  ActiveRecordAdapter
87
90
  end
88
91
  end
92
+
93
+ def normalize_scope(scope)
94
+ return scope unless defined?(Mongoid::Criteria)
95
+ return scope if scope.is_a?(Mongoid::Criteria)
96
+
97
+ criteria = scope.criteria if scope.respond_to?(:criteria)
98
+ criteria.is_a?(Mongoid::Criteria) ? criteria : scope
99
+ rescue
100
+ scope
101
+ end
89
102
  end
90
103
  end
91
104
  end
data/lib/dex/query.rb CHANGED
@@ -209,9 +209,10 @@ module Dex
209
209
  end
210
210
 
211
211
  def resolve
212
- base = _evaluate_scope
212
+ base = Query::Backend.normalize_scope(_evaluate_scope)
213
213
  base = _merge_injected_scope(base)
214
214
  base = _apply_filters(base)
215
+ base = Query::Backend.normalize_scope(base)
215
216
  _apply_sort(base)
216
217
  end
217
218
 
@@ -253,19 +254,22 @@ module Dex
253
254
  def _merge_injected_scope(base)
254
255
  return base unless @_injected_scope
255
256
 
257
+ base = Query::Backend.normalize_scope(base)
258
+ injected_scope = Query::Backend.normalize_scope(@_injected_scope)
259
+
256
260
  unless base.respond_to?(:klass)
257
261
  raise ArgumentError, "Scope block must return a queryable scope (ActiveRecord relation or Mongoid criteria), got #{base.class}."
258
262
  end
259
263
 
260
- unless @_injected_scope.respond_to?(:klass)
264
+ unless injected_scope.respond_to?(:klass)
261
265
  raise ArgumentError, "Injected scope must be a queryable scope (ActiveRecord relation or Mongoid criteria), got #{@_injected_scope.class}."
262
266
  end
263
267
 
264
- unless base.klass == @_injected_scope.klass
265
- raise ArgumentError, "Scope model mismatch: expected #{base.klass}, got #{@_injected_scope.klass}."
268
+ unless base.klass == injected_scope.klass
269
+ raise ArgumentError, "Scope model mismatch: expected #{base.klass}, got #{injected_scope.klass}."
266
270
  end
267
271
 
268
- base.merge(@_injected_scope)
272
+ base.merge(injected_scope)
269
273
  end
270
274
  end
271
275
  end
data/lib/dex/ref_type.rb CHANGED
@@ -7,6 +7,10 @@ module Dex
7
7
  attr_reader :model_class, :lock
8
8
 
9
9
  def initialize(model_class, lock: false)
10
+ if lock && !model_class.respond_to?(:lock)
11
+ raise ArgumentError, "_Ref(lock: true) requires a model class that responds to .lock"
12
+ end
13
+
10
14
  @model_class = model_class
11
15
  @lock = lock
12
16
  end
@@ -1,148 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "test_log"
3
+ require_relative "operation/test_helpers"
4
+ require_relative "event/test_helpers"
4
5
 
5
6
  module Dex
6
- module TestWrapper
7
- @_installed = false
8
-
9
- class << self
10
- def install!
11
- return if @_installed
12
-
13
- Dex::Operation.prepend(self)
14
- @_installed = true
15
- end
16
-
17
- def installed?
18
- @_installed
19
- end
20
-
21
- # Stub registry
22
-
23
- def stubs
24
- @_stubs ||= {}
25
- end
26
-
27
- def find_stub(klass)
28
- stubs[klass]
29
- end
30
-
31
- def register_stub(klass, **options)
32
- stubs[klass] = options
33
- end
34
-
35
- def clear_stub(klass)
36
- stubs.delete(klass)
37
- end
38
-
39
- def clear_all_stubs!
40
- stubs.clear
41
- end
42
- end
43
-
44
- def call
45
- stub = Dex::TestWrapper.find_stub(self.class)
46
- return _test_apply_stub(stub) if stub
47
-
48
- started_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
49
- result = nil
50
- err = nil
51
-
52
- begin
53
- result = super
54
- rescue Exception => e # rubocop:disable Lint/RescueException
55
- err = e
56
- raise
57
- ensure
58
- duration = Process.clock_gettime(Process::CLOCK_MONOTONIC) - started_at
59
- _test_record_to_log(result, err, duration)
60
- end
61
-
62
- result
63
- end
64
-
65
- private
66
-
67
- def _test_apply_stub(stub)
68
- if stub[:error]
69
- err_opts = stub[:error]
70
- case err_opts
71
- when Symbol
72
- raise Dex::Error.new(err_opts)
73
- when Hash
74
- raise Dex::Error.new(err_opts[:code], err_opts[:message], details: err_opts[:details])
75
- end
76
- else
77
- stub[:returns]
78
- end
79
- end
80
-
81
- def _test_safe_params
82
- respond_to?(:to_h) ? to_h : {}
83
- rescue
84
- {}
85
- end
86
-
87
- def _test_record_to_log(result, err, duration)
88
- safe_result = if err
89
- dex_err = if err.is_a?(Dex::Error)
90
- err
91
- else
92
- Dex::Error.new(:exception, err.message, details: { exception_class: err.class.name })
93
- end
94
- Dex::Operation::Err.new(dex_err)
95
- else
96
- Dex::Operation::Ok.new(result)
97
- end
98
-
99
- entry = Dex::TestLog::Entry.new(
100
- type: "Operation",
101
- name: self.class.name || self.class.to_s,
102
- operation_class: self.class,
103
- params: _test_safe_params,
104
- result: safe_result,
105
- duration: duration,
106
- caller_location: caller_locations(4, 1)&.first
107
- )
108
- Dex::TestLog.record(entry)
109
- end
110
- end
111
-
112
7
  module TestHelpers
113
- extend Dex::Concern
114
-
115
8
  def self.included(base)
116
- Dex::TestWrapper.install!
117
- super
118
- end
119
-
120
- def setup
121
- super
122
- Dex::TestLog.clear!
123
- Dex::TestWrapper.clear_all_stubs!
124
- end
125
-
126
- module ClassMethods
127
- def testing(klass)
128
- @_dex_test_subject = klass
129
- end
130
-
131
- def _dex_test_subject
132
- return @_dex_test_subject if defined?(@_dex_test_subject) && @_dex_test_subject
133
-
134
- superclass._dex_test_subject if superclass.respond_to?(:_dex_test_subject)
135
- end
136
- end
137
-
138
- private
139
-
140
- def _dex_test_subject
141
- self.class._dex_test_subject
9
+ base.include(Dex::Operation::TestHelpers)
10
+ base.include(Dex::Event::TestHelpers)
142
11
  end
143
12
  end
144
13
  end
145
-
146
- require_relative "test_helpers/execution"
147
- require_relative "test_helpers/assertions"
148
- require_relative "test_helpers/stubbing"
@@ -64,7 +64,10 @@ module Dex
64
64
  return _serialize_value(type.type, value) if type.is_a?(Literal::Types::NilableType)
65
65
 
66
66
  ref = _find_ref_type(type)
67
- return value.id if ref
67
+ if ref
68
+ serialized_id = value.id
69
+ return serialized_id.respond_to?(:as_json) ? serialized_id.as_json : serialized_id
70
+ end
68
71
 
69
72
  value.respond_to?(:as_json) ? value.as_json : value
70
73
  end
data/lib/dex/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dex
4
- VERSION = "0.8.0"
4
+ VERSION = "0.9.0"
5
5
  end