store_model 2.1.0 → 2.1.2

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: 61353c039b8274db76c67d73081ce718a5446e392df4bd5987f4dde023d5b244
4
- data.tar.gz: 40f5011957af3787650b1f3565468fc0b59a4c1a2e090735e8cbf9926e8b622e
3
+ metadata.gz: db36f4fa51b9f71a34198ba18b921565d46a450c62e4892628dfee8e067bf4ff
4
+ data.tar.gz: 4e72e8f0fc7051105a09843040a4cfc61f248884ac2dcc8def0f6a95f9b8390f
5
5
  SHA512:
6
- metadata.gz: 5e3065c9503681ac881fe2e2b2194bfc9f9c959c66e4a6430b47160ff4487cc1bdc6f4408dfc72e216f94eae729ce8462e09ae12364e3e37ef5341769588a796
7
- data.tar.gz: 427cb2dab39fd79ef74fb3b1a1e48f89003fbb2e00ca25488cea257cc9f87c9aad5eedb4c469252a605e5932420ad6e257898e85ab7622c904c7cec99c75af73
6
+ metadata.gz: 5c5073d90550cd90049cb0cc2555bfab36c4b036857319ac5bf0f0eeadaea70deabcf71e549c7e1348127f719ba9f03bda444fb700153b8bdbbb5ffdc3e36c0f
7
+ data.tar.gz: f5a3eed707850087dfc95c908725624352da067410b3fa69762764545fd83c243a99bfbf8a4b4ae53086fdff9094377769be720cf7497b0d82ae1d7ad736e411
@@ -7,7 +7,7 @@ require "store_model/nested_attributes"
7
7
 
8
8
  module StoreModel
9
9
  # When included into class configures it to handle JSON column
10
- module Model
10
+ module Model # rubocop:disable Metrics/ModuleLength
11
11
  def self.included(base) # :nodoc:
12
12
  base.include ActiveModel::Model
13
13
  base.include ActiveModel::Attributes
@@ -44,7 +44,10 @@ module StoreModel
44
44
  StoreModel.config.serialize_enums_using_as_json
45
45
  end
46
46
 
47
- result = attributes.with_indifferent_access
47
+ result = @attributes.keys.each_with_object({}) do |key, values|
48
+ values[key] = serialized_attribute(key)
49
+ end.with_indifferent_access
50
+
48
51
  result.merge!(unknown_attributes) if serialize_unknown_attributes
49
52
  result.as_json(options).tap do |json|
50
53
  serialize_enums!(json) if serialize_enums_using_as_json
@@ -175,17 +178,6 @@ module StoreModel
175
178
  @unknown_attributes ||= {}
176
179
  end
177
180
 
178
- # Serialized values for storing in db
179
- #
180
- # @return [Hash]
181
- def values_for_database
182
- result = @attributes.keys.each_with_object({}) do |key, values|
183
- values[key] = @attributes.fetch(key).value_for_database
184
- end
185
- result.merge!(unknown_attributes)
186
- result
187
- end
188
-
189
181
  private
190
182
 
191
183
  def attribute?(attribute)
@@ -207,5 +199,16 @@ module StoreModel
207
199
  json[name] = public_send(name).as_json unless json[name].nil?
208
200
  end
209
201
  end
202
+
203
+ def serialized_attribute(attr_name)
204
+ attr = @attributes.fetch(attr_name)
205
+ if attr.value.is_a? StoreModel::Model
206
+ Types::RawJSONEncoder.new(attr.value_for_database)
207
+ elsif attr.value.is_a? Array
208
+ attr.value.as_json
209
+ else
210
+ attr.value_for_database
211
+ end
212
+ end
210
213
  end
211
214
  end
@@ -35,25 +35,45 @@ module StoreModel
35
35
  #
36
36
  # See https://api.rubyonrails.org/classes/ActiveRecord/NestedAttributes/ClassMethods.html#method-i-accepts_nested_attributes_for
37
37
  def accepts_nested_attributes_for(*attributes)
38
- global_options = attributes.extract_options!
38
+ options = attributes.extract_options!
39
39
 
40
- attributes.each do |attribute, options|
41
- case attribute_types[attribute.to_s]
40
+ attributes.each do |attribute|
41
+ case nested_attribute_type(attribute)
42
42
  when Types::OneBase, Types::ManyBase
