store_attribute 1.0.1 → 1.1.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: 1886b0beecc6ce2b4195fce3e848a5257852e00121cd17de98b296578ae28d4e
4
- data.tar.gz: e3dc1d9d70138c0f0462048616c945dea13bd85abcfd80be00262312c00faef8
3
+ metadata.gz: 2134f240621f2f546e5521fe4db4b66e3b6ec0a4a76ebb832fbcae8654c0904e
4
+ data.tar.gz: 862ce5201d0c1ad548035161b0f51b50a9a540a85ae8be0a9c550976f918701c
5
5
  SHA512:
6
- metadata.gz: '081a50a78e7d8b769cae2a3067ec074ac3cf18b2b915af5e84b874e94799d5a9b6f7de96d4f59cfb3b6a2f7e7f164cc57e7866bde1d4c59941b1b8174355cc16'
7
- data.tar.gz: 743f05f7d8893ff9feb40238c3f7299b15d89042cc2cc77a89f4ec51bbb92face0bbd213375a85fc2862362845e854099fb72f091ba628bc96dd029c845fafd6
6
+ metadata.gz: efe4992702c419f392f4fcbfd16c0ab32c862acf4679e841a7a8c28d40e0fb2eb66c2b72b2ac0156517a49bce4ee7308ec75ffa64604d4b879c735da0201d7f3
7
+ data.tar.gz: a9b8d3683abcd37f99a7c0ef68431e80e32a79537c5dab426e150a85b7d15c4b8ce1cb92e0a6c1c24031418286223e854fb2e2c73083c801781553f6559cd2c1
data/CHANGELOG.md CHANGED
@@ -2,6 +2,32 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 1.1.0 (2023-03-08) 🌷
6
+
7
+ - Add configuration option to return default values when attribute key is not present in the serialized value ([@markedmondson][], [@palkan][]).
8
+
9
+ Add to the class (preferrable `ApplicationRecord` or some other base class):
10
+
11
+ ```ruby
12
+ class ApplicationRecord < ActiveRecord::Base
13
+ self.store_attribute_unset_values_fallback_to_default = true
14
+
15
+ store_attribute :extra, :color, :string, default: "grey"
16
+ end
17
+
18
+ user = User.create!(extra: {})
19
+ # without the fallback
20
+ user.color #=> nil
21
+ # with fallback
22
+ user.color #=> "grey"
23
+ ```
24
+
25
+ ## 1.0.2 (2022-07-29)
26
+
27
+ - Fix possible conflicts with Active Model objects. ([@palkan][])
28
+
29
+ - Fix passing suffix/prefix to `store_accessor` without types. ([@palkan][])
30
+
5
31
  ## 1.0.1 (2022-05-05)
6
32
 
