store_model 2.0.1 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
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