serialize_attributes 0.4.1 → 0.5.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 +4 -4
- data/README.md +49 -1
- data/lib/serialize_attributes/store.rb +31 -12
- data/lib/serialize_attributes/types/enum.rb +53 -0
- data/lib/serialize_attributes/version.rb +1 -1
- data/lib/serialize_attributes.rb +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f4fa19f41c2a597b312db2432d1df9a2efad508c91feebd885caab60f68304ac
|
4
|
+
data.tar.gz: a84b9c79d3bc59c8a416d8c2c3f2f08f46bdf3a25d8de465c8fb61593ce181aa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c5f6f45154a0b8f28b8712f81714c4d4eccccb9a58b7a54a0d2fe24499c101656d6416d80cf34eac0632de6444b8fe0b57503cbec6a5a2f8ffdea21badd7428c
|
7
|
+
data.tar.gz: 336dfe149b07fb43a171d417a79d33f1fc67eec5d6399cb73a803f38f0fc2afa72cafd1da859c489ad1c92551291337599dfb55827678f46c6717845288fc273
|
data/README.md
CHANGED
@@ -7,7 +7,7 @@ class MyModel
|
|
7
7
|
serialize_attributes :settings do
|
8
8
|
attribute :user_name, :string
|
9
9
|
attribute :subscribed, :boolean, default: false
|
10
|
-
attribute :subscriptions, :string, array: true
|
10
|
+
attribute :subscriptions, :string, array: true
|
11
11
|
end
|
12
12
|
end
|
13
13
|
```
|
@@ -145,6 +145,54 @@ specify a `default` attribute yourself explicitly:
|
|
145
145
|
attribute :emails, :string, array: true, default: ["unknown@example.com"]
|
146
146
|
```
|
147
147
|
|
148
|
+
### Enumerated ("enum") types
|
149
|
+
|
150
|
+
Since enum types are a common thing when managing external data, there is a special enum
|
151
|
+
type defined by the library:
|
152
|
+
|
153
|
+
```ruby
|
154
|
+
class MyModel
|
155
|
+
serialize_attributes :settings do
|
156
|
+
attribute :state, :enum, of: ["open", "closed"]
|
157
|
+
end
|
158
|
+
end
|
159
|
+
```
|
160
|
+
|
161
|
+
Unlike `ActiveRecord::Enum`, enums here work by attaching an inclusion validator to your
|
162
|
+
model. So for example, with the above code, I'll get a validation failure by default:
|
163
|
+
|
164
|
+
```ruby
|
165
|
+
MyModel.new(state: nil).tap(&:valid?).errors
|
166
|
+
#=> { state: "is not included in the list" }
|
167
|
+
```
|
168
|
+
|
169
|
+
If you wish to allow nil values in your enum, you should add it to the `of` collection:
|
170
|
+
|
171
|
+
```ruby
|
172
|
+
attribute :state, :enum, of: [nil, "open", "closed"]
|
173
|
+
```
|
174
|
+
|
175
|
+
The column is probably now the source of truth for correct values, so you can also
|
176
|
+
introspect the store to fetch these from elsewhere (e.g. for building documentation):
|
177
|
+
|
178
|
+
```ruby
|
179
|
+
MyModel.serialized_attributes_store(:settings).enum_options(:state)
|
180
|
+
#=> ["open", "closed"]
|
181
|
+
```
|
182
|
+
|
183
|
+
Finally, you can also use complex types within the enum itself, by passing an additional
|
184
|
+
`type:` attribute. Values will then be cast or deserialized per that type, and the result
|
185
|
+
of the casting is what is validated, e.g:
|
186
|
+
|
187
|
+
```ruby
|
188
|
+
attribute :state, :enum, of: [nil, true, false], type: :boolean
|
189
|
+
```
|
190
|
+
|
191
|
+
```ruby
|
192
|
+
MyModel.new(state: "f").state
|
193
|
+
#=> false
|
194
|
+
```
|
195
|
+
|
148
196
|
### Usage with ActiveModel alone
|
149
197
|
|
150
198
|
It's also possible to use this library without `ActiveRecord`:
|
@@ -19,16 +19,16 @@ module SerializeAttributes
|
|
19
19
|
# Get a list of the attributes managed by this store. Pass an optional `type` argument
|
20
20
|
# to filter attributes by their type.
|
21
21
|
#
|
22
|
-
# Model.
|
22
|
+
# Model.serialized_attributes_store(:settings).attribute_names
|
23
23
|
# => [:user_name, :subscribed, :subscriptions]
|
24
24
|
#
|
25
|
-
# Model.
|
25
|
+
# Model.serialized_attributes_store(:settings).attribute_names(type: :string)
|
26
26
|
# => [:user_name, :subscriptions]
|
27
27
|
#
|
28
|
-
# Model.
|
28
|
+
# Model.serialized_attributes_store(:settings).attribute_names(type: :string, array: true)
|
29
29
|
# => [:subscriptions]
|
30
30
|
#
|
31
|
-
# Model.
|
31
|
+
# Model.serialized_attributes_store(:settings).attribute_names(type: :string, array: false)
|
32
32
|
# => [:user_name]
|
33
33
|
#
|
34
34
|
#
|
@@ -42,6 +42,17 @@ module SerializeAttributes
|
|
42
42
|
end.keys
|
43
43
|
end
|
44
44
|
|
45
|
+
# Get a list of enumerated options for the column `name` in this store.
|
46
|
+
#
|
47
|
+
# Model.serialized_attributes_store(:settings).enum_options(:enumy)
|
48
|
+
# => [nil, "placed", "confirmed"]
|
49
|
+
def enum_options(name)
|
50
|
+
type = @attributes.fetch(name.to_sym)
|
51
|
+
raise ArgumentError, "`#{name}` attribute is not an enum type" unless type.respond_to?(:options)
|
52
|
+
|
53
|
+
type.options
|
54
|
+
end
|
55
|
+
|
45
56
|
# Cast a stored attribute against a given name
|
46
57
|
#
|
47
58
|
# Model.serialized_attributes_store(:settings).cast(:user_name, 42)
|
@@ -88,20 +99,28 @@ module SerializeAttributes
|
|
88
99
|
end
|
89
100
|
end
|
90
101
|
|
91
|
-
|
102
|
+
NO_DEFAULT = Object.new
|
103
|
+
|
104
|
+
def attribute(name, type, default: NO_DEFAULT, array: false, **type_options)
|
92
105
|
name = name.to_sym
|
93
|
-
|
94
|
-
|
95
|
-
|
106
|
+
type = ActiveModel::Type.lookup(type, **type_options) if type.is_a?(Symbol)
|
107
|
+
|
108
|
+
if array
|
109
|
+
raise ArgumentError, "Enum-arrays not currently supported" if type.is_a?(Types::Enum)
|
110
|
+
|
111
|
+
type = ArrayWrapper.new(type)
|
112
|
+
end
|
96
113
|
|
97
114
|
@attributes[name] = type
|
98
115
|
|
99
|
-
if
|
100
|
-
@defaults[name] =
|
101
|
-
elsif
|
116
|
+
if default != NO_DEFAULT
|
117
|
+
@defaults[name] = default
|
118
|
+
elsif array
|
102
119
|
@defaults[name] = []
|
103
120
|
end
|
104
121
|
|
122
|
+
type.attach_validations_to(@model_class, name) if type.respond_to?(:attach_validations_to)
|
123
|
+
|
105
124
|
@model_class.module_eval <<~RUBY, __FILE__, __LINE__ + 1
|
106
125
|
def #{name} # def user_name
|
107
126
|
if @_bad_typcasting # if @_bad_typcasting
|
@@ -129,7 +148,7 @@ module SerializeAttributes
|
|
129
148
|
.cast(:#{name}, value) # .cast(:user_name, value)
|
130
149
|
store = public_send(:#{@column_name}) # store = public_send(:settings)
|
131
150
|
#
|
132
|
-
if #{
|
151
|
+
if #{array} && cast_value == ArrayWrapper::EMPTY # if array && cast_value == ArrayWrapper::EMPTY
|
133
152
|
store.delete("#{name}") # store.delete("user_name")
|
134
153
|
else # else
|
135
154
|
store.merge!("#{name}" => cast_value) # store.merge!("user_name" => cast_value)
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/module/delegation"
|
4
|
+
|
5
|
+
module SerializeAttributes
|
6
|
+
module Types
|
7
|
+
# A custom type which can only hold one of a set of predetermined values.
|
8
|
+
class Enum
|
9
|
+
attr_reader :options
|
10
|
+
|
11
|
+
# Construct a type instance.
|
12
|
+
#
|
13
|
+
# @param of [Array] One or more possible values that this type can take
|
14
|
+
# @param type [Symbol] An optional ActiveModel::Type instance, or symbol, for
|
15
|
+
# casting/uncasting (only required if enum has non-primitive types)
|
16
|
+
#
|
17
|
+
# @example Required to be one of two values
|
18
|
+
# attribute :state, :enum, of: ["placed", "confirmed"]
|
19
|
+
#
|
20
|
+
# @example Optionally allowing nil
|
21
|
+
# attribute :folding, :enum, of: [nil, "top-fold", "bottom-fold"]
|
22
|
+
#
|
23
|
+
# @example Casting input/output using another type
|
24
|
+
# attribute :loves_pizza, :enum, of: [true], type: :boolean
|
25
|
+
# # object.loves_pizza = "t"
|
26
|
+
# #=> true
|
27
|
+
def initialize(of: [], type: nil)
|
28
|
+
@options = of.freeze
|
29
|
+
@type = resolve_type(type)
|
30
|
+
end
|
31
|
+
|
32
|
+
def attach_validations_to(object, field_name)
|
33
|
+
object.validates_inclusion_of(field_name, in: @options)
|
34
|
+
end
|
35
|
+
|
36
|
+
delegate_missing_to :@type
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
UNTYPED_TYPE = ActiveModel::Type::Value.new
|
41
|
+
|
42
|
+
def resolve_type(given)
|
43
|
+
case given
|
44
|
+
in Symbol then ActiveModel::Type.lookup(given)
|
45
|
+
in nil then UNTYPED_TYPE
|
46
|
+
in _ then given
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
ActiveModel::Type.register(:enum, SerializeAttributes::Types::Enum)
|
data/lib/serialize_attributes.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: serialize_attributes
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Zaikio
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-07-
|
11
|
+
date: 2022-07-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|
@@ -36,6 +36,7 @@ files:
|
|
36
36
|
- Rakefile
|
37
37
|
- lib/serialize_attributes.rb
|
38
38
|
- lib/serialize_attributes/store.rb
|
39
|
+
- lib/serialize_attributes/types/enum.rb
|
39
40
|
- lib/serialize_attributes/version.rb
|
40
41
|
homepage: https://github.com/zaikio/serialize_attributes
|
41
42
|
licenses:
|