bitmasker 0.1.0 → 0.2.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.
- data/README.md +59 -2
- data/lib/bitmasker/bitmask_scope.rb +61 -0
- data/lib/bitmasker/generator.rb +12 -2
- data/lib/bitmasker/version.rb +1 -1
- data/lib/bitmasker.rb +1 -0
- data/test/bitmasker/bitmask_attributes_test.rb +1 -1
- data/test/bitmasker/bitmask_scope_test.rb +53 -0
- data/test/integration_test.rb +1 -1
- data/test/test_helper.rb +1 -1
- metadata +12 -3
data/README.md
CHANGED
@@ -16,6 +16,10 @@ Synopsis
|
|
16
16
|
end
|
17
17
|
```
|
18
18
|
|
19
|
+
[](https://codeclimate.com/github/amiel/bitmasker)
|
20
|
+
[](https://travis-ci.org/amiel/bitmasker)
|
21
|
+
|
22
|
+
|
19
23
|
Examples
|
20
24
|
--------
|
21
25
|
|
@@ -34,7 +38,8 @@ Examples
|
|
34
38
|
end
|
35
39
|
```
|
36
40
|
|
37
|
-
|
41
|
+
This will define the following methods:
|
42
|
+
|
38
43
|
* `User#notifications` -- returns a BitmaskAttributes object representing all values
|
39
44
|
* `User#send_weekly_newsletter?` -- predicate
|
40
45
|
* `User#send_weekly_newsletter` -- works just like the predicate, makes it easy to use actionview form helpers
|
@@ -43,8 +48,28 @@ this will define the following methods:
|
|
43
48
|
* `User#send_monthly_newsletter`
|
44
49
|
* `User#send_monthly_newsletter=(value)`
|
45
50
|
|
46
|
-
the call to `config.accessible` calls `attr_accessible :send_weekly_newsletter, :send_monthly_newsletter` in your model
|
51
|
+
the call to `config.accessible` calls `attr_accessible :send_weekly_newsletter, :send_monthly_newsletter` in your model.
|
52
|
+
|
53
|
+
### Scopes
|
54
|
+
|
55
|
+
Bitmasker also sets up a few useful scopes:
|
47
56
|
|
57
|
+
* `User#with_notifications`
|
58
|
+
* `User#without_notifications`
|
59
|
+
* `User#with_any_notifications`
|
60
|
+
|
61
|
+
#### Scopes Examples
|
62
|
+
|
63
|
+
```ruby
|
64
|
+
# Users that want weekly newsletter
|
65
|
+
User.with_notifications(:send_weekly_newsletter)
|
66
|
+
|
67
|
+
# Users that want monthly AND weekly newsletters
|
68
|
+
User.with_notifications(:send_monthly_newsletter, :send_weekly_newsletter)
|
69
|
+
|
70
|
+
# Users that want monthly OR weekly newsletters
|
71
|
+
User.with_any_notifications(:send_monthly_newsletter, :send_weekly_newsletter)
|
72
|
+
```
|
48
73
|
|
49
74
|
|
50
75
|
View Example
|
@@ -79,5 +104,37 @@ if you are using attr_accessible in your model and you want to mass-assign your
|
|
79
104
|
* `name` name of the field name in the database where all this info is stored, should be an integer
|
80
105
|
|
81
106
|
|
107
|
+
Updating from `has_bitmask_attributes`
|
108
|
+
--------------------------------------
|
109
|
+
|
110
|
+
If you used the `method_format` feature from `has_bitmask_attributes`, you will need to change
|
111
|
+
your configuration as `method_format` has been removed.
|
112
|
+
|
113
|
+
### Before
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
# in app/models/user.rb
|
117
|
+
class User < ActiveRecord::Base
|
118
|
+
has_bitmask_attributes :notifications do |config|
|
119
|
+
config.attribute :weekly_newsletter, 0b0001
|
120
|
+
config.attribute :monthly_newsletter, 0b0010, true
|
121
|
+
config.method_format 'send_%s'
|
122
|
+
end
|
123
|
+
end
|
124
|
+
```
|
125
|
+
|
126
|
+
### After
|
127
|
+
|
128
|
+
```ruby
|
129
|
+
# in app/models/user.rb
|
130
|
+
class User < ActiveRecord::Base
|
131
|
+
has_bitmask_attributes :notifications do |config|
|
132
|
+
config.attribute :send_weekly_newsletter, 0b0001
|
133
|
+
config.attribute :send_monthly_newsletter, 0b0010, true
|
134
|
+
end
|
135
|
+
end
|
136
|
+
```
|
137
|
+
|
138
|
+
|
82
139
|
Copyright (c) 2012 Amiel Martin, released under the MIT license
|
83
140
|
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Bitmasker
|
2
|
+
class BitmaskScope
|
3
|
+
include ActiveModel::AttributeMethods
|
4
|
+
attribute_method_prefix 'with_'
|
5
|
+
attribute_method_prefix 'with_any_'
|
6
|
+
attribute_method_prefix 'without_'
|
7
|
+
|
8
|
+
class_attribute :model_class
|
9
|
+
class_attribute :field_name
|
10
|
+
class_attribute :mask_name
|
11
|
+
class_attribute :bitmask_attributes
|
12
|
+
|
13
|
+
def self.make(model_class, field_name, mask_name, bitmask_attributes)
|
14
|
+
klass = Class.new(self) do
|
15
|
+
define_attribute_methods [mask_name]
|
16
|
+
|
17
|
+
def self.to_s
|
18
|
+
"#{superclass}(#{model_class}##{field_name})"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
klass.model_class = model_class
|
23
|
+
klass.field_name = field_name
|
24
|
+
klass.mask_name = mask_name
|
25
|
+
klass.bitmask_attributes = bitmask_attributes.stringify_keys
|
26
|
+
|
27
|
+
klass
|
28
|
+
end
|
29
|
+
|
30
|
+
def bitmask
|
31
|
+
Bitmask.new(bitmask_attributes, 0)
|
32
|
+
end
|
33
|
+
|
34
|
+
# REVIEW: This (the unused _ attribute) tells me I have the design wrong
|
35
|
+
def with_attribute(_, *attributes)
|
36
|
+
# TODO: Test lots of databases
|
37
|
+
bitmask_query attributes, "#{field_name} & :mask = :mask"
|
38
|
+
end
|
39
|
+
|
40
|
+
def with_any_attribute(_, *attributes)
|
41
|
+
# TODO: Test lots of databases
|
42
|
+
bitmask_query attributes, "#{field_name} & :mask <> 0"
|
43
|
+
end
|
44
|
+
|
45
|
+
def without_attribute(_, *attributes)
|
46
|
+
# TODO: Test lots of databases
|
47
|
+
bitmask_query attributes, "#{field_name} & :mask = 0 OR #{field_name} IS NULL"
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def bitmask_query(attributes, query)
|
53
|
+
mask = bitmask
|
54
|
+
mask.set_array(Array.wrap(attributes).flatten.map(&:to_s))
|
55
|
+
|
56
|
+
# TODO: Test lots of databases
|
57
|
+
model_class.where(query, mask: mask.to_i)
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
data/lib/bitmasker/generator.rb
CHANGED
@@ -8,6 +8,8 @@ module Bitmasker
|
|
8
8
|
@mask_name = mask_name
|
9
9
|
@field_name = mask_name.to_s + '_mask'
|
10
10
|
|
11
|
+
@scope_name = mask_name.to_s + '_scope'
|
12
|
+
|
11
13
|
@use_attr_accessible = false
|
12
14
|
end
|
13
15
|
|
@@ -33,14 +35,22 @@ module Bitmasker
|
|
33
35
|
|
34
36
|
def generate
|
35
37
|
klass = BitmaskAttributes.make(@model, @field_name, @bitmask_attributes, @bitmask_defaults)
|
38
|
+
scope_klass = BitmaskScope.make(@model, @field_name, @mask_name, @bitmask_attributes)
|
36
39
|
|
37
40
|
@model.send :define_method, @mask_name do
|
38
41
|
klass.new(self)
|
39
42
|
end
|
40
43
|
|
44
|
+
@model.singleton_class.send :define_method, @scope_name do
|
45
|
+
scope_klass.new
|
46
|
+
end
|
47
|
+
|
48
|
+
@model.singleton_class.delegate "with_#{@mask_name}",
|
49
|
+
"without_#{@mask_name}", "with_any_#{@mask_name}",
|
50
|
+
to: @scope_name
|
51
|
+
|
41
52
|
@bitmask_attributes.each do |attribute, mask|
|
42
|
-
@model.delegate attribute, "#{attribute}?", "#{attribute}=",
|
43
|
-
"#{attribute}_was",
|
53
|
+
@model.delegate attribute, "#{attribute}?", "#{attribute}=", "#{attribute}_was",
|
44
54
|
to: @mask_name
|
45
55
|
|
46
56
|
@model.attr_accessible attribute if @use_attr_accessible
|
data/lib/bitmasker/version.rb
CHANGED
data/lib/bitmasker.rb
CHANGED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class Bitmasker::BitmaskScopeTest < MiniTest::Test
|
4
|
+
|
5
|
+
MockModel = Class.new
|
6
|
+
|
7
|
+
def model_instance
|
8
|
+
@model_instance ||= MockModel.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def setup
|
12
|
+
@klass = Bitmasker::BitmaskScope.make(
|
13
|
+
MockModel, 'email_mask', 'emails',
|
14
|
+
send_weekly_email: 0b0001,
|
15
|
+
send_monthly_newsletter: 0b0010,
|
16
|
+
send_daily_spam: 0b0100,
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
def subject
|
21
|
+
@subject ||= @klass.new
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_klass_to_s
|
25
|
+
assert_equal "Bitmasker::BitmaskScope(Bitmasker::BitmaskScopeTest::MockModel#email_mask)", @klass.to_s
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
def test_with_attribute
|
30
|
+
MockModel.expects(:where).with("email_mask & :mask = :mask", mask: 1)
|
31
|
+
subject.with_emails(:send_weekly_email)
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_with_attributes_array
|
35
|
+
MockModel.expects(:where).with("email_mask & :mask = :mask", mask: 6)
|
36
|
+
subject.with_emails([:send_monthly_newsletter, :send_daily_spam])
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_with_attributes
|
40
|
+
MockModel.expects(:where).with("email_mask & :mask = :mask", mask: 6)
|
41
|
+
subject.with_emails(:send_monthly_newsletter, :send_daily_spam)
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_without_attribute
|
45
|
+
MockModel.expects(:where).with("email_mask & :mask = 0 OR email_mask IS NULL", mask: 2)
|
46
|
+
subject.without_emails(:send_monthly_newsletter)
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_with_any_attribute
|
50
|
+
MockModel.expects(:where).with("email_mask & :mask <> 0", mask: 3)
|
51
|
+
subject.with_any_emails([:send_weekly_email, :send_monthly_newsletter])
|
52
|
+
end
|
53
|
+
end
|
data/test/integration_test.rb
CHANGED
data/test/test_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bitmasker
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2013-10-18 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bitmask
|
@@ -100,6 +100,7 @@ extensions: []
|
|
100
100
|
extra_rdoc_files: []
|
101
101
|
files:
|
102
102
|
- lib/bitmasker/bitmask_attributes.rb
|
103
|
+
- lib/bitmasker/bitmask_scope.rb
|
103
104
|
- lib/bitmasker/generator.rb
|
104
105
|
- lib/bitmasker/model.rb
|
105
106
|
- lib/bitmasker/rails.rb
|
@@ -109,6 +110,7 @@ files:
|
|
109
110
|
- Rakefile
|
110
111
|
- README.md
|
111
112
|
- test/bitmasker/bitmask_attributes_test.rb
|
113
|
+
- test/bitmasker/bitmask_scope_test.rb
|
112
114
|
- test/integration_test.rb
|
113
115
|
- test/test_helper.rb
|
114
116
|
homepage: https://github.com/amiel/bitmasker
|
@@ -123,20 +125,27 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
123
125
|
- - ! '>='
|
124
126
|
- !ruby/object:Gem::Version
|
125
127
|
version: '0'
|
128
|
+
segments:
|
129
|
+
- 0
|
130
|
+
hash: -2321849182877328955
|
126
131
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
127
132
|
none: false
|
128
133
|
requirements:
|
129
134
|
- - ! '>='
|
130
135
|
- !ruby/object:Gem::Version
|
131
136
|
version: '0'
|
137
|
+
segments:
|
138
|
+
- 0
|
139
|
+
hash: -2321849182877328955
|
132
140
|
requirements: []
|
133
141
|
rubyforge_project:
|
134
|
-
rubygems_version: 1.8.
|
142
|
+
rubygems_version: 1.8.24
|
135
143
|
signing_key:
|
136
144
|
specification_version: 3
|
137
145
|
summary: Bitmasker allows you to store many boolean values as one integer field in
|
138
146
|
the database.
|
139
147
|
test_files:
|
140
148
|
- test/bitmasker/bitmask_attributes_test.rb
|
149
|
+
- test/bitmasker/bitmask_scope_test.rb
|
141
150
|
- test/integration_test.rb
|
142
151
|
- test/test_helper.rb
|