store_attribute 0.9.3 → 1.0.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/CHANGELOG.md +8 -0
- data/README.md +50 -3
- data/lib/store_attribute/active_record/mutation_tracker.rb +28 -0
- data/lib/store_attribute/active_record/store.rb +51 -131
- data/lib/store_attribute/active_record/type/typed_store.rb +30 -17
- data/lib/store_attribute/version.rb +1 -1
- metadata +6 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f7594a9e113dc4e6f0872b45d64904354d8e88020111046c443416d3b9b90192
|
4
|
+
data.tar.gz: 5dd045f4f13862a9df72ecb20746a03fcea120308f500faa6e773c6bd47c2996
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 59d6d8f7dffbfe818e728012ccb3a1394f6a3c83a6b0a9b6b753576c28941a1a32d3fb00abc91cc8a32d8e8329d33e50358e0fe3aaa4157b3d573c7637265fd2
|
7
|
+
data.tar.gz: 559c01f0dc9dc5c193c46c55af999aaab586ba1d924546cf9eb226f7e88777147c5c3eea2650cef976870200bd43ce5fe019ddb08459de5abc5ebfd37f48e23a
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,14 @@
|
|
2
2
|
|
3
3
|
## master
|
4
4
|
|
5
|
+
## 1.0.0 (2022-03-17)
|
6
|
+
|
7
|
+
- **Ruby 2.6+ and Rails 6+** is required.
|
8
|
+
|
9
|
+
- Refactored internal implementation to use Rails Store implementation as much as possible. ([@palkan][])
|
10
|
+
|
11
|
+
Use existing Attributes API and Store API instead of duplicating and monkey-patching. Dirty-tracking, accessors and prefixes/suffixes are now handled by Rails. We only provide type coercions for stores.
|
12
|
+
|
5
13
|
## 0.9.3 (2021-11-17)
|
6
14
|
|
7
15
|
- Fix keeping empty store hashes in the changes. ([@markedmondson][])
|
data/README.md
CHANGED
@@ -6,15 +6,16 @@
|
|
6
6
|
|
7
7
|
ActiveRecord extension which adds typecasting to store accessors.
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
Extracted from not merged PR to Rails: [rails/rails#18942](https://github.com/rails/rails/pull/18942).
|
9
|
+
Originally extracted from not merged PR to Rails: [rails/rails#18942](https://github.com/rails/rails/pull/18942).
|
12
10
|
|
13
11
|
### Install
|
14
12
|
|
15
13
|
In your Gemfile:
|
16
14
|
|
17
15
|
```ruby
|
16
|
+
# for Rails 6+ (7 is supported)
|
17
|
+
gem "store_attribute", "~> 1.0"
|
18
|
+
|
18
19
|
# for Rails 5+ (6 is supported)
|
19
20
|
gem "store_attribute", "~> 0.8.0"
|
20
21
|
|
@@ -31,6 +32,7 @@ store_attribute(store_name, name, type, options)
|
|
31
32
|
```
|
32
33
|
|
33
34
|
Where:
|
35
|
+
|
34
36
|
- `store_name` The name of the store.
|
35
37
|
- `name` The name of the accessor to the store.
|
36
38
|
- `type` A symbol such as `:string` or `:integer`, or a type object to be used for the accessor.
|
@@ -98,3 +100,48 @@ class User < ActiveRecord::Base
|
|
98
100
|
store :settings, accessors: [:color, :homepage, login_at: :datetime], coder: JSON
|
99
101
|
end
|
100
102
|
```
|
103
|
+
|
104
|
+
### Using defaults
|
105
|
+
|
106
|
+
With `store_attribute`, you can provide default values for the store attribute. This functionality follows Rails behaviour for `attribute ..., default: ...` (and is backed by Attribute API).
|
107
|
+
|
108
|
+
You must remember two things when using defaults:
|
109
|
+
|
110
|
+
- A default value is only populated if no value for the **store** attribute was set, i.e., only when creating a new record.
|
111
|
+
- Default values persist as soon as you save the record.
|
112
|
+
|
113
|
+
The examples below demonstrate these caveats:
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
# Database schema
|
117
|
+
create_table("users") do |t|
|
118
|
+
t.string :name
|
119
|
+
t.jsonb :extra
|
120
|
+
end
|
121
|
+
|
122
|
+
class RawUser < ActiveRecord::Base
|
123
|
+
self.table_name = "users"
|
124
|
+
end
|
125
|
+
|
126
|
+
class User < ActiveRecord::Base
|
127
|
+
attribute :name, :string, default: "Joe"
|
128
|
+
store_attribute :extra, :expired_at, :date, default: -> { 2.days.from_now }
|
129
|
+
end
|
130
|
+
|
131
|
+
Date.current #=> 2022-03-17
|
132
|
+
|
133
|
+
user = User.new
|
134
|
+
user.name #=> "john"
|
135
|
+
user.expired_at #=> 2022-03-19
|
136
|
+
user.save!
|
137
|
+
|
138
|
+
raw_user = RawUser.find(user.id)
|
139
|
+
user.name #=> "john"
|
140
|
+
user.expired_at #=> 2022-03-19
|
141
|
+
|
142
|
+
another_raw_user = RawUser.create!
|
143
|
+
another_user = User.find(another_raw_user.id)
|
144
|
+
|
145
|
+
user.name #=> nil
|
146
|
+
user.expired_at #=> nil
|
147
|
+
```
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module StoreAttribute
|
4
|
+
# Upgrade mutation tracker to return partial changes for typed stores
|
5
|
+
module MutationTracker
|
6
|
+
def change_to_attribute(attr_name)
|
7
|
+
return super unless attributes[attr_name].type.is_a?(ActiveRecord::Type::TypedStore)
|
8
|
+
|
9
|
+
orig_changes = super
|
10
|
+
|
11
|
+
return unless orig_changes
|
12
|
+
|
13
|
+
prev_store, new_store = orig_changes.map(&:dup)
|
14
|
+
|
15
|
+
prev_store&.each do |key, value|
|
16
|
+
if new_store[key] == value
|
17
|
+
prev_store.except!(key)
|
18
|
+
new_store&.except!(key)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
[prev_store, new_store]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
require "active_model/attribute_mutation_tracker"
|
28
|
+
ActiveModel::AttributeMutationTracker.prepend(StoreAttribute::MutationTracker)
|
@@ -2,11 +2,14 @@
|
|
2
2
|
|
3
3
|
require "active_record/store"
|
4
4
|
require "store_attribute/active_record/type/typed_store"
|
5
|
+
require "store_attribute/active_record/mutation_tracker"
|
5
6
|
|
6
7
|
module ActiveRecord
|
7
8
|
module Store
|
8
9
|
module ClassMethods # :nodoc:
|
9
|
-
alias_method :
|
10
|
+
alias_method :_orig_store_without_types, :store
|
11
|
+
alias_method :_orig_store_accessor_without_types, :store_accessor
|
12
|
+
|
10
13
|
# Defines store on this model.
|
11
14
|
#
|
12
15
|
# +store_name+ The name of the store.
|
@@ -32,7 +35,7 @@ module ActiveRecord
|
|
32
35
|
{}
|
33
36
|
end
|
34
37
|
|
35
|
-
|
38
|
+
_orig_store_without_types(store_name, options)
|
36
39
|
store_accessor(store_name, *accessors, **typed_accessors) if accessors
|
37
40
|
end
|
38
41
|
|
@@ -57,13 +60,7 @@ module ActiveRecord
|
|
57
60
|
keys = keys.flatten
|
58
61
|
typed_keys = typed_keys.except(keys)
|
59
62
|
|
60
|
-
|
61
|
-
|
62
|
-
_define_accessors_methods(store_name, *keys, prefix: accessor_prefix, suffix: accessor_suffix)
|
63
|
-
|
64
|
-
_prepare_local_stored_attributes(store_name, *keys)
|
65
|
-
|
66
|
-
_define_dirty_tracking_methods(store_name, keys + typed_keys.keys, prefix: accessor_prefix, suffix: accessor_suffix)
|
63
|
+
_orig_store_accessor_without_types(store_name, *(keys - typed_keys.keys), prefix: nil, suffix: nil)
|
67
64
|
|
68
65
|
typed_keys.each do |key, type|
|
69
66
|
store_attribute(store_name, key, type, prefix: prefix, suffix: suffix)
|
@@ -121,154 +118,77 @@ module ActiveRecord
|
|
121
118
|
#
|
122
119
|
# For more examples on using types, see documentation for ActiveRecord::Attributes.
|
123
120
|
def store_attribute(store_name, name, type, prefix: nil, suffix: nil, **options)
|
124
|
-
|
125
|
-
|
126
|
-
_define_accessors_methods(store_name, name, prefix: prefix, suffix: suffix)
|
127
|
-
|
121
|
+
_orig_store_accessor_without_types(store_name, name.to_s, prefix: prefix, suffix: suffix)
|
128
122
|
_define_predicate_method(name, prefix: prefix, suffix: suffix) if type == :boolean
|
129
123
|
|
130
|
-
|
131
|
-
|
132
|
-
attr_name = store_name.to_s
|
133
|
-
was_type = attributes_to_define_after_schema_loads[attr_name]&.first
|
134
|
-
attribute(attr_name) do |subtype|
|
135
|
-
if defined?(_lookup_cast_type)
|
136
|
-
Type::TypedStore.create_from_type(_lookup_cast_type(attr_name, was_type, {}), name, type, **options)
|
137
|
-
else
|
138
|
-
Type::TypedStore.create_from_type(subtype, name, type, **options)
|
139
|
-
end
|
140
|
-
end
|
141
|
-
else
|
142
|
-
decorate_attribute_type(store_name, "typed_accessor_for_#{name}") do |subtype|
|
143
|
-
Type::TypedStore.create_from_type(subtype, name, type, **options)
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
_prepare_local_stored_attributes(store_name, name)
|
148
|
-
|
149
|
-
_define_dirty_tracking_methods(store_name, [name], prefix: prefix, suffix: suffix)
|
124
|
+
_define_store_attribute(store_name) if !_local_typed_stored_attributes? || _local_typed_stored_attributes[store_name].empty?
|
125
|
+
_store_local_stored_attribute(store_name, name, type, **options)
|
150
126
|
end
|
151
127
|
|
152
|
-
def
|
153
|
-
|
154
|
-
|
155
|
-
self.local_stored_attributes ||= {}
|
156
|
-
self.local_stored_attributes[store_name] ||= []
|
157
|
-
self.local_stored_attributes[store_name] |= keys
|
128
|
+
def _store_local_stored_attribute(store_name, key, cast_type, default: Type::TypedStore::UNDEFINED, **options) # :nodoc:
|
129
|
+
cast_type = ActiveRecord::Type.lookup(cast_type, **options) if cast_type.is_a?(Symbol)
|
130
|
+
_local_typed_stored_attributes[store_name][key] = [cast_type, default]
|
158
131
|
end
|
159
132
|
|
160
|
-
def
|
161
|
-
|
162
|
-
|
163
|
-
accessor_key = "#{prefix}#{key}#{suffix}"
|
133
|
+
def _local_typed_stored_attributes?
|
134
|
+
instance_variable_defined?(:@local_typed_stored_attributes)
|
135
|
+
end
|
164
136
|
|
165
|
-
|
166
|
-
|
167
|
-
end
|
137
|
+
def _local_typed_stored_attributes
|
138
|
+
return @local_typed_stored_attributes if _local_typed_stored_attributes?
|
168
139
|
|
169
|
-
|
170
|
-
|
140
|
+
@local_typed_stored_attributes =
|
141
|
+
if superclass.respond_to?(:_local_typed_stored_attributes)
|
142
|
+
superclass._local_typed_stored_attributes.dup.tap do |h|
|
143
|
+
h.transform_values!(&:dup)
|
171
144
|
end
|
145
|
+
else
|
146
|
+
Hash.new { |h, k| h[k] = {}.with_indifferent_access }.with_indifferent_access
|
172
147
|
end
|
173
|
-
end
|
174
148
|
end
|
175
149
|
|
176
|
-
def
|
177
|
-
|
178
|
-
|
150
|
+
def _define_store_attribute(store_name)
|
151
|
+
attr_name = store_name.to_s
|
152
|
+
was_type = attributes_to_define_after_schema_loads[attr_name]&.first
|
179
153
|
|
180
|
-
|
181
|
-
|
182
|
-
end
|
183
|
-
end
|
184
|
-
end
|
154
|
+
# For Rails <6.1
|
155
|
+
use_decorator = respond_to?(:decorate_attribute_type) && method(:decorate_attribute_type).parameters.count { |type, _| type == :req } == 2
|
185
156
|
|
186
|
-
|
187
|
-
_store_accessors_module.module_eval do
|
188
|
-
define_method("changes") do
|
189
|
-
changes = super()
|
190
|
-
self.class.local_stored_attributes.each do |accessor, attributes|
|
191
|
-
next unless attribute_changed?(accessor)
|
157
|
+
defaultik = Type::TypedStore::Defaultik.new
|
192
158
|
|
193
|
-
|
159
|
+
if use_decorator
|
160
|
+
decorate_attribute_type(attr_name, "typed_accessor_for_#{attr_name}") do |subtype|
|
161
|
+
subtypes = _local_typed_stored_attributes[attr_name]
|
162
|
+
type = Type::TypedStore.create_from_type(subtype)
|
163
|
+
defaultik.type = type
|
164
|
+
subtypes.each { |name, (cast_type, default)| type.add_typed_key(name, cast_type, default: default) }
|
194
165
|
|
195
|
-
|
196
|
-
if new_store[key] == value
|
197
|
-
prev_store.except!(key)
|
198
|
-
new_store&.except!(key)
|
199
|
-
end
|
200
|
-
end
|
166
|
+
define_default_attribute(attr_name, defaultik.proc, type, from_user: true)
|
201
167
|
|
202
|
-
|
203
|
-
changes[accessor] = prev_store, new_store
|
204
|
-
else
|
205
|
-
changes.delete(accessor)
|
206
|
-
end
|
207
|
-
end
|
208
|
-
changes
|
168
|
+
type
|
209
169
|
end
|
170
|
+
else
|
171
|
+
attribute(attr_name, default: defaultik.proc) do |subtype|
|
172
|
+
subtypes = _local_typed_stored_attributes[attr_name]
|
173
|
+
subtype = _lookup_cast_type(attr_name, was_type, {}) if defined?(_lookup_cast_type)
|
210
174
|
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
define_method("#{accessor_key}_changed?") do
|
216
|
-
return false unless attribute_changed?(store_attribute)
|
217
|
-
prev_store, new_store = changes[store_attribute]
|
218
|
-
prev_store&.dig(key) != new_store&.dig(key)
|
219
|
-
end
|
220
|
-
|
221
|
-
define_method("#{accessor_key}_change") do
|
222
|
-
return unless attribute_changed?(store_attribute)
|
223
|
-
prev_store, new_store = changes[store_attribute]
|
224
|
-
[prev_store&.dig(key), new_store&.dig(key)]
|
225
|
-
end
|
226
|
-
|
227
|
-
define_method("#{accessor_key}_was") do
|
228
|
-
return unless attribute_changed?(store_attribute)
|
229
|
-
prev_store, _new_store = changes[store_attribute]
|
230
|
-
prev_store&.dig(key)
|
231
|
-
end
|
232
|
-
|
233
|
-
define_method("saved_change_to_#{accessor_key}?") do
|
234
|
-
return false unless saved_change_to_attribute?(store_attribute)
|
235
|
-
prev_store, new_store = saved_change_to_attribute(store_attribute)
|
236
|
-
prev_store&.dig(key) != new_store&.dig(key)
|
237
|
-
end
|
238
|
-
|
239
|
-
define_method("saved_change_to_#{accessor_key}") do
|
240
|
-
return unless saved_change_to_attribute?(store_attribute)
|
241
|
-
prev_store, new_store = saved_change_to_attribute(store_attribute)
|
242
|
-
[prev_store&.dig(key), new_store&.dig(key)]
|
243
|
-
end
|
175
|
+
type = Type::TypedStore.create_from_type(subtype)
|
176
|
+
defaultik.type = type
|
177
|
+
subtypes.each { |name, (cast_type, default)| type.add_typed_key(name, cast_type, default: default) }
|
244
178
|
|
245
|
-
|
246
|
-
return unless saved_change_to_attribute?(store_attribute)
|
247
|
-
prev_store, _new_store = saved_change_to_attribute(store_attribute)
|
248
|
-
prev_store&.dig(key)
|
249
|
-
end
|
179
|
+
type
|
250
180
|
end
|
251
181
|
end
|
252
182
|
end
|
253
183
|
|
254
|
-
def
|
255
|
-
|
256
|
-
|
257
|
-
when String, Symbol
|
258
|
-
"#{prefix}_"
|
259
|
-
when TrueClass
|
260
|
-
"#{store_name}_"
|
261
|
-
end
|
184
|
+
def _define_predicate_method(name, prefix: nil, suffix: nil)
|
185
|
+
_store_accessors_module.module_eval do
|
186
|
+
name = "#{prefix}#{name}#{suffix}"
|
262
187
|
|
263
|
-
|
264
|
-
|
265
|
-
when String, Symbol
|
266
|
-
"_#{suffix}"
|
267
|
-
when TrueClass
|
268
|
-
"_#{store_name}"
|
188
|
+
define_method("#{name}?") do
|
189
|
+
send(name) == true
|
269
190
|
end
|
270
|
-
|
271
|
-
[prefix, suffix]
|
191
|
+
end
|
272
192
|
end
|
273
193
|
end
|
274
194
|
end
|
@@ -5,12 +5,24 @@ require "active_record/type"
|
|
5
5
|
module ActiveRecord
|
6
6
|
module Type # :nodoc:
|
7
7
|
class TypedStore < DelegateClass(ActiveRecord::Type::Value) # :nodoc:
|
8
|
+
class Defaultik
|
9
|
+
attr_accessor :type
|
10
|
+
|
11
|
+
def proc
|
12
|
+
@proc ||= Kernel.proc do
|
13
|
+
raise ArgumentError, "Has no type attached" unless type
|
14
|
+
|
15
|
+
type.build_defaults
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
8
20
|
# Creates +TypedStore+ type instance and specifies type caster
|
9
21
|
# for key.
|
10
|
-
def self.create_from_type(basetype,
|
11
|
-
|
12
|
-
|
13
|
-
|
22
|
+
def self.create_from_type(basetype, **options)
|
23
|
+
return basetype.dup if basetype.is_a?(self)
|
24
|
+
|
25
|
+
new(basetype)
|
14
26
|
end
|
15
27
|
|
16
28
|
def initialize(subtype)
|
@@ -21,7 +33,6 @@ module ActiveRecord
|
|
21
33
|
end
|
22
34
|
|
23
35
|
UNDEFINED = Object.new
|
24
|
-
private_constant :UNDEFINED
|
25
36
|
|
26
37
|
def add_typed_key(key, type, default: UNDEFINED, **options)
|
27
38
|
type = ActiveRecord::Type.lookup(type, **options) if type.is_a?(Symbol)
|
@@ -36,15 +47,13 @@ module ActiveRecord
|
|
36
47
|
accessor_types.each do |key, type|
|
37
48
|
if hash.key?(key)
|
38
49
|
hash[key] = type.deserialize(hash[key])
|
39
|
-
elsif defaults.key?(key)
|
40
|
-
hash[key] = get_default(key)
|
41
50
|
end
|
42
51
|
end
|
43
52
|
hash
|
44
53
|
end
|
45
54
|
|
46
55
|
def changed_in_place?(raw_old_value, new_value)
|
47
|
-
raw_old_value !=
|
56
|
+
deserialize(raw_old_value) != new_value
|
48
57
|
end
|
49
58
|
|
50
59
|
def serialize(value)
|
@@ -55,8 +64,6 @@ module ActiveRecord
|
|
55
64
|
next unless key
|
56
65
|
if value.key?(key)
|
57
66
|
typed_casted[key] = type.serialize(value[key])
|
58
|
-
elsif defaults.key?(str_key)
|
59
|
-
typed_casted[key] = type.serialize(get_default(str_key))
|
60
67
|
end
|
61
68
|
end
|
62
69
|
super(value.merge(typed_casted))
|
@@ -68,8 +75,6 @@ module ActiveRecord
|
|
68
75
|
accessor_types.each do |key, type|
|
69
76
|
if hash.key?(key)
|
70
77
|
hash[key] = type.cast(hash[key])
|
71
|
-
elsif defaults.key?(key)
|
72
|
-
hash[key] = get_default(key)
|
73
78
|
end
|
74
79
|
end
|
75
80
|
hash
|
@@ -86,6 +91,19 @@ module ActiveRecord
|
|
86
91
|
|
87
92
|
delegate :read, :prepare, to: :store_accessor
|
88
93
|
|
94
|
+
def build_defaults
|
95
|
+
defaults.transform_values do |val|
|
96
|
+
val.is_a?(Proc) ? val.call : val
|
97
|
+
end.with_indifferent_access
|
98
|
+
end
|
99
|
+
|
100
|
+
def dup
|
101
|
+
self.class.new(__getobj__).tap do |dtype|
|
102
|
+
dtype.accessor_types.merge!(accessor_types)
|
103
|
+
dtype.defaults.merge!(defaults)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
89
107
|
protected
|
90
108
|
|
91
109
|
# We cannot rely on string keys 'cause user input can contain symbol keys
|
@@ -103,11 +121,6 @@ module ActiveRecord
|
|
103
121
|
accessor_types.fetch(key.to_s)
|
104
122
|
end
|
105
123
|
|
106
|
-
def get_default(key)
|
107
|
-
value = defaults.fetch(key)
|
108
|
-
value.is_a?(Proc) ? value.call : value
|
109
|
-
end
|
110
|
-
|
111
124
|
attr_reader :accessor_types, :defaults, :store_accessor
|
112
125
|
end
|
113
126
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: store_attribute
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- palkan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-03-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '6.0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '6.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: pg
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -78,6 +78,7 @@ files:
|
|
78
78
|
- README.md
|
79
79
|
- lib/store_attribute.rb
|
80
80
|
- lib/store_attribute/active_record.rb
|
81
|
+
- lib/store_attribute/active_record/mutation_tracker.rb
|
81
82
|
- lib/store_attribute/active_record/store.rb
|
82
83
|
- lib/store_attribute/active_record/type/typed_store.rb
|
83
84
|
- lib/store_attribute/version.rb
|
@@ -98,7 +99,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
98
99
|
requirements:
|
99
100
|
- - ">="
|
100
101
|
- !ruby/object:Gem::Version
|
101
|
-
version: 2.
|
102
|
+
version: 2.6.0
|
102
103
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
103
104
|
requirements:
|
104
105
|
- - ">="
|