store_model 2.0.1 → 2.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: 5678f8d809e7ed7745c5bd21b10bb455ecef32fac89cbc0fe9d5928c99dcf7c8
4
- data.tar.gz: 13fd4acba36ff7b96d8111043de74e94442f63a02d718571fdda3682521e0dab
3
+ metadata.gz: 61353c039b8274db76c67d73081ce718a5446e392df4bd5987f4dde023d5b244
4
+ data.tar.gz: 40f5011957af3787650b1f3565468fc0b59a4c1a2e090735e8cbf9926e8b622e
5
5
  SHA512:
6
- metadata.gz: e57e9ebc4b7e6547c75c8b0375742194eafa6104d2abfd2b55e9e657c64e2af935e99505bb712e8e1ef3d38123f4852d89603f10bfacfa620ec2cb38751995b6
7
- data.tar.gz: a2c9e761568bc100697c7240762ed27aac4d8ccde2429f4e037357121351f120a1b9a17909f6670f0215691f909032131979ebbe0001d67bba1d2cadeebaadeb
6
+ metadata.gz: 5e3065c9503681ac881fe2e2b2194bfc9f9c959c66e4a6430b47160ff4487cc1bdc6f4408dfc72e216f94eae729ce8462e09ae12364e3e37ef5341769588a796
7
+ data.tar.gz: 427cb2dab39fd79ef74fb3b1a1e48f89003fbb2e00ca25488cea257cc9f87c9aad5eedb4c469252a605e5932420ad6e257898e85ab7622c904c7cec99c75af73
data/README.md CHANGED
@@ -76,7 +76,49 @@ product.save
76
76
  _Usage note: Rails and assigning Arrays/Hashes to records_
77
77
 
78
78
  - Assigned attributes must be a String, Hash, Array of Hashes, or StoreModel. For example, if the attributes are coming from a controller, be sure to convert any ActionController::Parameters as needed.
79
- - Any changes made to a StoreModel instance requires the attribute be re-assigned; Rails doesn't track mutations of objects. For example: `self.my_stored_models = my_stored_models.map(&:as_json)`
79
+ - Any changes made to a StoreModel instance requires the attribute be flagged as dirty, either by reassignment (`self.my_stored_models = my_stored_models.map(&:as_json)`) or by `will_change!` (`self.my_stored_models_will_change!`)
80
+ - Mixing `StoreModel::NestedAttributes` into your model will allow you to use `accepts_nested_attributes_for` in the same way as ActiveRecord.
81
+
82
+ ```ruby
83
+ class Supplier < ActiveRecord::Base
84
+ include StoreModel::NestedAttributes
85
+
86
+ has_many :bicycles, dependent: :destroy
87
+
88
+ attribute :products, Product.to_array_type
89
+
90
+ accepts_nested_attributes_for :bicycles, :products, allow_destroy: true
91
+ end
92
+ ```
93
+
94
+ This will allow the form builders to work their magic:
95
+
96
+ ```erb
97
+ <%= form_with model: @supplier do |form| %>
98
+ <%= form.fields_for :products do |product_fields| %>
99
+ <%= product_fields.text_field :name %>
100
+ <% end %>
101
+ <% end %>
102
+ ```
103
+
104
+ Resulting in:
105
+ ```html
106
+ <input type="text" name="supplier[products_attributes][0][name]" id="supplier_products_attributes_0_name">
107
+ ```
108
+
109
+ In the controller:
110
+ ```ruby
111
+ def create
112
+ @supplier = Supplier.new(supplier_params)
113
+ @supplier.save
114
+ end
115
+
116
+ private
117
+
118
+ def supplier_params
119
+ params.require(:supplier).permit(products_attributes: [:name])
120
+ end
121
+ ```
80
122
 
81
123
  ## Documentation
82
124
 
@@ -15,6 +15,10 @@ module StoreModel
15
15
  # @return [Boolean]
16
16
  attr_accessor :serialize_unknown_attributes
17
17
 
18
+ # Controls if the result of `as_json` will serialize enum fiels using `as_json`
19
+ # @return [Boolean]
20
+ attr_accessor :serialize_enums_using_as_json
21
+
18
22
  def initialize
19
23
  @serialize_unknown_attributes = true
20
24
  end
@@ -31,16 +31,24 @@ module StoreModel
31
31
  # @param options [Hash]
32
32
  #
33
33
  # @return [Hash]
34
- def as_json(options = {})
34
+ def as_json(options = {}) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
35
35
  serialize_unknown_attributes = if options.key?(:serialize_unknown_attributes)
36
36
  options[:serialize_unknown_attributes]
37
37
  else
38
38
  StoreModel.config.serialize_unknown_attributes
39
39
  end
40
40
 
41
+ serialize_enums_using_as_json = if options.key?(:serialize_enums_using_as_json)
42
+ options[:serialize_enums_using_as_json]
43
+ else
44
+ StoreModel.config.serialize_enums_using_as_json
45
+ end
46
+
41
47
  result = attributes.with_indifferent_access
42
48
  result.merge!(unknown_attributes) if serialize_unknown_attributes
