active_record_compose 0.1.0 → 0.1.2

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: f87118b489cd061b4912c625ec2b9bdc91ad4b024e69cd6a6c45ab9c884018f3
4
- data.tar.gz: 22eec41668831a5f97d5935502d4c4d61de723b78dd2f70d2dca2bbfb4d9fa52
3
+ metadata.gz: e22d1fa89d36de73833897c0bead3b9b0405fe357aad4565d873741648a3664e
4
+ data.tar.gz: d9c543c3c0048428d22a0f591bd6086c6da991a9768d6836f84b212126ff9cab
5
5
  SHA512:
6
- metadata.gz: 180d9e45c2e18dc362d0169a22040759614882e09254c4fc8ad80ead07302c280446cab288c5042f81f33d339ab00b114d888536f691ab817063dc5dc6765cc0
7
- data.tar.gz: a4ba3164829709bbdbb38f0232b555ef210b6ce0783348317c430ffb9d1931f9b9b48bec3f28fefe757ee9ce6f9c8b3dcfb3a53ec557650eab4d6e7a8870e13f
6
+ metadata.gz: 85dcb6f44d523a9cd85905dca4bfb5557594b123872d61015fab442e4a92957bd05835f43f2113fbb44b5771e3f4c4b8856a4bbb97d3a6ef39f4a35803a5457b
7
+ data.tar.gz: 31f30d80ebbf8ab7f752330f25e5cc8777f2e4801a3648c71c5e36e1d6717e46530540b555c7efe96cb0e2f144d93206d7f181b6aedcfab79a140b1e37ce86cd
data/.rubocop.yml CHANGED
@@ -3,6 +3,9 @@ AllCops:
3
3
  NewCops: enable
4
4
  SuggestExtensions: false
5
5
 
6
+ Gemspec/DevelopmentDependencies:
7
+ Enabled: false
8
+
6
9
  Style/Documentation:
7
10
  Enabled: false
8
11
 
@@ -29,3 +32,6 @@ Layout/LineLength:
29
32
 
30
33
  Metrics/BlockLength:
31
34
  Enabled: false
35
+
36
+ Naming/MemoizedInstanceVariableName:
37
+ Enabled: false
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  ## [Unreleased]
2
2
 
3
- ## [0.1.0] - 2023-07-29
3
+ ## [0.1.2] - 2024-01-09
4
+
5
+ - fix and add doc.
6
+ - add development dependency
7
+ - avoid instance variable name conflict (`@models` to `@__models`)
8
+ - add #empty?, #clear to InnerModelCollection
9
+ - add #delete to InnerModelCollection
10
+
11
+ ## [0.1.1] - 2024-01-08
12
+
13
+ - fix 0.1.0 release date.
14
+ - add doc.
15
+ - Make it easier for application developers to work with `#models`
16
+
17
+ ## [0.1.0] - 2024-01-06
4
18
 
5
19
  - Initial release
data/README.md CHANGED
@@ -199,7 +199,7 @@ It provides a macro description that expresses access to the attributes of the A
199
199
 
200
200
  ```ruby
201
201
  class AccountRegistration < ActiveRecordCompose::Model
202
- def initialize(account)
202
+ def initialize(account, attributes = {})
203
203
  @account = account
204
204
  super(attributes)
205
205
  models.push(account)
@@ -1,6 +1,39 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecordCompose
4
+ # = Delegate \Attribute
5
+ #
6
+ # It provides a macro description that expresses access to the attributes of the AR model through delegation.
7
+ #
8
+ # class AccountRegistration < ActiveRecordCompose::Model
9
+ # def initialize(account, attributes = {})
10
+ # @account = account
11
+ # super(attributes)
12
+ # models.push(account)
13
+ # end
14
+ #
15
+ # attribute :original_attribute, :string, default: 'qux'
16
+ #
17
+ # # like a `delegate :name, :name=, to: :account`
18
+ # delegate_attribute :name, to: :account
19
+ #
20
+ # private
21
+ #
22
+ # attr_reader :account
23
+ # end
24
+ #
25
+ # account = Account.new
26
+ # account.name = 'foo'
27
+ #
28
+ # registration = AccountRegistration.new(account)
29
+ # registration.name #=> 'foo' # delegate to account#name
30
+ #
31
+ # registration.name = 'bar' # delegate to account#name=
32
+ # account.name #=> 'bar'
33
+ #
34
+ # # Attributes defined in delegate_attribute will be included in the original `#attributes`.
35
+ # registration.attributes #=> { 'original_attribute' => 'qux', 'name' => 'bar' }
36
+ #
4
37
  module DelegateAttribute
5
38
  extend ActiveSupport::Concern
6
39
 
@@ -1,40 +1,66 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'active_support/core_ext/object'
4
+
3
5
  module ActiveRecordCompose
4
6
  class InnerModel
5
- def initialize(inner_model, context: :save)
6
- @inner_model = inner_model
7
+ # @param model [Object] the model instance.
8
+ # @param context [Symbol] :save or :destroy
9
+ def initialize(model, context: :save)
10
+ @model = model
7
11
  @context = context
