store_attribute 0.5.3 → 0.8.1
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 +39 -0
- data/{MIT-LICENSE → LICENSE.txt} +1 -1
- data/README.md +9 -3
- data/lib/store_attribute/active_record/store.rb +118 -14
- data/lib/store_attribute/active_record/type/typed_store.rb +38 -18
- data/lib/store_attribute/version.rb +1 -1
- metadata +17 -72
- data/.gitignore +0 -37
- data/.rspec +0 -1
- data/.rubocop.yml +0 -54
- data/.travis.yml +0 -21
- data/Gemfile +0 -11
- data/Rakefile +0 -10
- data/bench/bench.rb +0 -38
- data/bench/setup.rb +0 -67
- data/bin/console +0 -7
- data/bin/setup +0 -8
- data/gemfiles/rails5.gemfile +0 -5
- data/gemfiles/rails6.gemfile +0 -5
- data/gemfiles/railsmaster.gemfile +0 -5
- data/spec/cases/store_attribute_spec.rb +0 -173
- data/spec/spec_helper.rb +0 -45
- data/spec/store_attribute/typed_store_spec.rb +0 -109
- data/spec/support/money_type.rb +0 -14
- data/spec/support/user.rb +0 -19
- data/store_attribute.gemspec +0 -34
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 23a5221f6ede53f448653e54b3aa7432134c56e06369162a5370470e954fd1a1
|
4
|
+
data.tar.gz: 8df0761793ef5c39cda9c97039ca501fa8f9393fcb3ecc8abf85367414e3533c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2b4cedb5566a137159e863a59dc38797dcf7ddc590f779a957a6b6f6ac73e2c8f05f82876649304143f5160eefcbfc001773e9219d6d1d044fe9f483bad9c426
|
7
|
+
data.tar.gz: f95f478824a19e55243ca1b1cda44c284db8429b9afcad333fb7933ebca2c2ef54f0fbe98095523f1564a15e31f3a236e1b5546a249023c0c196635d8ec175c7
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# Change log
|
2
|
+
|
3
|
+
## master
|
4
|
+
|
5
|
+
## 0.8.1 (2020-12-03)
|
6
|
+
|
7
|
+
- Fix adding dirty tracking methods for `store_attribute`. ([@palkan][])
|
8
|
+
|
9
|
+
## 0.8.0
|
10
|
+
|
11
|
+
- Add Rails 6.1 compatibility. ([@palkan][])
|
12
|
+
|
13
|
+
- Add support for `prefix` and `suffix` options. ([@palkan][])
|
14
|
+
|
15
|
+
## 0.7.1
|
16
|
+
|
17
|
+
- Fixed bug with `store` called without accessors. ([@ioki-klaus][])
|
18
|
+
|
19
|
+
See [#10](https://github.com/palkan/store_attribute/pull/10).
|
20
|
+
|
21
|
+
## 0.7.0 (2020-03-23)
|
22
|
+
|
23
|
+
- Added dirty tracking methods. ([@glaszig][])
|
24
|
+
|
25
|
+
[PR #8](https://github.com/palkan/store_attribute/pull/8).
|
26
|
+
|
27
|
+
## 0.6.0 (2019-07-24)
|
28
|
+
|
29
|
+
- Added default values support. ([@dreikanter][], [@SumLare][])
|
30
|
+
|
31
|
+
See [PR #7](https://github.com/palkan/store_attribute/pull/7).
|
32
|
+
|
33
|
+
- Start keeping changelog. ([@palkan][])
|
34
|
+
|
35
|
+
[@palkan]: https://github.com/palkan
|
36
|
+
[@dreikanter]: https://github.com/dreikanter
|
37
|
+
[@SumLare]: https://github.com/SumLare
|
38
|
+
[@glaszig]: https://github.com/glaszig
|
39
|
+
[@ioki-klaus]: https://github.com/ioki-klaus
|
data/{MIT-LICENSE → LICENSE.txt}
RENAMED
data/README.md
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
[](https://cultofmartians.com/tasks/store-attribute-defaults.html#task)
|
1
2
|
[](https://rubygems.org/gems/store_attribute) [](https://travis-ci.org/palkan/store_attribute)
|
2
3
|
|
3
4
|
## Store Attribute
|
@@ -6,8 +7,7 @@ ActiveRecord extension which adds typecasting to store accessors.
|
|
6
7
|
|
7
8
|
Compatible with Rails 4.2 and Rails 5+.
|
8
9
|
|
9
|
-
|
10
|
-
<img src="https://evilmartians.com/badges/sponsored-by-evil-martians.svg" alt="Sponsored by Evil Martians" width="236" height="54"></a>
|
10
|
+
Extracted from not merged PR to Rails: [rails/rails#18942](https://github.com/rails/rails/pull/18942).
|
11
11
|
|
12
12
|
### Install
|
13
13
|
|
@@ -33,7 +33,7 @@ Where:
|
|
33
33
|
- `store_name` The name of the store.
|
34
34
|
- `name` The name of the accessor to the store.
|
35
35
|
- `type` A symbol such as `:string` or `:integer`, or a type object to be used for the accessor.
|
36
|
-
- `options` (optional) A hash of cast type options such as `precision`, `limit`, `scale`.
|
36
|
+
- `options` (optional) A hash of cast type options such as `precision`, `limit`, `scale`, `default`.
|
37
37
|
|
38
38
|
Type casting occurs every time you write data through accessor or update store itself
|
39
39
|
and when object is loaded from database.
|
@@ -47,6 +47,8 @@ class MegaUser < User
|
|
47
47
|
store_attribute :settings, :ratio, :integer, limit: 1
|
48
48
|
store_attribute :settings, :login_at, :datetime
|
49
49
|
store_attribute :settings, :active, :boolean
|
50
|
+
store_attribute :settings, :color, :string, default: "red"
|
51
|
+
store_attribute :settings, :data, :datetime, default: -> { Time.now }
|
50
52
|
end
|
51
53
|
|
52
54
|
u = MegaUser.new(active: false, login_at: "2015-01-01 00:01", ratio: "63.4608")
|
@@ -55,6 +57,10 @@ u.login_at.is_a?(DateTime) # => true
|
|
55
57
|
u.login_at = DateTime.new(2015, 1, 1, 11, 0, 0)
|
56
58
|
u.ratio # => 63
|
57
59
|
u.active # => false
|
60
|
+
# Default value is set
|
61
|
+
u.color # => red
|
62
|
+
# A dynamic default can also be provided
|
63
|
+
u.data # => Current time
|
58
64
|
# And we also have a predicate method
|
59
65
|
u.active? # => false
|
60
66
|
u.reload
|
@@ -25,8 +25,15 @@ module ActiveRecord
|
|
25
25
|
# end
|
26
26
|
def store(store_name, options = {})
|
27
27
|
accessors = options.delete(:accessors)
|
28
|
+
typed_accessors =
|
29
|
+
if accessors && accessors.last.is_a?(Hash)
|
30
|
+
accessors.pop
|
31
|
+
else
|
32
|
+
{}
|
33
|
+
end
|
34
|
+
|
28
35
|
_orig_store(store_name, options)
|
29
|
-
store_accessor(store_name, *accessors) if accessors
|
36
|
+
store_accessor(store_name, *accessors, **typed_accessors) if accessors
|
30
37
|
end
|
31
38
|
|
32
39
|
# Adds additional accessors to an existing store on this model.
|
@@ -37,21 +44,29 @@ module ActiveRecord
|
|
37
44
|
#
|
38
45
|
# +typed_keys+ The key-to-type hash of the accesors with type to the store.
|
39
46
|
#
|
47
|
+
# +prefix+ Accessor method name prefix
|
48
|
+
#
|
49
|
+
# +suffix+ Accessor method name suffix
|
50
|
+
#
|
40
51
|
# Examples:
|
41
52
|
#
|
42
53
|
# class SuperUser < User
|
43
54
|
# store_accessor :settings, :privileges, login_at: :datetime
|
44
55
|
# end
|
45
|
-
def store_accessor(store_name, *keys, **typed_keys)
|
56
|
+
def store_accessor(store_name, *keys, prefix: nil, suffix: nil, **typed_keys)
|
46
57
|
keys = keys.flatten
|
47
58
|
typed_keys = typed_keys.except(keys)
|
48
59
|
|
49
|
-
|
60
|
+
accessor_prefix, accessor_suffix = _normalize_prefix_suffix(store_name, prefix, suffix)
|
61
|
+
|
62
|
+
_define_accessors_methods(store_name, *keys, prefix: accessor_prefix, suffix: accessor_suffix)
|
63
|
+
|
64
|
+
_define_dirty_tracking_methods(store_name, keys + typed_keys.keys, prefix: accessor_prefix, suffix: accessor_suffix)
|
50
65
|
|
51
66
|
_prepare_local_stored_attributes(store_name, *keys)
|
52
67
|
|
53
68
|
typed_keys.each do |key, type|
|
54
|
-
store_attribute(store_name, key, type)
|
69
|
+
store_attribute(store_name, key, type, prefix: prefix, suffix: suffix)
|
55
70
|
end
|
56
71
|
end
|
57
72
|
|
@@ -68,6 +83,10 @@ module ActiveRecord
|
|
68
83
|
# +type+ A symbol such as +:string+ or +:integer+, or a type object
|
69
84
|
# to be used for the accessor.
|
70
85
|
#
|
86
|
+
# +prefix+ Accessor method name prefix
|
87
|
+
#
|
88
|
+
# +suffix+ Accessor method name suffix
|
89
|
+
#
|
71
90
|
# +options+ A hash of cast type options such as +precision+, +limit+, +scale+.
|
72
91
|
#
|
73
92
|
# Examples:
|
@@ -75,13 +94,16 @@ module ActiveRecord
|
|
75
94
|
# class MegaUser < User
|
76
95
|
# store_attribute :settings, :ratio, :integer, limit: 1
|
77
96
|
# store_attribute :settings, :login_at, :datetime
|
97
|
+
#
|
98
|
+
# store_attribute :extra, :version, :integer, prefix: :meta
|
78
99
|
# end
|
79
100
|
#
|
80
|
-
# u = MegaUser.new(active: false, login_at: '2015-01-01 00:01', ratio: "63.4608")
|
101
|
+
# u = MegaUser.new(active: false, login_at: '2015-01-01 00:01', ratio: "63.4608", meta_version: "1")
|
81
102
|
#
|
82
103
|
# u.login_at.is_a?(DateTime) # => true
|
83
104
|
# u.login_at = DateTime.new(2015,1,1,11,0,0)
|
84
105
|
# u.ratio # => 63
|
106
|
+
# u.meta_version #=> 1
|
85
107
|
# u.reload
|
86
108
|
#
|
87
109
|
# # After loading record from db store contains casted data
|
@@ -98,15 +120,28 @@ module ActiveRecord
|
|
98
120
|
# u.settings['ratio'] # => 3
|
99
121
|
#
|
100
122
|
# For more examples on using types, see documentation for ActiveRecord::Attributes.
|
101
|
-
def store_attribute(store_name, name, type, **options)
|
102
|
-
|
123
|
+
def store_attribute(store_name, name, type, prefix: nil, suffix: nil, **options)
|
124
|
+
prefix, suffix = _normalize_prefix_suffix(store_name, prefix, suffix)
|
125
|
+
|
126
|
+
_define_accessors_methods(store_name, name, prefix: prefix, suffix: suffix)
|
103
127
|
|
104
|
-
_define_predicate_method(name) if type == :boolean
|
128
|
+
_define_predicate_method(name, prefix: prefix, suffix: suffix) if type == :boolean
|
105
129
|
|
106
|
-
|
107
|
-
|
130
|
+
# Rails >6.0
|
131
|
+
if method(:decorate_attribute_type).parameters.count { |type, _| type == :req } == 1
|
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
|
+
Type::TypedStore.create_from_type(_lookup_cast_type(attr_name, was_type, {}), name, type, **options)
|
136
|
+
end
|
137
|
+
else
|
138
|
+
decorate_attribute_type(store_name, "typed_accessor_for_#{name}") do |subtype|
|
139
|
+
Type::TypedStore.create_from_type(subtype, name, type, **options)
|
140
|
+
end
|
108
141
|
end
|
109
142
|
|
143
|
+
_define_dirty_tracking_methods(store_name, [name], prefix: prefix, suffix: suffix)
|
144
|
+
|
110
145
|
_prepare_local_stored_attributes(store_name, name)
|
111
146
|
end
|
112
147
|
|
@@ -118,27 +153,96 @@ module ActiveRecord
|
|
118
153
|
self.local_stored_attributes[store_name] |= keys
|
119
154
|
end
|
120
155
|
|
121
|
-
def _define_accessors_methods(store_name, *keys) # :nodoc:
|
156
|
+
def _define_accessors_methods(store_name, *keys, prefix: nil, suffix: nil) # :nodoc:
|
122
157
|
_store_accessors_module.module_eval do
|
123
158
|
keys.each do |key|
|
124
|
-
|
159
|
+
accessor_key = "#{prefix}#{key}#{suffix}"
|
160
|
+
|
161
|
+
define_method("#{accessor_key}=") do |value|
|
125
162
|
write_store_attribute(store_name, key, value)
|
126
163
|
end
|
127
164
|
|
128
|
-
define_method(
|
165
|
+
define_method(accessor_key) do
|
129
166
|
read_store_attribute(store_name, key)
|
130
167
|
end
|
131
168
|
end
|
132
169
|
end
|
133
170
|
end
|
134
171
|
|
135
|
-
def _define_predicate_method(name)
|
172
|
+
def _define_predicate_method(name, prefix: nil, suffix: nil)
|
136
173
|
_store_accessors_module.module_eval do
|
174
|
+
name = "#{prefix}#{name}#{suffix}"
|
175
|
+
|
137
176
|
define_method("#{name}?") do
|
138
177
|
send(name) == true
|
139
178
|
end
|
140
179
|
end
|
141
180
|
end
|
181
|
+
|
182
|
+
def _define_dirty_tracking_methods(store_attribute, keys, prefix: nil, suffix: nil)
|
183
|
+
_store_accessors_module.module_eval do
|
184
|
+
keys.flatten.each do |key|
|
185
|
+
key = key.to_s
|
186
|
+
accessor_key = "#{prefix}#{key}#{suffix}"
|
187
|
+
|
188
|
+
define_method("#{accessor_key}_changed?") do
|
189
|
+
return false unless attribute_changed?(store_attribute)
|
190
|
+
prev_store, new_store = changes[store_attribute]
|
191
|
+
prev_store&.dig(key) != new_store&.dig(key)
|
192
|
+
end
|
193
|
+
|
194
|
+
define_method("#{accessor_key}_change") do
|
195
|
+
return unless attribute_changed?(store_attribute)
|
196
|
+
prev_store, new_store = changes[store_attribute]
|
197
|
+
[prev_store&.dig(key), new_store&.dig(key)]
|
198
|
+
end
|
199
|
+
|
200
|
+
define_method("#{accessor_key}_was") do
|
201
|
+
return unless attribute_changed?(store_attribute)
|
202
|
+
prev_store, _new_store = changes[store_attribute]
|
203
|
+
prev_store&.dig(key)
|
204
|
+
end
|
205
|
+
|
206
|
+
define_method("saved_change_to_#{accessor_key}?") do
|
207
|
+
return false unless saved_change_to_attribute?(store_attribute)
|
208
|
+
prev_store, new_store = saved_change_to_attribute(store_attribute)
|
209
|
+
prev_store&.dig(key) != new_store&.dig(key)
|
210
|
+
end
|
211
|
+
|
212
|
+
define_method("saved_change_to_#{accessor_key}") do
|
213
|
+
return unless saved_change_to_attribute?(store_attribute)
|
214
|
+
prev_store, new_store = saved_change_to_attribute(store_attribute)
|
215
|
+
[prev_store&.dig(key), new_store&.dig(key)]
|
216
|
+
end
|
217
|
+
|
218
|
+
define_method("#{accessor_key}_before_last_save") do
|
219
|
+
return unless saved_change_to_attribute?(store_attribute)
|
220
|
+
prev_store, _new_store = saved_change_to_attribute(store_attribute)
|
221
|
+
prev_store&.dig(key)
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def _normalize_prefix_suffix(store_name, prefix, suffix)
|
228
|
+
prefix =
|
229
|
+
case prefix
|
230
|
+
when String, Symbol
|
231
|
+
"#{prefix}_"
|
232
|
+
when TrueClass
|
233
|
+
"#{store_name}_"
|
234
|
+
end
|
235
|
+
|
236
|
+
suffix =
|
237
|
+
case suffix
|
238
|
+
when String, Symbol
|
239
|
+
"_#{suffix}"
|
240
|
+
when TrueClass
|
241
|
+
"_#{store_name}"
|
242
|
+
end
|
243
|
+
|
244
|
+
[prefix, suffix]
|
245
|
+
end
|
142
246
|
end
|
143
247
|
end
|
144
248
|
end
|
@@ -15,43 +15,57 @@ module ActiveRecord
|
|
15
15
|
|
16
16
|
def initialize(subtype)
|
17
17
|
@accessor_types = {}
|
18
|
+
@defaults = {}
|
18
19
|
@store_accessor = subtype.accessor
|
19
20
|
super(subtype)
|
20
21
|
end
|
21
22
|
|
22
|
-
|
23
|
-
|
24
|
-
|
23
|
+
UNDEFINED = Object.new
|
24
|
+
private_constant :UNDEFINED
|
25
|
+
|
26
|
+
def add_typed_key(key, type, default: UNDEFINED, **options)
|
27
|
+
type = ActiveRecord::Type.lookup(type, **options) if type.is_a?(Symbol)
|
28
|
+
safe_key = key.to_s
|
29
|
+
@accessor_types[safe_key] = type
|
30
|
+
@defaults[safe_key] = default unless default == UNDEFINED
|
25
31
|
end
|
26
32
|
|
27
33
|
def deserialize(value)
|
28
34
|
hash = super
|
29
|
-
|
30
|
-
|
31
|
-
|
35
|
+
return hash unless hash
|
36
|
+
accessor_types.each do |key, type|
|
37
|
+
if hash.key?(key)
|
38
|
+
hash[key] = type.deserialize(hash[key])
|
39
|
+
elsif defaults.key?(key)
|
40
|
+
hash[key] = get_default(key)
|
32
41
|
end
|
33
42
|
end
|
34
43
|
hash
|
35
44
|
end
|
36
45
|
|
37
46
|
def serialize(value)
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
47
|
+
return super(value) unless value.is_a?(Hash)
|
48
|
+
typed_casted = {}
|
49
|
+
accessor_types.each do |str_key, type|
|
50
|
+
key = key_to_cast(value, str_key)
|
51
|
+
next unless key
|
52
|
+
if value.key?(key)
|
53
|
+
typed_casted[key] = type.serialize(value[key])
|
54
|
+
elsif defaults.key?(str_key)
|
55
|
+
typed_casted[key] = type.serialize(get_default(str_key))
|
43
56
|
end
|
44
|
-
super(value.merge(typed_casted))
|
45
|
-
else
|
46
|
-
super(value)
|
47
57
|
end
|
58
|
+
super(value.merge(typed_casted))
|
48
59
|
end
|
49
60
|
|
50
61
|
def cast(value)
|
51
62
|
hash = super
|
52
|
-
|
53
|
-
|
54
|
-
|
63
|
+
return hash unless hash
|
64
|
+
accessor_types.each do |key, type|
|
65
|
+
if hash.key?(key)
|
66
|
+
hash[key] = type.cast(hash[key])
|
67
|
+
elsif defaults.key?(key)
|
68
|
+
hash[key] = get_default(key)
|
55
69
|
end
|
56
70
|
end
|
57
71
|
hash
|
@@ -74,6 +88,7 @@ module ActiveRecord
|
|
74
88
|
def key_to_cast(val, key)
|
75
89
|
return key if val.key?(key)
|
76
90
|
return key.to_sym if val.key?(key.to_sym)
|
91
|
+
return key if defaults.key?(key)
|
77
92
|
end
|
78
93
|
|
79
94
|
def typed?(key)
|
@@ -84,7 +99,12 @@ module ActiveRecord
|
|
84
99
|
accessor_types.fetch(key.to_s)
|
85
100
|
end
|
86
101
|
|
87
|
-
|
102
|
+
def get_default(key)
|
103
|
+
value = defaults.fetch(key)
|
104
|
+
value.is_a?(Proc) ? value.call : value
|
105
|
+
end
|
106
|
+
|
107
|
+
attr_reader :accessor_types, :defaults, :store_accessor
|
88
108
|
end
|
89
109
|
end
|
90
110
|
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: 0.8.1
|
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: 2020-12-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -44,84 +44,42 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
47
|
+
version: '13.0'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: simplecov
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - ">="
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: 0.3.8
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - ">="
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: 0.3.8
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: pry-byebug
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - ">="
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: '0'
|
76
|
-
type: :development
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - ">="
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: '0'
|
83
|
-
- !ruby/object:Gem::Dependency
|
84
|
-
name: rubocop
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
86
|
-
requirements:
|
87
|
-
- - "~>"
|
88
|
-
- !ruby/object:Gem::Version
|
89
|
-
version: 0.65.0
|
90
|
-
type: :development
|
91
|
-
prerelease: false
|
92
|
-
version_requirements: !ruby/object:Gem::Requirement
|
93
|
-
requirements:
|
94
|
-
- - "~>"
|
95
|
-
- !ruby/object:Gem::Version
|
96
|
-
version: 0.65.0
|
54
|
+
version: '13.0'
|
97
55
|
- !ruby/object:Gem::Dependency
|
98
56
|
name: rubocop-md
|
99
57
|
requirement: !ruby/object:Gem::Requirement
|
100
58
|
requirements:
|
101
59
|
- - "~>"
|
102
60
|
- !ruby/object:Gem::Version
|
103
|
-
version: '0.
|
61
|
+
version: '0.3'
|
104
62
|
type: :development
|
105
63
|
prerelease: false
|
106
64
|
version_requirements: !ruby/object:Gem::Requirement
|
107
65
|
requirements:
|
108
66
|
- - "~>"
|
109
67
|
- !ruby/object:Gem::Version
|
110
|
-
version: '0.
|
68
|
+
version: '0.3'
|
111
69
|
- !ruby/object:Gem::Dependency
|
112
70
|
name: standard
|
113
71
|
requirement: !ruby/object:Gem::Requirement
|
114
72
|
requirements:
|
115
73
|
- - "~>"
|
116
74
|
- !ruby/object:Gem::Version
|
117
|
-
version: 0.0
|
75
|
+
version: 0.2.0
|
118
76
|
type: :development
|
119
77
|
prerelease: false
|
120
78
|
version_requirements: !ruby/object:Gem::Requirement
|
121
79
|
requirements:
|
122
80
|
- - "~>"
|
123
81
|
- !ruby/object:Gem::Version
|
124
|
-
version: 0.0
|
82
|
+
version: 0.2.0
|
125
83
|
- !ruby/object:Gem::Dependency
|
126
84
|
name: rspec
|
127
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -143,36 +101,23 @@ executables: []
|
|
143
101
|
extensions: []
|
144
102
|
extra_rdoc_files: []
|
145
103
|
files:
|
146
|
-
-
|
147
|
-
-
|
148
|
-
- ".rubocop.yml"
|
149
|
-
- ".travis.yml"
|
150
|
-
- Gemfile
|
151
|
-
- MIT-LICENSE
|
104
|
+
- CHANGELOG.md
|
105
|
+
- LICENSE.txt
|
152
106
|
- README.md
|
153
|
-
- Rakefile
|
154
|
-
- bench/bench.rb
|
155
|
-
- bench/setup.rb
|
156
|
-
- bin/console
|
157
|
-
- bin/setup
|
158
|
-
- gemfiles/rails5.gemfile
|
159
|
-
- gemfiles/rails6.gemfile
|
160
|
-
- gemfiles/railsmaster.gemfile
|
161
107
|
- lib/store_attribute.rb
|
162
108
|
- lib/store_attribute/active_record.rb
|
163
109
|
- lib/store_attribute/active_record/store.rb
|
164
110
|
- lib/store_attribute/active_record/type/typed_store.rb
|
165
111
|
- lib/store_attribute/version.rb
|
166
|
-
- spec/cases/store_attribute_spec.rb
|
167
|
-
- spec/spec_helper.rb
|
168
|
-
- spec/store_attribute/typed_store_spec.rb
|
169
|
-
- spec/support/money_type.rb
|
170
|
-
- spec/support/user.rb
|
171
|
-
- store_attribute.gemspec
|
172
112
|
homepage: http://github.com/palkan/store_attribute
|
173
113
|
licenses:
|
174
114
|
- MIT
|
175
|
-
metadata:
|
115
|
+
metadata:
|
116
|
+
bug_tracker_uri: http://github.com/palkan/store_attribute/issues
|
117
|
+
changelog_uri: https://github.com/palkan/store_attribute/blob/master/CHANGELOG.md
|
118
|
+
documentation_uri: http://github.com/palkan/store_attribute
|
119
|
+
homepage_uri: http://github.com/palkan/store_attribute
|
120
|
+
source_code_uri: http://github.com/palkan/store_attribute
|
176
121
|
post_install_message:
|
177
122
|
rdoc_options: []
|
178
123
|
require_paths:
|
@@ -188,7 +133,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
188
133
|
- !ruby/object:Gem::Version
|
189
134
|
version: '0'
|
190
135
|
requirements: []
|
191
|
-
rubygems_version: 3.0.
|
136
|
+
rubygems_version: 3.0.6
|
192
137
|
signing_key:
|
193
138
|
specification_version: 4
|
194
139
|
summary: ActiveRecord extension which adds typecasting to store accessors
|
data/.gitignore
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
# Numerous always-ignore extensions
|
2
|
-
*.diff
|
3
|
-
*.err
|
4
|
-
*.orig
|
5
|
-
*.log
|
6
|
-
*.rej
|
7
|
-
*.swo
|
8
|
-
*.swp
|
9
|
-
*.vi
|
10
|
-
*~
|
11
|
-
*.sass-cache
|
12
|
-
*.iml
|
13
|
-
.idea/
|
14
|
-
|
15
|
-
# Sublime
|
16
|
-
*.sublime-project
|
17
|
-
*.sublime-workspace
|
18
|
-
|
19
|
-
# OS or Editor folders
|
20
|
-
.DS_Store
|
21
|
-
.cache
|
22
|
-
.project
|
23
|
-
.settings
|
24
|
-
.tmproj
|
25
|
-
Thumbs.db
|
26
|
-
coverage/
|
27
|
-
|
28
|
-
.bundle/
|
29
|
-
*.log
|
30
|
-
*.gem
|
31
|
-
pkg/
|
32
|
-
spec/dummy/log/*.log
|
33
|
-
spec/dummy/tmp/
|
34
|
-
spec/dummy/.sass-cache
|
35
|
-
Gemfile.local
|
36
|
-
Gemfile.lock
|
37
|
-
tmp/
|
data/.rspec
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
--color
|
data/.rubocop.yml
DELETED
@@ -1,54 +0,0 @@
|
|
1
|
-
require:
|
2
|
-
- standard/cop/semantic_blocks
|
3
|
-
- rubocop-md
|
4
|
-
|
5
|
-
inherit_gem:
|
6
|
-
standard: config/base.yml
|
7
|
-
|
8
|
-
AllCops:
|
9
|
-
Exclude:
|
10
|
-
- 'bin/*'
|
11
|
-
- 'tmp/**/*'
|
12
|
-
- 'Gemfile'
|
13
|
-
- 'vendor/**/*'
|
14
|
-
- 'gemfiles/**/*'
|
15
|
-
- 'bench/**/*'
|
16
|
-
DisplayCopNames: true
|
17
|
-
TargetRubyVersion: 2.4
|
18
|
-
|
19
|
-
Standard/SemanticBlocks:
|
20
|
-
Enabled: false
|
21
|
-
|
22
|
-
Style/FrozenStringLiteralComment:
|
23
|
-
Enabled: true
|
24
|
-
|
25
|
-
Style/TrailingCommaInArrayLiteral:
|
26
|
-
EnforcedStyleForMultiline: no_comma
|
27
|
-
|
28
|
-
Style/TrailingCommaInHashLiteral:
|
29
|
-
EnforcedStyleForMultiline: no_comma
|
30
|
-
|
31
|
-
Layout/AlignParameters:
|
32
|
-
EnforcedStyle: with_first_parameter
|
33
|
-
|
34
|
-
Lint/Void:
|
35
|
-
Exclude:
|
36
|
-
- '**/*.md'
|
37
|
-
|
38
|
-
# See https://github.com/rubocop-hq/rubocop/issues/4222
|
39
|
-
Lint/AmbiguousBlockAssociation:
|
40
|
-
Exclude:
|
41
|
-
- 'spec/**/*'
|
42
|
-
- '**/*.md'
|
43
|
-
|
44
|
-
Lint/DuplicateMethods:
|
45
|
-
Exclude:
|
46
|
-
- '**/*.md'
|
47
|
-
|
48
|
-
Naming/FileName:
|
49
|
-
Exclude:
|
50
|
-
- '**/*.md'
|
51
|
-
|
52
|
-
Layout/InitialIndentation:
|
53
|
-
Exclude:
|
54
|
-
- 'CHANGELOG.md'
|
data/.travis.yml
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
language: ruby
|
2
|
-
cache: bundler
|
3
|
-
|
4
|
-
addons:
|
5
|
-
postgresql: "9.4"
|
6
|
-
|
7
|
-
before_script:
|
8
|
-
- createdb store_attribute_test
|
9
|
-
- psql -U postgres -d store_attribute_test -c 'CREATE EXTENSION IF NOT EXISTS hstore;'
|
10
|
-
|
11
|
-
matrix:
|
12
|
-
include:
|
13
|
-
- rvm: 2.4.1
|
14
|
-
gemfile: gemfiles/rails5.gemfile
|
15
|
-
- rvm: 2.6.2
|
16
|
-
gemfile: gemfiles/rails6.gemfile
|
17
|
-
- rvm: ruby-head
|
18
|
-
gemfile: gemfiles/railsmaster.gemfile
|
19
|
-
allow_failures:
|
20
|
-
- rvm: ruby-head
|
21
|
-
gemfile: gemfiles/railsmaster.gemfile
|
data/Gemfile
DELETED
data/Rakefile
DELETED
data/bench/bench.rb
DELETED
@@ -1,38 +0,0 @@
|
|
1
|
-
require 'benchmark/ips'
|
2
|
-
require './setup'
|
3
|
-
|
4
|
-
Benchmark.ips do |x|
|
5
|
-
x.report('SA initialize') do
|
6
|
-
User.new(public: '1', published_at: '2016-01-01', age: '23')
|
7
|
-
end
|
8
|
-
|
9
|
-
x.report('AR-T initialize') do
|
10
|
-
Looser.new(public: '1', published_at: '2016-01-01', age: '23')
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
Benchmark.ips do |x|
|
15
|
-
x.report('SA accessors') do
|
16
|
-
u = User.new
|
17
|
-
u.public = '1'
|
18
|
-
u.published_at = '2016-01-01'
|
19
|
-
u.age = '23'
|
20
|
-
end
|
21
|
-
|
22
|
-
x.report('AR-T accessors') do
|
23
|
-
u = Looser.new
|
24
|
-
u.public = '1'
|
25
|
-
u.published_at = '2016-01-01'
|
26
|
-
u.age = '23'
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
Benchmark.ips do |x|
|
31
|
-
x.report('SA create') do
|
32
|
-
User.create!(public: '1', published_at: '2016-01-01', age: '23')
|
33
|
-
end
|
34
|
-
|
35
|
-
x.report('AR-T create') do
|
36
|
-
Looser.create(public: '1', published_at: '2016-01-01', age: '23')
|
37
|
-
end
|
38
|
-
end
|
data/bench/setup.rb
DELETED
@@ -1,67 +0,0 @@
|
|
1
|
-
begin
|
2
|
-
require 'bundler/inline'
|
3
|
-
rescue LoadError => e
|
4
|
-
$stderr.puts 'Bundler version 1.10 or later is required. Please update your Bundler'
|
5
|
-
raise e
|
6
|
-
end
|
7
|
-
|
8
|
-
gemfile(true) do
|
9
|
-
source 'https://rubygems.org'
|
10
|
-
gem 'activerecord', '~>4.2'
|
11
|
-
gem 'pg'
|
12
|
-
gem 'activerecord-typedstore', require: false
|
13
|
-
gem 'pry-byebug'
|
14
|
-
gem 'benchmark-ips'
|
15
|
-
gem 'memory_profiler'
|
16
|
-
end
|
17
|
-
|
18
|
-
DB_NAME = ENV['DB_NAME'] || 'sa_bench'
|
19
|
-
|
20
|
-
begin
|
21
|
-
system("createdb #{DB_NAME}")
|
22
|
-
rescue
|
23
|
-
$stdout.puts "DB already exists"
|
24
|
-
end
|
25
|
-
|
26
|
-
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
27
|
-
|
28
|
-
require 'active_record'
|
29
|
-
require 'logger'
|
30
|
-
require 'store_attribute'
|
31
|
-
require 'activerecord-typedstore'
|
32
|
-
|
33
|
-
ActiveRecord::Base.establish_connection(adapter: 'postgresql', database: DB_NAME)
|
34
|
-
|
35
|
-
at_exit do
|
36
|
-
ActiveRecord::Base.connection.disconnect!
|
37
|
-
end
|
38
|
-
|
39
|
-
module Bench
|
40
|
-
module_function
|
41
|
-
def setup_db
|
42
|
-
ActiveRecord::Schema.define do
|
43
|
-
create_table :users, force: true do |t|
|
44
|
-
t.jsonb :data
|
45
|
-
end
|
46
|
-
|
47
|
-
create_table :loosers, force: true do |t|
|
48
|
-
t.jsonb :data
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
class User < ActiveRecord::Base
|
55
|
-
store_accessor :data, public: :boolean, published_at: :datetime, age: :integer
|
56
|
-
end
|
57
|
-
|
58
|
-
class Looser < ActiveRecord::Base
|
59
|
-
typed_store :data, coder: JSON do |s|
|
60
|
-
s.boolean :public
|
61
|
-
s.datetime :published_at
|
62
|
-
s.integer :age
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
# Run migration only if neccessary
|
67
|
-
Bench.setup_db if ENV['FORCE'].present? || !ActiveRecord::Base.connection.tables.include?('users')
|
data/bin/console
DELETED
data/bin/setup
DELETED
data/gemfiles/rails5.gemfile
DELETED
data/gemfiles/rails6.gemfile
DELETED
@@ -1,173 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "spec_helper"
|
4
|
-
|
5
|
-
describe StoreAttribute do
|
6
|
-
before do
|
7
|
-
@connection = ActiveRecord::Base.connection
|
8
|
-
|
9
|
-
@connection.transaction do
|
10
|
-
@connection.create_table("users") do |t|
|
11
|
-
t.jsonb :jparams, default: {}, null: false
|
12
|
-
t.text :custom
|
13
|
-
t.hstore :hdata, default: {}, null: false
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
User.reset_column_information
|
18
|
-
end
|
19
|
-
|
20
|
-
after do
|
21
|
-
@connection.drop_table "users", if_exists: true
|
22
|
-
end
|
23
|
-
|
24
|
-
let(:time) { DateTime.new(2015, 2, 14, 17, 0, 0) }
|
25
|
-
let(:time_str) { "2015-02-14 17:00" }
|
26
|
-
let(:time_str_utc) { "2015-02-14 17:00:00 UTC" }
|
27
|
-
|
28
|
-
context "hstore" do
|
29
|
-
it "typecasts on build" do
|
30
|
-
user = User.new(visible: "t", login_at: time_str)
|
31
|
-
expect(user.visible).to eq true
|
32
|
-
expect(user).to be_visible
|
33
|
-
expect(user.login_at).to eq time
|
34
|
-
end
|
35
|
-
|
36
|
-
it "typecasts on reload" do
|
37
|
-
user = User.new(visible: "t", login_at: time_str)
|
38
|
-
user.save!
|
39
|
-
user = User.find(user.id)
|
40
|
-
|
41
|
-
expect(user.visible).to eq true
|
42
|
-
expect(user).to be_visible
|
43
|
-
expect(user.login_at).to eq time
|
44
|
-
end
|
45
|
-
|
46
|
-
it "works with accessors" do
|
47
|
-
user = User.new
|
48
|
-
user.visible = false
|
49
|
-
user.login_at = time_str
|
50
|
-
user.save!
|
51
|
-
|
52
|
-
user = User.find(user.id)
|
53
|
-
|
54
|
-
expect(user.visible).to be false
|
55
|
-
expect(user).not_to be_visible
|
56
|
-
expect(user.login_at).to eq time
|
57
|
-
|
58
|
-
ron = RawUser.find(user.id)
|
59
|
-
expect(ron.hdata["visible"]).to eq "false"
|
60
|
-
expect(ron.hdata["login_at"]).to eq time_str_utc
|
61
|
-
end
|
62
|
-
|
63
|
-
it "handles options" do
|
64
|
-
expect { User.create!(ratio: 1024) }.to raise_error(RangeError)
|
65
|
-
end
|
66
|
-
|
67
|
-
it "YAML roundtrip" do
|
68
|
-
user = User.create!(visible: "0", login_at: time_str)
|
69
|
-
dumped = YAML.load(YAML.dump(user)) # rubocop:disable Security/YAMLLoad
|
70
|
-
|
71
|
-
expect(dumped.visible).to be false
|
72
|
-
expect(dumped.login_at).to eq time
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
context "jsonb" do
|
77
|
-
it "typecasts on build" do
|
78
|
-
jamie = User.new(
|
79
|
-
active: "true",
|
80
|
-
salary: 3.1999,
|
81
|
-
birthday: "2000-01-01"
|
82
|
-
)
|
83
|
-
expect(jamie).to be_active
|
84
|
-
expect(jamie.salary).to eq 3
|
85
|
-
expect(jamie.birthday).to eq Date.new(2000, 1, 1)
|
86
|
-
expect(jamie.jparams["birthday"]).to eq Date.new(2000, 1, 1)
|
87
|
-
expect(jamie.jparams["active"]).to eq true
|
88
|
-
end
|
89
|
-
|
90
|
-
it "typecasts on reload" do
|
91
|
-
jamie = User.create!(jparams: {"active" => "1", "birthday" => "01/01/2000", "salary" => "3.14"})
|
92
|
-
jamie = User.find(jamie.id)
|
93
|
-
|
94
|
-
expect(jamie).to be_active
|
95
|
-
expect(jamie.salary).to eq 3
|
96
|
-
expect(jamie.birthday).to eq Date.new(2000, 1, 1)
|
97
|
-
expect(jamie.jparams["birthday"]).to eq Date.new(2000, 1, 1)
|
98
|
-
expect(jamie.jparams["active"]).to eq true
|
99
|
-
end
|
100
|
-
|
101
|
-
it "works with accessors" do
|
102
|
-
john = User.new
|
103
|
-
john.active = 1
|
104
|
-
|
105
|
-
expect(john).to be_active
|
106
|
-
expect(john.jparams["active"]).to eq true
|
107
|
-
|
108
|
-
john.jparams = {active: "true", salary: "123.123", birthday: "01/01/2012"}
|
109
|
-
expect(john).to be_active
|
110
|
-
expect(john.birthday).to eq Date.new(2012, 1, 1)
|
111
|
-
expect(john.salary).to eq 123
|
112
|
-
|
113
|
-
john.save!
|
114
|
-
|
115
|
-
ron = RawUser.find(john.id)
|
116
|
-
expect(ron.jparams["active"]).to eq true
|
117
|
-
expect(ron.jparams["birthday"]).to eq "2012-01-01"
|
118
|
-
expect(ron.jparams["salary"]).to eq 123
|
119
|
-
end
|
120
|
-
|
121
|
-
it "re-typecast old data" do
|
122
|
-
jamie = User.create!
|
123
|
-
User.update_all('jparams = \'{"active":"1", "salary":"12.02"}\'::jsonb')
|
124
|
-
|
125
|
-
jamie = User.find(jamie.id)
|
126
|
-
expect(jamie).to be_active
|
127
|
-
expect(jamie.salary).to eq 12
|
128
|
-
|
129
|
-
jamie.save!
|
130
|
-
|
131
|
-
ron = RawUser.find(jamie.id)
|
132
|
-
expect(ron.jparams["active"]).to eq true
|
133
|
-
expect(ron.jparams["salary"]).to eq 12
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
context "custom types" do
|
138
|
-
it "typecasts on build" do
|
139
|
-
user = User.new(price: "$1")
|
140
|
-
expect(user.price).to eq 100
|
141
|
-
end
|
142
|
-
|
143
|
-
it "typecasts on reload" do
|
144
|
-
jamie = User.create!(custom: {price: "$12"})
|
145
|
-
expect(jamie.reload.price).to eq 1200
|
146
|
-
|
147
|
-
jamie = User.find(jamie.id)
|
148
|
-
|
149
|
-
expect(jamie.price).to eq 1200
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
context "store subtype" do
|
154
|
-
it "typecasts on build" do
|
155
|
-
user = User.new(inner_json: {x: 1})
|
156
|
-
expect(user.inner_json).to eq("x" => 1)
|
157
|
-
end
|
158
|
-
|
159
|
-
it "typecasts on update" do
|
160
|
-
user = User.new
|
161
|
-
user.update!(inner_json: {x: 1})
|
162
|
-
expect(user.inner_json).to eq("x" => 1)
|
163
|
-
|
164
|
-
expect(user.reload.inner_json).to eq("x" => 1)
|
165
|
-
end
|
166
|
-
|
167
|
-
it "typecasts on reload" do
|
168
|
-
jamie = User.create!(inner_json: {x: 1})
|
169
|
-
jamie = User.find(jamie.id)
|
170
|
-
expect(jamie.inner_json).to eq("x" => 1)
|
171
|
-
end
|
172
|
-
end
|
173
|
-
end
|
data/spec/spec_helper.rb
DELETED
@@ -1,45 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
|
4
|
-
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
5
|
-
|
6
|
-
if ENV["COVER"]
|
7
|
-
require "simplecov"
|
8
|
-
SimpleCov.root File.join(File.dirname(__FILE__), "..")
|
9
|
-
SimpleCov.start
|
10
|
-
end
|
11
|
-
|
12
|
-
require "rspec"
|
13
|
-
require "pry-byebug"
|
14
|
-
require "active_record"
|
15
|
-
require "pg"
|
16
|
-
require "store_attribute"
|
17
|
-
|
18
|
-
RAILS_5_1 = ActiveRecord.version.release >= Gem::Version.new("5.1.0")
|
19
|
-
|
20
|
-
ActiveRecord::Base.establish_connection(
|
21
|
-
adapter: "postgresql",
|
22
|
-
database: "store_attribute_test"
|
23
|
-
)
|
24
|
-
connection = ActiveRecord::Base.connection
|
25
|
-
|
26
|
-
unless connection.extension_enabled?("hstore")
|
27
|
-
connection.enable_extension "hstore"
|
28
|
-
connection.commit_db_transaction
|
29
|
-
end
|
30
|
-
|
31
|
-
connection.reconnect!
|
32
|
-
|
33
|
-
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
|
34
|
-
|
35
|
-
RSpec.configure do |config|
|
36
|
-
config.mock_with :rspec
|
37
|
-
|
38
|
-
config.filter_run_when_matching :focus
|
39
|
-
|
40
|
-
config.example_status_persistence_file_path = "tmp/rspec_examples.txt"
|
41
|
-
|
42
|
-
if config.files_to_run.one?
|
43
|
-
config.default_formatter = "doc"
|
44
|
-
end
|
45
|
-
end
|
@@ -1,109 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "spec_helper"
|
4
|
-
|
5
|
-
describe ActiveRecord::Type::TypedStore do
|
6
|
-
let(:json_type) { ActiveRecord::Type::Serialized.new(ActiveRecord::Type::Text.new, ActiveRecord::Coders::JSON) }
|
7
|
-
|
8
|
-
context "with json store" do
|
9
|
-
subject { described_class.new(json_type) }
|
10
|
-
|
11
|
-
describe "#cast" do
|
12
|
-
it "without key types", :aggregate_failures do
|
13
|
-
expect(subject.cast([1, 2])).to eq [1, 2]
|
14
|
-
expect(subject.cast("a" => "b")).to eq("a" => "b")
|
15
|
-
end
|
16
|
-
|
17
|
-
it "with type keys" do
|
18
|
-
subject.add_typed_key("date", :date)
|
19
|
-
|
20
|
-
date = ::Date.new(2016, 6, 22)
|
21
|
-
expect(subject.cast(date: "2016-06-22")).to eq("date" => date)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
describe "#deserialize" do
|
26
|
-
it "without key types", :aggregate_failures do
|
27
|
-
expect(subject.deserialize("[1,2]")).to eq [1, 2]
|
28
|
-
expect(subject.deserialize('{"a":"b"}')).to eq("a" => "b")
|
29
|
-
end
|
30
|
-
|
31
|
-
it "with type keys" do
|
32
|
-
subject.add_typed_key("date", :date)
|
33
|
-
|
34
|
-
date = ::Date.new(2016, 6, 22)
|
35
|
-
expect(subject.deserialize('{"date":"2016-06-22"}')).to eq("date" => date)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
describe "#serialize" do
|
40
|
-
it "without key types", :aggregate_failures do
|
41
|
-
expect(subject.serialize([1, 2])).to eq "[1,2]"
|
42
|
-
expect(subject.serialize("a" => "b")).to eq '{"a":"b"}'
|
43
|
-
end
|
44
|
-
|
45
|
-
it "with type keys" do
|
46
|
-
subject.add_typed_key("date", :date)
|
47
|
-
|
48
|
-
date = ::Date.new(2016, 6, 22)
|
49
|
-
expect(subject.serialize(date: date)).to eq '{"date":"2016-06-22"}'
|
50
|
-
end
|
51
|
-
|
52
|
-
it "with type key with option" do
|
53
|
-
subject.add_typed_key("val", :integer, limit: 1)
|
54
|
-
|
55
|
-
expect { subject.serialize(val: 1024) }.to raise_error(RangeError)
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
describe ".create_from_type" do
|
60
|
-
it "creates with valid types", :aggregate_failures do
|
61
|
-
type = described_class.create_from_type(json_type, "date", :date)
|
62
|
-
new_type = described_class.create_from_type(type, "val", :integer)
|
63
|
-
|
64
|
-
date = ::Date.new(2016, 6, 22)
|
65
|
-
|
66
|
-
expect(type.cast(date: "2016-06-22", val: "1.2")).to eq("date" => date, "val" => "1.2")
|
67
|
-
expect(new_type.cast(date: "2016-06-22", val: "1.2")).to eq("date" => date, "val" => 1)
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
context "with yaml coder" do
|
73
|
-
if RAILS_5_1
|
74
|
-
let(:yaml_type) do
|
75
|
-
ActiveRecord::Type::Serialized.new(
|
76
|
-
ActiveRecord::Type::Text.new,
|
77
|
-
ActiveRecord::Store::IndifferentCoder.new(
|
78
|
-
"test",
|
79
|
-
ActiveRecord::Coders::YAMLColumn.new("test", Hash)
|
80
|
-
)
|
81
|
-
)
|
82
|
-
end
|
83
|
-
else
|
84
|
-
let(:yaml_type) do
|
85
|
-
ActiveRecord::Type::Serialized.new(
|
86
|
-
ActiveRecord::Type::Text.new,
|
87
|
-
ActiveRecord::Store::IndifferentCoder.new(
|
88
|
-
ActiveRecord::Coders::YAMLColumn.new(Hash)
|
89
|
-
)
|
90
|
-
)
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
let(:subject) { described_class.new(yaml_type) }
|
95
|
-
|
96
|
-
it "works", :aggregate_failures do
|
97
|
-
subject.add_typed_key("date", :date)
|
98
|
-
|
99
|
-
date = ::Date.new(2016, 6, 22)
|
100
|
-
|
101
|
-
expect(subject.cast(date: "2016-06-22")).to eq("date" => date)
|
102
|
-
expect(subject.cast("date" => "2016-06-22")).to eq("date" => date)
|
103
|
-
expect(subject.deserialize("---\n:date: 2016-06-22\n")).to eq("date" => date)
|
104
|
-
expect(subject.deserialize("---\ndate: 2016-06-22\n")).to eq("date" => date)
|
105
|
-
expect(subject.serialize(date: date)).to eq "--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess\ndate: 2016-06-22\n"
|
106
|
-
expect(subject.serialize("date" => date)).to eq "--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess\ndate: 2016-06-22\n"
|
107
|
-
end
|
108
|
-
end
|
109
|
-
end
|
data/spec/support/money_type.rb
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class MoneyType < ActiveRecord::Type::Integer
|
4
|
-
def cast(value)
|
5
|
-
if !value.is_a?(Numeric) && value.include?("$")
|
6
|
-
price_in_dollars = value.delete("$").to_f
|
7
|
-
super(price_in_dollars * 100)
|
8
|
-
else
|
9
|
-
super
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
ActiveRecord::Type.register(:money_type, MoneyType)
|
data/spec/support/user.rb
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class RawUser < ActiveRecord::Base
|
4
|
-
self.table_name = "users"
|
5
|
-
end
|
6
|
-
|
7
|
-
class User < ActiveRecord::Base
|
8
|
-
store_accessor :jparams, :version, active: :boolean, salary: :integer
|
9
|
-
store_attribute :jparams, :birthday, :date
|
10
|
-
|
11
|
-
store_attribute :jparams, :inner_json, :json
|
12
|
-
|
13
|
-
store :custom, accessors: [price: :money_type]
|
14
|
-
|
15
|
-
store_accessor :hdata, visible: :boolean
|
16
|
-
|
17
|
-
store_attribute :hdata, :ratio, :integer, limit: 1
|
18
|
-
store_attribute :hdata, :login_at, :datetime
|
19
|
-
end
|
data/store_attribute.gemspec
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
$:.push File.expand_path("../lib", __FILE__)
|
4
|
-
|
5
|
-
# Maintain your gem's version:
|
6
|
-
require "store_attribute/version"
|
7
|
-
|
8
|
-
# Describe your gem and declare its dependencies:
|
9
|
-
Gem::Specification.new do |s|
|
10
|
-
s.name = "store_attribute"
|
11
|
-
s.version = StoreAttribute::VERSION
|
12
|
-
s.authors = ["palkan"]
|
13
|
-
s.email = ["dementiev.vm@gmail.com"]
|
14
|
-
s.homepage = "http://github.com/palkan/store_attribute"
|
15
|
-
s.summary = "ActiveRecord extension which adds typecasting to store accessors"
|
16
|
-
s.description = "ActiveRecord extension which adds typecasting to store accessors"
|
17
|
-
s.license = "MIT"
|
18
|
-
|
19
|
-
s.files = `git ls-files`.split($/)
|
20
|
-
s.require_paths = ["lib"]
|
21
|
-
|
22
|
-
s.required_ruby_version = ">= 2.4.0"
|
23
|
-
|
24
|
-
s.add_runtime_dependency "activerecord", ">= 5.0"
|
25
|
-
|
26
|
-
s.add_development_dependency "pg", ">= 0.18"
|
27
|
-
s.add_development_dependency "rake", ">= 10.1"
|
28
|
-
s.add_development_dependency "simplecov", ">= 0.3.8"
|
29
|
-
s.add_development_dependency "pry-byebug"
|
30
|
-
s.add_development_dependency "rubocop", "~> 0.65.0"
|
31
|
-
s.add_development_dependency "rubocop-md", "~> 0.2"
|
32
|
-
s.add_development_dependency "standard", "~> 0.0.36"
|
33
|
-
s.add_development_dependency "rspec", ">= 3.5.0"
|
34
|
-
end
|