store_attribute 1.3.1 → 2.0.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: d79c58e23fa0838516c2eab7130ce8f121941bb99355d9ad7e24dbcb94f231be
4
- data.tar.gz: 70cb3d8ff0a587f726756b2d2a1d89cd162fa49e286ee343c8311c9013a4a7f9
3
+ metadata.gz: 5530282b5ea4488c94d5f90ba1474c9a8b6dd1b153c412cce62a6dca0c69c3d3
4
+ data.tar.gz: 67f65c84644842ebc7c6cdbcc455f034854d5ada9dd469180cf94a0a4cf02d38
5
5
  SHA512:
6
- metadata.gz: d15c7344b6f758cea9d405a3c5f765d4e9d6e734cae8866d44030c1788bf3c889ef6e3c2dae9bdb91976a45c7a0dbe3d4439f6cb5b81c169b97eefd1b264fc58
7
- data.tar.gz: 8913905537ab1a151d6194b481ec46c17564515346dfe6d79cedb02281879b7e8b3ac906e7eb8dcacfcbab19d2cbe14a5d4097c2285d19827335c4420e0bf572
6
+ metadata.gz: f00159fa7de150635efa6c2d623ab8aacac3b166582fa5b48d623621f38ee79a123903e538faddd8b42d9cca955a9c18ea0fc4ebd32cfaba7de51b233b203891
7
+ data.tar.gz: f7ee58675bc257f21343054e131b3708ee3d8f5c1f504e814e80e3eb95eb0e506bf69361815736cf50732f8846b1b73671533802854f402bf72881ceaada3de9
data/CHANGELOG.md CHANGED
@@ -2,6 +2,32 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 2.0.1 (2025-05-09) 🎇
6
+
7
+ - Register store_attributes as attributes. ([@rickcsong](https://github.com/rickcsong))
8
+
9
+ ```ruby
10
+ class User < ActiveRecord::Base
11
+ self.store_attribute_register_attributes = true
12
+
13
+ store_attribute :extra, :color, :string, default: "grey"
14
+ end
15
+
16
+ User.attribute_types.keys.include?("color") #=> true
17
+ ```
18
+
19
+ ## 2.0.0 (2024-12-12)
20
+
21
+ - **Breaking:** The `store_attribute_unset_values_fallback_to_default` option is now true by default, meaning that the default value will be returned when the attribute key is not present in the serialized value.
22
+
23
+ For v1.x behavior, set the option to `false` globally as follows:
24
+
25
+ ```ruby
26
+ StoreAttribute.store_attribute_unset_values_fallback_to_default = false
27
+ ```
28
+
29
+ - Ruby >= 3.0 is required.
30
+
5
31
  ## 1.3.1 (2024-09-19)
6
32
 
7
33
  - Populate missing defaults on user input when `store_attribute_unset_values_fallback_to_default` is true. ([@palkan][])
data/README.md CHANGED
@@ -146,15 +146,44 @@ another_user.name #=> nil
146
146
  another_user.expired_at #=> nil
147
147
  ```
148
148
 
149
- It is possible to configure `store_attribute` to return the default value even when the record is persisted and the attribute name is not present. By using the `store_attribute_unset_values_fallback_to_default` class option, default values will be returned for missing keys. For example:
149
+ By default, Store Attribute returns the default value even when the record is persisted but the attribute name is not present:
150
+
151
+ ```ruby
152
+ user = User.create!(extra: {})
153
+ user.expired_at #=> 2022-03-19
154
+ ```
155
+
156
+ You can disable this behaviour by setting the `store_attribute_unset_values_fallback_to_default` class option to `false` in your model:
150
157
 
151
158
  ```ruby
152
159
  class User < ApplicationRecord
153
- self.store_attribute_unset_values_fallback_to_default = true
160
+ self.store_attribute_unset_values_fallback_to_default = false
154
161
  end
155
162
 
156
163
  user = User.create!(extra: {})
157
- user.expired_at #=> 2022-03-19
164
+ user.expired_at #=> nil
158
165
  ```
159
166
 
160
- **IMPORTANT:** Due to implementation limitations, it's not recommended to toggle the value of `store_attribute_unset_values_fallback_to_default` in sub-classes. We recommend to set this value in base classes (e.g., `ApplicationRecord`).
167
+ You can also configure the global default for this option in an initializer or application configuration:
168
+
169
+ ```ruby
170
+ # config/initializers/store_attribute.rb
171
+ # # or
172
+ # config/application.rb
173
+ StoreAttribute.store_attribute_unset_values_fallback_to_default = false
174
+ ```
175
+
176
+ ## Contributing
177
+
178
+ Bug reports and pull requests are welcome on GitHub at [https://github.com/palkan/store_attribute](https://github.com/palkan/store_attribute).
179
+
180
+ For local development, you'll need PostgreSQL up and running. You can either install it on your host machine or run via Docker as follows:
181
+
182
+ ```bash
183
+ docker run --name store_attribute_postgres -e POSTGRES_HOST_AUTH_METHOD=trust -e POSTGRES_USER=$USER -e POSTGRES_PASSWORD=postgres -p 5432:5432 -d postgres
184
+ docker exec -it store_attribute_postgres createdb -U $USER store_attribute_test
185
+ ```
186
+
187
+ ## License
188
+
189
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -14,7 +14,7 @@ module StoreAttribute
14
14
  prev_store, new_store = orig_changes.map(&:dup)
15
15
 
16
16
  prev_store&.each do |key, value|
17
- if new_store[key] == value
17
+ if new_store&.dig(key) == value
18
18
  prev_store.except!(key)
19
19
  new_store&.except!(key)
20
20
  end
@@ -10,6 +10,7 @@ module ActiveRecord
10
10
  alias_method :_orig_store_without_types, :store
11
11
  alias_method :_orig_store_accessor_without_types, :store_accessor
12
12
 
13
+ attr_writer :store_attribute_register_attributes
13
14
  attr_writer :store_attribute_unset_values_fallback_to_default
14
15
 
15
16
  # Defines store on this model.
@@ -133,6 +134,28 @@ module ActiveRecord
133
134
 
134
135
  _local_typed_stored_attributes[store_name][:owner] = self if options.key?(:default) || !_local_typed_stored_attributes?
135
136
  _local_typed_stored_attributes[store_name][:types][name] = [type, options]
137
+
138
+ if store_attribute_register_attributes
139
+ cast_type =
140
+ if type == :value
141
+ ActiveModel::Type::Value.new(**options.except(:default))
142
+ else
143
+ ActiveRecord::Type.lookup(type, **options.except(:default))
144
+ end
145
+
146
+ attribute(name, cast_type, **options)
147
+ end
148
+ end
149
+
150
+ def store_attribute_register_attributes
151
+ return @store_attribute_register_attributes if instance_variable_defined?(:@store_attribute_register_attributes)
152
+
153
+ @store_attribute_register_attributes =
154
+ if superclass.respond_to?(:store_attribute_register_attributes)
155
+ superclass.store_attribute_register_attributes
156
+ else
157
+ StoreAttribute.store_attribute_register_attributes
158
+ end
136
159
  end
137
160
 
138
161
  def store_attribute_unset_values_fallback_to_default
@@ -142,7 +165,7 @@ module ActiveRecord
142
165
  if superclass.respond_to?(:store_attribute_unset_values_fallback_to_default)
143
166
  superclass.store_attribute_unset_values_fallback_to_default
144
167
  else
145
- false
168
+ StoreAttribute.store_attribute_unset_values_fallback_to_default
146
169
  end
147
170
  end
148
171
 
@@ -170,23 +193,8 @@ module ActiveRecord
170
193
 
171
194
  owner = self
172
195
 
173
- # For Rails <6.1
174
- if respond_to?(:decorate_attribute_type) && method(:decorate_attribute_type).parameters.count { |type, _| type == :req } == 2
175
- decorate_attribute_type(attr_name, "typed_accessor_for_#{attr_name}") do |subtype|
176
- subtypes = _local_typed_stored_attributes[attr_name][:types]
177
- type = Type::TypedStore.create_from_type(subtype)
178
- type.owner = owner
179
- defaultik.type = type
180
- subtypes.each do |name, (cast_type, options)|
181
- type.add_typed_key(name, cast_type, **options.symbolize_keys)
182
- end
183
-
184
- define_default_attribute(attr_name, defaultik.proc, type, from_user: true)
185
-
186
- type
187
- end
188
196
  # Rails >7.1
189
- elsif respond_to?(:decorate_attributes)
197
+ if respond_to?(:decorate_attributes)
190
198
  decorate_attributes([attr_name]) do |_, subtype|
191
199
  subtypes = _local_typed_stored_attributes[attr_name][:types]
192
200
  type = Type::TypedStore.create_from_type(subtype)
@@ -215,6 +223,13 @@ module ActiveRecord
215
223
  type.add_typed_key(name, cast_type, **options.symbolize_keys)
216
224
  end
217
225
 
226
+ # Make sure default attribute uses the correct type, so #changed? works as expected
227
+ # This is dirty hack that makes Rails <7.2 works similar to Rails >=7.2. Please, upgrade :)
228
+ if type.defaults.any? && _default_attributes[attr_name] && !_default_attributes[attr_name].type.is_a?(Type::TypedStore)
229
+ _default_attributes[attr_name] =
230
+ ActiveModel::Attribute.from_database(attr_name, _default_attributes[attr_name].value.deep_dup, type)
231
+ end
232
+
218
233
  type
219
234
  end
220
235
  end
@@ -26,6 +26,7 @@ module ActiveRecord
26
26
  end
27
27
 
28
28
  attr_writer :owner
29
+ attr_reader :defaults
29
30
 
30
31
  def initialize(subtype)
31
32
  @accessor_types = {}
@@ -91,12 +92,16 @@ module ActiveRecord
91
92
  self
92
93
  end
93
94
 
95
+ def mutable?
96
+ true
97
+ end
98
+
94
99
  def write(object, attribute, key, value)
95
100
  value = type_for(key).cast(value) if typed?(key)
96
101
  store_accessor.write(object, attribute, key, value)
97
102
  end
98
103
 
99
- delegate :read, :prepare, to: :store_accessor
104
+ delegate :get, :read, :prepare, to: :store_accessor
100
105
 
101
106
  def build_defaults
102
107
  defaults.transform_values do |val|
@@ -140,7 +145,7 @@ module ActiveRecord
140
145
  subtype.accessor
141
146
  end
142
147
 
143
- attr_reader :accessor_types, :defaults, :subtype, :owner
148
+ attr_reader :accessor_types, :subtype, :owner
144
149
  end
145
150
  end
146
151
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module StoreAttribute # :nodoc:
4
- VERSION = "1.3.1"
4
+ VERSION = "2.0.1"
5
5
  end
@@ -4,4 +4,13 @@ require "store_attribute/version"
4
4
  require "store_attribute/active_record"
5
5
 
6
6
  module StoreAttribute
7
+ class << self
8
+ # Global default value for `store_attribute_unset_values_fallback_to_default` option.
9
+ # Must be set before any model is loaded
10
+ attr_accessor :store_attribute_unset_values_fallback_to_default
11
+ attr_accessor :store_attribute_register_attributes
12
+ end
13
+
14
+ self.store_attribute_unset_values_fallback_to_default = true
15
+ self.store_attribute_register_attributes = false
7
16
  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: 1.3.1
4
+ version: 2.0.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: 2024-09-19 00:00:00.000000000 Z
11
+ date: 2025-05-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -91,7 +91,7 @@ metadata:
91
91
  documentation_uri: http://github.com/palkan/store_attribute
92
92
  homepage_uri: http://github.com/palkan/store_attribute
93
93
  source_code_uri: http://github.com/palkan/store_attribute
94
- post_install_message:
94
+ post_install_message:
95
95
  rdoc_options: []
96
96
  require_paths:
97
97
  - lib
@@ -99,7 +99,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
99
99
  requirements:
100
100
  - - ">="
101
101
  - !ruby/object:Gem::Version
102
- version: 2.7.0
102
+ version: 3.0.0
103
103
  required_rubygems_version: !ruby/object:Gem::Requirement
104
104
  requirements:
105
105
  - - ">="
@@ -107,7 +107,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
107
107
  version: '0'
108
108
  requirements: []
109
109
  rubygems_version: 3.4.19
110
- signing_key:
110
+ signing_key:
111
111
  specification_version: 4
112
112
  summary: ActiveRecord extension which adds typecasting to store accessors
113
113
  test_files: []