8
12
  end
9
13
 
10
- delegate :errors, to: :inner_model
14
+ delegate :errors, to: :model
11
15
 
16
+ # @return [Symbol] :save or :destroy
12
17
  def context
13
- @context.respond_to?(:call) ? @context.call(inner_model) : @context
18
+ ret = @context.respond_to?(:call) ? @context.call(model) : @context
19
+ ret.presence_in(%i[save destroy]) || :save
14
20
  end
15
21
 
22
+ # @return [InnerModel] self
16
23
  def save!
17
24
  case context
18
25
  when :destroy
19
- inner_model.destroy!
26
+ model.destroy!
20
27
  else
21
- inner_model.save!
28
+ model.save!
22
29
  end
23
30
  end
24
31
 
32
+ # @return [Boolean]
25
33
  def invalid?
26
34
  case context
27
35
  when :destroy
28
36
  false
29
37
  else
30
- inner_model.invalid?
38
+ model.invalid?
31
39
  end
32
40
  end
33
41
 
42
+ # @return [Boolean]
34
43
  def valid? = !invalid?
35
44
 
45
+ # Returns true if equivalent.
46
+ #
47
+ # @return [Object] other
48
+ def ==(other)
49
+ return false unless self.class == other.class
50
+ return false unless __raw_model == other.__raw_model
51
+ return false unless context == other.context
52
+
53
+ true
54
+ end
55
+
56
+ # Returns a model instance of raw, but it should
57
+ # be noted that application developers are not expected to use this interface.
58
+ #
59
+ # @return [Object] raw model instance
60
+ def __raw_model = model
61
+
36
62
  private
37
63
 
38
- attr_reader :inner_model
64
+ attr_reader :model
39
65
  end
40
66
  end
@@ -6,36 +6,86 @@ module ActiveRecordCompose
6
6
  class InnerModelCollection
7
7
  include Enumerable
8
8
 
9
- def initialize
10
- @inner_models = []
11
- end
12
-
9
+ # Enumerates model objects.
10
+ #
11
+ # @yieldparam [Object] the model instance
12
+ # @return [Enumerator] when not block given.
13
+ # @return [InnerModelCollection] self
13
14
  def each
14
15
  return enum_for(:each) unless block_given?
15
16
 
16
- inner_models.each { yield _1 }
17
+ models.each { yield _1.__raw_model }
18
+ self
19
+ end
20
+
21
+ # Appends model to collection.
22
+ #
23
+ # @param model [Object] the model instance
24
+ # @return [InnerModelCollection] self
25
+ def <<(model)
26
+ models << wrap(model, context: :save)
27
+ self
28
+ end
29
+
30
+ # Appends model to collection.
31
+ #
32
+ # @param model [Object] the model instance
33
+ # @param context [Symbol] :save or :destroy
34
+ # @return [InnerModelCollection] self
35
+ def push(model, context: :save)
36
+ models << wrap(model, context:)
17
37
  self
18
38
  end
19
39
 
20
- def <<(inner_model)
21
- inner_models << wrap(inner_model, context: :save)
40
+ # Returns true if the element exists.
41
+ #
42
+ # @return [Boolean] Returns true if the element exists
43
+ def empty? = models.empty?
44
+
45
+ # Set to empty.
46
+ #
47
+ # @return [InnerModelCollection] self
48
+ def clear
49
+ models.clear
22
50
  self
23
51
  end
24
52
 
25
- def push(inner_model, context: :save)
26
- inner_models << wrap(inner_model, context:)
53
+ # Removes the specified model from the collection.
54
+ # Returns nil if the deletion fails, self if it succeeds.
55
+ #
56
+ # @param model [Object] the model instance
57
+ # @param context [Symbol] :save or :destroy
58
+ # @return [InnerModelCollection] Successful deletion
59
+ # @return [nil] If deletion fails
60
+ def delete(model, context: :save)
61
+ wrapped = wrap(model, context:)
62
+ return nil unless models.delete(wrapped)
63
+
64
+ self
65
+ end
66
+
67
+ # Enumerates model objects, but it should be noted that
68
+ # application developers are not expected to use this interface.
69
+ #
70
+ # @yieldparam [InnerModel] rawpped model instance.
71
+ # @return [Enumerator] when not block given.
72
+ # @return [InnerModelCollection] self
73
+ def __each_by_wrapped
74
+ return enum_for(:__each_by_wrapped) unless block_given?
75
+
76
+ models.each { yield _1 }
27
77
  self
28
78
  end
29
79
 
30
80
  private
31
81
 
32
- attr_reader :inner_models
82
+ def models = @models ||= []
33
83
 
34
- def wrap(inner_model, context:)
35
- if inner_model.is_a?(ActiveRecordCompose::InnerModel)
36
- inner_model
84
+ def wrap(model, context:)
85
+ if model.is_a?(ActiveRecordCompose::InnerModel)
86
+ model
37
87
  else
