store_attribute 0.9.3 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
- - ">="
|