attr_json 2.2.0 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +5 -2
- data/CHANGELOG.md +18 -1
- data/lib/attr_json/attribute_definition/registry.rb +43 -9
- data/lib/attr_json/attribute_definition.rb +3 -1
- data/lib/attr_json/model.rb +10 -8
- data/lib/attr_json/nested_attributes/builder.rb +2 -0
- data/lib/attr_json/nested_attributes/multiparameter_attribute_writer.rb +2 -0
- data/lib/attr_json/nested_attributes/writer.rb +2 -0
- data/lib/attr_json/nested_attributes.rb +2 -0
- data/lib/attr_json/record/query_builder.rb +2 -0
- data/lib/attr_json/record/query_scopes.rb +2 -0
- data/lib/attr_json/record.rb +8 -14
- data/lib/attr_json/serialization_coder_from_type.rb +2 -0
- data/lib/attr_json/type/array.rb +2 -0
- data/lib/attr_json/type/container_attribute.rb +2 -0
- data/lib/attr_json/type/model.rb +2 -0
- data/lib/attr_json/type/polymorphic_model.rb +2 -0
- data/lib/attr_json/version.rb +1 -1
- data/lib/attr_json.rb +21 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eb4f8078039ec81cc25e7a95318ff5e42cb5bf85a955b40326f60f9022af16a2
|
4
|
+
data.tar.gz: 635f08ce5fa34ce45dfdd5256ae0e81fdcc2d455f4525bad5f71f5b0c4391c6b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 22c35f674c7b5f3a4e91f348d20439df3e8fe175ca5d3c8938720a5e71739df2474cdddd5fdab5f0527f8751ed0fb6dee11ba7f75889c143daca92bd84afc86b
|
7
|
+
data.tar.gz: 636628f0083dd61c869de5b9eb65a0c11988560e26d5dcd8e822759788bdac4bdb3c6f0359e2257867ac91a61721fda32df54a36f51a001450ef883289be433a
|
data/.github/workflows/ci.yml
CHANGED
@@ -18,7 +18,10 @@ jobs:
|
|
18
18
|
tests:
|
19
19
|
services:
|
20
20
|
db:
|
21
|
-
|
21
|
+
# used to be 9.4, should work on 9.4, only moved to 10.0
|
22
|
+
# because of rails 7.1 bug (rails intends to support 9 too!)
|
23
|
+
# https://github.com/jrochkind/attr_json/issues/211
|
24
|
+
image: postgres:10.0
|
22
25
|
env:
|
23
26
|
POSTGRES_USER: postgres
|
24
27
|
POSTGRES_PASSWORD: postgres
|
@@ -49,7 +52,7 @@ jobs:
|
|
49
52
|
|
50
53
|
- gemfile: rails_7_0
|
51
54
|
ruby: 3.2
|
52
|
-
|
55
|
+
|
53
56
|
- gemfile: rails_7_1
|
54
57
|
ruby: '3.0'
|
55
58
|
|
data/CHANGELOG.md
CHANGED
@@ -4,7 +4,7 @@ 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.
|
7
|
+
## [Unreleased](https://github.com/jrochkind/attr_json/compare/v2.3.0...HEAD)
|
8
8
|
|
9
9
|
### Fixed
|
10
10
|
|
@@ -22,6 +22,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
22
22
|
|
23
23
|
*
|
24
24
|
|
25
|
+
### Changed
|
26
|
+
|
27
|
+
*
|
28
|
+
|
29
|
+
*
|
30
|
+
|
31
|
+
*
|
32
|
+
|
33
|
+
## [2.3.0]((https://github.com/jrochkind/attr_json/compare/v2.2.0...v2.3.0))
|
34
|
+
|
35
|
+
|
36
|
+
### Changed
|
37
|
+
|
38
|
+
* Avoid private ActiveRecord API when lazily registering container attributes. (Compat with Rails post 7.1) https://github.com/jrochkind/attr_json/pull/214
|
39
|
+
|
40
|
+
* Freeze more strings to reduce String allocations https://github.com/jrochkind/attr_json/pull/216
|
41
|
+
|
25
42
|
## [2.2.0](https://github.com/jrochkind/attr_json/compare/v2.1.0...v2.2.0)
|
26
43
|
|
27
44
|
### Added
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'attr_json/attribute_definition'
|
2
4
|
|
3
5
|
module AttrJson
|
@@ -25,7 +27,10 @@ module AttrJson
|
|
25
27
|
def initialize(hash = {})
|
26
28
|
@name_to_definition = hash.dup
|
27
29
|
@store_key_to_definition = {}
|
28
|
-
|
30
|
+
|
31
|
+
@name_to_definition.values.each { |d| store_key_index!(d) }
|
32
|
+
|
33
|
+
@container_attributes_registered = Hash.new { Set.new }
|
29
34
|
end
|
30
35
|
|
31
36
|
def fetch(key, *args, &block)
|
@@ -46,12 +51,13 @@ module AttrJson
|
|
46
51
|
|
47
52
|
# Can return nil if none found.
|
48
53
|
def store_key_lookup(container_attribute, store_key)
|
49
|
-
@store_key_to_definition[container_attribute
|
50
|
-
@store_key_to_definition[container_attribute
|
54
|
+
@store_key_to_definition[AttrJson.efficient_to_s(container_attribute)] &&
|
55
|
+
@store_key_to_definition[AttrJson.efficient_to_s(container_attribute)][AttrJson.efficient_to_s(store_key)]
|
51
56
|
end
|
52
57
|
|
53
58
|
def definitions
|
54
|
-
|
59
|
+
# Since we are immutable, we can cache this to avoid allocation in a hot spot
|
60
|
+
@stored_definitions ||= @name_to_definition.values
|
55
61
|
end
|
56
62
|
|
57
63
|
# Returns all registered attributes as an array of symbols
|
@@ -60,7 +66,7 @@ module AttrJson
|
|
60
66
|
end
|
61
67
|
|
62
68
|
def container_attributes
|
63
|
-
@store_key_to_definition.keys.collect(
|
69
|
+
@store_key_to_definition.keys.collect { |s| AttrJson.efficient_to_s(s) }
|
64
70
|
end
|
65
71
|
|
66
72
|
# This is how you register additional definitions, as a non-mutating
|
@@ -73,6 +79,32 @@ module AttrJson
|
|
73
79
|
end
|
74
80
|
end
|
75
81
|
|
82
|
+
|
83
|
+
# We need to lazily set the container type only the FIRST time
|
84
|
+
#
|
85
|
+
# While also avoiding this triggering ActiveRecord to actually go to DB,
|
86
|
+
# we don't want DB connection forced on boot, that's a problem for many apps,
|
87
|
+
# including that may not have a DB connection in initial development.
|
88
|
+
# (#type_for_attribute forces DB connection)
|
89
|
+
#
|
90
|
+
# AND we need to call container attriubte on SUB-CLASS AGAIN, iff sub-class
|
91
|
+
# has any of it's own new registrations, to make sure we get the right type in
|
92
|
+
# sub-class!
|
93
|
+
#
|
94
|
+
# So we just keep track of whether we've registered ourselves, so we can
|
95
|
+
# first time we need to.
|
96
|
+
#
|
97
|
+
# While current implementation is simple, this has ended up a bit fragile,
|
98
|
+
# a different API that doesn't require us to do this implicitly lazily
|
99
|
+
# might be preferred! But this is what we got for now.
|
100
|
+
def register_container_attribute(attribute_name:, model:)
|
101
|
+
@container_attributes_registered[attribute_name.to_sym] << model
|
102
|
+
end
|
103
|
+
|
104
|
+
def container_attribute_registered?(attribute_name:, model:)
|
105
|
+
@container_attributes_registered[attribute_name.to_sym].include?(model)
|
106
|
+
end
|
107
|
+
|
76
108
|
protected
|
77
109
|
|
78
110
|
def add!(definition)
|
@@ -81,17 +113,19 @@ module AttrJson
|
|
81
113
|
end
|
82
114
|
@name_to_definition[definition.name.to_sym] = definition
|
83
115
|
store_key_index!(definition)
|
116
|
+
|
117
|
+
@stored_definitions = nil
|
84
118
|
end
|
85
119
|
|
86
120
|
def store_key_index!(definition)
|
87
|
-
container_hash = (@store_key_to_definition[definition.container_attribute
|
121
|
+
container_hash = (@store_key_to_definition[AttrJson.efficient_to_s(definition.container_attribute)] ||= {})
|
88
122
|
|
89
|
-
if container_hash.has_key?(definition.store_key
|
90
|
-
existing = container_hash[definition.store_key
|
123
|
+
if container_hash.has_key?(AttrJson.efficient_to_s(definition.store_key))
|
124
|
+
existing = container_hash[AttrJson.efficient_to_s(definition.store_key)]
|
91
125
|
raise ArgumentError, "Can't add, store key `#{definition.store_key}` conflicts with existing attribute: #{existing.original_args}"
|
92
126
|
end
|
93
127
|
|
94
|
-
container_hash[definition.store_key
|
128
|
+
container_hash[AttrJson.efficient_to_s(definition.store_key)] = definition
|
95
129
|
end
|
96
130
|
end
|
97
131
|
end
|
data/lib/attr_json/model.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'active_support/concern'
|
2
4
|
require 'active_model/type'
|
3
5
|
|
@@ -136,8 +138,8 @@ module AttrJson
|
|
136
138
|
def new_from_serializable(attributes = {})
|
137
139
|
attributes = attributes.collect do |key, value|
|
138
140
|
# store keys in arguments get translated to attribute names on initialize.
|
139
|
-
if attribute_def = self.attr_json_registry.store_key_lookup("", key
|
140
|
-
key = attribute_def.name
|
141
|
+
if attribute_def = self.attr_json_registry.store_key_lookup("".freeze, AttrJson.efficient_to_s(key))
|
142
|
+
key = AttrJson.efficient_to_s(attribute_def.name)
|
141
143
|
end
|
142
144
|
|
143
145
|
attr_type = self.attr_json_registry.has_attribute?(key) && self.attr_json_registry.type_for_attribute(key)
|
@@ -184,7 +186,7 @@ module AttrJson
|
|
184
186
|
|
185
187
|
# like the ActiveModel::Attributes method, hash with name keys, and ActiveModel::Type values
|
186
188
|
def attribute_types
|
187
|
-
attribute_names.collect { |name| [name
|
189
|
+
attribute_names.collect { |name| [AttrJson.efficient_to_s(name), attr_json_registry.type_for_attribute(name)]}.to_h
|
188
190
|
end
|
189
191
|
|
190
192
|
|
@@ -225,11 +227,11 @@ module AttrJson
|
|
225
227
|
|
226
228
|
_attr_jsons_module.module_eval do
|
227
229
|
define_method("#{name}=") do |value|
|
228
|
-
_attr_json_write(name
|
230
|
+
_attr_json_write(AttrJson.efficient_to_s(name), value)
|
229
231
|
end
|
230
232
|
|
231
233
|
define_method("#{name}") do
|
232
|
-
attributes[name
|
234
|
+
attributes[AttrJson.efficient_to_s(name)]
|
233
235
|
end
|
234
236
|
end
|
235
237
|
end
|
@@ -427,7 +429,7 @@ module AttrJson
|
|
427
429
|
|
428
430
|
def fill_in_defaults!
|
429
431
|
self.class.attr_json_registry.definitions.each do |definition|
|
430
|
-
if definition.has_default? && !attributes.has_key?(definition.name
|
432
|
+
if definition.has_default? && !attributes.has_key?(AttrJson.efficient_to_s(definition.name))
|
431
433
|
self.send("#{definition.name.to_s}=", definition.provide_default!)
|
432
434
|
end
|
433
435
|
end
|
@@ -435,10 +437,10 @@ module AttrJson
|
|
435
437
|
|
436
438
|
def _attr_json_write(key, value)
|
437
439
|
if attribute_def = self.class.attr_json_registry[key.to_sym]
|
438
|
-
attributes[key
|
440
|
+
attributes[AttrJson.efficient_to_s(key)] = attribute_def.cast(value)
|
439
441
|
else
|
440
442
|
# TODO, strict mode, ignore, raise, allow.
|
441
|
-
attributes[key
|
443
|
+
attributes[AttrJson.efficient_to_s(key)] = value
|
442
444
|
end
|
443
445
|
end
|
444
446
|
|
data/lib/attr_json/record.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'attr_json/attribute_definition'
|
2
4
|
require 'attr_json/attribute_definition/registry'
|
3
5
|
require 'attr_json/type/container_attribute'
|
@@ -160,22 +162,14 @@ module AttrJson
|
|
160
162
|
options.assert_valid_keys(AttributeDefinition::VALID_OPTIONS + [:validate, :accepts_nested_attributes])
|
161
163
|
container_attribute = options[:container_attribute]
|
162
164
|
|
163
|
-
# TODO arg check container_attribute make sure it exists. Hard cause
|
164
|
-
# schema isn't loaded yet when class def is loaded. Maybe not.
|
165
165
|
|
166
|
-
#
|
167
|
-
#
|
168
|
-
#
|
169
|
-
#
|
170
|
-
|
171
|
-
# We set default to empty hash, because that 'tricks' AR into knowing any
|
172
|
-
# application of defaults is a change that needs to be saved.
|
173
|
-
unless attributes_to_define_after_schema_loads[container_attribute.to_s] &&
|
174
|
-
attributes_to_define_after_schema_loads[container_attribute.to_s].first.is_a?(AttrJson::Type::ContainerAttribute) &&
|
175
|
-
attributes_to_define_after_schema_loads[container_attribute.to_s].first.model == self
|
176
|
-
# If this is already defined, but was for superclass, we need to define it again for
|
177
|
-
# this class.
|
166
|
+
# Make sure to "lazily" register attribute for *container* class if this is the first time
|
167
|
+
# this container attribute hsa been encountered for this specific class. The registry
|
168
|
+
# helps us keep track. Kinda messy, in future we may want a more explicit API
|
169
|
+
# that does not require us to implicitly track first-time per-container.
|
170
|
+
unless self.attr_json_registry.container_attribute_registered?(model: self, attribute_name: container_attribute.to_sym)
|
178
171
|
attribute container_attribute.to_sym, AttrJson::Type::ContainerAttribute.new(self, container_attribute), default: -> { {} }
|
172
|
+
self.attr_json_registry.register_container_attribute(model: self, attribute_name: container_attribute.to_sym)
|
179
173
|
end
|
180
174
|
|
181
175
|
self.attr_json_registry = attr_json_registry.with(
|
data/lib/attr_json/type/array.rb
CHANGED
data/lib/attr_json/type/model.rb
CHANGED
data/lib/attr_json/version.rb
CHANGED
data/lib/attr_json.rb
CHANGED
@@ -10,5 +10,25 @@ require 'attr_json/record/query_scopes'
|
|
10
10
|
require 'attr_json/type/polymorphic_model'
|
11
11
|
|
12
12
|
module AttrJson
|
13
|
-
|
13
|
+
# We need to convert Symbols to strings a lot at present -- ActiveRecord does too, so
|
14
|
+
# not too suprrising.
|
15
|
+
#
|
16
|
+
# In Rails 3.0 and above, we can use Symbol#name to get a frozen string back
|
17
|
+
# and avoid extra allocations. https://bugs.ruby-lang.org/issues/16150
|
18
|
+
#
|
19
|
+
# Ruby 2.7 doens't yet have it though. As long as we are supporting ruby 2.7,
|
20
|
+
# we'll just check at runtime to keep this lean
|
21
|
+
if RUBY_VERSION.split('.').first.to_i >= 3
|
22
|
+
def self.efficient_to_s(obj)
|
23
|
+
if obj.kind_of?(Symbol)
|
24
|
+
obj.name
|
25
|
+
else
|
26
|
+
obj.to_s
|
27
|
+
end
|
28
|
+
end
|
29
|
+
else
|
30
|
+
def self.efficient_to_s(obj)
|
31
|
+
obj.to_s
|
32
|
+
end
|
33
|
+
end
|
14
34
|
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.
|
4
|
+
version: 2.3.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-
|
11
|
+
date: 2023-12-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -207,7 +207,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
207
207
|
- !ruby/object:Gem::Version
|
208
208
|
version: '0'
|
209
209
|
requirements: []
|
210
|
-
rubygems_version: 3.4.
|
210
|
+
rubygems_version: 3.4.21
|
211
211
|
signing_key:
|
212
212
|
specification_version: 4
|
213
213
|
summary: ActiveRecord attributes stored serialized in a json column, super smooth.
|