bitswitch 1.1.0 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/lib/bitswitch.rb +357 -325
  2. data/lib/bitswitch/version.rb +7 -7
  3. metadata +3 -2
@@ -2,344 +2,376 @@ require 'bitswitch/version'
2
2
 
3
3
  class BitSwitch
4
4
 
5
- def initialize(input = 0, labels = {})
6
-
7
- # Placeholder
8
- @labels = {}
9
- @val = 0
10
-
11
- # Validate the value input
12
- unless input.is_a?(Fixnum) || input.is_a?(Hash)
13
- raise KellyLSB::BitSwitch::Error,
14
- "BitSwitch: BitSwitch can only accept an instance of `Fixnum` or `Hash` as the first argument"
15
- end
16
-
17
- # Validate the labels input
18
- unless labels.is_a?(Hash)
19
- raise KellyLSB::BitSwitch::Error,
20
- "BitSwitch: BitSwitch expected the second argument to be a `Hash`"
21
- end
22
-
23
- # Validate hash value input
24
- if input.is_a?(Hash)
25
- input.each do |label, value|
26
-
27
- # Require a String, Symbol or Fixnum value for input hash keys
28
- unless label.is_a?(String) || label.is_a?(Symbol) || label.is_a?(Fixnum)
29
- raise KellyLSB::BitSwitch::Error,
30
- "BitSwitch: Input Hash keys must be a String, Symbol or Fixnum representation of the bit."
31
- end
32
-
33
- # Require input hash values to be true or false
34
- unless value === true || value === false
35
- raise KellyLSB::BitSwitch::Error,
36
- "BitSwitch: Input Hash values must be either true or false."
37
- end
38
- end
39
- end
40
-
41
- # Validate label hash format
42
- labels.each do |bit, label|
43
-
44
- # Require label bits to be Fixnum
45
- unless bit.is_a?(Fixnum)
46
- raise KellyLSB::BitSwitch::Error,
47
- "BitSwitch: Label Hash keys must be instances of Fixnum"
48
- end
49
-
50
- # Require labels to be Strings or Symbols
51
- unless label.is_a?(String) || label.is_a?(Symbol)
52
- raise KellyLSB::BitSwitch::Error,
53
- "BitSwitch: Label Hash values must be either Symbols or Strings"
54
- end
55
- end
56
-
57
- # Apply label hash into the instance variable and assume 0
58
- @labels = labels.inject({}){|h, (k, v)|h.merge(k => v.to_sym)}
59
- @val = input.is_a?(Hash) ? 0 : input
60
-
61
- # Handle hash input
62
- if input.is_a?(Hash)
63
-
64
- # If no labels are set
65
- # Loop through the input and set the values
66
- input.each_with_index { |(label, value), index|
67
- @labels[index] = label.to_sym
68
- self[index] = value
69
- } if @labels.empty?
70
-
71
- # Otherwise just set
72
- input.each { |label, value|
73
- self[label] = value
74
- } unless @labels.empty?
75
- end
76
- end
77
-
78
- # Set a bit (or label)
79
- def []=(bit, val)
80
-
81
- # Validate input label / bit
82
- unless bit.is_a?(Symbol) || bit.is_a?(String) || bit.is_a?(Fixnum)
83
- raise KellyLSB::BitSwitch::Error,
84
- "BitSwitch (#{__method__}): Expected the key to be a Symbol, String or Fixnum"
85
- end
86
-
87
- # Validate input value
88
- unless val === true || val === false || val.is_a?(Fixnum)
89
- raise KellyLSB::BitSwitch::Error,
90
- "BitSwitch (#{__method__}): Expected the value to be true, false or Fixnum"
91
- end
92
-
93
- # Convert numerical to boolean
94
- val = val > 0 if val.is_a?(Fixnum)
95
-
96
- # Get the numerical representation of the label
97
- bit = @labels.invert[bit.to_sym] unless bit.is_a?(Fixnum)
98
-
99
- # If nil return false
100
- if bit.nil?
101
- raise KellyLSB::BitSwitch::Error,
102
- "BitSwitch (#{__method__}): There was no bit to match the requested label."
103
- end
104
-
105
- # Set/Unset the bits
106
- @val |= 2 ** bit if val
107
- @val &= ~(2 ** bit) if !val && self[bit]
108
-
109
- # Return self
110
- self
111
- end
112
-
113
- # Check a bit status
114
- def [](bit)
115
-
116
- # Validate input label / bit
117
- unless bit.is_a?(Symbol) || bit.is_a?(String) || bit.is_a?(Fixnum)
118
- raise KellyLSB::BitSwitch::Error,
119
- "BitSwitch (#{__method__}): Expected the key to be a Symbol, String or Fixnum"
120
- end
121
-
122
- # Get the numerical representation of the label
123
- bit = @labels.invert[bit.to_sym] unless bit.is_a?(Fixnum)
124
-
125
- # If nil return false
126
- if bit.nil?
127
- raise KellyLSB::BitSwitch::Error,
128
- "BitSwitch (#{__method__}): There was no bit to match the requested label."
129
- end
130
-
131
- # Check if the bit was set
132
- (2 ** bit) & @val > 0
133
- end
134
-
135
- # Set an integer
136
- def set=(input)
137
-
138
- # Validate input
139
- unless input.is_a?(Fixnum)
140
- raise KellyLSB::BitSwitch::Error,
141
- "BitSwitch (#{__method__}): Expected value to be a Fixnum"
142
- end
143
-
144
- # Set the value
145
- @val = input
146
-
147
- # Return self
148
- self
149
- end
150
-
151
- def labels(hash = {}, reset = false)
152
-
153
- # Either merge or overwrite labels
154
- @labels.merge!(hash) unless reset
155
- @labels = hash if reset
156
-
157
- # Return self
158
- self
159
- end
160
-
161
- # Get value
162
- def to_i
163
- @val
164
- end
165
-
166
- # Get hash
167
- def to_hash
168
-
169
- # Make sure labels are set
170
- if @labels.empty?
171
- raise KellyLSB::BitSwitch::Error,
172
- "BitSwitch (#{__method__}): No labels were set!"
173
- end
174
-
175
- # Prepare new hash
176
- serialized = Hash.new
177
-
178
- # Loop through the labels
179
- @labels.each do |bit, label|
180
- serialized[label] = self[bit]
181
- end
182
-
183
- # Return serialization
184
- serialized
185
- end
186
-
187
- # Method access
188
- def method_missing(method, *args)
189
-
190
- # Handle setting values
191
- if method[-1] == '='
192
- method = method[0..-2]
193
- return self[method] = args.first
194
- end
195
-
196
- # Return a value
197
- self[method]
198
- end
5
+ def initialize(input = 0, labels = {})
6
+
7
+ # Placeholder
8
+ @labels = {}
9
+ @val = 0
10
+
11
+ # Validate the value input
12
+ unless input.is_a?(Fixnum) || input.is_a?(Hash)
13
+ raise KellyLSB::BitSwitch::Error,
14
+ "BitSwitch: BitSwitch can only accept an instance of `Fixnum` or `Hash` as the first argument"
15
+ end
16
+
17
+ # Validate the labels input
18
+ unless labels.is_a?(Hash)
19
+ raise KellyLSB::BitSwitch::Error,
20
+ "BitSwitch: BitSwitch expected the second argument to be a `Hash`"
21
+ end
22
+
23
+ # Validate hash value input
24
+ if input.is_a?(Hash)
25
+ input.each do |label, value|
26
+
27
+ # Require a String, Symbol or Fixnum value for input hash keys
28
+ unless label.is_a?(String) || label.is_a?(Symbol) || label.is_a?(Fixnum)
29
+ raise KellyLSB::BitSwitch::Error,
30
+ "BitSwitch: Input Hash keys must be a String, Symbol or Fixnum representation of the bit."
31
+ end
32
+
33
+ # Require input hash values to be true or false
34
+ unless value === true || value === false
35
+ raise KellyLSB::BitSwitch::Error,
36
+ "BitSwitch: Input Hash values must be either true or false."
37
+ end
38
+ end
39
+ end
40
+
41
+ # Validate label hash format
42
+ labels.each do |bit, label|
43
+
44
+ # Require label bits to be Fixnum
45
+ unless bit.is_a?(Fixnum)
46
+ raise KellyLSB::BitSwitch::Error,
47
+ "BitSwitch: Label Hash keys must be instances of Fixnum"
48
+ end
49
+
50
+ # Require labels to be Strings or Symbols
51
+ unless label.is_a?(String) || label.is_a?(Symbol)
52
+ raise KellyLSB::BitSwitch::Error,
53
+ "BitSwitch: Label Hash values must be either Symbols or Strings"
54
+ end
55
+ end
56
+
57
+ # Apply label hash into the instance variable and assume 0
58
+ @labels = labels.inject({}){|h, (k, v)|h.merge(k => v.to_sym)}
59
+ @val = input.is_a?(Hash) ? 0 : input
60
+
61
+ # Handle hash input
62
+ if input.is_a?(Hash)
63
+
64
+ # If no labels are set
65
+ # Loop through the input and set the values
66
+ input.each_with_index { |(label, value), index|
67
+ @labels[index] = label.to_sym
68
+ self[index] = value
69
+ } if @labels.empty?
70
+
71
+ # Otherwise just set
72
+ input.each { |label, value|
73
+ self[label] = value
74
+ } unless @labels.empty?
75
+ end
76
+ end
77
+
78
+ # Set a bit (or label)
79
+ def []=(bit, val)
80
+
81
+ # Validate input label / bit
82
+ unless bit.is_a?(Symbol) || bit.is_a?(String) || bit.is_a?(Fixnum)
83
+ raise KellyLSB::BitSwitch::Error,
84
+ "BitSwitch (#{__method__}): Expected the key to be a Symbol, String or Fixnum"
85
+ end
86
+
87
+ # Validate input value
88
+ unless val === true || val === false || val.is_a?(Fixnum)
89
+ raise KellyLSB::BitSwitch::Error,
90
+ "BitSwitch (#{__method__}): Expected the value to be true, false or Fixnum"
91
+ end
92
+
93
+ # Convert numerical to boolean
94
+ val = val > 0 if val.is_a?(Fixnum)
95
+
96
+ # Get the numerical representation of the label
97
+ bit = @labels.invert[bit.to_sym] unless bit.is_a?(Fixnum)
98
+
99
+ # If nil return false
100
+ if bit.nil?
101
+ raise KellyLSB::BitSwitch::Error,
102
+ "BitSwitch (#{__method__}): There was no bit to match the requested label."
103
+ end
104
+
105
+ # Set/Unset the bits
106
+ @val |= 2 ** bit if val
107
+ @val &= ~(2 ** bit) if !val && self[bit]
108
+
109
+ # Return self
110
+ self
111
+ end
112
+
113
+ # Check a bit status
114
+ def [](bit)
115
+
116
+ # Validate input label / bit
117
+ unless bit.is_a?(Symbol) || bit.is_a?(String) || bit.is_a?(Fixnum)
118
+ raise KellyLSB::BitSwitch::Error,
119
+ "BitSwitch (#{__method__}): Expected the key to be a Symbol, String or Fixnum"
120
+ end
121
+
122
+ # Get the numerical representation of the label
123
+ bit = @labels.invert[bit.to_sym] unless bit.is_a?(Fixnum)
124
+
125
+ # If nil return false
126
+ if bit.nil?
127
+ raise KellyLSB::BitSwitch::Error,
128
+ "BitSwitch (#{__method__}): There was no bit to match the requested label."
129
+ end
130
+
131
+ # Check if the bit was set
132
+ (2 ** bit) & @val > 0
133
+ end
134
+
135
+ # Set an integer
136
+ def set=(input)
137
+
138
+ # Validate input
139
+ unless input.is_a?(Fixnum)
140
+ raise KellyLSB::BitSwitch::Error,
141
+ "BitSwitch (#{__method__}): Expected value to be a Fixnum"
142
+ end
143
+
144
+ # Set the value
145
+ @val = input
146
+
147
+ # Return self
148
+ self
149
+ end
150
+
151
+ def labels(hash = {}, reset = false)
152
+
153
+ # Either merge or overwrite labels
154
+ @labels.merge!(hash) unless reset
155
+ @labels = hash if reset
156
+
157
+ # Return self
158
+ self
159
+ end
160
+
161
+ # Get value
162
+ def to_i
163
+ @val
164
+ end
165
+
166
+ # Get hash
167
+ def to_hash
168
+
169
+ # Make sure labels are set
170
+ if @labels.empty?
171
+ raise KellyLSB::BitSwitch::Error,
172
+ "BitSwitch (#{__method__}): No labels were set!"
173
+ end
174
+
175
+ # Prepare new hash
176
+ serialized = Hash.new
177
+
178
+ # Loop through the labels
179
+ @labels.each do |bit, label|
180
+ serialized[label] = self[bit]
181
+ end
182
+
183
+ # Return serialization
184
+ serialized
185
+ end
186
+
187
+ # Method access
188
+ def method_missing(method, *args)
189
+
190
+ # Handle setting values
191
+ if method[-1] == '='
192
+ method = method[0..-2]
193
+ return self[method] = args.first
194
+ end
195
+
196
+ # Return a value
197
+ self[method]
198
+ end
199
199
  end
