store_attribute 0.7.0 → 0.9.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 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: []