enummer 0.1.0 → 1.0.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 +59 -6
- data/lib/enummer/enummer_type.rb +10 -8
- data/lib/enummer/extension.rb +20 -19
- data/lib/enummer/version.rb +1 -1
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: adf5116258e6b0e0e5e3b4fc962f97228c26369c41c200a8af07dccd67f8d48e
|
4
|
+
data.tar.gz: ea4399838cf8e5cf8be9b23629b88af16c5355f0684d645a7f27db1d94fa642d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 328f419c099db1f1845c9e851fc7f85d7e26728d20ac2d400161ca8f0ba27738e835361499bb3e34f78b4be06b940cc1126d952d48a59cb9d287c6cd2d5cc9af
|
7
|
+
data.tar.gz: e119a0af64fa3318df0cfe09ff927c6e15780f592c13163565a6ce33cce7e61246a0395e97c86e7323de1829411efb5e25714959e6da98dbfcffe37824a1a6fd
|
data/README.md
CHANGED
@@ -1,17 +1,25 @@
|
|
1
1
|
# Enummer
|
2
|
-
|
2
|
+
|
3
|
+
[](https://rubygems.org/gems/enummer)
|
4
|
+
[](https://app.codecov.io/gh/shkm/enummer)
|
5
|
+
[](https://github.com/shkm/enummer/blob/main/MIT-LICENSE)
|
6
|
+
[](https://www.rubydoc.info/github/shkm/enummer/main)
|
7
|
+
|
8
|
+
Enummer is a lightweight answer for adding enums with multiple values to Rails, with a similar syntax to Rails' built-in `enum`. At the moment it officially supports only PostgreSQL and recent Rails versions, though YMMV on another DBMS.
|
3
9
|
|
4
10
|
## Installation
|
5
11
|
Add `gem "enummer"` to your Gemfile and `bundle`.
|
6
12
|
|
7
13
|
## Usage
|
8
|
-
|
14
|
+
|
15
|
+
### Setup
|
16
|
+
Create a migration for an integer that looks something like this:
|
9
17
|
|
10
18
|
```ruby
|
11
19
|
class CreateUsers < ActiveRecord::Migration[7.0]
|
12
20
|
def change
|
13
21
|
create_table :users do |t|
|
14
|
-
t.
|
22
|
+
t.integer :permissions, default: 0, null: false
|
15
23
|
end
|
16
24
|
end
|
17
25
|
end
|
@@ -23,11 +31,55 @@ Now set up enummer with the available values in your model:
|
|
23
31
|
enummer permissions: %i[read write execute]
|
24
32
|
```
|
25
33
|
|
26
|
-
|
34
|
+
### Scopes
|
35
|
+
|
36
|
+
Scopes will now be provided for `<option>` and `not_<option>`.
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
User.read
|
40
|
+
User.not_read
|
41
|
+
User.write
|
42
|
+
User.not_write
|
43
|
+
User.execute
|
44
|
+
User.not_execute
|
45
|
+
```
|
46
|
+
|
47
|
+
### Getter methods
|
48
|
+
|
49
|
+
Simply calling the instance method for the column will return an array of options. Question mark methods are also provided.
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
user = User.last
|
53
|
+
|
54
|
+
user.permissions # => [:read, :write]
|
55
|
+
|
56
|
+
user.read? # => true
|
57
|
+
user.write? # => true
|
58
|
+
user.execute? # => false
|
59
|
+
```
|
60
|
+
|
61
|
+
### Setter methods
|
62
|
+
|
63
|
+
Options can be set with an array of symbols or via bang methods. Bang methods will additionally persist the changes.
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
user.update(permissions: %i[read write])
|
67
|
+
user.write!
|
68
|
+
```
|
69
|
+
|
70
|
+
## FAQ
|
71
|
+
|
72
|
+
### Which data type should I use?
|
73
|
+
That depends on how many options you expect to store. [In PostgreSQL](https://www.postgresql.org/docs/9.1/datatype-numeric.html) you should be able to store `bytes * 8 - 1` of your data type:
|
27
74
|
|
28
|
-
|
75
|
+
| Type | Bytes | Values |
|
76
|
+
|----------|-------|-------------|
|
77
|
+
| smallint | 2 | 15 |
|
78
|
+
| integer | 4 | 31 |
|
79
|
+
| bigint | 8 | 65 |
|
80
|
+
| numeric | ??? | all of them |
|
29
81
|
|
30
|
-
|
82
|
+
### How can I use it outside of Rails?
|
31
83
|
lol stop
|
32
84
|
|
33
85
|
## Contributing
|
@@ -36,6 +88,7 @@ Make an issue / PR and we'll see.
|
|
36
88
|
## Alternatives
|
37
89
|
- [flag_shih_tzu](https://github.com/pboling/flag_shih_tzu)
|
38
90
|
- Lots of booleans
|
91
|
+
- DB Arrays
|
39
92
|
|
40
93
|
## License
|
41
94
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/lib/enummer/enummer_type.rb
CHANGED
@@ -4,30 +4,32 @@ require "active_record/type"
|
|
4
4
|
|
5
5
|
module Enummer
|
6
6
|
class EnummerType < ::ActiveRecord::Type::Value
|
7
|
-
|
8
|
-
|
9
|
-
def initialize(value_names:, limit:)
|
7
|
+
# @param [Array<Symbol>] value_names list of all possible values for this type
|
8
|
+
def initialize(value_names:)
|
10
9
|
@value_names = value_names
|
11
10
|
@bit_pairs = determine_bit_pairs(value_names)
|
12
|
-
@limit = limit
|
13
11
|
end
|
14
12
|
|
13
|
+
# @return Symbol Representation of this type
|
14
|
+
# @example
|
15
|
+
# :enummer[read|write|execute]
|
15
16
|
def type
|
16
17
|
"enummer[#{@value_names.join("|")}]".to_sym
|
17
18
|
end
|
18
19
|
|
20
|
+
# @param [Symbol|Array<Symbol>] value Current value represented as one or more symbols
|
21
|
+
# @return Numeric Numeric representation of values
|
19
22
|
def serialize(value)
|
20
23
|
return unless value
|
21
24
|
|
22
|
-
|
23
|
-
|
24
|
-
int.to_s(2).rjust(@limit, "0")
|
25
|
+
Array.wrap(value).sum { |value_name| @bit_pairs.fetch(value_name, 0) }
|
25
26
|
end
|
26
27
|
|
28
|
+
# @param [Numeric] value Numeric representation of values
|
29
|
+
# @return [Array<Symbol>] Current value represented as symbols
|
27
30
|
def deserialize(value)
|
28
31
|
return [] unless value
|
29
32
|
|
30
|
-
value = value.to_i(2)
|
31
33
|
@bit_pairs.each_with_object([]) do |(pair_name, pair_value), value_names|
|
32
34
|
next if (value & pair_value).zero?
|
33
35
|
|
data/lib/enummer/extension.rb
CHANGED
@@ -2,6 +2,11 @@
|
|
2
2
|
|
3
3
|
module Enummer
|
4
4
|
module Extension
|
5
|
+
# @param [Hash] values The attribute name to options mapping for an multi-option enum
|
6
|
+
# @option values [Boolean|String] :_prefix The prefix to give to generated methods. If true, uses the attribute name.
|
7
|
+
# @option values [Boolean|String] :_suffix The suffix to give to generated methods. If true, uses the attribute name.
|
8
|
+
# @example Defining an enummer with a prefix. This would generate `#can_read?`, `#can_read=`, `#can_read!`, `.can_read`, etc.
|
9
|
+
# enummer permissions: %i[read write execute], :_prefix: 'can'
|
5
10
|
def enummer(values)
|
6
11
|
options = {}
|
7
12
|
options[:_prefix] = values.delete(:_prefix)
|
@@ -9,19 +14,25 @@ module Enummer
|
|
9
14
|
|
10
15
|
name, values = values.first
|
11
16
|
|
12
|
-
|
13
|
-
|
14
|
-
attribute(name, :enummer, value_names: values, limit: limit)
|
17
|
+
attribute(name, :enummer, value_names: values)
|
15
18
|
|
16
19
|
singleton_class.__send__(:define_method, name) { values }
|
17
20
|
|
18
|
-
_enummer_build_with_scope(name, values
|
19
|
-
_enummer_build_values(name, values,
|
21
|
+
_enummer_build_with_scope(name, values)
|
22
|
+
_enummer_build_values(name, values, options)
|
20
23
|
end
|
21
24
|
|
22
25
|
private
|
23
26
|
|
24
|
-
def
|
27
|
+
def _enummer_build_with_scope(attribute_name, value_names)
|
28
|
+
scope "with_#{attribute_name}", lambda { |desired|
|
29
|
+
expected = Array.wrap(desired).sum(0) { |value| 1 << value_names.index(value) }
|
30
|
+
|
31
|
+
where("#{attribute_name} & :expected = :expected", expected: expected)
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
def _enummer_build_values(attribute_name, value_names, options)
|
25
36
|
value_names.each_with_index do |name, i|
|
26
37
|
method_name = _enummer_method_name(attribute_name, name, options)
|
27
38
|
|
@@ -37,23 +48,13 @@ module Enummer
|
|
37
48
|
update(attribute_name => self[attribute_name] + [name])
|
38
49
|
end
|
39
50
|
|
40
|
-
|
51
|
+
bit = 1 << i
|
41
52
|
|
42
|
-
scope method_name, -> { where("
|
43
|
-
scope "not_#{method_name}", -> { where("
|
53
|
+
scope method_name, -> { where("#{attribute_name} & :bit = :bit", bit: bit) }
|
54
|
+
scope "not_#{method_name}", -> { where("#{attribute_name} & :bit != :bit", bit: bit) }
|
44
55
|
end
|
45
56
|
end
|
46
57
|
|
47
|
-
def _enummer_build_with_scope(attribute_name, value_names, limit)
|
48
|
-
scope "with_#{attribute_name}", lambda { |desired|
|
49
|
-
desired = Array.wrap(desired)
|
50
|
-
|
51
|
-
bitstring = (0..limit - 1).to_a.sum("") { |i| desired.include?(value_names[i]) ? "1" : "0" }.reverse
|
52
|
-
|
53
|
-
where("#{attribute_name} & :expected = :expected", expected: bitstring)
|
54
|
-
}
|
55
|
-
end
|
56
|
-
|
57
58
|
def _enummer_method_name(attribute_name, value_name, options)
|
58
59
|
prefix = _enummer_affix(attribute_name, options[:_prefix])
|
59
60
|
suffix = _enummer_affix(attribute_name, options[:_suffix])
|
data/lib/enummer/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: enummer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jamie Schembri
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-02-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -24,7 +24,7 @@ dependencies:
|
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 7.0.0
|
27
|
-
description: Enummer implements multi-value enums with
|
27
|
+
description: Enummer implements multi-value enums with bitwise operations.
|
28
28
|
email:
|
29
29
|
- jamie@schembri.me
|
30
30
|
executables: []
|
@@ -55,7 +55,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
55
55
|
requirements:
|
56
56
|
- - ">="
|
57
57
|
- !ruby/object:Gem::Version
|
58
|
-
version: '
|
58
|
+
version: '2.7'
|
59
59
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
60
60
|
requirements:
|
61
61
|
- - ">="
|
@@ -65,5 +65,5 @@ requirements: []
|
|
65
65
|
rubygems_version: 3.3.3
|
66
66
|
signing_key:
|
67
67
|
specification_version: 4
|
68
|
-
summary: Multi-value enums for Rails
|
68
|
+
summary: Multi-value enums for Rails.
|
69
69
|
test_files: []
|