38
- ActiveRecordCompose::InnerModel.new(inner_model, context:)
88
+ ActiveRecordCompose::InnerModel.new(model, context:)
39
89
  end
40
90
  end
41
91
  end
@@ -32,6 +32,31 @@ module ActiveRecordCompose
32
32
 
33
33
  def save! = save || raise(error_class_on_save_error.new('Failed to save the model.', self))
34
34
 
35
+ # Behavior is same to `#save`, but `before_create` and `after_create` hooks fires.
36
+ #
37
+ # class ComposedModel < ActiveRecordCompose::Model
38
+ # # ...
39
+ #
40
+ # before_save { puts 'before_save called!' }
41
+ # before_create { puts 'before_create called!' }
42
+ # before_update { puts 'before_update called!' }
43
+ # after_save { puts 'after_save called!' }
44
+ # after_create { puts 'after_create called!' }
45
+ # after_update { puts 'after_update called!' }
46
+ # end
47
+ #
48
+ # model = ComposedModel.new
49
+ #
50
+ # model.save
51
+ # # before_save called!
52
+ # # after_save called!
53
+ #
54
+ # model.create
55
+ # # before_save called!
56
+ # # before_create called!
57
+ # # after_create called!
58
+ # # after_save called!
59
+ #
35
60
  def create(attributes = {})
36
61
  assign_attributes(attributes)
37
62
  return false if invalid?
@@ -39,10 +64,37 @@ module ActiveRecordCompose
39
64
  save_in_transaction { run_callbacks(:save) { run_callbacks(:create) { save_models } } }
40
65
  end
41
66
 
67
+ # Behavior is same to `#create`, but raises an exception prematurely on failure.
68
+ #
42
69
  def create!(attributes = {})
43
70
  create(attributes) || raise(error_class_on_save_error.new('Failed to create the model.', self))
44
71
  end
45
72
 
73
+ # Behavior is same to `#save`, but `before_update` and `after_update` hooks fires.
74
+ #
75
+ # class ComposedModel < ActiveRecordCompose::Model
76
+ # # ...
77
+ #
78
+ # before_save { puts 'before_save called!' }
79
+ # before_create { puts 'before_create called!' }
80
+ # before_update { puts 'before_update called!' }
81
+ # after_save { puts 'after_save called!' }
82
+ # after_create { puts 'after_create called!' }
83
+ # after_update { puts 'after_update called!' }
84
+ # end
85
+ #
86
+ # model = ComposedModel.new
87
+ #
88
+ # model.save
89
+ # # before_save called!
90
+ # # after_save called!
91
+ #
92
+ # model.update
93
+ # # before_save called!
94
+ # # before_update called!
95
+ # # after_update called!
96
+ # # after_save called!
97
+ #
46
98
  def update(attributes = {})
47
99
  assign_attributes(attributes)
48
100
  return false if invalid?
@@ -50,15 +102,19 @@ module ActiveRecordCompose
50
102
  save_in_transaction { run_callbacks(:save) { run_callbacks(:update) { save_models } } }
51
103
  end
52
104
 
105
+ # Behavior is same to `#update`, but raises an exception prematurely on failure.
106
+ #
53
107
  def update!(attributes = {})
54
108
  update(attributes) || raise(error_class_on_save_error.new('Failed to update the model.', self))
55
109
  end
56
110
 
57
111
  private
58
112
 
59
- def models = @models ||= ActiveRecordCompose::InnerModelCollection.new
113
+ def models = @__models ||= ActiveRecordCompose::InnerModelCollection.new
60
114
 
61
- def validate_models = models.select { _1.invalid? }.each { errors.merge!(_1) }
115
+ def wrapped_models = models.__each_by_wrapped
116
+
117
+ def validate_models = wrapped_models.select { _1.invalid? }.each { errors.merge!(_1) }
62
118
 
63
119
  def save_in_transaction
64
120
  run_callbacks(:commit) do
@@ -74,6 +130,6 @@ module ActiveRecordCompose
74
130
  end.present?
75
131
  end
76
132
 
77
- def save_models = models.all? { _1.save! }
133
+ def save_models = wrapped_models.all? { _1.save! }
78
134
  end
79
135
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecordCompose
4
- VERSION = '0.1.0'
4
+ VERSION = '0.1.2'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_record_compose
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - hamajyotan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-01-06 00:00:00.000000000 Z
11
+ date: 2024-01-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '6.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  description: activemodel form object pattern
28
42
  email:
29
43
  - hamajyotan@gmail.com
@@ -49,6 +63,10 @@ homepage: https://github.com/active_record_compose
49
63
  licenses:
50
64
  - MIT
51
65
  metadata:
66
+ homepage_uri: https://github.com/active_record_compose
67
+ source_code_uri: https://github.com/active_record_compose
68
+ changelog_uri: https://github.com/active_record_compose/blob/main/CHANGELOG.md
69
+ documentation_uri: https://www.rubydoc.info/gems/active_record_compose/0.1.2
52
70
  rubygems_mfa_required: 'true'
53
71
  post_install_message:
54
72
  rdoc_options: []