200
200
 
201
201
  # Convert Fixnum to Switch
202
202
  class Fixnum
203
- def to_switch(labels = {})
204
- BitSwitch.new(self, labels)
205
- end
203
+ def to_switch(labels = {})
204
+ BitSwitch.new(self, labels)
205
+ end
206
206
  end
207
207
 
208
208
  # Convert hash of booleans to Switch
209
209
  class Hash
210
- def to_switch(labels = {})
210
+ def to_switch(labels = {})
211
211
 
212
- # Remove any non boolean values
213
- cleaned = self.delete_if{|k,v| ![true, false, 1, 0, '1', '0'].include?(v)}
212
+ # Remove any non boolean values
213
+ cleaned = self.delete_if{|k,v| ![true, false, 1, 0, '1', '0'].include?(v)}
214
214
 
215
- # Convert Numerical Booleans
216
- cleaned.collect!{|k,v|(v.is_a?(String) ? v.to_i : v)}
217
- cleaned.collect!{|k,v|(v.is_a?(Fixnum) ? !v.zero? : v)}
215
+ # Convert Numerical Booleans
216
+ cleaned.collect!{|k,v|(v.is_a?(String) ? v.to_i : v)}
217
+ cleaned.collect!{|k,v|(v.is_a?(Fixnum) ? !v.zero? : v)}
218
218
 
