bitwise_attribute 0.2.2 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 728afc2714df27029947e0f98b2c542ad8835bf44e052f720c588d22f2bcb821
4
- data.tar.gz: 5e8fe818d69624b1888f954416a15f9cf938039067a5f67fea12eef73bf76e31
3
+ metadata.gz: '09a20c421bbc1d6c4c7803c43be1b937f53725c9f9cea4be27c05c0c00b4d23a'
4
+ data.tar.gz: 15af80b8780cfa9e081c45186049ee7bbd9a96266f140fcf2294e14adadf8dcc
5
5
  SHA512:
6
- metadata.gz: 257fb003a2eb0421a5ca03b5b6cd32a2b4317c7172cb26a204e2516bc9b7aeb07989a99867bb5f33bbb6ccc6667f7bd88d5e1c116b18f6d782eee9dd469c5ece
7
- data.tar.gz: 58b7153d9e3b51898d387252a1a91ed6f1514c778eff46f1941ed78cb4ff88b9d933131e493b4dbb184aaa27720d430335df741ba9a5ea103a66305b15d73a8e
6
+ metadata.gz: 8bbf57d1f5e5a69a2ab6952ca9d8cf5376e28846e8ab2b7d0c858d9838bf7d6563e6ceb283b8641b0095e2dc05150a17aa60bdafaad8c872c775bf967d4b9c61
7
+ data.tar.gz: 90a8b4ca18833063b57d64279ef2e2c8bef0c2a7975788c128dbf31fca0a4b3535a6820955a6e0e42412e4afb3f98a7db8e18549d8f5cfc93f16c5b66406fb24
data/.rspec CHANGED
@@ -1,4 +1,4 @@
1
1
  --format documentation
2
2
  --color
3
3
  --require spec_helper
4
- --order random
4
+ --order rand
data/.rubocop.yml CHANGED
@@ -2,20 +2,19 @@ Metrics/AbcSize:
2
2
  Max: 30
3
3
 
4
4
  Style/Documentation:
5
- Exclude:
6
- - 'spec/**/*'
7
- - 'test/**/*'
8
- - 'lib/bitwise_attribute.rb'
9
- - 'lib/bitwise_attribute/values_list.rb'
5
+ Enabled: false
10
6
 
11
7
  Metrics/LineLength:
12
8
  Max: 100
13
9
 
14
10
  Metrics/MethodLength:
15
- Max: 30
11
+ Max: 50
16
12
 
17
13
  Metrics/BlockLength:
18
14
  Max: 150
19
15
 
20
16
  Metrics/ModuleLength:
21
17
  Max: 200
18
+
19
+ Metrics/AbcSize:
20
+ Max: 70
data/.travis.yml CHANGED
@@ -3,3 +3,6 @@ language: ruby
3
3
  rvm:
4
4
  - 2.5.0
5
5
  before_install: gem install bundler -v 1.16.1
6
+ script:
7
+ - bundle exec rspec --fail-fast
8
+ - bundle exec rubocop
data/Gemfile.lock CHANGED
@@ -1,17 +1,23 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- bitwise_attribute (0.2.2)
5
- activesupport (~> 5.1.5)
4
+ bitwise_attribute (0.3.0)
6
5
 
7
6
  GEM
8
7
  remote: https://rubygems.org/
9
8
  specs:
9
+ activemodel (5.1.5)
10
+ activesupport (= 5.1.5)
11
+ activerecord (5.1.5)
12
+ activemodel (= 5.1.5)
13
+ activesupport (= 5.1.5)
14
+ arel (~> 8.0)
10
15
  activesupport (5.1.5)
11
16
  concurrent-ruby (~> 1.0, >= 1.0.2)
12
17
  i18n (~> 0.7)
13
18
  minitest (~> 5.1)
14
19
  tzinfo (~> 1.1)
20
+ arel (8.0.0)
15
21
  ast (2.4.0)
16
22
  byebug (10.0.1)
17
23
  coderay (1.1.2)
@@ -61,6 +67,7 @@ GEM
61
67
  json (>= 1.8, < 3)
62
68
  simplecov-html (~> 0.10.0)
63
69
  simplecov-html (0.10.2)
70
+ sqlite3 (1.3.13)
64
71
  thread_safe (0.3.6)
65
72
  tzinfo (1.2.5)
66
73
  thread_safe (~> 0.1)
@@ -70,6 +77,7 @@ PLATFORMS
70
77
  ruby
71
78
 
72
79
  DEPENDENCIES
80
+ activerecord (>= 3)
73
81
  bitwise_attribute!
74
82
  bundler (~> 1.16)
75
83
  pry-byebug
@@ -77,6 +85,7 @@ DEPENDENCIES
77
85
  rspec (~> 3.0)
