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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4f7599c1e54a52e62cd02fa52bda47b02b108193508575c9c7e4d46e7e67deb0
4
- data.tar.gz: 9b26e5c4c28e8229519dd3eb1fed85c725d953472b7442b4291196740b7c6561
3
+ metadata.gz: adf5116258e6b0e0e5e3b4fc962f97228c26369c41c200a8af07dccd67f8d48e
4
+ data.tar.gz: ea4399838cf8e5cf8be9b23629b88af16c5355f0684d645a7f27db1d94fa642d
5
5
  SHA512:
6
- metadata.gz: 647d8fa2a18291044340ba71dc0ba0524c1e838ae1bb2187b54321c27888a0daa433cdd6ce132e7743a41b365540fdc3aa054e1f7c37c8813b4dc9bf1f1dff09
7
- data.tar.gz: 5530474e36838b2189ad4d138520bd689e792086e8a1d5661d1b0846db5d17511e535ec7fab0c6969cdb82223ec33801fb990a843c62a099cf0527de2f287771
6
+ metadata.gz: 328f419c099db1f1845c9e851fc7f85d7e26728d20ac2d400161ca8f0ba27738e835361499bb3e34f78b4be06b940cc1126d952d48a59cb9d287c6cd2d5cc9af
7
+ data.tar.gz: e119a0af64fa3318df0cfe09ff927c6e15780f592c13163565a6ce33cce7e61246a0395e97c86e7323de1829411efb5e25714959e6da98dbfcffe37824a1a6fd
data/README.md CHANGED
@@ -1,17 +1,25 @@
1
1
  # Enummer
2
- Enummer is a lightweight answer for adding enums with multiple values to Rails, with a similar syntax to Rails' built-in `enum`. It officially supports only PostgreSQL and recent Rails versions because I don't really care about anything else, but PRs are welcome.
2
+
3
+ [![Gem](https://img.shields.io/gem/v/enummer?color=green)](https://rubygems.org/gems/enummer)
4
+ [![Codecov](https://img.shields.io/codecov/c/github/shkm/enummer/main)](https://app.codecov.io/gh/shkm/enummer)
5
+ [![Licence](https://img.shields.io/github/license/shkm/enummer)](https://github.com/shkm/enummer/blob/main/MIT-LICENSE)
6
+ [![Documentation](https://img.shields.io/badge/yard-docs-informational)](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
- Create a migration for a bitstring that looks something like this:
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.column :permissions, "bit(8)"
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
- Note that the limit is currently **required** — do not use variable bit fields. 1 bit gets you 1 option.
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
- *TODO*: a generator. Because.
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
- ## Usage outside of Rails
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).
@@ -4,30 +4,32 @@ require "active_record/type"
4
4
 
5
5
  module Enummer
6
6
  class EnummerType < ::ActiveRecord::Type::Value
7
- attr_reader :bit_pairs
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
- int = Array.wrap(value).sum { |value_name| @bit_pairs.fetch(value_name, 0) }
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
 
@@ -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
- limit = column_for_attribute(name).limit
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, limit)
19
- _enummer_build_values(name, values, limit, options)
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 _enummer_build_values(attribute_name, value_names, limit, options)
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
- bit_position = limit - i - 1
51
+ bit = 1 << i
41
52
 
42
- scope method_name, -> { where("get_bit(#{attribute_name}, ?) = 1", bit_position) }
43
- scope "not_#{method_name}", -> { where("get_bit(#{attribute_name}, ?) = 0", bit_position) }
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])
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Enummer
4
- VERSION = "0.1.0"
4
+ VERSION = "1.0.0"
5
5
  end
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: 0.1.0
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-01-30 00:00:00.000000000 Z
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 PostgreSQL bitstrings.
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: '0'
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: []