store_attribute 0.7.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 721458d48d2bb0a7d065ce63e4e3c34829453f3f3060c8536cc4b5a40f378a12
4
- data.tar.gz: 0fc7432f8f08b3f3e3d8e8c1c5a77d56267f2cdc6d31c802fbac87dc48dd15ae
3
+ metadata.gz: 8e666327899587f17df5c5196abd1f6094388d08c4ca4c020b9a67f182fa0660
4
+ data.tar.gz: 6a23e7d6122483843caaa7ae6d2da59e33e0c250c96306839bbb4cac8242ea87
5
5
  SHA512:
6
- metadata.gz: f598a6db05ed959744fb1959d7b3675b99e15459668ec4ca957276ada7fc5915cbdcdaedd88a46aa57805d11c99ef710adcd56d45d27e1aa9d7b5eb31fff4974
7
- data.tar.gz: 5bca850c0073164d07f607482e4711e0341a06521f3129c92ef1faaf47e0d76b3fa9701d6a37648a3ba98be3b57f9d6afe2986a18a3aab916c0e1f4432d0896f
6
+ metadata.gz: b61d2f76d32c9ddbe3c993896a1826662b8afc881e7b72674ab21d8954e70dbae25630996ab920eeeb752436e433949bec36914d3b6a3278909ca85fce889de5
7
+ data.tar.gz: 07bb42459a9925b247b6cea363df401c4c9a92787bf6aca1de2bf795575e5895f331a0d4cd983e9b82ee1d27d0a35b47cd6a791314a756fcf78b438e8a25aea0
data/CHANGELOG.md CHANGED
@@ -2,6 +2,28 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 0.9.0 (2021-08-17) 📉
6
+
7
+ - Default values no longer marked as dirty. ([@markedmondson][])
8
+
9
+ ## 0.8.1 (2020-12-03)
10
+
11
+ - Fix adding dirty tracking methods for `store_attribute`. ([@palkan][])
12
+
13
+ ## 0.8.0
14
+
15
+ - Add Rails 6.1 compatibility. ([@palkan][])
16
+
17
+ - Add support for `prefix` and `suffix` options. ([@palkan][])
18
+
19
+ ## 0.7.1
20
+
21
+ - Fixed bug with `store` called without accessors. ([@ioki-klaus][])
22
+
23
+ See [#10](https://github.com/palkan/store_attribute/pull/10).
24
+
25
+ ## 0.7.0 (2020-03-23)
26
+
5
27
  - Added dirty tracking methods. ([@glaszig][])
6
28
 
7
29
  [PR #8](https://github.com/palkan/store_attribute/pull/8).
@@ -18,3 +40,5 @@
18
40
  [@dreikanter]: https://github.com/dreikanter
19
41
  [@SumLare]: https://github.com/SumLare
20
42
  [@glaszig]: https://github.com/glaszig
43
+ [@ioki-klaus]: https://github.com/ioki-klaus
44
+ [@markedmondson]: https://github.com/markedmondson
data/README.md CHANGED
@@ -1,4 +1,6 @@
1
- [![Gem Version](https://badge.fury.io/rb/store_attribute.svg)](https://rubygems.org/gems/store_attribute) [![Build Status](https://travis-ci.org/palkan/store_attribute.svg?branch=master)](https://travis-ci.org/palkan/store_attribute)
1
+ [![Cult Of Martians](http://cultofmartians.com/assets/badges/badge.svg)](https://cultofmartians.com/tasks/store-attribute-defaults.html#task)
2
+ [![Gem Version](https://badge.fury.io/rb/store_attribute.svg)](https://rubygems.org/gems/store_attribute)
3
+ ![Build](https://github.com/palkan/store_attribute/workflows/Build/badge.svg)
2
4
 
3
5
  ## Store Attribute
4
6
 
@@ -14,7 +16,7 @@ In your Gemfile:
14
16
 
15
17
  ```ruby
16
18
  # for Rails 5+ (6 is supported)
17
- gem "store_attribute", "~> 0.5.0"
19
+ gem "store_attribute", "~> 0.8.0"
18
20
 
19
21
  # for Rails 4.2
20
22
  gem "store_attribute", "~> 0.4.0"
@@ -47,6 +49,7 @@ class MegaUser < User
47
49
  store_attribute :settings, :login_at, :datetime
48
50
  store_attribute :settings, :active, :boolean
49
51
  store_attribute :settings, :color, :string, default: "red"
52
+ store_attribute :settings, :colors, :json, default: ["red", "blue"]
50
53
  store_attribute :settings, :data, :datetime, default: -> { Time.now }
51
54
  end
52
55
 
@@ -58,6 +61,8 @@ u.ratio # => 63
58
61
  u.active # => false
59
62
  # Default value is set
60
63
  u.color # => red
64
+ # Default array is set
65
+ u.colors # => ["red", "blue"]
61
66
  # A dynamic default can also be provided
62
67
  u.data # => Current time
63
68
  # And we also have a predicate method
@@ -26,7 +26,7 @@ module ActiveRecord
26
26
  def store(store_name, options = {})
27
27
  accessors = options.delete(:accessors)
28
28
  typed_accessors =
29
- if accessors.last.is_a?(Hash)
29
+ if accessors && accessors.last.is_a?(Hash)
30
30
  accessors.pop
31
31
  else
32
32
  {}
@@ -44,24 +44,48 @@ module ActiveRecord
44
44
  #
45
45
  # +typed_keys+ The key-to-type hash of the accesors with type to the store.
46
46
  #
47
+ # +prefix+ Accessor method name prefix
48
+ #
49
+ # +suffix+ Accessor method name suffix
50
+ #
47
51
  # Examples:
48
52
  #
49
53
  # class SuperUser < User
50
54
  # store_accessor :settings, :privileges, login_at: :datetime
51
55
  # end
52
- def store_accessor(store_name, *keys, **typed_keys)
56
+ def store_accessor(store_name, *keys, prefix: nil, suffix: nil, **typed_keys)
53
57
  keys = keys.flatten
54
58
  typed_keys = typed_keys.except(keys)
55
59
 
56
- _define_accessors_methods(store_name, *keys)
60
+ accessor_prefix, accessor_suffix = _normalize_prefix_suffix(store_name, prefix, suffix)
57
61
 
58
- _define_dirty_tracking_methods(store_name, keys)
59
- _define_dirty_tracking_methods(store_name, typed_keys.keys)
62
+ _define_accessors_methods(store_name, *keys, prefix: accessor_prefix, suffix: accessor_suffix)
60
63
 
61
64
  _prepare_local_stored_attributes(store_name, *keys)
62
65
 
66
+ _store_accessors_module.module_eval do
67
+ define_method("changes") do
68
+ return @changes if defined?(@changes)
69
+ changes = super()
70
+ self.class.local_stored_attributes.each do |accessor, attributes|
71
+ next unless attribute_changed?(accessor)
72
+
73
+ prev_store, new_store = changes[accessor]
74
+ prev_store.each do |key, value|
75
+ if new_store[key] == value
76
+ changes[accessor][0].except!(key)
77
+ changes[accessor][1].except!(key)
78
+ end
79
+ end
80
+ end
81
+ @changes = changes
82
+ end
83
+ end
84
+
85
+ _define_dirty_tracking_methods(store_name, keys + typed_keys.keys, prefix: accessor_prefix, suffix: accessor_suffix)
86
+
63
87
  typed_keys.each do |key, type|
64
- store_attribute(store_name, key, type)
88
+ store_attribute(store_name, key, type, prefix: prefix, suffix: suffix)
65
89
  end
66
90
  end
67
91
 
@@ -78,6 +102,10 @@ module ActiveRecord
78
102
  # +type+ A symbol such as +:string+ or +:integer+, or a type object
79
103
  # to be used for the accessor.
80
104
  #
105
+ # +prefix+ Accessor method name prefix
106
+ #
107
+ # +suffix+ Accessor method name suffix
108
+ #
81
109
  # +options+ A hash of cast type options such as +precision+, +limit+, +scale+.
82
110
  #
83
111
  # Examples:
@@ -85,13 +113,16 @@ module ActiveRecord
85
113
  # class MegaUser < User
86
114
  # store_attribute :settings, :ratio, :integer, limit: 1
87
115
  # store_attribute :settings, :login_at, :datetime
116
+ #
117
+ # store_attribute :extra, :version, :integer, prefix: :meta
88
118
  # end
89
119
  #
90
- # u = MegaUser.new(active: false, login_at: '2015-01-01 00:01', ratio: "63.4608")
120
+ # u = MegaUser.new(active: false, login_at: '2015-01-01 00:01', ratio: "63.4608", meta_version: "1")
91
121
  #
92
122
  # u.login_at.is_a?(DateTime) # => true
93
123
  # u.login_at = DateTime.new(2015,1,1,11,0,0)
94
124
  # u.ratio # => 63
125
+ # u.meta_version #=> 1
95
126
  # u.reload
96
127
  #
97
128
  # # After loading record from db store contains casted data
@@ -108,16 +139,33 @@ module ActiveRecord
108
139
  # u.settings['ratio'] # => 3
109
140
  #
110
141
  # For more examples on using types, see documentation for ActiveRecord::Attributes.
111
- def store_attribute(store_name, name, type, **options)
112
- _define_accessors_methods(store_name, name)
142
+ def store_attribute(store_name, name, type, prefix: nil, suffix: nil, **options)
143
+ prefix, suffix = _normalize_prefix_suffix(store_name, prefix, suffix)
144
+
145
+ _define_accessors_methods(store_name, name, prefix: prefix, suffix: suffix)
113
146
 
114
- _define_predicate_method(name) if type == :boolean
147
+ _define_predicate_method(name, prefix: prefix, suffix: suffix) if type == :boolean
115
148
 
116
- decorate_attribute_type(store_name, "typed_accessor_for_#{name}") do |subtype|
117
- Type::TypedStore.create_from_type(subtype, name, type, **options)
149
+ # Rails >6.0
150
+ if !respond_to?(:decorate_attribute_type) || method(:decorate_attribute_type).parameters.count { |type, _| type == :req } == 1
151
+ attr_name = store_name.to_s
152
+ was_type = attributes_to_define_after_schema_loads[attr_name]&.first
153
+ attribute(attr_name) do |subtype|
154
+ if defined?(_lookup_cast_type)
155
+ Type::TypedStore.create_from_type(_lookup_cast_type(attr_name, was_type, {}), name, type, **options)
156
+ else
157
+ Type::TypedStore.create_from_type(subtype, name, type, **options)
158
+ end
159
+ end
160
+ else
161
+ decorate_attribute_type(store_name, "typed_accessor_for_#{name}") do |subtype|
162
+ Type::TypedStore.create_from_type(subtype, name, type, **options)
163
+ end
118
164
  end
119
165
 
120
166
  _prepare_local_stored_attributes(store_name, name)
167
+
168
+ _define_dirty_tracking_methods(store_name, [name], prefix: prefix, suffix: suffix)
121
169
  end
122
170
 
123
171
  def _prepare_local_stored_attributes(store_name, *keys) # :nodoc:
@@ -128,64 +176,69 @@ module ActiveRecord
128
176
  self.local_stored_attributes[store_name] |= keys
129
177
  end
130
178
 
131
- def _define_accessors_methods(store_name, *keys) # :nodoc:
179
+ def _define_accessors_methods(store_name, *keys, prefix: nil, suffix: nil) # :nodoc:
132
180
  _store_accessors_module.module_eval do
133
181
  keys.each do |key|
134
- define_method("#{key}=") do |value|
182
+ accessor_key = "#{prefix}#{key}#{suffix}"
183
+
184
+ define_method("#{accessor_key}=") do |value|
135
185
  write_store_attribute(store_name, key, value)
136
186
  end
137
187
 
138
- define_method(key) do
188
+ define_method(accessor_key) do
139
189
  read_store_attribute(store_name, key)
140
190
  end
141
191
  end
142
192
  end
143
193
  end
144
194
 
145
- def _define_predicate_method(name)
195
+ def _define_predicate_method(name, prefix: nil, suffix: nil)
146
196
  _store_accessors_module.module_eval do
197
+ name = "#{prefix}#{name}#{suffix}"
198
+
147
199
  define_method("#{name}?") do
148
200
  send(name) == true
149
201
  end
150
202
  end
151
203
  end
152
204
 
153
- def _define_dirty_tracking_methods(store_attribute, keys)
205
+ def _define_dirty_tracking_methods(store_attribute, keys, prefix: nil, suffix: nil)
154
206
  _store_accessors_module.module_eval do
155
207
  keys.flatten.each do |key|
156
208
  key = key.to_s
209
+ accessor_key = "#{prefix}#{key}#{suffix}"
157
210
 
158
- define_method("#{key}_changed?") do
211
+ define_method("#{accessor_key}_changed?") do
159
212
  return false unless attribute_changed?(store_attribute)
160
213
  prev_store, new_store = changes[store_attribute]
161
214
  prev_store&.dig(key) != new_store&.dig(key)
162
215
  end
163
216
 
164
- define_method("#{key}_change") do
217
+ define_method("#{accessor_key}_change") do
165
218
  return unless attribute_changed?(store_attribute)
166
219
  prev_store, new_store = changes[store_attribute]
167
220
  [prev_store&.dig(key), new_store&.dig(key)]
168
221
  end
169
222
 
170
- define_method("#{key}_was") do
223
+ define_method("#{accessor_key}_was") do
171
224
  return unless attribute_changed?(store_attribute)
172
225
  prev_store, _new_store = changes[store_attribute]
173
226
  prev_store&.dig(key)
174
227
  end
175
228
 
176
- define_method("saved_change_to_#{key}?") do
229
+ define_method("saved_change_to_#{accessor_key}?") do
177
230
  return false unless saved_change_to_attribute?(store_attribute)
178
231
  prev_store, new_store = saved_change_to_attribute(store_attribute)
179
232
  prev_store&.dig(key) != new_store&.dig(key)
180
233
  end
181
234
 
182
- define_method("saved_change_to_#{key}") do
235
+ define_method("saved_change_to_#{accessor_key}") do
183
236
  return unless saved_change_to_attribute?(store_attribute)
184
237
  prev_store, new_store = saved_change_to_attribute(store_attribute)
185
238
  [prev_store&.dig(key), new_store&.dig(key)]
186
239
  end
187
240
 
188
- define_method("#{key}_before_last_save") do
241
+ define_method("#{accessor_key}_before_last_save") do
189
242
  return unless saved_change_to_attribute?(store_attribute)
190
243
  prev_store, _new_store = saved_change_to_attribute(store_attribute)
191
244
  prev_store&.dig(key)
@@ -193,6 +246,26 @@ module ActiveRecord
193
246
  end
194
247
  end
195
248
  end
249
+
250
+ def _normalize_prefix_suffix(store_name, prefix, suffix)
251
+ prefix =
252
+ case prefix
253
+ when String, Symbol
254
+ "#{prefix}_"
255
+ when TrueClass
256
+ "#{store_name}_"
257
+ end
258
+
259
+ suffix =
260
+ case suffix
261
+ when String, Symbol
262
+ "_#{suffix}"
263
+ when TrueClass
264
+ "_#{store_name}"
265
+ end
266
+
267
+ [prefix, suffix]
268
+ end
196
269
  end
197
270
  end
198
271
  end
@@ -43,6 +43,10 @@ module ActiveRecord
43
43
  hash
44
44
  end
45
45
 
46
+ def changed_in_place?(raw_old_value, new_value)
47
+ raw_old_value != serialize(new_value)
48
+ end
49
+
46
50
  def serialize(value)
47
51
  return super(value) unless value.is_a?(Hash)
48
52
  typed_casted = {}
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module StoreAttribute # :nodoc:
4
- VERSION = "0.7.0"
4
+ VERSION = "0.9.0"
5
5
  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.7.0
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - palkan
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-03-23 00:00:00.000000000 Z
11
+ date: 2021-08-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -118,7 +118,7 @@ metadata:
118
118
  documentation_uri: http://github.com/palkan/store_attribute
119
119
  homepage_uri: http://github.com/palkan/store_attribute
120
120
  source_code_uri: http://github.com/palkan/store_attribute
121
- post_install_message:
121
+ post_install_message:
122
122
  rdoc_options: []
123
123
  require_paths:
124
124
  - lib
@@ -133,8 +133,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
133
133
  - !ruby/object:Gem::Version
134
134
  version: '0'
135
135
  requirements: []
136
- rubygems_version: 3.0.6
137
- signing_key:
136
+ rubygems_version: 3.2.15
137
+ signing_key:
138
138
  specification_version: 4
139
139
  summary: ActiveRecord extension which adds typecasting to store accessors
140
140
  test_files: []