flagpole 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ ![](https://upload.wikimedia.org/wikipedia/commons/thumb/8/8b/Expo_2010_Shanghai_flags_and_China_Pavilion.jpg/1024px-Expo_2010_Shanghai_flags_and_China_Pavilion.jpg)
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
@@ -0,0 +1,3 @@
1
+ class Flagpole
2
+ VERSION = "0.1.0".freeze
3
+ 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: []