43
- define_store_model_attr_accessors(attribute, options || global_options)
43
+ options.reverse_merge!(allow_destroy: false, update_only: false)
44
+ define_store_model_attr_accessors(attribute, options)
44
45
  else
45
- super(attribute, options || global_options)
46
+ super(*attribute, options)
46
47
  end
47
48
  end
48
49
  end
49
50
 
50
51
  private
51
52
 
53
+ # If attribute defined in ActiveRecord model but you dont yet have database created
54
+ # you cannot access attribute types.
55
+ # To handle this case, we can use ActiveRecord::Attributes 'attributes_to_define_after_schema_loads'
56
+ # which stores information about custom defined attributes.
57
+ # See ActiveRecord::Attributes#atribute
58
+ # If #accepts_nested_attributes_for is used inside active model instance
59
+ # schema is not required to determine attribute type so we can still use attribute_types
60
+ # If schema loaded the attribute_types already populated and we can safely use it
61
+ # See ActiveRecord::ModelSchema#load_schema!
62
+ def nested_attribute_type(attribute)
63
+ if self < ActiveRecord::Base && !schema_loaded?
64
+ attributes_to_define_after_schema_loads[attribute.to_s]&.first
65
+ else
66
+ attribute_types[attribute.to_s]
67
+ end
68
+ end
69
+
52
70
  def define_store_model_attr_accessors(attribute, options)
53
- case attribute_types[attribute.to_s]
71
+ case nested_attribute_type(attribute)
54
72
  when Types::OneBase
55
73
  define_association_setter_for_single(attribute, options)
56
- alias_method "#{attribute}_attributes=", "#{attribute}="
74
+ define_method "#{attribute}_attributes=" do |*args, **kwargs|
75
+ send("#{attribute}=", *args, **kwargs)
76
+ end
57
77
  when Types::ManyBase
58
78
  define_association_setter_for_many(attribute, options)
59
79
  end
@@ -88,7 +108,10 @@ module StoreModel
88
108
  end
89
109
  end
90
110
 
91
- def assign_nested_attributes_for_collection_association(association, attributes, options)
111
+ # Base
112
+ def assign_nested_attributes_for_collection_association(association, attributes, options = nil)
113
+ return super(association, attributes) unless options
114
+
92
115
  attributes = attributes.values if attributes.is_a?(Hash)
93
116
 
94
117
  if options&.dig(:allow_destroy)
@@ -46,9 +46,7 @@ module StoreModel
46
46
  # @return [String] serialized value
47
47
  def serialize(value)
48
48
  case value
49
- when @model_klass
50
- ActiveSupport::JSON.encode(value.values_for_database)
51
- when Hash
49
+ when @model_klass, Hash
52
50
  ActiveSupport::JSON.encode(value)
53
51
  else
54
52
  super
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StoreModel
4
+ module Types
5
+ # Implements #encode_json and #as_json methods.
6
+ # By wrapping serialized objects in this type, it prevents duplicate
7
+ # JSON serialization passes on nested object. It is named as Encoder
8
+ # as it will not work to inflate typed attributes and is intended
9
+ # to be used internally.
10
+ class RawJSONEncoder < String
11
+ def encode_json(_encoder)
12
+ self
13
+ end
14
+
15
+ def as_json(_options = nil)
16
+ JSON.parse(self)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -14,6 +14,8 @@ require "store_model/types/enum_type"
14
14
 
15
15
  require "store_model/types/one_of"
16
16
 
17
+ require "store_model/types/raw_json"
18
+
17
19
  module StoreModel
18
20
  # Contains all custom types.
19
21
  module Types
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module StoreModel # :nodoc:
4
- VERSION = "2.1.0"
4
+ VERSION = "2.1.2"
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.1.0
4
+ version: 2.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - DmitryTsepelev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-07-31 00:00:00.000000000 Z
11
+ date: 2023-10-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -115,6 +115,7 @@ files:
115
115
  - lib/store_model/types/one_of.rb
116
116
  - lib/store_model/types/one_polymorphic.rb
117
117
  - lib/store_model/types/polymorphic_helper.rb
118
+ - lib/store_model/types/raw_json.rb
118
119
  - lib/store_model/version.rb
119
120
  homepage: https://github.com/DmitryTsepelev/store_model
120
121
  licenses: