store_attribute 0.7.1 → 0.9.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 941970a7a98bfa915d206d2fde758075a81bc610109426e0eaa74859d81bfde7
4
- data.tar.gz: 3bb9f5fa94b6f9f321006172e9f8cb4a4e7c9ba7030364f8468c829989a3f5d7
3
+ metadata.gz: 5de07480a22b55413fe34512b0d36a6572da92390c998dcc1ad7838866667e5c
4
+ data.tar.gz: 78d3d38f95d1f614f6d9233f0a0bfa52b67a65853f741fedbb8ff510e38e6988
5
5
  SHA512:
6
- metadata.gz: ac9e82f2c78173e0054ea97f1c3d1318fd36b671b531b71d3f1e73be96a3247d7565b43cd27b21d3c2f01b6585143eeddb75a9cec99ef43cd77e0d43a3035130
7
- data.tar.gz: edd8234b7dc2c704d8f3086e29641a9ff1fa866f6266341d71c6f566dfb8f72fed3d360b972b0c63c41a5c0a383af645655d75d8beabb731521ab97f4b38a8da
6
+ metadata.gz: 3831baa1c5de7d7dee01dfe5ee0be5a1478b95a2e110c37e1c9ebd405c1246e300cc4eadbbf0b3e83742c8ea258db7399ddd0dc3df10c8797e7b911428bbd3e9
7
+ data.tar.gz: 774eb359b1ba5e02022f7e931089990f0d169116cbd8f9c725bc7f8bfa8189dce9c649ca3b7b3face0eccbca13b73dbb2a1028ab6f282dfe797b65adffab627c
data/CHANGELOG.md CHANGED
@@ -2,6 +2,24 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 0.9.1
6
+
7
+ - Fix bug with dirty nullable stores. ([@palkan][])
8
+
9
+ ## 0.9.0 (2021-08-17) 📉
10
+
11
+ - Default values no longer marked as dirty. ([@markedmondson][])
12
+
13
+ ## 0.8.1 (2020-12-03)
14
+
15
+ - Fix adding dirty tracking methods for `store_attribute`. ([@palkan][])
16
+
17
+ ## 0.8.0
18
+
19
+ - Add Rails 6.1 compatibility. ([@palkan][])
20
+
21
+ - Add support for `prefix` and `suffix` options. ([@palkan][])
22
+
5
23
  ## 0.7.1
6
24
 
7
25
  - Fixed bug with `store` called without accessors. ([@ioki-klaus][])
@@ -26,4 +44,5 @@
26
44
  [@dreikanter]: https://github.com/dreikanter
27
45
  [@SumLare]: https://github.com/SumLare
28
46
  [@glaszig]: https://github.com/glaszig