7
33
  - Fixed suffix/prefix for predicates. ([@Alan-Marx](https://github.com/Alan-Marx))
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright 2016-2020 palkan
1
+ Copyright 2016-2023 palkan
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -36,7 +36,7 @@ Where:
36
36
  - `store_name` The name of the store.
37
37
  - `name` The name of the accessor to the store.
38
38
  - `type` A symbol such as `:string` or `:integer`, or a type object to be used for the accessor.
39
- - `options` (optional) A hash of cast type options such as `precision`, `limit`, `scale`, `default`.
39
+ - `options` (optional) A hash of cast type options such as `precision`, `limit`, `scale`, `default`. Regular `store_accessor` options, such as `prefix`, `suffix` are also supported.
40
40
 
41
41
  Type casting occurs every time you write data through accessor or update store itself
42
42
  and when object is loaded from database.
@@ -131,17 +131,30 @@ end
131
131
  Date.current #=> 2022-03-17
132
132
 
133
133
  user = User.new
134
- user.name #=> "john"
134
+ user.name #=> "Joe"
135
135
  user.expired_at #=> 2022-03-19
136
136
  user.save!
137
137
 
138
138
  raw_user = RawUser.find(user.id)
139
- user.name #=> "john"
140
- user.expired_at #=> 2022-03-19
139
+ raw_user.name #=> "Joe"
140
+ raw_user.expired_at #=> 2022-03-19
141
141
 
142
142
  another_raw_user = RawUser.create!
143
143
  another_user = User.find(another_raw_user.id)
144
144
 
145
- user.name #=> nil
146
- user.expired_at #=> nil
145
+ another_user.name #=> nil
146
+ another_user.expired_at #=> nil
147
147
  ```
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:
150
+
151
+ ```ruby
152
+ class User < ApplicationRecord
153
+ self.store_attribute_unset_values_fallback_to_default = true
154
+ end
155
+
156
+ user = User.create!(extra: {})
157
+ user.expired_at #=> 2022-03-19
158
+ ```
159
+
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`).
@@ -4,6 +4,7 @@ module StoreAttribute
4
4
  # Upgrade mutation tracker to return partial changes for typed stores
5
5
  module MutationTracker
6
6
  def change_to_attribute(attr_name)
7
+ return super unless attributes.is_a?(ActiveModel::AttributeSet)
7
8
  return super unless attributes[attr_name].type.is_a?(ActiveRecord::Type::TypedStore)
8
9
 
9
10
  orig_changes = super
@@ -10,6 +10,8 @@ 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_unset_values_fallback_to_default
14
+
13
15
  # Defines store on this model.
14
16
  #
15
17
  # +store_name+ The name of the store.
@@ -60,7 +62,7 @@ module ActiveRecord
60
62
  keys = keys.flatten
61
63
  typed_keys = typed_keys.except(keys)
62
64
 
63
- _orig_store_accessor_without_types(store_name, *(keys - typed_keys.keys), prefix: nil, suffix: nil)
65
+ _orig_store_accessor_without_types(store_name, *(keys - typed_keys.keys), prefix: prefix, suffix: suffix)
64
66
 
65
67
  typed_keys.each do |key, type|
66
68
  store_attribute(store_name, key, type, prefix: prefix, suffix: suffix)
@@ -125,6 +127,17 @@ module ActiveRecord
125
127
  _store_local_stored_attribute(store_name, name, type, **options)
126
128
  end
127
129
 
130
+ def store_attribute_unset_values_fallback_to_default
131
+ return @store_attribute_unset_values_fallback_to_default if instance_variable_defined?(:@store_attribute_unset_values_fallback_to_default)
132
+
133
+ @store_attribute_unset_values_fallback_to_default =
134
+ if superclass.respond_to?(:store_attribute_unset_values_fallback_to_default)
135
+ superclass.store_attribute_unset_values_fallback_to_default
136
+ else
137
+ false
138
+ end
139
+ end
140
+
128
141
  def _store_local_stored_attribute(store_name, key, cast_type, default: Type::TypedStore::UNDEFINED, **options) # :nodoc:
129
142
  cast_type = ActiveRecord::Type.lookup(cast_type, **options) if cast_type.is_a?(Symbol)
130
143
  _local_typed_stored_attributes[store_name][key] = [cast_type, default]
@@ -156,10 +169,13 @@ module ActiveRecord
156
169
 
157
170
  defaultik = Type::TypedStore::Defaultik.new
158
171
 
172
+ owner = self
173
+
159
174
  if use_decorator
160
175
  decorate_attribute_type(attr_name, "typed_accessor_for_#{attr_name}") do |subtype|
161
176
  subtypes = _local_typed_stored_attributes[attr_name]
162
177
  type = Type::TypedStore.create_from_type(subtype)
178
+ type.owner = owner
163
179
  defaultik.type = type
164
180
  subtypes.each { |name, (cast_type, default)| type.add_typed_key(name, cast_type, default: default) }
165
181
 
@@ -173,6 +189,7 @@ module ActiveRecord
173
189
  subtype = _lookup_cast_type(attr_name, was_type, {}) if defined?(_lookup_cast_type)
174
190
 
175
191
  type = Type::TypedStore.create_from_type(subtype)
192
+ type.owner = owner
176
193
  defaultik.type = type
177
194
  subtypes.each { |name, (cast_type, default)| type.add_typed_key(name, cast_type, default: default) }
178
195
 
@@ -25,6 +25,8 @@ module ActiveRecord
25
25
  new(basetype)
26
26
  end
27
27
 
28
+ attr_writer :owner
29
+
28
30
  def initialize(subtype)
29
31
  @accessor_types = {}
30
32
  @defaults = {}
@@ -47,6 +49,8 @@ module ActiveRecord
47
49
  accessor_types.each do |key, type|
48
50
  if hash.key?(key)
49
51
  hash[key] = type.deserialize(hash[key])
52
+ elsif fallback_to_default?(key)
53
+ hash[key] = built_defaults[key]
50
54
  end
51
55
  end
52
56
  hash
@@ -106,6 +110,10 @@ module ActiveRecord
106
110
 
107
111
  protected
108
112
 
113
+ def built_defaults
114
+ @built_defaults ||= build_defaults
115
+ end
116
+
109
117
  # We cannot rely on string keys 'cause user input can contain symbol keys
110
118
  def key_to_cast(val, key)
111
119
  return key if val.key?(key)
@@ -121,7 +129,11 @@ module ActiveRecord
121
129
  accessor_types.fetch(key.to_s)
122
130
  end
123
131
 
124
- attr_reader :accessor_types, :defaults, :store_accessor
132
+ def fallback_to_default?(key)
133
+ owner&.store_attribute_unset_values_fallback_to_default && defaults.key?(key)
134
+ end
135
+
136
+ attr_reader :accessor_types, :defaults, :store_accessor, :owner
125
137
  end
126
138
  end
127
139
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module StoreAttribute # :nodoc:
4
- VERSION = "1.0.1"
4
+ VERSION = "1.1.0"
5
5
  end
@@ -2,3 +2,6 @@
2
2
 
3
3
  require "store_attribute/version"
4
4
  require "store_attribute/active_record"
5
+
6
+ module StoreAttribute
7
+ 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.0.1
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - palkan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-05-06 00:00:00.000000000 Z
11
+ date: 2023-03-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -106,7 +106,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
106
106
  - !ruby/object:Gem::Version
107
107
  version: '0'
108
108
  requirements: []
109
- rubygems_version: 3.3.7
109
+ rubygems_version: 3.4.6
110
110
  signing_key:
111
111
  specification_version: 4
112
112
  summary: ActiveRecord extension which adds typecasting to store accessors