78
86
  rubocop (~> 0.54.0)
79
87
  simplecov (~> 0.15.1)
88
+ sqlite3
80
89
 
81
90
  BUNDLED WITH
82
91
  1.16.1
data/README.md CHANGED
@@ -2,6 +2,9 @@
2
2
  [![Build Status](https://travis-ci.org/rikas/bitwise_attribute.svg?branch=master)](https://travis-ci.org/rikas/bitwise_attribute)
3
3
 
4
4
  # BitwiseAttribute
5
+ Manipulation of bitmask attributes in your classes (typically ActiveRecord models). You can have multiple values mapped to the same column — for example when you need a User with multiple roles.
6
+
7
+ It adds a lot of helper methods so you don't have to deal with the underlying mask.
5
8
 
6
9
  ## Installation
7
10
 
@@ -24,6 +27,10 @@ Or install it yourself as:
24
27
  Include `BitwiseAttribute` and then define your attribute with `attr_bitwise`. By default it will
25
28
  use the singularized name with `_mask`.
26
29
 
30
+ Check the example below to see how to use the helpers and methods created automatically in your classes.
31
+
32
+ ## Examples
33
+
27
34
  For the `roles` attribute you need to have `role_mask` column in your model, so add the migration:
28
35
 
29
36
  ```ruby
@@ -37,19 +44,93 @@ end
37
44
  ```ruby
38
45
  class User < ActiveRecord::Base
39
46
  include BitwiseAttribute
40
-
47
+
48
+ # This line will do all the magic!
49
+ #
50
+ # By default we assume that your column will be called `role_mask`.
51
+ # You can send the `column_name` option if your column has another name.
52
+ #
41
53
  attr_bitwise :roles, values: %i[user moderator admin]
42
54
  end
43
55
  ```
44
56
 
45
- You can then use
57
+ ### Instance manipulation
58
+
59
+ You can then access the `roles` field without having to know the underlying value of `role_mask`.
60
+
61
+ ```ruby
62
+ user = User.new(roles: [:user, :admin])
63
+
64
+ user.roles
65
+ #=> [:user, :admin]
66
+
67
+ user.role_mask
68
+ #=> 5
69
+
70
+ user.roles << :moderator
71
+ user.roles
72
+ #=> [:user, :admin, :moderator]
73
+ ```
74
+
75
+ You can see if a particular record has a given value:
76
+
77
+ ```ruby
78
+ user.admin?
79
+ #=> true
80
+ ```
81
+
82
+ ### Class methods
83
+
84
+ You can get all available values and correspondent mask value:
85
+
86
+ ```ruby
87
+ User.roles
88
+ #=> { :user => 1, :moderator => 2, :admin => 4 }
89
+ ```
90
+
91
+ So if you need all the keys just use:
92
+
93
+ ```ruby
94
+ User.roles.keys
95
+ #=> [:user, :moderator, :admin]
96
+ ```
97
+
98
+ ### ActiveRecord named scopes
99
+
100
+ BitwiseAttribte will add some methods for easier queries on the database:
101
+
102
+ ```ruby
103
+ User.with_roles
104
+ #=> Users that have at least one role
105
+
106
+ User.with_roles(:admin)
107
+ #=> Users that have the :admin role
108
+
109
+ User.with_roles(:admin, :moderator)
110
+ #=> Users that have the admin role AND the moderator role
111
+
112
+ User.with_any_roles(:admin, :moderator)
113
+ #=> Users that have the admin role OR the moderator role
114
+
115
+ User.with_exact_roles(:moderator)
116
+ #=> Users that have ONLY the moderator role
117
+
118
+ User.with_exact_roles(:moderator, :admin)
119
+ #=> Users that have ONLY the moderator AND admin roles
120
+
121
+ User.without_roles(:admin)
122
+ #=> Users without the admin role
123
+
124
+ User.without_roles(:admin, :moderator)
125
+ #=> Users without the admin role AND without the moderator role
126
+ ```
127
+
128
+ These are the same as using `with_roles`:
46
129
 
47
130
  ```ruby
48
- user = User.first
49
- user.roles #=> []
50
- user.roles = [:user, :admin] #=> [:user, :admin]
51
- user.save
52
- user.role_mask #=> 5
131
+ User.admin
132
+ User.user
133
+ User.admin.moderator
53
134
  ```
54
135
 
55
136
  ## Development
data/bin/console CHANGED
@@ -1,7 +1,7 @@
1
- # frozen_string_literal: true
2
-
3
1
  #!/usr/bin/env ruby
4
2
 
3
+ # frozen_string_literal: true
4
+
5
5
  require 'bundler/setup'
6
6
  require 'bitwise_attribute'
7
7
 
@@ -24,12 +24,12 @@ Gem::Specification.new do |spec|
24
24
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
25
25
  spec.require_paths = %w[lib]
26
26
 
27
- spec.add_dependency 'activesupport', '~> 5.1.5'
28
-
27
+ spec.add_development_dependency 'activerecord', '>= 3'
29
28
  spec.add_development_dependency 'bundler', '~> 1.16'
30
29
  spec.add_development_dependency 'pry-byebug'
31
30
  spec.add_development_dependency 'rake', '~> 10.0'
32
31
  spec.add_development_dependency 'rspec', '~> 3.0'
33
32
  spec.add_development_dependency 'rubocop', '~> 0.54.0'
34
33
  spec.add_development_dependency 'simplecov', '~> 0.15.1'
34
+ spec.add_development_dependency 'sqlite3'
35
35
  end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BitwiseAttribute
4
+ module ActiveRecordMethods
5
+ def define_named_scopes(name, column_name, mapping)
6
+ define_singleton_method("with_#{name}") do |*keys|
7
+ keys = keys.flatten.uniq
8
+
9
+ return [] unless keys&.any?
10
+
11
+ records = where("#{column_name} & #{mapping[keys.first]} = #{mapping[keys.first]}")
12
+
13
+ keys[1..-1].each do |key|
14
+ records = records.where("#{column_name} & #{mapping[key]} = #{mapping[key]}")
15
+ end
16
+
17
+ records
18
+ end
19
+
20
+ define_singleton_method("with_any_#{name}") do |*keys|
21
+ keys = keys.flatten.uniq
22
+
23
+ return where.not(column_name => nil) unless keys&.any?
24
+
25
+ records = where("#{column_name} & #{mapping[keys.first]} = #{mapping[keys.first]}")
26
+
27
+ keys[1..-1].each do |key|
28
+ records = records.or(where("#{column_name} & #{mapping[key]} = #{mapping[key]}"))
29
+ end
30
+
31
+ records
32
+ end
33
+
34
+ define_singleton_method("with_exact_#{name}") do |*keys|
35
+ keys = keys.flatten.uniq
36
+
37
+ return [] unless keys&.any?
38
+
39
+ records = send("with_#{name}", keys)
40
+
41
+ (mapping.keys - keys).each do |key|
42
+ records = records.where("#{column_name} & #{mapping[key]} != #{mapping[key]}")
43
+ end
44
+
45
+ records
46
+ end
47
+
48
+ define_singleton_method("without_#{name}") do |*keys|
49
+ keys = keys.flatten.uniq
50
+
51
+ return where(column_name => nil).or(where(column_name => 0)) unless keys&.any?
52
+
53
+ records = where("#{column_name} & #{mapping[keys.first]} != #{mapping[keys.first]}")
54
+
55
+ keys[1..-1].each do |key|
56
+ records = records.where("#{column_name} & #{mapping[key]} != #{mapping[key]}")
57
+ end
58
+
59
+ records
60
+ end
61
+
62
+ # Defines a class method for each key of the mapping, returning records that have *at least*
63
+ # the corresponding value.
64
+ mapping.keys.each do |key|
65
+ define_singleton_method(key) do
66
+ send("with_#{name}", key)
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module BitwiseAttribute
4
- VERSION = '0.2.2'
4
+ VERSION = '0.3.0'
5
5
  end
@@ -3,52 +3,39 @@
3
3
  require 'active_support'
4
4
  require 'active_support/concern'
5
5
  require 'active_support/core_ext'
6
+
6
7
  require 'bitwise_attribute/version'
7
8
  require 'bitwise_attribute/values_list'
9
+ require 'bitwise_attribute/active_record_methods'
8
10
 
9
11
  module BitwiseAttribute
10
- extend ActiveSupport::Concern
12
+ def self.included(base)
13
+ base.extend ClassMethods
14
+ end
15
+
16
+ module ClassMethods
17
+ include BitwiseAttribute::ActiveRecordMethods
11
18
 
12
- class_methods do
13
19
  def attr_bitwise(name, column_name: nil, values:)
14
20
  column_name ||= "#{name.to_s.singularize}_mask"
15
21
 
16
- # Check if the values is an array or hash with valid values. Raise ArgumentError otherwise.
17
- validate_values!(values)
18
-
19
22
  mapping = build_mapping(values)
20
23
 
21
- @mapping ||= {}
22
- @mapping[name] = mapping
23
-
24
24
  define_class_methods(name, column_name, mapping)
25
- define_instante_methods(name, column_name, mapping)
25
+ define_instance_methods(name, column_name, mapping)
26
26
  end
27
27
 
28
+ private
29
+
28
30
  def define_class_methods(name, column_name, mapping)
29
- # Class methods
30
31
  define_singleton_method(name) do
31
32
  mapping
32
33
  end
33
34
 
34
- define_singleton_method("with_#{name}") do |*keys|
35
- where(column_name => bitwise_union(keys, name))
36
- end
37
-
38
- define_singleton_method("with_all_#{name}") do |*keys|
39
- where(column_name => bitwise_intersection(keys, name))
40
- end
41
-
42
- # Defines a class method for each key of the mapping, returning records that have *at least*
43
- # the corresponding value.
44
- mapping.keys.each do |key|
45
- define_singleton_method(key) do
46
- send("with_#{key}")
47
- end
48
- end
35
+ define_named_scopes(name, column_name, mapping) if defined?(ActiveRecord)
49
36
  end
50
37
 
51
- def define_instante_methods(name, column_name, mapping)
38
+ def define_instance_methods(name, column_name, mapping)
52
39
  define_method(name) do
53
40
  roles = value_getter(column_name, mapping)
54
41
 
@@ -72,65 +59,9 @@ module BitwiseAttribute
72
59
  # Each sym get a power of 2 value
73
60
  def build_mapping(values)
74
61
  {}.tap do |hash|
75
- if values.is_a?(Hash)
76
- hash.merge!(values.sort_by { |_, value| value }.to_h)
77
- else
78
- values.each_with_index { |key, index| hash[key] = 2**index }
79
- end
80
- end
81
- end
82
-
83
- # Validates the numeric values for each key. If it's not a power of 2 then raise ArgumentError.
84
- def validate_values!(values)
85
- return true if values.is_a?(Array)
86
-
87
- values.reject { |_, value| (Math.log2(value) % 1.0).zero? }.tap do |invalid_options|
88
- if invalid_options.any?
89
- raise(ArgumentError, "Values should be a power of 2 (#{invalid_options})")
90
- end
62
+ values.each_with_index { |key, index| hash[key] = (0b1 << index) }
91
63
  end
92
64
  end
93
-
94
- def mapping_for_name(name)
95
- @mapping[name.to_sym]
96
- end
97
-
98
- def values_for_column(name)
99
- mapping_for_name(name).values
100
- end
101
-
102
- def map_to_values(name, keys)
103
- mapping = mapping_for_name(name)
104
-
105
- keys.map { |key| mapping[key.to_sym] }.compact
106
- end
107
-
108
- def bitwise_union(keys, name)
109
- mask = []
110
-
111
- mapped = map_to_values(name, keys)
112
-
113
- mapped.each do |mv|
114
- values_for_column(name).each do |value|
115
- mask << (mv | value)
116
- end
117
- end
118
-
119
- mask.uniq
120
- end
121
-
122
- def bitwise_intersection(keys, name)
123
- mask = []
124
-
125
- mapped = map_to_values(name, keys)
126
- mapped = mapped.reduce(&:|)
127
-
128
- values_for_column(name).each do |value|
129
- mask << (value | mapped)
130
- end
131
-
132
- mask.uniq
133
- end
134
65
  end
135
66
 
136
67
  private
@@ -150,16 +81,12 @@ module BitwiseAttribute
150
81
  values.each do |value|
151
82
  raise(ArgumentError, "Unknown value #{value}!") unless mapping[value]
152
83
 
153
- add_value(mask_column, mapping[value])
84
+ send("#{mask_column}=", send(mask_column) | mapping[value])
154
85
  end
155
86
  end
156
87
 
157
- # Return if value presents in mask (raw value)
88
+ # Return if value is present in mask (raw value)
158
89
  def value?(mask_column, val)
159
90
  send(mask_column) & val != 0
160
91
  end
161
-
162
- def add_value(mask_column, val)
163
- send("#{mask_column}=", send(mask_column) | val)
164
- end
165
92
  end
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bitwise_attribute
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ricardo Otero
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-03-28 00:00:00.000000000 Z
11
+ date: 2018-03-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: activesupport
14
+ name: activerecord
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 5.1.5
20
- type: :runtime
19
+ version: '3'
20
+ type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 5.1.5
26
+ version: '3'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -108,6 +108,20 @@ dependencies:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
110
  version: 0.15.1
111
+ - !ruby/object:Gem::Dependency
112
+ name: sqlite3
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
111
125
  description: Bitwise attribute for ruby class and Rails model.
112
126
  email:
113
127
  - oterosantos@gmail.com
@@ -129,6 +143,7 @@ files:
129
143
  - bin/setup
130
144
  - bitwise_attribute.gemspec
131
145
  - lib/bitwise_attribute.rb
146
+ - lib/bitwise_attribute/active_record_methods.rb
132
147
  - lib/bitwise_attribute/values_list.rb
133
148
  - lib/bitwise_attribute/version.rb
134
149
  homepage: https://github.com/rikas/bitwise_attribute