unmagic-enum 0.2.0 → 0.3.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/CHANGELOG.md +8 -1
- data/README.md +27 -0
- data/lib/unmagic/enum/active_record_extensions.rb +67 -3
- data/lib/unmagic/enum/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ccf66f61ae488422b3d515b215511b3a0be62d10d72c56123a175ff779666782
|
|
4
|
+
data.tar.gz: 1ee14dcf4a39545056630e5793bc4928eb7096225c61c2eb8b3588a0c3067789
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8b5fe7f97a7ccecf276d2657af7c530f594fa64aef1eff9b0d38b05139cd050d62f7423944ba8e69baf42bd30e52504c4a40b05e1e0fb13caa17bf287c2c1fef
|
|
7
|
+
data.tar.gz: 442edaf6a0ed84cd1486cb5e297448d93726271b1c4452bfb0ca01443023f760983f438bd42be19e985a15e622f0e9050f80a9bae69b2b74527cbd174e11c3ef
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.3.0] - 2026-06-12
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- `column_type(array: true)` for columns holding multiple values of one enum as a JSON array (a `json`/`jsonb` column). Elements get the same treatment as a scalar `column_type`: cast to enum instances, validated eagerly on assignment (honouring `validate:`), serialized to database values. Blank elements are dropped on cast — so the blank entry a check-box collection's auxiliary hidden field submits never reaches the stored array — and unknown stored values are dropped on read.
|
|
14
|
+
|
|
10
15
|
## [0.2.0] - 2026-06-09
|
|
11
16
|
|
|
12
17
|
### Added
|
|
@@ -33,6 +38,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
33
38
|
- Rails presence support (`blank?`/`present?`) and JSON serialization (`as_json`)
|
|
34
39
|
- Empty strings treated as `nil`
|
|
35
40
|
|
|
36
|
-
[Unreleased]: https://github.com/unreasonable-magic/unmagic-enum/compare/v0.
|
|
41
|
+
[Unreleased]: https://github.com/unreasonable-magic/unmagic-enum/compare/v0.3.0...HEAD
|
|
42
|
+
[0.3.0]: https://github.com/unreasonable-magic/unmagic-enum/compare/v0.2.0...v0.3.0
|
|
43
|
+
[0.2.0]: https://github.com/unreasonable-magic/unmagic-enum/compare/v0.1.1...v0.2.0
|
|
37
44
|
[0.1.1]: https://github.com/unreasonable-magic/unmagic-enum/compare/v0.1.0...v0.1.1
|
|
38
45
|
[0.1.0]: https://github.com/unreasonable-magic/unmagic-enum/releases/tag/v0.1.0
|
data/README.md
CHANGED
|
@@ -81,6 +81,33 @@ message.state # => Message::State::SENT
|
|
|
81
81
|
message.state.sent? # => true
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
+
### Multiple Values (JSON Array Columns)
|
|
85
|
+
|
|
86
|
+
For a `json`/`jsonb` column holding several values of one enum, pass `array: true`:
|
|
87
|
+
|
|
88
|
+
```ruby
|
|
89
|
+
class Article < ApplicationRecord
|
|
90
|
+
class Topic < Unmagic::Enum
|
|
91
|
+
SCIENCE = new("science")
|
|
92
|
+
POLITICS = new("politics")
|
|
93
|
+
SPORT = new("sport")
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
attribute :topics, Topic.column_type(array: true)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
article = Article.new(topics: ["science", "sport"])
|
|
100
|
+
article.topics # => [Article::Topic::SCIENCE, Article::Topic::SPORT]
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Each element behaves like a scalar `column_type`: cast to an enum instance,
|
|
104
|
+
validated eagerly on assignment (an unknown element raises, unless built with
|
|
105
|
+
`validate: true`), and serialized to its database value. Blank elements are
|
|
106
|
+
dropped on cast — so the blank entry a check-box collection's auxiliary hidden
|
|
107
|
+
field submits never reaches the stored array — and stored values the enum no
|
|
108
|
+
longer recognises are dropped on read. The attribute always reads as an array,
|
|
109
|
+
never `nil`.
|
|
110
|
+
|
|
84
111
|
### Key/Value Separation
|
|
85
112
|
|
|
86
113
|
Useful when database values differ from code identifiers:
|
|
@@ -71,13 +71,77 @@ module Unmagic
|
|
|
71
71
|
end
|
|
72
72
|
end
|
|
73
73
|
|
|
74
|
+
# A column that stores multiple values of one enum, as a JSON array of the
|
|
75
|
+
# enum's database values (a `json`/`jsonb` column). Each element gets the
|
|
76
|
+
# same treatment ColumnType gives a single value: cast to an enum
|
|
77
|
+
# instance, validated eagerly on assignment, serialized to its database
|
|
78
|
+
# value. Blank elements are dropped during cast — so the blank entry a
|
|
79
|
+
# check-box collection's auxiliary hidden field submits never reaches the
|
|
80
|
+
# stored array — and unknown stored values are dropped on read, so the
|
|
81
|
+
# attribute always reads as an array of valid enum instances (never nil).
|
|
82
|
+
class ArrayColumnType < ActiveRecord::Type::Value
|
|
83
|
+
def initialize(enum_class, validate: false)
|
|
84
|
+
@enum_class = enum_class
|
|
85
|
+
@element_type = ColumnType.new(enum_class, validate: validate)
|
|
86
|
+
super()
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def type
|
|
90
|
+
:json
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Cast to an array of enum instances. A single value wraps into a
|
|
94
|
+
# one-element array; nil casts to []. Elements cast leniently like
|
|
95
|
+
# ColumnType#cast (blank or unknown resolves to nil) and are compacted
|
|
96
|
+
# away — eager rejection of unknowns happens in #assert_valid_value.
|
|
97
|
+
def cast(value)
|
|
98
|
+
Array(value).map { |element| @element_type.cast(element) }.compact
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Deserialize the database's JSON document. Lenient like
|
|
102
|
+
# ColumnType#deserialize: an element the enum no longer recognises is
|
|
103
|
+
# dropped rather than raising on read.
|
|
104
|
+
def deserialize(value)
|
|
105
|
+
return [] if value.nil? || value == ''
|
|
106
|
+
|
|
107
|
+
value = ActiveSupport::JSON.decode(value) if value.is_a?(::String)
|
|
108
|
+
|
|
109
|
+
Array(value).map { |element| @element_type.deserialize(element) }.compact
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Validate each element at assignment time, the way ColumnType does for
|
|
113
|
+
# a single value: blanks are allowed (they're dropped by #cast); an
|
|
114
|
+
# unknown element raises unless the type was built with validate: true.
|
|
115
|
+
def assert_valid_value(value)
|
|
116
|
+
Array(value).each { |element| @element_type.assert_valid_value(element) }
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Serialize to a JSON array of database values for storage.
|
|
120
|
+
def serialize(value)
|
|
121
|
+
return nil if value.nil?
|
|
122
|
+
|
|
123
|
+
ActiveSupport::JSON.encode(Array(value).map { |element| @element_type.serialize(element) })
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Detect in-place mutation (e.g. `record.statuses << Status::ACTIVE`)
|
|
127
|
+
# by comparing the serialized forms. Order is significant.
|
|
128
|
+
def changed_in_place?(raw_old_value, new_value)
|
|
129
|
+
serialize(new_value) != raw_old_value
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
74
133
|
module ClassMethods
|
|
75
134
|
# For ActiveRecord attribute type definition. `validate:` mirrors
|
|
76
135
|
# ActiveRecord::Enum (default false = raise eagerly on an unknown value;
|
|
77
|
-
# true = let model validations handle it).
|
|
78
|
-
|
|
79
|
-
|
|
136
|
+
# true = let model validations handle it). `array: true` returns a type
|
|
137
|
+
# for columns holding multiple values of the enum as a JSON array.
|
|
138
|
+
# Memoised per option set.
|
|
139
|
+
def column_type(validate: false, array: false)
|
|
140
|
+
(@column_types ||= {})[[validate, array]] ||= if array
|
|
141
|
+
Unmagic::Enum::ActiveRecordExtensions::ArrayColumnType.new(self, validate: validate)
|
|
142
|
+
else
|
|
80
143
|
Unmagic::Enum::ActiveRecordExtensions::ColumnType.new(self, validate: validate)
|
|
144
|
+
end
|
|
81
145
|
end
|
|
82
146
|
end
|
|
83
147
|
|
data/lib/unmagic/enum/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: unmagic-enum
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Keith Pitt
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-06-
|
|
11
|
+
date: 2026-06-12 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activesupport
|