active_record_compose 1.0.0 → 1.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9cdfb23b1af43681e6c4af3e9fed615bc3dc64873461bdee6f49c92f706f33e9
4
- data.tar.gz: 4530c0987f038605cb6dc3da229df21e8cf16af29d6ca8cf3078b1dc933cc838
3
+ metadata.gz: 76bd8c66869f7b24c2938ecb72cf2c3b9f47cdcc34a8e1c87f57329f50880ecd
4
+ data.tar.gz: f6efc5ec40f5e870064dec9928e56a3ee8a3ca07ea00bd5dcbe86f0855d440ce
5
5
  SHA512:
6
- metadata.gz: 10c37b41ff66acd993e2f5c412ed5f48213f29344a94e0ab27d40766e11f60197808d4d1b9e97118f729b4be1d272a4bdae100c8e06489c40008ac3c92f6fe61
7
- data.tar.gz: 67c4086fce4661e89188161fd6d23610bca3dcb9ad8a6e781162909d07fbf750add3ad804d8e58bd92bf4fc1305bcebd2452a4a9d924af70e56e6a0217ecf70f
6
+ metadata.gz: c0361c3c95394e550e8571ea4880545dd8e566e9a6d4c829eb494db90226c009487361ca7eed8e3691d6dbf59674e7fc2fa782b2db006b79113c95c5b229f95c
7
+ data.tar.gz: 2a2ac6e1d86640c8d7df1448fc362e459790561a8223066351cadd7ea297e456bc093a015fb8a765e755deccbd5071e342fe035180a4c222ce81584305388085
data/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [1.1.0] - 2025-11-19
4
+
5
+ * Implemented ActiveRecord-like #inspect
6
+ In activerecord's `#inspect`, the string is a list of attributes, and we have reproduced a similar format.
7
+ (https://github.com/hamajyotan/active_record_compose/pull/45)
8
+ * `.with_connection` `.lease_connection` and `.connection` are deprecated. Use `ActiveRecord::Base.with_connection` etc. instead.
9
+ (https://github.com/hamajyotan/active_record_compose/pull/46)
10
+ * refactor: Remove `ActiveRecord::Transactions` module dependency
11
+ (https://github.com/hamajyotan/active_record_compose/pull/44)
12
+
13
+ ## [1.0.1] - 2025-10-17
14
+
15
+ * Removed the private interface `composite_primary_key?`
16
+ This was previously an internal ActiveRecord dependency, but was not exposed in the release version.
17
+ (https://github.com/hamajyotan/active_record_compose/pull/39)
18
+ * Relaxed ActiveRecord dependency upper bound to < 8.2
19
+ (https://github.com/hamajyotan/active_record_compose/pull/42)
20
+
3
21
  ## [1.0.0] - 2025-09-23
4
22
 
5
23
  - drop support rails 7.0.x
data/README.md CHANGED
@@ -120,6 +120,12 @@ registration.update!(
120
120
  email_confirmation: "bar@example.com",
121
121
  terms_of_service: true,
122
122
  )
123
+ # `#update!` SQL log
124
+ # BEGIN immediate TRANSACTION
125
+ # INSERT INTO "accounts" ("created_at", "email", "name", "updated_at") VALUES (...
126
+ # INSERT INTO "profiles" ("account_id", "age", "created_at", "firstname", "lastname", ...
127
+ # COMMIT TRANSACTION
128
+
123
129
 
124
130
  # === Or, in a Rails controller with strong parameters ===
125
131
  class UserRegistrationsController < ApplicationController
@@ -0,0 +1,144 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/parameter_filter"
4
+ require_relative "attributes"
5
+
6
+ module ActiveRecordCompose
7
+ # @private
8
+ #
9
+ # It provides #inspect behavior.
10
+ # It tries to replicate the inspect format provided by ActiveRecord as closely as possible.
11
+ #
12
+ # @example
13
+ # class Model < ActiveRecordCompose::Model
14
+ # def initialize(ar_model)
15
+ # @ar_model = ar_model
16
+ # super
17
+ # end
18
+ #
19
+ # attribute :foo, :date, default: -> { Date.today }
20
+ # delegate_attribute :bar, to: :ar_model
21
+ #
22
+ # private attr_reader :ar_model
23
+ # end
24
+ #
25
+ # m = Model.new(ar_model)
26
+ # m.inspect #=> #<Model:0x00007ff0fe75fe58 foo: "2025-11-14", bar: "bar">
27
+ #
28
+ # @example
29
+ # class Model < ActiveRecordCompose::Model
30
+ # self.filter_attributes += %i[foo]
31
+ #
32
+ # # ...
33
+ # end
34
+ #
35
+ # m = Model.new(ar_model)
36
+ # m.inspect #=> #<Model:0x00007ff0fe75fe58 foo: [FILTERED], bar: "bar">
37
+ #
38
+ module Inspectable
39
+ extend ActiveSupport::Concern
40
+ include ActiveRecordCompose::Attributes
41
+
42
+ included do
43
+ self.filter_attributes = []
44
+ end
45
+
46
+ module ClassMethods
47
+ def filter_attributes
48
+ if @filter_attributes.nil?
49
+ superclass.filter_attributes # steep:ignore
50
+ else
51
+ @filter_attributes
52
+ end
53
+ end
54
+
55
+ def filter_attributes=(value)
56
+ @inspection_filter = nil
57
+ @filter_attributes = value
58
+ end
59
+
60
+ # steep:ignore:start
61
+
62
+ def inspection_filter
63
+ if @filter_attributes.nil?
64
+ superclass.inspection_filter
65
+ else
66
+ @inspection_filter ||= ActiveSupport::ParameterFilter.new(filter_attributes, mask: FILTERED_MASK)
67
+ end
68
+ end
69
+
70
+ private
71
+
72
+ def inherited(subclass)
73
+ super
74
+
75
+ subclass.class_eval do
76
+ @inspection_filter = nil
77
+ @filter_attributes ||= nil
78
+ end
79
+ end
80
+
81
+ FILTERED_MASK =
82
+ Class.new(DelegateClass(::String)) do
83
+ def pretty_print(pp)
84
+ pp.text __getobj__
85
+ end
86
+ end.new(ActiveSupport::ParameterFilter::FILTERED).freeze
87
+ private_constant :FILTERED_MASK
88
+
89
+ # steep:ignore:end
90
+ end
91
+
92
+ # Returns a formatted string representation of the record's attributes.
93
+ #
94
+ def inspect
95
+ inspection =
96
+ if @attributes
97
+ attributes.map { |k, v| "#{k}: #{format_for_inspect(k, v)}" }.join(", ")
98
+ else
99
+ "not initialized"
100
+ end
101
+
102
+ "#<#{self.class} #{inspection}>"
103
+ end
104
+
105
+ # It takes a PP and pretty prints that record.
106
+ #
107
+ def pretty_print(pp)
108
+ pp.object_address_group(self) do
109
+ if @attributes
110
+ attrs = attributes
111
+ pp.seplist(attrs.keys, proc { pp.text "," }) do |attr|
112
+ pp.breakable " "
113
+ pp.group(1) do
114
+ pp.text attr
115
+ pp.text ":"
116
+ pp.breakable
117
+ pp.text format_for_inspect(attr, attrs[attr])
118
+ end
119
+ end
120
+ else
121
+ pp.breakable " "
122
+ pp.text "not initialized"
123
+ end
124
+ end
125
+ end
126
+
127
+ private
128
+
129
+ def format_for_inspect(name, value)
130
+ return value.inspect if value.nil?
131
+
132
+ inspected_value =
133
+ if value.is_a?(String) && value.length > 50
134
+ "#{value[0, 50]}...".inspect
135
+ elsif value.is_a?(Date) || value.is_a?(Time)
136
+ %("#{value.to_fs(:inspect)}")
137
+ else
138
+ value.inspect
139
+ end
140
+
141
+ self.class.inspection_filter.filter_param(name, inspected_value)
142
+ end
143
+ end
144
+ end
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative "attributes"
4
4
  require_relative "composed_collection"
5
+ require_relative "inspectable"
5
6
  require_relative "persistence"
6
7
  require_relative "transaction_support"
7
8
  require_relative "validations"
@@ -86,6 +87,7 @@ module ActiveRecordCompose
86
87
  include ActiveRecordCompose::Persistence
87
88
  include ActiveRecordCompose::Validations
88
89
  include ActiveRecordCompose::TransactionSupport
90
+ include ActiveRecordCompose::Inspectable
89
91
 
90
92
  begin
91
93
  # @group Model Core
@@ -282,7 +284,7 @@ module ActiveRecordCompose
282
284
  # The need for such a value indicates that operations from multiple contexts are being processed.
283
285
  # If the contexts differ, we recommend separating them into different model definitions.
284
286
  #
285
- # @params [Hash] Optional parameters.
287
+ # @param options [Hash] parameters.
286
288
  # @option options [Boolean] :validate Whether to run validations.
287
289
  # This option is intended for internal use only.
288
290
  # Users should avoid explicitly passing <tt>validate: false</tt>,
@@ -351,14 +353,80 @@ module ActiveRecordCompose
351
353
  # Registers a block to be called after the transaction is rolled back.
352
354
 
353
355
  # @endgroup
354
- end
355
356
 
356
- # @group Model Core
357
+ # @!method inspect
358
+ # Returns a formatted string representation of the record's attributes.
359
+ # It tries to replicate the inspect format provided by ActiveRecord as closely as possible.
360
+ #
361
+ # @example
362
+ # class Model < ActiveRecordCompose::Model
363
+ # def initialize(ar_model)
364
+ # @ar_model = ar_model
365
+ # super
366
+ # end
367
+ #
368
+ # attribute :foo, :date, default: -> { Date.today }
369
+ # delegate_attribute :bar, to: :ar_model
370
+ #
371
+ # private attr_reader :ar_model
372
+ # end
373
+ #
374
+ # m = Model.new(ar_model)
375
+ # m.inspect #=> #<Model:0x00007ff0fe75fe58 foo: "2025-11-14", bar: "bar">
376
+ #
377
+ # @example use {.filter_attributes}
378
+ # class Model < ActiveRecordCompose::Model
379
+ # self.filter_attributes += %i[foo]
380
+ #
381
+ # # ...
382
+ # end
383
+ #
384
+ # m = Model.new(ar_model)
385
+ # m.inspect #=> #<Model:0x00007ff0fe75fe58 foo: [FILTERED], bar: "bar">
386
+ #
387
+ # @return [String] formatted string representation of the record's attributes.
388
+ # @see .filter_attributes
389
+
390
+ # @!method self.filter_attributes
391
+ # Returns columns not to expose when invoking {#inspect}.
392
+ # @return [Array<Symbol>]
393
+ # @see #inspect
394
+
395
+ # @!method self.filter_attributes=(value)
396
+ # Specify columns not to expose when invoking {#inspect}.
397
+ # @param [Array<Symbol>] value
398
+ # @see #inspect
399
+ end
357
400
 
358
401
  def initialize(attributes = {})
359
402
  super
360
403
  end
361
404
 
405
+ # Returns the ID value. This value is used when passing it to the `:model` option of `form_with`, etc.
406
+ # Normally it returns nil, but it can be overridden to delegate to the containing model.
407
+ #
408
+ # @example Redefine the id method by delegating to the containing model
409
+ # class Foo < ActiveRecordCompose::Model
410
+ # def initialize(primary_model)
411
+ # @primary_model = primary_model
412
+ # # ...
413
+ # end
414
+ #
415
+ # def id
416
+ # primary_model.id
417
+ # end
418
+ #
419
+ # private
420
+ #
421
+ # attr_reader :primary_model
422
+ # end
423
+ #
424
+ # @return [Object] ID value
425
+ #
426
+ def id = nil
427
+
428
+ # @group Model Core
429
+
362
430
  private
363
431
 
364
432
  # Returns a collection of model elements to encapsulate.
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails"
4
+ require "active_record_compose"
5
+ require "active_record/railtie"
6
+
7
+ module ActiveRecordCompose
8
+ class Railtie < Rails::Railtie
9
+ initializer "active_record_compose.set_filter_attributes", after: "active_record.set_filter_attributes" do
10
+ ActiveSupport.on_load(:active_record) do
11
+ ActiveRecordCompose::Model.filter_attributes += ActiveRecord::Base.filter_attributes
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,35 +1,130 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/module"
4
+
3
5
  module ActiveRecordCompose
4
6
  # @private
5
7
  module TransactionSupport
6
8
  extend ActiveSupport::Concern
7
- include ActiveRecord::Transactions
8
9
 
9
10
  included do
10
- # ActiveRecord::Transactions is defined so that methods such as save,
11
- # destroy and touch are wrapped with_transaction_returning_status.
12
- # However, ActiveRecordCompose::Model does not support destroy and touch, and
13
- # we want to keep these operations as undefined behavior, so we remove the definition here.
14
- undef_method :destroy, :touch
11
+ define_callbacks :commit, :rollback, :before_commit, scope: [ :kind, :name ]
15
12
  end
16
13
 
17
14
  module ClassMethods
18
- delegate :with_connection, :lease_connection, to: :ar_class
15
+ # steep:ignore:start
16
+
17
+ # @deprecated
18
+ def with_connection(...)
19
+ ActiveRecord.deprecator.warn("`with_connection` is deprecated. Use `ActiveRecord::Base.with_connection` instead.")
20
+ ActiveRecord::Base.with_connection(...)
21
+ end
22
+
23
+ # @deprecated
24
+ def lease_connection(...)
25
+ ActiveRecord.deprecator.warn("`lease_connection` is deprecated. Use `ActiveRecord::Base.lease_connection` instead.")
26
+ ActiveRecord::Base.lease_connection(...)
27
+ end
28
+
29
+ # @deprecated
30
+ def connection(...)
31
+ ActiveRecord.deprecator.warn("`connection` is deprecated. Use `ActiveRecord::Base.connection` instead.")
32
+ ActiveRecord::Base.connection(...)
33
+ end
19
34
 
20
- # In ActiveRecord, it is soft deprecated.
21
- delegate :connection, to: :ar_class
35
+ # steep:ignore:end
22
36
 
23
- def composite_primary_key? = false # steep:ignore
37
+ def before_commit(*args, &block)
38
+ set_options_for_callbacks!(args)
39
+ set_callback(:before_commit, :before, *args, &block) # steep:ignore
40
+ end
41
+
42
+ def after_commit(*args, &block)
43
+ set_options_for_callbacks!(args, prepend_option)
44
+ set_callback(:commit, :after, *args, &block) # steep:ignore
45
+ end
46
+
47
+ def after_rollback(*args, &block)
48
+ set_options_for_callbacks!(args, prepend_option)
49
+ set_callback(:rollback, :after, *args, &block) # steep:ignore
50
+ end
24
51
 
25
52
  private
26
53
 
27
- def ar_class = ActiveRecord::Base
54
+ def prepend_option
55
+ if ActiveRecord.run_after_transaction_callbacks_in_order_defined # steep:ignore
56
+ { prepend: true }
57
+ else
58
+ {}
59
+ end
60
+ end
61
+
62
+ def set_options_for_callbacks!(args, enforced_options = {})
63
+ options = args.extract_options!.merge!(enforced_options)
64
+ args << options
65
+ end
28
66
  end
29
67
 
30
- def id = nil
68
+ def save(**options) = with_transaction_returning_status { super }
69
+
70
+ def save!(**options) = with_transaction_returning_status { super }
71
+
72
+ concerning :SupportForActiveRecordConnectionAdaptersTransaction do
73
+ def trigger_transactional_callbacks? = true
74
+
75
+ def before_committed!
76
+ _run_before_commit_callbacks
77
+ end
78
+
79
+ def committed!(should_run_callbacks: true)
80
+ _run_commit_callbacks if should_run_callbacks
81
+ end
82
+
83
+ def rolledback!(force_restore_state: false, should_run_callbacks: true)
84
+ _run_rollback_callbacks if should_run_callbacks
85
+ end
86
+ end
31
87
 
32
- def trigger_transactional_callbacks? = true
33
- def restore_transaction_record_state(_force_restore_state = false) = nil
88
+ private
89
+
90
+ def with_transaction_returning_status
91
+ connection_pool.with_connection do |connection|
92
+ with_pool_transaction_isolation_level(connection) do
93
+ ensure_finalize = !connection.transaction_open?
94
+
95
+ connection.transaction do
96
+ connection.add_transaction_record(self, ensure_finalize || has_transactional_callbacks?) # steep:ignore
97
+
98
+ yield.tap { raise ActiveRecord::Rollback unless _1 }
99
+ end
100
+ end
101
+ end
102
+ end
103
+
104
+ def default_ar_class = ActiveRecord::Base
105
+
106
+ def connection_pool(ar_class: default_ar_class)
107
+ connection_specification_name = ar_class.connection_specification_name
108
+ role = ar_class.current_role
109
+ shard = ar_class.current_shard # steep:ignore
110
+ connection_handler = ar_class.connection_handler # steep:ignore
111
+ retrieve_options = { role:, shard: }
112
+ retrieve_options[:strict] = true if ActiveRecord.gem_version.release >= Gem::Version.new("7.2.0")
113
+
114
+ connection_handler.retrieve_connection_pool(connection_specification_name, **retrieve_options)
115
+ end
116
+
117
+ def with_pool_transaction_isolation_level(connection, &block)
118
+ if ActiveRecord.gem_version.release >= Gem::Version.new("8.1.0")
119
+ isolation_level = ActiveRecord.default_transaction_isolation_level # steep:ignore
120
+ connection.pool.with_pool_transaction_isolation_level(isolation_level, connection.transaction_open?, &block)
121
+ else
122
+ block.call
123
+ end
124
+ end
125
+
126
+ def has_transactional_callbacks?
127
+ _rollback_callbacks.present? || _commit_callbacks.present? || _before_commit_callbacks.present? # steep:ignore
128
+ end
34
129
  end
35
130
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecordCompose
4
- VERSION = "1.0.0"
4
+ VERSION = "1.1.0"
5
5
  end
@@ -11,3 +11,5 @@ require_relative "active_record_compose/model"
11
11
  #
12
12
  module ActiveRecordCompose
13
13
  end
14
+
15
+ require "active_record_compose/railtie" if defined?(::Rails::Railtie)
@@ -6,6 +6,8 @@ module ActiveRecordCompose
6
6
 
7
7
  def delegated_attributes: () -> Array[Delegation]
8
8
 
9
+ @attributes: untyped
10
+
9
11
  module ClassMethods : Module
10
12
  include ActiveModel::Attributes::ClassMethods
11
13
  include ActiveModel::AttributeMethods::ClassMethods
@@ -83,6 +85,29 @@ module ActiveRecordCompose
83
85
  include PackagePrivate
84
86
  end
85
87
 
88
+ module Inspectable
89
+ extend ActiveSupport::Concern
90
+ include Attributes
91
+ extend Inspectable::ClassMethods
92
+
93
+ def inspect: () -> String
94
+ def pretty_print: (untyped q) -> void
95
+
96
+ private
97
+ def format_for_inspect: (String name, untyped value) -> String
98
+
99
+ module ClassMethods
100
+ FILTERED_MASK: String
101
+
102
+ def inspection_filter: () -> ActiveSupport::ParameterFilter
103
+ def filter_attributes: () -> Array[untyped]
104
+ def filter_attributes=: (Array[untyped]) -> void
105
+
106
+ @inspection_filter: ActiveSupport::ParameterFilter?
107
+ @filter_attributes: untyped
108
+ end
109
+ end
110
+
86
111
  class Model
87
112
  include Attributes
88
113
  extend Attributes::ClassMethods
@@ -100,17 +125,30 @@ module ActiveRecordCompose
100
125
  module TransactionSupport
101
126
  extend ActiveSupport::Concern
102
127
  include ActiveRecord::Transactions
103
-
104
- def id: -> untyped
128
+ extend TransactionSupport::ClassMethods
105
129
 
106
130
  module ClassMethods
107
- def connection: -> ActiveRecord::ConnectionAdapters::AbstractAdapter
108
- def lease_connection: -> ActiveRecord::ConnectionAdapters::AbstractAdapter
109
- def with_connection: [T] () { () -> T } -> T
131
+ include ActiveSupport::Callbacks::ClassMethods
132
+
133
+ def before_commit: (*untyped) -> untyped
134
+ def after_commit: (*untyped) -> untyped
135
+ def after_rollback: (*untyped) -> untyped
110
136
 
111
137
  private
112
- def ar_class: -> singleton(ActiveRecord::Base)
138
+ def prepend_option: -> ({ prepend: true } | {})
139
+ def set_options_for_callbacks!: (untyped, ?({ prepend: true } | {})) -> untyped
113
140
  end
141
+
142
+ def save: (**untyped options) -> bool
143
+ def save!: (**untyped options) -> untyped
144
+ def _run_before_commit_callbacks: () -> untyped
145
+ def _run_commit_callbacks: () -> untyped
146
+ def _run_rollback_callbacks: () -> untyped
147
+
148
+ private
149
+ def default_ar_class: -> singleton(ActiveRecord::Base)
150
+ def connection_pool: (?ar_class: singleton(ActiveRecord::Base)) -> ActiveRecord::ConnectionAdapters::ConnectionPool
151
+ def with_pool_transaction_isolation_level: [T] (ActiveRecord::ConnectionAdapters::AbstractAdapter) { () -> T } -> T
114
152
  end
115
153
 
116
154
  module Persistence
@@ -130,6 +168,10 @@ module ActiveRecordCompose
130
168
  def raise_on_save_error_message: -> String
131
169
  end
132
170
 
171
+ class Railtie < Rails::Railtie
172
+ extend Rails::Initializable::ClassMethods
173
+ end
174
+
133
175
  module Validations : Model
134
176
  extend ActiveSupport::Concern
135
177
  extend ActiveModel::Validations::ClassMethods
@@ -72,9 +72,9 @@ module ActiveRecordCompose
72
72
  def self.after_rollback: (*callback[instance], ?if: condition[instance], ?unless: condition[instance], **untyped) ?{ () [self: instance] -> void } -> void
73
73
 
74
74
  def self.delegate_attribute: (*untyped methods, to: untyped, ?allow_nil: bool) -> untyped
75
- def self.connection: -> ActiveRecord::ConnectionAdapters::AbstractAdapter
76
- def self.lease_connection: -> ActiveRecord::ConnectionAdapters::AbstractAdapter
77
- def self.with_connection: [T] () { () -> T } -> T
75
+
76
+ def self.filter_attributes: () -> Array[untyped]
77
+ def self.filter_attributes=: (Array[untyped]) -> untyped
78
78
 
79
79
  def initialize: (?Hash[attribute_name, untyped]) -> void
80
80
  def save: (**untyped options) -> bool
@@ -82,6 +82,11 @@ module ActiveRecordCompose
82
82
  def update: (Hash[attribute_name, untyped]) -> bool
83
83
  def update!: (Hash[attribute_name, untyped]) -> untyped
84
84
 
85
+ def id: -> untyped
86
+
87
+ def inspect: () -> String
88
+ def pretty_print: (untyped q) -> void
89
+
85
90
  private
86
91
  def models: -> ComposedCollection
87
92
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_record_compose
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - hamajyotan
@@ -18,7 +18,7 @@ dependencies:
18
18
  version: '7.1'
19
19
  - - "<"
20
20
  - !ruby/object:Gem::Version
21
- version: '8.1'
21
+ version: '8.2'
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
@@ -28,7 +28,7 @@ dependencies:
28
28
  version: '7.1'
29
29
  - - "<"
30
30
  - !ruby/object:Gem::Version
31
- version: '8.1'
31
+ version: '8.2'
32
32
  description: activemodel form object pattern. it embraces multiple AR models and provides
33
33
  a transparent interface as if they were a single model.
34
34
  email:
@@ -50,8 +50,10 @@ files:
50
50
  - lib/active_record_compose/attributes/querying.rb
51
51
  - lib/active_record_compose/callbacks.rb
52
52
  - lib/active_record_compose/composed_collection.rb
53
+ - lib/active_record_compose/inspectable.rb
53
54
  - lib/active_record_compose/model.rb
54
55
  - lib/active_record_compose/persistence.rb
56
+ - lib/active_record_compose/railtie.rb
55
57
  - lib/active_record_compose/transaction_support.rb
56
58
  - lib/active_record_compose/validations.rb
57
59
  - lib/active_record_compose/version.rb