active_typed_store 2.3.0 → 2.4.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 +4 -4
- data/README.md +68 -5
- data/lib/active_typed_store/attrs.rb +7 -6
- data/lib/active_typed_store/disallow_symbol_keys.rb +1 -1
- data/lib/active_typed_store/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 89b2c39e6bd9afe68635f920a123392538160c0eb6bbe9484738ced7600b58fa
|
|
4
|
+
data.tar.gz: 30ed4d583b2fdb4ebbb4480b479826a54088f6fb2467dc87ba99e393c6c2270a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a54b0a60a62d550f6493ac64f36c42d9a5f52eb207572b47b269b2cdd77815a2659b01e16648f1ed53a0ee943b698515ed04a3eb5ed0fcce7ef94b3eda942366
|
|
7
|
+
data.tar.gz: d57e21409b72efcc79086186aeb4ee8ea542380738cfdd2ddff94892dcb215b9ac05f509188dc5671e799e92f57b9aadabeb9b4f1473c3137f0b8d0f4f9b888b
|
data/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# ActiveTypedStore
|
|
2
2
|
|
|
3
|
-
`active_typed_store` is a lightweight (
|
|
3
|
+
`active_typed_store` is a lightweight (__~100 lines of code [1](https://github.com/corp-gp/active_typed_store/blob/master/lib/active_typed_store/attrs.rb) [2](https://github.com/corp-gp/active_typed_store/blob/master/lib/active_typed_store/store.rb)__) and highly performant gem (see [benchmarks](#benchmarks))
|
|
4
4
|
designed to help you store and manage typed data in JSON format within database.
|
|
5
5
|
This gem provides a simple, yet powerful way to ensure that your JSON data cast
|
|
6
6
|
to specific types, enabling more structured and reliable use of JSON fields in your Rails models.
|
|
@@ -80,6 +80,69 @@ class Model < ActiveRecord::Base
|
|
|
80
80
|
end
|
|
81
81
|
```
|
|
82
82
|
|
|
83
|
+
### Active Values
|
|
84
|
+
|
|
85
|
+
```ruby
|
|
86
|
+
class Model < ActiveRecord::Base
|
|
87
|
+
typed_store(:params) do
|
|
88
|
+
attr :name, :string
|
|
89
|
+
attr :parcel, Parcel::TYPE, default: Parcel.new
|
|
90
|
+
attr :parcels, Parcel::ARRAY_TYPE, default: []
|
|
91
|
+
|
|
92
|
+
# if types registered in ActiveRecord, you can use as symbol name
|
|
93
|
+
# ActiveRecord::Type.register(:parcel, ParcelType)
|
|
94
|
+
# ActiveRecord::Type.register(:parcel_array, ParcelArrayType)
|
|
95
|
+
# attr :parcel, :parcel, default: Parcel.new
|
|
96
|
+
# attr :parcels, :parcel_array, default: []
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
record = Model.new
|
|
101
|
+
record.parcel.weight = 10
|
|
102
|
+
record.parcel.height # => 0 is default value
|
|
103
|
+
record.parcels << Parcel.new(weight: 10, height: 20)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Example Active Value defined class:
|
|
107
|
+
```ruby
|
|
108
|
+
class Parcel
|
|
109
|
+
include ActiveModel::Attributes
|
|
110
|
+
include ActiveModel::AttributeAssignment
|
|
111
|
+
|
|
112
|
+
attribute :height, :float, default: 0
|
|
113
|
+
attribute :weight, :float
|
|
114
|
+
|
|
115
|
+
def initialize(attributes = {})
|
|
116
|
+
super()
|
|
117
|
+
assign_attributes(attributes)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def inspect = "<Parcel #{attributes.map { |k, v| "#{k}=#{v}" }.join(' ')}>"
|
|
121
|
+
def as_json = attributes.compact
|
|
122
|
+
|
|
123
|
+
class Type < ActiveRecord::Type::Json
|
|
124
|
+
def cast(value)
|
|
125
|
+
case value
|
|
126
|
+
when Hash then Parcel.new(value)
|
|
127
|
+
when Parcel then value
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
class ArrayType < ActiveRecord::Type::Json
|
|
133
|
+
def cast(value)
|
|
134
|
+
case value
|
|
135
|
+
when Array then value.map { it.is_a?(Parcel) ? it : Parcel.new(it) }
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Memoize types to enable sharing across models or attributes, reducing memory usage and optimizing YJIT warm-up.
|
|
141
|
+
TYPE = Type.new
|
|
142
|
+
ARRAY_TYPE = ArrayType.new
|
|
143
|
+
end
|
|
144
|
+
```
|
|
145
|
+
|
|
83
146
|
### Hash safety
|
|
84
147
|
This gem assumes you're using a database that supports structured data types, such as `json` in `PostgreSQL` or `MySQL`, and leverages Rails' [store_accessor](https://edgeapi.rubyonrails.org/classes/ActiveRecord/Store.html) under the hood. However, there’s one caveat: JSON columns use a string-keyed hash and don’t support access via symbols. To avoid unexpected errors when accessing the hash, we raise an error if a symbol is used. You can disable this behavior by setting `config.hash_safety = false`.
|
|
85
148
|
|
|
@@ -91,16 +154,16 @@ class Model < ActiveRecord::Base
|
|
|
91
154
|
end
|
|
92
155
|
|
|
93
156
|
record = Model.new(price: 1)
|
|
94
|
-
record["price"] # 1
|
|
95
|
-
record[:price] # raises "Symbol keys are not allowed `:price` (ActiveTypedStore::SymbolKeysDisallowed)"
|
|
157
|
+
record.params["price"] # 1
|
|
158
|
+
record.params[:price] # raises "Symbol keys are not allowed `:price` (ActiveTypedStore::SymbolKeysDisallowed)"
|
|
96
159
|
|
|
97
160
|
# initializers/active_type_store.rb
|
|
98
161
|
ActiveTypeStore.configure do |config|
|
|
99
162
|
config.hash_safety = false # default :disallow_symbol_keys
|
|
100
163
|
end
|
|
101
164
|
|
|
102
|
-
record["price"] # 1
|
|
103
|
-
record[:price] # nil - isn't the expected behavior for most applications
|
|
165
|
+
record.params["price"] # 1
|
|
166
|
+
record.params[:price] # nil - isn't the expected behavior for most applications
|
|
104
167
|
```
|
|
105
168
|
|
|
106
169
|
### Benchmarks
|
|
@@ -37,7 +37,7 @@ module ActiveTypedStore
|
|
|
37
37
|
store_module.define_method(:"#{field}=") do |value|
|
|
38
38
|
v = (type.respond_to?(:cast) ? type.cast(value) : type[value]) unless value.nil?
|
|
39
39
|
write_store_attribute(store_attribute, field, v)
|
|
40
|
-
|
|
40
|
+
read_attribute(store_attribute).delete(field) if v.nil?
|
|
41
41
|
end
|
|
42
42
|
end
|
|
43
43
|
|
|
@@ -50,18 +50,19 @@ module ActiveTypedStore
|
|
|
50
50
|
casted_val =
|
|
51
51
|
if val.nil? && !default.nil?
|
|
52
52
|
v = default.dup
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
is_changed = attribute_changed?(store_attribute)
|
|
54
|
+
read_attribute(store_attribute)[field] = v
|
|
55
|
+
clear_attribute_change(store_attribute) unless is_changed
|
|
56
|
+
read_attribute(store_attribute)[field] = v
|
|
56
57
|
elsif val.nil?
|
|
57
58
|
return nil
|
|
58
59
|
elsif instance_variable_get(ivar_prev).eql?(val)
|
|
59
60
|
return val
|
|
60
61
|
elsif type.respond_to?(:cast)
|
|
61
62
|
casted = type.cast(val)
|
|
62
|
-
casted.eql?(val) ? val : (
|
|
63
|
+
casted.eql?(val) ? val : (read_attribute(store_attribute)[field] = casted)
|
|
63
64
|
else
|
|
64
|
-
|
|
65
|
+
read_attribute(store_attribute)[field] = type[val]
|
|
65
66
|
end
|
|
66
67
|
|
|
67
68
|
instance_variable_set(ivar_prev, casted_val)
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: active_typed_store
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ermolaev Andrey
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2025-
|
|
11
|
+
date: 2025-11-13 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activemodel
|