enummer 0.1.0 → 1.0.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: 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: []