flagpole 0.1.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 +7 -0
- data/LICENSE +22 -0
- data/README.md +95 -0
- data/lib/flagpole.rb +64 -0
- data/lib/flagpole/version.rb +3 -0
- metadata +62 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0d780ac84b47a9d2fdd5ee20b34bb7ebb5ef88b6
|
4
|
+
data.tar.gz: 6fb5247148b3bcf18d786037aa51f9750ed888ed
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7eebec9b05c2460a278f338723c45be41861827970c61da69f1fb6079b6754f629ddeb6ed2ae3b7470d60b49bed90cba87d8a8d938a866e63f9b5ef9144d27c6
|
7
|
+
data.tar.gz: 0cca77e4b8f07aa081144c52920cc2a2454f5b67d013d401bc3769bfe900a5f0d7710f23487669115262c11256dad127f8784807f813a78036b3d4d810741c12
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Nicolas Sanguinetti <hi@nicolassanguinetti.info>
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person
|
4
|
+
obtaining a copy of this software and associated documentation
|
5
|
+
files (the "Software"), to deal in the Software without
|
6
|
+
restriction, including without limitation the rights to use,
|
7
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
copies of the Software, and to permit persons to whom the
|
9
|
+
Software is furnished to do so, subject to the following
|
10
|
+
conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
17
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
19
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
20
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
21
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
# Flagpole: Simple bitmap for storing flags
|
2
|
+
|
3
|
+