219
- # Return new BitSwitch
220
- return BitSwitch.new(0, labels) if cleaned.empty?
221
- return BitSwitch.new(cleaned, labels)
222
- end
219
+ # Return new BitSwitch
220
+ return BitSwitch.new(0, labels) if cleaned.empty?
221
+ return BitSwitch.new(cleaned, labels)
222
+ end
223
223
  end
224
224
 
225
225
  # Rails 3 Extension
226
226
  if defined? ActiveRecord::Base
227
- module KellyLSB
228
- module BitSwitch
229
- extend ActiveSupport::Concern
230
-
231
- module ClassMethods
232
- def bitswitch(column, hash = {})
233
- columne = column.to_s + '='
234
- send(:include, Module.new {
235
- send(:define_method, column) do |*args|
236
- val = read_attribute(column)
237
-
238
- # If nil make 0
239
- val = 0 if val.nil?
240
-
241
- # Make sure the value is an integer
242
- raise KellyLSB::BitSwitch::Error, "Column: #{column} is not an integer!" unless val.is_a?(Fixnum)
243
-
244
- # Get the BitSwitch
245
- val = val.to_switch hash
246
-
247
- # Return the value of a specific key
248
- return val[args.first.to_s] unless args[0].nil?
249
-
250
- # Return the switch
251
- return val
252
- end
253
-
254
- send(:define_method, columne) do |args|
255
- val = read_attribute(column)
256
-
257
- # If nil make 0
258
- val = 0 if val.nil?
259
-
260
- # Get the input data
261
- if args.is_a?(Array)
262
- input = args[0]
263
- truncate = args[1]
264
- else
265
- input = args
266
- truncate = false
267
- end
268
-
269
- # Make sure the value is an integer
270
- raise KellyLSB::BitSwitch::Error, "Column: #{column} is not an integer!" unless val.is_a?(Fixnum)
271
-
272
- # Make sure the first input is a hash
273
- raise KellyLSB::BitSwitch::Error, "Input: We are expecting at least one argument that is a Hash" unless input.is_a?(Hash)
274
-
275
- # Get the BitSwitch as a Hash
276
- val = val.to_switch(hash).to_hash
277
-
278
- # If a second argument was passed and was true set all other keys to false
279
- if truncate == true
280
-
281
- # Get list of unset keys
282
- remove = val.keys.collect(&:to_s) - input.keys.collect(&:to_s)
283
-
284
- # Set those keys to false
285
- for key in remove
286
- input[key] = false
287
- end
288
- end
289
-
290
- # Merge in the changes
291
- val = val.merge(input).to_switch(hash)
292
-
293
- # Dont save if this is a new model
294
- return false if read_attribute(:id).nil?
295
-
296
- # Write the updated value
297
- update_column(column, val.to_i)
298
-
299
- # Return the switch
300
- return self.send(column)
301
- end
302
- })
303
-
304
- send(:extend, Module.new {
305
- send(:define_method, column) do |*args|
306
- raise KellyLSB::BitSwitch::Error, "Missing arguments!" if args.empty?
307
- bits = hash.invert
308
-
309
- # Type of condition
310
- if args.first.is_a?(String) && ['AND', 'OR'].include?(args.first.upcase)
311
- delimiter = args.shift
312
- else
313
- delimiter = 'AND'
314
- end
315
-
316
- # Empty conditions
317
- conditions = Array.new
318
-
319
- # Build conditions
320
- if args.first.is_a?(Hash)
321
- args.first.each do |slug,tf|
322
- bit = bits[slug.to_s]
323
- conditions << "POW(2, #{bit}) & `#{self.table_name}`.`#{column}`" + (tf ? ' > 0' : ' <= 0')
324
- end
325
- else
326
- args.each do |slug|
327
- bit = bits[slug.to_s]
328
- conditions << "POW(2, #{bit}) & `#{self.table_name}`.`#{column}` > 0"
329
- end
330
- end
331
-
332
- # Run add query
333
- return self.where(conditions.join(" #{delimiter} ")) unless conditions.empty?
334
-
335
- # Return update query
336
- return query
337
- end
338
- })
339
- end
340
- end
341
- end
342
- end
343
-
344
- ActiveRecord::Base.send(:include, KellyLSB::BitSwitch)
227
+ module KellyLSB
228
+ module BitSwitch
229
+ extend ActiveSupport::Concern
230
+
231
+ module ClassMethods
232
+
233
+ # Generate switch methods
234
+ def bitswitch(column, hash = {})
235
+
236
+ # Set column method name
237
+ columne = column.to_s + '='
238
+
239
+ # Instance methods
240
+ send(:include, Module.new {
241
+
242
+ # BitSwitch access method
243
+ send(:define_method, column) do |*args|
244
+ val = read_attribute(column)
245
+
246
+ # If nil make 0
247
+ val = 0 if val.nil?
248
+
249
+ # Make sure the column value is a Fixnum
250
+ unless val.is_a?(Fixnum)
251
+ raise KellyLSB::BitSwitch::Error,
252
+ "Column: #{column} is not an integer!"
253
+ end
254
+
255
+ # Convert the Fixnum to a BitSwitch
256
+ val = val.to_switch hash
257
+
258
+ # Return the value of a specific key if requested
259
+ return val[args.first] unless args[0].nil?
260
+
261
+ # Return the switch
262
+ val
263
+ end
264
+
265
+ # BitSwitch set method
266
+ send(:define_method, columne) do |args|
267
+ val = read_attribute(column)
268
+
269
+ # If nil make 0
270
+ val = 0 if val.nil?
271
+
272
+ # Get the input data
273
+ if args.is_a?(Array)
274
+ input = args[0]
275
+ truncate = args[1]
276
+ else
277
+ input = args
278
+ truncate = false
279
+ end
280
+
281
+ # Make sure the value is an integer
282
+ unless val.is_a?(Fixnum)
283
+ raise KellyLSB::BitSwitch::Error,
284
+ "Column: #{column} is not an integer!"
285
+ end
286
+
287
+ # Make sure the first input is a hash
288
+ unless input.is_a?(Hash)
289
+ raise KellyLSB::BitSwitch::Error,
290
+ "Input: We are expecting at least one argument that is a Hash"
291
+ end
292
+
293
+ # Convert Fixnum -> BitSwitch -> Hash
294
+ val = val.to_switch(hash).to_hash
295
+
296
+ # Convert all keys to symbols
297
+ input.delete_if do |key, val|
298
+ if key.is_a?(String)
299
+ input[key.to_sym] = val
300
+ true
301
+ else
302
+ false
303
+ end
304
+ end
305
+
306
+ # If we are requested to truncate other keys
307
+ if truncate == true
308
+
309
+ # Get list of unset keys and set them to false
310
+ remove = val.keys.collect(&:to_sym) - input.keys.collect(&:to_sym)
311
+ remove.each { |key| input[key] = false }
312
+ end
313
+
314
+ # Merge in the changes then convert to BitSwitch
315
+ val = val.merge(input).to_switch(hash)
316
+
317
+ # Dont save if this is a new model
318
+ return false if new_record?
319
+
320
+ # Write the updated value
321
+ update_column(column, val.to_i)
322
+
323
+ # Return the switch
324
+ return self.send(column)
325
+ end
326
+ })
327
+
328
+ # Scoping methods
329
+ send(:extend, Module.new {
330
+ send(:define_method, column) do |*args|
331
+
332
+ # Require at least one argument
333
+ if args.empty?
334
+ raise KellyLSB::BitSwitch::Error,
335
+ "Missing arguments! We were expecing at least one label or bit to query by."
336
+ end
337
+
338
+ # Invert the label hash
339
+ bits = hash.invert
340
+
341
+ # Type of condition
342
+ if args.first.is_a?(String) && ['AND', 'OR'].include?(args.first.to_s.upcase)
343
+ delimiter = args.shift
344
+ else
345
+ delimiter = 'AND'
346
+ end
347
+
348
+ # Empty conditions
349
+ conditions = Array.new
350
+
351
+ # Build conditions
352
+ if args.first.is_a?(Hash)
353
+ args.first.each do |slug, tf|
354
+ bit = bits[slug.to_s]
355
+ conditions << "POW(2, #{bit}) & #{self.table_name}.#{column}" + (tf ? ' > 0' : ' <= 0')
356
+ end
357
+ else
358
+ args.each do |slug|
359
+ bit = bits[slug.to_s]
360
+ conditions << "POW(2, #{bit}) & #{self.table_name}.#{column} > 0"
361
+ end
362
+ end
363
+
364
+ # If we have query conditions go ahead and return the updated scope
365
+ return self.where(conditions.join(" #{delimiter} ")) unless conditions.empty?
366
+
367
+ # Return self
368
+ self
369
+ end
370
+ })
371
+ end
372
+ end
373
+ end
374
+ end
375
+
376
+ ActiveRecord::Base.send(:include, KellyLSB::BitSwitch)
345
377
  end
@@ -1,11 +1,11 @@
1
1
  module KellyLSB
2
- module BitSwitch
2
+ module BitSwitch
3
3
 
4
- # Error class
5
- class Error < StandardError
6
- end
4
+ # Error class
5
+ class Error < StandardError
6
+ end
7
7
 
8
- # Version of the gem
9
- VERSION = "1.1.0"
10
- end
8
+ # Version of the gem
9
+ VERSION = "1.1.1"
10
+ end
11
11
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bitswitch
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.1.1
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: 2013-01-29 00:00:00.000000000 Z
12
+ date: 2013-02-14 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: Have you ever wanted to store multiple true/false values in your database,
15
15
  but annoyed with how many fields your tables have, then BitSwitcher is good for
@@ -55,3 +55,4 @@ specification_version: 3
55
55
  summary: Bitswitch lets you store multiple true/false values in an integer using boolean
56
56
  math.
57
57
  test_files: []
58
+ has_rdoc: