store_model 4.5.0 → 4.6.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: b9c6db50613221bc69d952fdaed0cb2f1ae07a8c3730748e1166fb93d9538386
4
- data.tar.gz: f40dae854bd0900ed4f79096fa721da565c6d472b9b187a367cb03f7b3cd2da8
3
+ metadata.gz: e69ab65c67b3cd46e0285be5860d8cd5b1d501b38e126f9833fbe01b3dda3ee8
4
+ data.tar.gz: 04b8d780feaaa6bfcb578465582b845d66960ba5f9df6e5d7aaa8d99f3d568bb
5
5
  SHA512:
6
- metadata.gz: 50a646560709d22bbb3a6c828cf7834e84dcc9998e1bebc619fcee0953a4b1962728573e18169444c28ef0fa49fcbfc88c3f3bb0de0a1828293a1397b4f13110
7
- data.tar.gz: 684aa3e90eb4886709ad0960d1ff5092b6223d3393e9dc72276ef149262e82c549ac2de1727385655598e5c953e2f346a160daf15ce4df4f27e163af3a1a5c08
6
+ metadata.gz: f8176e449148b7b7aa04596bf75b4932a156f737bac32395e79ae4ca5af9d6e186addb5c1de47b503f754b491f3a98a2ae081ce44fb8f3608c077f81fbcbe8d7
7
+ data.tar.gz: 9a771879567e9bbe266fa3825327469ac5b2e2008e7f90a7b608b72d28bbbb9704bd7f28e1498e4d65879c6014321a368557f54ec04fe23af0c7d4f8074d0613
data/README.md CHANGED
@@ -22,7 +22,10 @@ class Product < ApplicationRecord
22
22
  end