|
4
|
+
|
5
|
+
Flagpole allows bundling a bunch of flags into a single integer field, by
|
6
|
+
storing each flag as a bit.
|
7
|
+
|
8
|
+
``` ruby
|
9
|
+
notifications_via = Flagpole.new([:email, :sms, :phone_push])
|
10
|
+
notifications_via.to_h #=> { email: false, sms: false, phone_push: false }
|
11
|
+
|
12
|
+
notifications_via[:email] = true
|
13
|
+
notifications_via[:sms] = true
|
14
|
+
|
15
|
+
notifications_via.to_h #=> { email: true, sms: true, phone_push: false }
|
16
|
+
notifications_via.to_i #=> 3
|
17
|
+
```
|
18
|
+
|
19
|
+
Now you can just store that `3` in your database. In order to get that back,
|
20
|
+
just pass it to the constructor:
|
21
|
+
|
22
|
+
``` ruby
|
23
|
+
notifications_via = Flagpole.new(3, [:email, :sms, :phone_push])
|
24
|
+
notifications_via.to_h #=> { email: true, sms: true, phone_push: false }
|
25
|
+
```
|
26
|
+
|
27
|
+
## Install
|
28
|
+
|
29
|
+
gem install flagpole
|
30
|
+
|
31
|
+
## What kind of sorcery is this?!
|
32
|
+
|
33
|
+
If you consider each flag as a "bit" (1 when true, 0 when false), then the above
|
34
|
+
example could be represented as `[1, 1, 0]`. If you reverse this, you get the
|
35
|
+
binary representation of the number `3` (`11`).
|
36
|
+
|
37
|
+
All Flagpole does is give you a nice API to treat sets of named binary settings
|
38
|
+
("flags") as an integer, by doing the marshaling for you.
|
39
|
+
|
40
|
+
[Read more about bitmaps on Wikipedia](https://en.wikipedia.org/wiki/Bit_field).
|
41
|
+
|
42
|
+
## I want to add a new flag
|
43
|
+
|
44
|
+
Easy! Just add a new name **at the end** of the array of flag names. So, In the
|
45
|
+
example above, if you later add support for twitter notifications, just do this:
|
46
|
+
|
47
|
+
``` ruby
|
48
|
+
notifications_via = Flagpole.new(3, [:email, :sms, :phone_push, :twitter])
|
49
|
+
notifications_via.to_h #=> { email: true, sms: true, phone_push: false, twitter: false }
|
50
|
+
```
|
51
|
+
|
52
|
+
As you see, the values are maintained.
|
53
|
+
|
54
|
+
If you wanted the new flag to default to `true`, you just need to increment all
|
55
|
+
the values. You do this by using `#value_of`:
|
56
|
+
|
57
|
+
``` ruby
|
58
|
+
flags = Flagpole([:email, :sms, :phone_push, :twitter])
|
59
|
+
flags.value_of(:twitter) #=> 8
|
60
|
+
```
|
61
|
+
|
62
|
+
Now all you have to do is add `8` to all your stored values, and voila, everyone
|
63
|
+
has twitter notifications enabled.
|
64
|
+
|
65
|
+
## I want to remove an existing flag
|
66
|
+
|
67
|
+
In order to remove a flag, you will need to modify the integer values from your
|
68
|
+
storage. Assuming the example from above, let's say you no longer wish to
|
69
|
+
support SMS notifications.
|
70
|
+
|
71
|
+
In order to find out the value to substract, you can use `#value_of`:
|
72
|
+
|
73
|
+
``` ruby
|
74
|
+
flags = Flagpole([:email, :sms, :phone_push, :twitter])
|
75
|
+
flags.value_of(:sms) #=> 2
|
76
|
+
```
|
77
|
+
|
78
|
+
Now you need to substract 2 from all the values that _have the SMS flag set_. In
|
79
|
+
order to do this, you need to do a _bitwise and_ between the value and `2`.
|
80
|
+
Those that are non-zero, are the ones that have it set. In SQL this would be:
|
81
|
+
|
82
|
+
``` sql
|
83
|
+
UPDATE users
|
84
|
+
SET notification_settings = notification_settings - 2
|
85
|
+
WHERE notification_settings & 2 <> 0
|
86
|
+
```
|
87
|
+
|
88
|
+
## License
|
89
|
+
|
90
|
+
This project is shared under the MIT license. See the attached [LICENSE][] file
|
91
|
+
for details.
|
92
|
+
|
93
|
+
Photo credit: [_Cesarexpo via Wikimedia Commons_](https://commons.wikimedia.org/wiki/File%3AExpo_2010_Shanghai_flags_and_China_Pavilion.jpg).
|
94
|
+
|
95
|
+
[LICENSE]: ./LICENSE
|
data/lib/flagpole.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
class Flagpole
|
2
|
+
# Public: Initialize the set of flags.
|
3
|
+
#
|
4
|
+
# value - An Integer with the initial value of the set.
|
5
|
+
# flags - The name of the flags you're using.
|
6
|
+
def initialize(value = 0, flags)
|
7
|
+
@flags = flags.zip(bitmap(value, flags.size)).to_h
|
8
|
+
end
|
9
|
+
|
10
|
+
# Public: Get a specific flag by name.
|
11
|
+
#
|
12
|
+
# key - One of the flag names passed to the initializer.
|
13
|
+
#
|
14
|
+
# Returns Boolean.
|
15
|
+
def [](key)
|
16
|
+
@flags.fetch(key)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Public: Set a specific flag by name.
|
20
|
+
#
|
21
|
+
# flag - One of the flag names passed to the initializer.
|
22
|
+
# value - A Boolean.
|
23
|
+
#
|
24
|
+
# Returns nothing.
|
25
|
+
# Raises ArgumentError if passed a flag that isn't in the set.
|
26
|
+
def []=(flag, val)
|
27
|
+
fail ArgumentError, "#{flag} isn't a valid flag" unless @flags.key?(flag)
|
28
|
+
@flags[flag] = val
|
29
|
+
end
|
30
|
+
|
31
|
+
# Public: Returns the current value of the flag set as an Integer.
|
32
|
+
def to_i
|
33
|
+
@flags.each_value.with_index.inject(0) do |flag, (bit, power)|
|
34
|
+
flag | (bit ? 1 : 0) * 2**power
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Public: Returns a Hash with all the flags and their current values.
|
39
|
+
def to_h
|
40
|
+
@flags
|
41
|
+
end
|
42
|
+
|
43
|
+
# Public: Find the integer value of a single flag, regardless of whether it is
|
44
|
+
# set or not. This value can then be added/substracted from the integer value
|
45
|
+
# of this set for low-level manipulation of the flag set.
|
46
|
+
#
|
47
|
+
# flag - The name of a flag.
|
48
|
+
#
|
49
|
+
# Returns an Integer.
|
50
|
+
# Raises ArgumentError if passed a flag that isn't in the set.
|
51
|
+
def value_of(flag)
|
52
|
+
fail ArgumentError, "#{flag} isn't a valid flag" unless @flags.key?(flag)
|
53
|
+
2 ** @flags.keys.index(flag)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Internal: Generate the list of bits that make a given number. Each index of
|
57
|
+
# the array corresponds to the bit for 2**index.
|
58
|
+
#
|
59
|
+
# Returns an Array of Booleans.
|
60
|
+
def bitmap(num, size)
|
61
|
+
Array.new(size).map.with_index { |_, i| num[i] == 1 }
|
62
|
+
end
|
63
|
+
private :bitmap
|
64
|
+
end
|
metadata
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: flagpole
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Nicolas Sanguinetti
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-06-22 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: cutest
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.2'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.2'
|
27
|
+
description: Use a bitmap to represent multiple flags (e.g. user settings)
|
28
|
+
email:
|
29
|
+
- contacto@nicolassanguinetti.info
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- LICENSE
|
35
|
+
- README.md
|
36
|
+
- lib/flagpole.rb
|
37
|
+
- lib/flagpole/version.rb
|
38
|
+
homepage: http://github.com/foca/flagpole
|
39
|
+
licenses:
|
40
|
+
- MIT
|
41
|
+
metadata: {}
|
42
|
+
post_install_message:
|
43
|
+
rdoc_options: []
|
44
|
+
require_paths:
|
45
|
+
- lib
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: '0'
|
51
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
requirements: []
|
57
|
+
rubyforge_project:
|
58
|
+
rubygems_version: 2.4.8
|
59
|
+
signing_key:
|
60
|
+
specification_version: 4
|
61
|
+
summary: Handle multiple flags in a single integer
|
62
|
+
test_files: []
|