29
- [@ioki-klaus]: https://github.com/ioki-klaus
47
+ [@ioki-klaus]: https://github.com/ioki-klaus
48
+ [@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
@@ -6,7 +6,7 @@ require "store_attribute/active_record/type/typed_store"
6
6
  module ActiveRecord
7
7
  module Store
8
8
  module ClassMethods # :nodoc:
9
- alias _orig_store store
9
+ alias_method :_orig_store, :store
10
10
  # Defines store on this model.
11
11
  #
12
12
  # +store_name+ The name of the store.
@@ -44,24 +44,29 @@ 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
+ _define_dirty_tracking_methods(store_name, keys + typed_keys.keys, prefix: accessor_prefix, suffix: accessor_suffix)
67
+
63
68
  typed_keys.each do |key, type|
64
- store_attribute(store_name, key, type)
69
+ store_attribute(store_name, key, type, prefix: prefix, suffix: suffix)
65
70
  end
66
71
  end
67
72
 
@@ -78,6 +83,10 @@ module ActiveRecord
78
83
  # +type+ A symbol such as +:string+ or +:integer+, or a type object
79
84
  # to be used for the accessor.
80
85
  #
86
+ # +prefix+ Accessor method name prefix
87
+ #
88
+ # +suffix+ Accessor method name suffix
89
+ #
81
90
  # +options+ A hash of cast type options such as +precision+, +limit+, +scale+.
82
91
  #
83
92
  # Examples:
@@ -85,13 +94,16 @@ module ActiveRecord
85
94
  # class MegaUser < User
86
95
  # store_attribute :settings, :ratio, :integer, limit: 1
87
96
  # store_attribute :settings, :login_at, :datetime
97
+ #
98
+ # store_attribute :extra, :version, :integer, prefix: :meta
88
99
  # end
89
100
  #
90
- # 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")
91
102
  #
92
103
  # u.login_at.is_a?(DateTime) # => true
93
104
  # u.login_at = DateTime.new(2015,1,1,11,0,0)
94
105
  # u.ratio # => 63
106
+ # u.meta_version #=> 1
95
107
  # u.reload
96
108
  #
97
109
  # # After loading record from db store contains casted data
@@ -108,16 +120,33 @@ module ActiveRecord
108
120
  # u.settings['ratio'] # => 3
109
121
  #
110
122
  # 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)
123
+ def store_attribute(store_name, name, type, prefix: nil, suffix: nil, **options)
124
+ prefix, suffix = _normalize_prefix_suffix(store_name, prefix, suffix)
113
125
 
114
- _define_predicate_method(name) if type == :boolean
126
+ _define_accessors_methods(store_name, name, prefix: prefix, suffix: suffix)
115
127
 
116
- decorate_attribute_type(store_name, "typed_accessor_for_#{name}") do |subtype|
117
- Type::TypedStore.create_from_type(subtype, name, type, **options)
128
+ _define_predicate_method(name, prefix: prefix, suffix: suffix) if type == :boolean
129
+
130
+ # Rails >6.0
131
+ if !respond_to?(:decorate_attribute_type) || 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
+ 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
118
145
  end
119
146
 
120
147
  _prepare_local_stored_attributes(store_name, name)
148
+
149
+ _define_dirty_tracking_methods(store_name, [name], prefix: prefix, suffix: suffix)
121
150
  end
122
151
 
123
152
  def _prepare_local_stored_attributes(store_name, *keys) # :nodoc:
@@ -128,64 +157,87 @@ module ActiveRecord
128
157
  self.local_stored_attributes[store_name] |= keys
129
158
  end
130
159
 
131
- def _define_accessors_methods(store_name, *keys) # :nodoc:
160
+ def _define_accessors_methods(store_name, *keys, prefix: nil, suffix: nil) # :nodoc:
132
161
  _store_accessors_module.module_eval do
133
162
  keys.each do |key|
134
- define_method("#{key}=") do |value|
163
+ accessor_key = "#{prefix}#{key}#{suffix}"
164
+
165
+ define_method("#{accessor_key}=") do |value|
135
166
  write_store_attribute(store_name, key, value)
136
167
  end
137
168
 
138
- define_method(key) do
169
+ define_method(accessor_key) do
139
170
  read_store_attribute(store_name, key)
140
171
  end
141
172
  end
142
173
  end
143
174
  end
144
175
 
145
- def _define_predicate_method(name)
176
+ def _define_predicate_method(name, prefix: nil, suffix: nil)
146
177
  _store_accessors_module.module_eval do
178
+ name = "#{prefix}#{name}#{suffix}"
179
+
147
180
  define_method("#{name}?") do
148
181
  send(name) == true
149
182
  end
150
183
  end
151
184
  end
152
185
 
153
- def _define_dirty_tracking_methods(store_attribute, keys)
186
+ def _define_dirty_tracking_methods(store_attribute, keys, prefix: nil, suffix: nil)
154
187
  _store_accessors_module.module_eval do
188
+ define_method("changes") do
189
+ return @changes if defined?(@changes)
190
+ changes = super()
191
+ self.class.local_stored_attributes.each do |accessor, attributes|
192
+ next unless attribute_changed?(accessor)
193
+
194
+ prev_store, new_store = changes[accessor]
195
+
196
+ prev_store&.each do |key, value|
197
+ if new_store[key] == value
198
+ prev_store.except!(key)
199
+ new_store&.except!(key)
200
+ end
201
+ end
202
+ end
203
+ @changes = changes
204
+ end
205
+
155
206
  keys.flatten.each do |key|
156
207
  key = key.to_s
208
+ accessor_key = "#{prefix}#{key}#{suffix}"
157
209
 
158
- define_method("#{key}_changed?") do
210
+ define_method("#{accessor_key}_changed?") do
159
211
  return false unless attribute_changed?(store_attribute)
160
212
  prev_store, new_store = changes[store_attribute]
161
213
  prev_store&.dig(key) != new_store&.dig(key)
162
214
  end
163
215
 
164
- define_method("#{key}_change") do
216
+ define_method("#{accessor_key}_change") do
165
217
  return unless attribute_changed?(store_attribute)
166
218
  prev_store, new_store = changes[store_attribute]
167
219
  [prev_store&.dig(key), new_store&.dig(key)]
168
220
  end
169
221
 
170
- define_method("#{key}_was") do
222
+ define_method("#{accessor_key}_was") do
171
223
  return unless attribute_changed?(store_attribute)
172
224
  prev_store, _new_store = changes[store_attribute]
173
225
  prev_store&.dig(key)
174
226
  end
175
227
 
176
- define_method("saved_change_to_#{key}?") do
228
+ define_method("saved_change_to_#{accessor_key}?") do
177
229
  return false unless saved_change_to_attribute?(store_attribute)
178
230
  prev_store, new_store = saved_change_to_attribute(store_attribute)
179
231
  prev_store&.dig(key) != new_store&.dig(key)
180
232
  end
181
233
 
182
- define_method("saved_change_to_#{key}") do
234
+ define_method("saved_change_to_#{accessor_key}") do
183
235
  return unless saved_change_to_attribute?(store_attribute)
184
236
  prev_store, new_store = saved_change_to_attribute(store_attribute)
185
237
  [prev_store&.dig(key), new_store&.dig(key)]
186
238
  end
187
239
 
188
- define_method("#{key}_before_last_save") do
240
+ define_method("#{accessor_key}_before_last_save") do
189
241
  return unless saved_change_to_attribute?(store_attribute)
190
242
  prev_store, _new_store = saved_change_to_attribute(store_attribute)
191
243
  prev_store&.dig(key)
@@ -193,6 +245,26 @@ module ActiveRecord
193
245
  end
194
246
  end
195
247
  end
248
+
249
+ def _normalize_prefix_suffix(store_name, prefix, suffix)
250
+ prefix =
251
+ case prefix
252
+ when String, Symbol
253
+ "#{prefix}_"
254
+ when TrueClass
255
+ "#{store_name}_"
256
+ end
257
+
258
+ suffix =
259
+ case suffix
260
+ when String, Symbol
261
+ "_#{suffix}"
262
+ when TrueClass
263
+ "_#{store_name}"
264
+ end
265
+
266
+ [prefix, suffix]
267
+ end
196
268
  end
197
269
  end
198
270
  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.1"
4
+ VERSION = "0.9.1"
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.1
4
+ version: 0.9.1
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-06-04 00:00:00.000000000 Z
11
+ date: 2021-09-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -52,34 +52,6 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '13.0'
55
- - !ruby/object:Gem::Dependency
56
- name: rubocop-md
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - "~>"
60
- - !ruby/object:Gem::Version
61
- version: '0.3'
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'
69
- - !ruby/object:Gem::Dependency
70
- name: standard
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - "~>"
74
- - !ruby/object:Gem::Version
75
- version: 0.2.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.2.0
83
55
  - !ruby/object:Gem::Dependency
84
56
  name: rspec
85
57
  requirement: !ruby/object:Gem::Requirement
@@ -118,7 +90,7 @@ metadata:
118
90
  documentation_uri: http://github.com/palkan/store_attribute
119
91
  homepage_uri: http://github.com/palkan/store_attribute
120
92
  source_code_uri: http://github.com/palkan/store_attribute
121
- post_install_message:
93
+ post_install_message:
122
94
  rdoc_options: []
123
95
  require_paths:
124
96
  - lib
@@ -126,15 +98,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
126
98
  requirements:
127
99
  - - ">="
128
100
  - !ruby/object:Gem::Version
129
- version: 2.4.0
101
+ version: 2.5.0
130
102
  required_rubygems_version: !ruby/object:Gem::Requirement
131
103
  requirements:
132
104
  - - ">="
133
105
  - !ruby/object:Gem::Version
134
106
  version: '0'
135
107
  requirements: []
136
- rubygems_version: 3.0.6
137
- signing_key:
108
+ rubygems_version: 3.2.22
109
+ signing_key:
138
110
  specification_version: 4
139
111
  summary: ActiveRecord extension which adds typecasting to store accessors
140
112
  test_files: []