23
23
  ```
24
24
 
25
- You can support my open–source work [here](https://boosty.to/dmitry_tsepelev).
25
+ ## Professional Support
26
+
27
+ Working on a complex Rails application and need an experienced eye?
28
+ I'm available for consulting — [get in touch](https://dmitrytsepelev.dev/consulting).
26
29
 
27
30
  ## Why should I wrap my JSON columns?
28
31
 
@@ -39,12 +39,19 @@ module StoreModel
39
39
  # @return [Boolean]
40
40
  attr_accessor :active_admin_compatibility
41
41
 
42
+ # Controls whether nested attributes updates preserve existing model instances by default.
43
+ # When true, acts like `update_only: true` for all singular nested attributes unless overridden.
44
+ # Default: false
45
+ # @return [Boolean]
46
+ attr_accessor :nested_attributes_update_only
47
+
42
48
  def initialize
43
49
  @serialize_unknown_attributes = true
44
50
  @enable_parent_assignment = true
45
51
  @serialize_enums_using_as_json = true
46
52
  @serialize_empty_attributes = true
47
53
  @active_admin_compatibility = false
54
+ @nested_attributes_update_only = false
48
55
  end
49
56
  end
50
57
  end
@@ -8,14 +8,25 @@ module StoreModel
8
8
  include ParentAssignment
9
9
 
10
10
  def _read_attribute(*)
11
- super.tap do |attribute|
12
- assign_parent_to_store_model_relation(attribute)
13
- end
11
+ value = super
12
+ assign_parent_to_store_model_relation(value) if store_model_attribute?(value)
13
+ value
14
14
  end
15
15
 
16
16
  def _write_attribute(*)
17
- super.tap do |attribute|
18
- assign_parent_to_store_model_relation(attribute)
17
+ value = super
18
+ assign_parent_to_store_model_relation(value) if store_model_attribute?(value)
19
+ value
20
+ end
21
+
22
+ private
23
+
24
+ def store_model_attribute?(value)
25
+ case value
26
+ when StoreModel::Model then true
27
+ when Array then value.first.is_a?(StoreModel::Model)
28
+ when Hash then value.each_value.any? { |v| v.is_a?(StoreModel::Model) }
29
+ else false
19
30
  end
20
31
  end
21
32
  end
@@ -13,7 +13,10 @@ module StoreModel
13
13
  end
14
14
 
15
15
  def assign_parent_to_singular_store_model(item)
16
- item.parent = self if item.is_a?(StoreModel::Model)
16
+ return unless item.is_a?(StoreModel::Model)
17
+ return if item.frozen?
18
+
19
+ item.parent = self
17
20
  end
18
21
  end
19
22
  end
@@ -216,7 +216,10 @@ module StoreModel
216
216
  #
217
217
  # @return [Hash]
218
218
  def unknown_attributes
219
- @unknown_attributes ||= {}
219
+ return @unknown_attributes if defined?(@unknown_attributes)
220
+ return {}.freeze if frozen?
221
+
222
+ @unknown_attributes = {}
220
223
  end
221
224
 
222
225
  # Returns the value of the `@serialize_unknown_attributes` instance
@@ -314,6 +317,10 @@ module StoreModel
314
317
  return unless Array(attr.value).all? { |value| value.is_a?(StoreModel::Model) }
315
318
 
316
319
  Array(attr.value).each do |value|
320
+ # Skip frozen objects - they cannot have instance variables modified
321
+ # but will still serialize correctly with their existing settings
322
+ next if value.frozen?
323
+
317
324
  value.serialize_unknown_attributes = serialize_unknown_attributes
318
325
  value.serialize_enums_using_as_json = serialize_enums_using_as_json
319
326
  value.serialize_empty_attributes = serialize_empty_attributes
@@ -50,7 +50,7 @@ module StoreModel
50
50
 
51
51
  attributes.each do |attribute|
52
52
  if nested_attribute_type(attribute).is_a?(Types::Base)
53
- options.reverse_merge!(allow_destroy: false, update_only: false)
53
+ options.reverse_merge!(allow_destroy: false, update_only: StoreModel.config.nested_attributes_update_only)
54
54
  define_store_model_attr_accessors(attribute, options)
55
55
  else
56
56
  super(*attribute, options)
@@ -7,12 +7,13 @@ module StoreModel
7
7
  # Implements ActiveModel::Type::Value type for handling an array of
8
8
  # StoreModel::Model
9
9
  class OneOf
10
- def initialize(&block)
10
+ def initialize(union: false, &block)
11
11
  @block = block
12
+ @union = union
12
13
  end
13
14
 
14
15
  def to_type
15
- Types::OnePolymorphic.new(@block)
16
+ Types::OnePolymorphic.new(@block, union: @union)
16
17
  end
17
18
 
18
19
  def to_array_type
@@ -13,8 +13,9 @@ module StoreModel
13
13
  # @param model_wrapper [Proc] class to handle
14
14
  #
15
15
  # @return [StoreModel::Types::OnePolymorphic ]
16
- def initialize(model_wrapper)
16
+ def initialize(model_wrapper, union: false)
17
17
  @model_wrapper = model_wrapper
18
+ @union = union
18
19
  super()
19
20
  end
20
21
 
@@ -30,8 +31,9 @@ module StoreModel
30
31
  # @param value [Object] a value to cast
31
32
  #
32
33
  # @return StoreModel::Model
33
- def cast_value(value) # rubocop:disable Metrics/MethodLength
34
+ def cast_value(value) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
34
35
  return nil if value.nil?
36
+ return nil if @union && value.respond_to?(:empty?) && value.empty?
35
37
 
36
38
  if value.is_a?(String)
37
39
  decode_and_initialize(value)
@@ -87,6 +89,18 @@ module StoreModel
87
89
  "Hash or instances which implement StoreModel::Model are allowed"
88
90
  end
89
91
 
92
+ # rubocop:disable Style/RescueModifier
93
+ def decode_and_initialize(value)
94
+ decoded = ActiveSupport::JSON.decode(value) rescue nil
95
+ return nil if decoded.nil?
96
+ return nil if @union && decoded.respond_to?(:empty?) && decoded.empty?
97
+
98
+ model_instance(decoded)
99
+ rescue ActiveModel::UnknownAttributeError => e
100
+ handle_unknown_attribute(decoded, e)
101
+ end
102
+ # rubocop:enable Style/RescueModifier
103
+
90
104
  def model_instance(value)
91
105
  extract_model_klass(value).new(value)
92
106
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module StoreModel # :nodoc:
4
- VERSION = "4.5.0"
4
+ VERSION = "4.6.0"
5
5
  end
data/lib/store_model.rb CHANGED
@@ -58,7 +58,7 @@ module StoreModel # :nodoc:
58
58
  end
59
59
 
60
60
  def union_one_of(discriminator, class_map)
61
- Types::OneOf.new do |attributes|
61
+ Types::OneOf.new(union: true) do |attributes|
62
62
  next nil unless attributes
63
63
 
64
64
  discriminator_value = attributes.with_indifferent_access[discriminator]
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: store_model
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.5.0
4
+ version: 4.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - DmitryTsepelev