43
- result.as_json(options)
49
+ result.as_json(options).tap do |json|
50
+ serialize_enums!(json) if serialize_enums_using_as_json
51
+ end
44
52
  end
45
53
 
46
54
  # Returns an Object, similar to Hash#fetch, raises
@@ -186,5 +194,18 @@ module StoreModel
186
194
  else value.present?
187
195
  end
188
196
  end
197
+
198
+ def serialize_enums!(json)
199
+ enum_mappings =
200
+ self.class
201
+ .attribute_types
202
+ .select { |_, type| type.is_a?(StoreModel::Types::EnumType) }
203
+
204
+ enum_mappings.each do |name, _|
205
+ next unless json.key?(name)
206
+
207
+ json[name] = public_send(name).as_json unless json[name].nil?
208
+ end
209
+ end
189
210
  end
190
211
  end
@@ -13,27 +13,54 @@ module StoreModel
13
13
  # @param associations [Array] list of associations and options to define attributes, for example:
14
14
  # accepts_nested_attributes_for [:suppliers, allow_destroy: true]
15
15
  #
16
+ # Alternatively, use the standard Rails syntax:
17
+ #
18
+ # @param associations [Array] list of associations and attributes to define getters and setters.
19
+ #
20
+ # @param options [Hash] options not supported by StoreModel will still be passed to ActiveRecord.
21
+ #
16
22
  # Supported options:
17
23
  # [:allow_destroy]
18
24
  # If true, destroys any members from the attributes hash with a
19
25
  # <tt>_destroy</tt> key and a value that evaluates to +true+
20
26
  # (e.g. 1, '1', true, or 'true'). This option is off by default.
21
- def accepts_nested_attributes_for(*associations)
22
- associations.each do |association, options|
23
- case attribute_types[association.to_s]
24
- when Types::One
25
- define_association_setter_for_single(association, options)
26
- alias_method "#{association}_attributes=", "#{association}="
27
- when Types::Many
28
- define_association_setter_for_many(association, options)
29
- end
27
+ #
28
+ # [:reject_if]
29
+ # Allows you to specify a Proc or a Symbol pointing to a method that
30
+ # checks whether a record should be built for a certain attribute hash.
31
+ # The hash is passed to the supplied Proc or the method and it should
32
+ # return either true or false. Passing <tt>:all_blank</tt> instead of a Proc
33
+ # will create a proc that will reject a record where all the attributes
34
+ # are blank excluding any value for <tt>_destroy</tt>.
35
+ #
36
+ # See https://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html#method-i-accepts_nested_attributes_for
37
+ def accepts_nested_attributes_for(*attributes)
38
+ global_options = attributes.extract_options!
30
39
 
31
- define_attr_accessor_for_destroy(association, options)
40
+ attributes.each do |attribute, options|
41
+ case attribute_types[attribute.to_s]
42
+ when Types::OneBase, Types::ManyBase
43
+ define_store_model_attr_accessors(attribute, options || global_options)
44
+ else
45
+ super(attribute, options || global_options)
46
+ end
32
47
  end
33
48
  end
34
49
 
35
50
  private
36
51
 
52
+ def define_store_model_attr_accessors(attribute, options)
53
+ case attribute_types[attribute.to_s]
54
+ when Types::OneBase
55
+ define_association_setter_for_single(attribute, options)
56
+ alias_method "#{attribute}_attributes=", "#{attribute}="
57
+ when Types::ManyBase
58
+ define_association_setter_for_many(attribute, options)
59
+ end
60
+
61
+ define_attr_accessor_for_destroy(attribute, options)
62
+ end
63
+
37
64
  def define_attr_accessor_for_destroy(association, options)
38
65
  return unless options&.dig(:allow_destroy)
39
66
 
@@ -61,8 +88,6 @@ module StoreModel
61
88
  end
62
89
  end
63
90
 
64
- private
65
-
66
91
  def assign_nested_attributes_for_collection_association(association, attributes, options)
67
92
  attributes = attributes.values if attributes.is_a?(Hash)
68
93
 
@@ -72,7 +97,20 @@ module StoreModel
72
97
  end
73
98
  end
74
99
 
100
+ attributes.reject! { |attribute| call_reject_if(attribute, options[:reject_if]) } if options&.dig(:reject_if)
101
+
75
102
  send("#{association}=", attributes)
76
103
  end
104
+
105
+ def call_reject_if(attributes, callback)
106
+ callback = ActiveRecord::NestedAttributes::ClassMethods::REJECT_ALL_BLANK_PROC if callback == :all_blank
107
+
108
+ case callback
109
+ when Symbol
110
+ method(callback).arity.zero? ? send(callback) : send(callback, attributes)
111
+ when Proc
112
+ callback.call(attributes)
113
+ end
114
+ end
77
115
  end
78
116
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module StoreModel # :nodoc:
4
- VERSION = "2.0.1"
4
+ VERSION = "2.1.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: store_model
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.1
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - DmitryTsepelev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-05-09 00:00:00.000000000 Z
11
+ date: 2023-07-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord