attr_json 2.0.0 → 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: 979ced874e097dcf41e911b60320b8cf05dde0a3fce0256b7127d0ad28de4854
4
- data.tar.gz: cfa4e62ba695c5f952adcef69a47453781891f91e854bbe83cf83d4360364773
3
+ metadata.gz: 3e3a6009367cdd38597ef94a714897b21dd287f34251a501e54f0081981525e4
4
+ data.tar.gz: a4b4afb4af34a355eb8df4df1facbd17e03a34df8abb29a8335d4c0d589c4378
5
5
  SHA512:
6
- metadata.gz: 054c84ddcd6d63b3020656ba64a200c43a8994d28be693a61d37dee5a25352b215e375354ca55ea3cd8f3e80b6427639ba279d2075a52aaa9f9b7a4364506d75
7
- data.tar.gz: f2fe2af4a190f7e6c7fe18f2fff235158f209929a0ded8d66543833b8d8b2ca55b93dece3363f71b93000e4c174c48ce3516ab291948dad88b109d0d973987a5
6
+ metadata.gz: f2613f3e9e0bfc676ef5ff965e2656780315cd645dc9ddace261ac3f340621ff32e0c1e7029309c379ed79b969aecabed361dd442848504c279753af0452af64
7
+ data.tar.gz: 5aa56646f724d7426353327f8c396abc0a84f5e462979aa7a92bb0b3563f4cefdfdbb0a0c0c058ba74c9119f2267ff511ca83e1209e13c8d077615fd5971ac38
data/Appraisals CHANGED
@@ -37,11 +37,5 @@ appraise "rails-edge" do
37
37
  # Edge rails, future Rails 7.1 currently allows rack 3 -- but rails itself
38
38
  # and some of our other dependencies may not actually work with rack 3 yet,
39
39
  # let's test under rack 2. (Nothing in this gem deals with levels as low as rack)
40
- #
41
- # Bundler was having trouble resolving unless we specified rackup and rack-session
42
- # limits too, I think it was a bundler failure, we actually only care about
43
- # rack < 3 here.
44
40
  gem "rack", "~> 2.0"
45
- gem "rackup", "< 2"
46
- gem "rack-session", "< 2"
47
41
  end
data/CHANGELOG.md CHANGED
@@ -4,9 +4,9 @@ Notable changes to this project will be documented in this file.
4
4
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
5
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
- ## [Unreleased](https://github.com/jrochkind/attr_json/compare/v2.0.0...HEAD)
7
+ ## [Unreleased](https://github.com/jrochkind/attr_json/compare/v2.0.1...HEAD)
8
8
 
9
- ### Changed
9
+ ### Fixed
10
10
 
11
11
  *
12
12
 
@@ -22,6 +22,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
22
22
 
23
23
  *
24
24
 
25
+ ## [2.1.0](https://github.com/jrochkind/attr_json/compare/v2.0.1...v2.1.0)
26
+
27
+ ### Added
28
+
29
+ * Support for `store_key` in models used with polymorphic model feature. https://github.com/jrochkind/attr_json/pull/206 . Thanks @rdubya
30
+
31
+
32
+ ## [2.0.1](https://github.com/jrochkind/attr_json/compare/v2.0.0...v2.0.1)
33
+
34
+
35
+ ### Fixed
36
+
37
+ * You can now do a specified ActiveRecord `.select` without your json containers, to fetch an object with other attributes that you can access. https://github.com/jrochkind/attr_json/pull/193
38
+
39
+ ### Changed
40
+
41
+ * Refactor #attr_json_sync_to_rails_attributes for slightly improved performance. https://github.com/jrochkind/attr_json/pull/192
42
+
43
+ * Safety guard in sync_to_rails_attributes against unknown edge case where container is nil https://github.com/jrochkind/attr_json/pull/194
44
+
25
45
 
26
46
  ## [2.0.0](https://github.com/jrochkind/attr_json/compare/v1.5.0...v2.0.0)
27
47
 
data/README.md CHANGED
@@ -48,6 +48,9 @@ class MyModel < ActiveRecord::Base
48
48
 
49
49
  # You can have an _array_ of those things too. It will ordinarily default to empty array.
50
50
  attr_json :int_array, :integer, array: true
51
+
52
+ # The empty array default can be disabled with the following setting
53
+ attr_json :str_array, :string, array: true, default: AttrJson::AttributeDefinition::NO_DEFAULT_PROVIDED
51
54
 
52
55
  #and/or defaults
53
56
  attr_json :str_with_default, :string, default: "default value"
@@ -362,6 +365,9 @@ end
362
365
 
363
366
  class MyTable < ApplicationRecord
364
367
  serialize :some_json_column, MyModel.to_serialization_coder
368
+
369
+ # NOTE: In Rails 7.1+, write:
370
+ # serialize :some_json_column, coder: MyModel.to_serialization_coder
365
371
  end
366
372
 
367
373
  MyTable.create(some_json_column: MyModel.new(some_string: "string"))
@@ -456,7 +462,7 @@ Why might you want this?
456
462
 
457
463
  * Single-Table Inheritance, with sub-classes that have non-shared
458
464
  data fields. You rather not make all those columns, some of which will then also appear
459
- to inapplicable sub-classes.
465
+ to inapplicable sub-classes. (**note** you may have trouble with [ActiveRecord #becomes](https://api.rubyonrails.org/v7.0.4/classes/ActiveRecord/Persistence.html#method-i-becomes) in some versions of Rails due to Rails bug. See https://github.com/jrochkind/attr_json/issues/189 and https://github.com/rails/rails/issues/47538))
460
466
 
461
467
  * A "content management system" type project, where you need complex
462
468
  structured data of various types, maybe needs to be vary depending
data/attr_json.gemspec CHANGED
@@ -42,8 +42,10 @@ attributes use as much of the existing ActiveRecord architecture as we can.}
42
42
 
43
43
  spec.required_ruby_version = '>= 2.6.0'
44
44
 
45
- # Only to get CI to work on versions of Rails other than we release with,
46
- # should never release a gem with RAILS_GEM set!
45
+ # This conditional is only to get CI to work on versions of Rails other than
46
+ # we release with. The gem should never be released without the activerecord
47
+ # dependency included just as it is here, should never be released
48
+ # from an env tht has any of these variables set.
47
49
  unless ENV['APPRAISAL_INITIALIZED'] || ENV["TRAVIS"] || ENV['CI']
48
50
  spec.add_runtime_dependency "activerecord", ">= 6.0.0", "< 7.1"
49
51
  end
@@ -16,7 +16,5 @@ gem "webdrivers", "~> 5.0"
16
16
  gem "selenium-webdriver"
17
17
  gem "byebug"
18
18
  gem "rack", "~> 2.0"
19
- gem "rackup", "< 2"
20
- gem "rack-session", "< 2"
21
19
 
22
20
  gemspec path: "../"
@@ -168,6 +168,9 @@ module AttrJson
168
168
  #
169
169
  # class MyTable < ApplicationRecord
170
170
  # serialize :some_json_column, MyModel.to_serialization_coder
171
+ #
172
+ # # In Rails 7.1+:
173
+ # # serialize :some_json_column, coder: MyModel.to_serialization_coder
171
174
  # end
172
175
  #
173
176
  def to_serialization_coder
@@ -48,23 +48,33 @@ module AttrJson
48
48
  # mutation of mutable object will effect both places, for instance for dirty
49
49
  # tracking.
50
50
  def attr_json_sync_to_rails_attributes
51
- self.class.attr_json_registry.attribute_names.each do |attr_name|
51
+ self.class.attr_json_registry.definitions.group_by(&:container_attribute).each_pair do |container_attribute, definitions|
52
52
  begin
53
- attribute_def = self.class.attr_json_registry.fetch(attr_name.to_sym)
54
- json_value = public_send(attribute_def.container_attribute)
55
- value = json_value[attribute_def.store_key]
53
+ # column may have eg been left out of an explicit 'select'
54
+ next unless has_attribute?(container_attribute)
56
55
 
57
- if value
58
- # TODO, can we just make this use the setter?
59
- write_attribute(attr_name, value)
56
+ container_value = public_send(container_attribute)
60
57
 
61
- clear_attribute_change(attr_name) if persisted?
58
+ # isn't expected to be possible to be nil rather than empty hash, but
59
+ # if it is from some edge case, well, we don't have values to sync, fine
60
+ next if container_value.nil?
62
61
 
63
- # writing and clearning will result in a new object stored in
64
- # rails attributes, we want
65
- # to make sure the exact same object is in the json attribute,
66
- # so in-place mutation changes to it are reflected in both places.
67
- json_value[attribute_def.store_key] = read_attribute(attr_name)
62
+ definitions.each do |attribute_def|
63
+ attr_name = attribute_def.name
64
+ value = container_value[attribute_def.store_key]
65
+
66
+ if value
67
+ # TODO, can we just make this use the setter?
68
+ write_attribute(attr_name, value)
69
+
70
+ clear_attribute_change(attr_name) if persisted?
71
+
72
+ # writing and clearning will result in a new object stored in
73
+ # rails attributes, we want
74
+ # to make sure the exact same object is in the json attribute,
75
+ # so in-place mutation changes to it are reflected in both places.
76
+ container_value[attribute_def.store_key] = read_attribute(attr_name)
77
+ end
68
78
  end
69
79
  rescue AttrJson::Type::Model::BadCast, AttrJson::Type::PolymorphicModel::TypeError => e
70
80
  # There was bad data in the DB, we're just going to skip the Rails attribute sync.
@@ -100,17 +100,11 @@ module AttrJson
100
100
  end
101
101
 
102
102
  def cast(v)
103
- if v.nil?
104
- v
105
- elsif model_names.include?(v.class.name)
106
- v
107
- elsif v.respond_to?(:to_hash)
108
- cast_from_hash(v.to_hash)
109
- elsif v.respond_to?(:to_h)
110
- cast_from_hash(v.to_h)
111
- else
112
- raise_bad_model_name(v.class, v)
113
- end
103
+ cast_or_deserialize(v, :cast)
104
+ end
105
+
106
+ def deserialize(v)
107
+ cast_or_deserialize(v, :deserialize)
114
108
  end
115
109
 
116
110
  def serialize(v)
@@ -127,10 +121,6 @@ module AttrJson
127
121
  type.serialize(v).merge(type_key => model_name)
128
122
  end
129
123
 
130
- def deserialize(v)
131
- cast(v)
132
- end
133
-
134
124
  def type_for_model_name(model_name)
135
125
  model_type_lookup[model_name]
136
126
  end
@@ -153,15 +143,29 @@ module AttrJson
153
143
 
154
144
  protected
155
145
 
156
- def raise_missing_type_key(value)
157
- raise TypeError, "AttrJson::Type::Polymorphic can't cast without '#{type_key}' key: #{value}"
158
- end
159
-
160
- def raise_bad_model_name(name, value)
161
- raise TypeError, "This AttrJson::Type::PolymorphicType can only include {#{model_names.join(', ')}}, not '#{name}': #{value.inspect}"
146
+ # We need to make sure to call the correct operation on
147
+ # the model type, so that we get the same result as if
148
+ # we had called the type directly
149
+ #
150
+ # @param v [Object, nil] the value to cast or deserialize
151
+ # @param operation [Symbol] :cast or :deserialize
152
+ def cast_or_deserialize(v, operation)
153
+ if v.nil?
154
+ v
155
+ elsif model_names.include?(v.class.name)
156
+ v
157
+ elsif v.respond_to?(:to_hash)
158
+ model_from_hash(v.to_hash, operation)
159
+ elsif v.respond_to?(:to_h)
160
+ model_from_hash(v.to_h, operation)
161
+ else
162
+ raise_bad_model_name(v.class, v)
163
+ end
162
164
  end
163
165
 
164
- def cast_from_hash(hash)
166
+ # @param hash [Hash] the value to cast or deserialize
167
+ # @param operation [Symbol] :cast or :deserialize
168
+ def model_from_hash(hash, operation)
165
169
  new_hash = hash.stringify_keys
166
170
  model_name = new_hash.delete(type_key.to_s)
167
171
 
@@ -171,7 +175,21 @@ module AttrJson
171
175
 
172
176
  raise_bad_model_name(model_name, hash) if type.nil?
173
177
 
174
- type.cast(new_hash)
178
+ if operation == :deserialize
179
+ type.deserialize(new_hash)
180
+ elsif operation == :cast
181
+ type.cast(new_hash)
182
+ else
183
+ raise ArgumentError, "Unknown operation #{operation}"
184
+ end
185
+ end
186
+
187
+ def raise_missing_type_key(value)
188
+ raise TypeError, "AttrJson::Type::Polymorphic can't cast without '#{type_key}' key: #{value}"
189
+ end
190
+
191
+ def raise_bad_model_name(name, value)
192
+ raise TypeError, "This AttrJson::Type::PolymorphicType can only include {#{model_names.join(', ')}}, not '#{name}': #{value.inspect}"
175
193
  end
176
194
  end
177
195
  end
@@ -1,3 +1,3 @@
1
1
  module AttrJson
2
- VERSION = "2.0.0"
2
+ VERSION = "2.1.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: attr_json
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonathan Rochkind
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-01-30 00:00:00.000000000 Z
11
+ date: 2023-07-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -206,7 +206,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
206
206
  - !ruby/object:Gem::Version
207
207
  version: '0'
208
208
  requirements: []
209
- rubygems_version: 3.4.5
209
+ rubygems_version: 3.3.26
210
210
  signing_key:
211
211
  specification_version: 4
212
212
  summary: ActiveRecord attributes stored serialized in a json column, super smooth.