multi_bit_field 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.markdown CHANGED
@@ -25,12 +25,12 @@ Say you have daily, weekly and monthly counters:
25
25
 
26
26
  If this model is now sorted in ascending order, it'll sort first by day, then by week and then by month. You could also compare counters with a paired "limit" field.
27
27
 
28
- The methods only require an integer attribute (any ORM will do.) Here's how we'd set this up:
28
+ These methods only require an integer attribute (any ORM will do.) Here's how we'd set this up:
29
29
 
30
30
  ```ruby
31
31
  class User < ActiveRecord::Base
32
- has_bit_field :counter, :daily => 0..4, :weekly => 5..9, :monthly => 10..14
33
- has_bit_field :limit, :daily => 0..4, :weekly => 5..9, :monthly => 10..14
32
+ has_bit_field :counter, :daily_count => 0..4, :weekly_count => 5..9, :monthly_count => 10..14
33
+ has_bit_field :limit, :daily_limit => 0..4, :weekly_limit => 5..9, :monthly_limit => 10..14
34
34
  end
35
35
  ```
36
36
 
@@ -39,22 +39,66 @@ this provides the following methods:
39
39
  ```ruby
40
40
  person = Person.new :daily => 3, :weekly => 5, :monthly => 1
41
41
  person.daily
42
- -> 3
42
+ => 3
43
43
  person.counter
44
- -> 3233
44
+ => 3233
45
45
 
46
46
  person = Person.new :counter => 3233
47
47
  person.monthly
48
- -> 1
48
+ => 1
49
49
  person.weekly
50
- -> 5
50
+ => 5
51
51
  person.monthly = 4
52
52
  person.counter
53
- -> 3236
53
+ => 3236
54
+ ```
55
+
56
+ We can inspsect what this bitstring looks like to converting it like so:
54
57
 
55
- \# to view the binary string, you can convert to base-2
58
+ ```ruby
56
59
  person.counter.to_s(2)
57
60
  -> 000110010100100
61
+
62
+ ```
63
+
64
+ We also provide convenient methods for resetting and incrementing fields. These methods require active-record and active-relation since they use the "update_attributes" and "update_all" methods.
65
+
66
+ ```ruby
67
+ peron.reset(:counter, :daily)
68
+ person.daily
69
+ => 0
70
+ person.monthly
71
+ => 4
72
+
73
+ person.increment(:counter, :daily)
74
+ person.daily
75
+ => 1
76
+
77
+ person.reset(:counter, :daily, :monthly)
78
+ person.daily
79
+ => 0
80
+ person.monthly
81
+ => 0
82
+ ```
83
+
84
+ The same thing works with bulk assignment:
85
+
86
+ ```ruby
87
+ [person1.daily, person2.daily]
88
+ => [15, 23]
89
+
90
+ Person.reset :counter, :daily
91
+ [person1.daily, person2.daily]
92
+ => [0, 0]
93
+
94
+ Person.increment :counter, :daily
95
+ => [1, 1]
96
+ ```
97
+
98
+ By the way, these methods all work with your chainable active-relation query methods!
99
+
100
+ ```ruby
101
+ Person.where(:daily => 0).increment_bitfield(:counter, :daily)
58
102
  ```
59
103
 
60
104
  One limitation you should be aware of:
@@ -65,9 +109,7 @@ Since this technique pins the counters/limits to specific bits, you will need to
65
109
 
66
110
  I intend to add some more methods in the models for the following features:
67
111
 
68
- * Add class and instance "bitfield reset" methods
69
- - This may make the plugin more dependent on an ORM/database
70
- * Possibly replace current String-converstion solution with bit-functions
112
+ * Possibly replace current String-converstion solution with integer bit-functions
71
113
  * Add comparison methods between bitfields on the same column, or between multiple columns
72
114
  * Investigate if there's a use for right/left bit shifting
73
115
 
@@ -1,3 +1,3 @@
1
1
  module MultiBitField
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -1,21 +1,28 @@
1
+ # MultiBitField creates convenience methods for using
2
+ # multiple filum bit-fields with ActiveRecord.
3
+ # Author:: Aaron Spiegel
4
+ # Copyright:: Copyright (c) 2012 Aaron Spiegel
5
+ # License:: MIT License (http://www.opensource.org/licenses/mit-license.php)
1
6
  require 'multi_bit_field/core_ext'
2
7
 
3
8
  module MultiBitField
4
9
  module ClassMethods
5
- # MultiBitField creates convenience methods for using
6
- # multiple filum bit-fields with ActiveRecord.
7
- # Author:: Aaron Spiegel
8
- # Copyright:: Copyright (c) 2012 Aaron Spiegel
9
- # License:: MIT License (http://www.opensource.org/licenses/mit-license.php)
10
+ # alias :reset_bitfields :reset_bitfield
11
+
12
+ # Assign bitfields to a column
10
13
  #
11
14
  # +has_bit_field :column, :fields
12
15
  #
16
+ # @example
13
17
  # class User < ActiveRecord::Base
14
18
  # has_bit_field :counter, :daily => 0..4, :weekly => 5..9, :monthly => 10..14
15
19
  # end
16
20
  #
21
+ # @param [ Symbol ] column Integer attribute to store bitfields
22
+ # @param [ Hash ] fields Specify the bitfield name, and the columns
23
+ # of the bitstring assigned to it
17
24
  def has_bit_field column, fields
18
- set_bitfields! column, fields
25
+ bitfield_setup! column, fields
19
26
 
20
27
  fields.each do |field_name, filum|
21
28
  class_eval <<-EVAL
@@ -29,24 +36,134 @@ module MultiBitField
29
36
  EVAL
30
37
  end
31
38
  end
32
-
33
- def bitfields
34
- @@bitfields
39
+
40
+ # Returns the size of the bitfield in number of bits
41
+ #
42
+ # +bitfield_size :column
43
+ #
44
+ # @example
45
+ # user.bitfield_size :counter
46
+ #
47
+ # @param [ Symbol ] column_name column name that stores the bitfield integer
48
+ #
49
+ def bitfield_size column_name
50
+ @@bitfields[column_name].values.sum.count
51
+ end
52
+
53
+ # Returns a "reset mask" for a list of fields
54
+ #
55
+ # +reset_mask_for :fields
56
+ #
57
+ # @example
58
+ # user.reset_mask_for :field
59
+ #
60
+ # @param [ Symbol ] column name of the column these fields are in
61
+ # @param [ Symbol ] field(s) name of the field(s) for the mask
62
+ def reset_mask_for column_name, *fields
63
+ fields.inject("1" * bitfield_size(column_name)) do |mask, field_name|
64
+ column = @@bitfields[column_name]
65
+ raise ArgumentError, "Unknown column for bitfield: #{column_name}" if column.nil?
66
+ raise ArugmentError, "Unknown field: #{field_name} for column #{column_name}" if column[field_name].nil?
67
+ range = column[field_name]
68
+ mask[range] = "0" * range.count
69
+ mask
70
+ end.to_i(2)
35
71
  end
36
72
 
73
+ # Returns an "increment mask" for a list of fields
74
+ #
75
+ # +increment_mask_for :fields
76
+ #
77
+ # @example
78
+ # user.increment_mask_for :field
79
+ #
80
+ # @param [ Symbol ] column name of the column these fields are in
81
+ # @param [ Symbol ] field(s) name of the field(s) for the mask
82
+ def increment_mask_for column_name, *fields
83
+ fields.inject("0" * bitfield_size(column_name)) do |mask, field_name|
84
+ column = @@bitfields[column_name]
85
+ raise ArgumentError, "Unknown column for bitfield: #{column_name}" if column.nil?
86
+ raise ArugmentError, "Unknown field: #{field_name} for column #{column_name}" if column[field_name].nil?
87
+ range = column[field_name]
88
+ mask[range.last] = "1"
89
+ mask
90
+ end.to_i(2)
91
+ end
92
+
93
+ # Sets one or more bitfields to 0 within a column
94
+ #
95
+ # +reset_bitfield :column, :fields
96
+ #
97
+ # @example
98
+ # User.reset_bitfield :column, :daily, :monthly
99
+ #
100
+ # @param [ Symbol ] column name of the column these fields are in
101
+ # @param [ Symbol ] field(s) name of the field(s) to reset
102
+ def reset_bitfields column_name, *fields
103
+ mask = reset_mask_for column_name, *fields
104
+ update_all "#{column_name} = #{column_name} & #{mask}"
105
+ end
106
+ alias :reset_bitfield :reset_bitfields
107
+
108
+ # Increases one or more bitfields by 1 value
109
+ #
110
+ # +increment_bitfield :column, :fields
111
+ #
112
+ # @example
113
+ # user.increment_bitfield :column, :daily, :monthly
114
+ #
115
+ # @param [ Symbol ] column name of the column these fields are in
116
+ # @param [ Symbol ] field(s) name of the field(s) to reset
117
+ def increment_bitfields column_name, *fields
118
+ mask = increment_mask_for column_name, *fields
119
+ update_all "#{column_name} = #{column_name} + #{mask}"
120
+ end
121
+ alias :increment_bitfield :increment_bitfields
122
+
37
123
  private
38
124
 
39
- def set_bitfields! column, fields
125
+ def bitfield_setup! column, fields
40
126
  if defined?(@@bitfields)
41
- # set the
42
- @@bitfields[column] = fields.values.sum.count
127
+ @@bitfields[column] = fields
43
128
  else
44
- @@bitfields = { column => fields.values.sum.count }
129
+ @@bitfields = { column => fields }
45
130
  end
46
131
  end
47
132
  end
48
133
 
49
134
  module InstanceMethods
135
+ # Sets one or more bitfields to 0 within a column
136
+ #
137
+ # +reset_bitfield :column, :fields
138
+ #
139
+ # @example
140
+ # user.reset_bitfield :column, :daily, :monthly
141
+ #
142
+ # @param [ Symbol ] column name of the column these fields are in
143
+ # @param [ Symbol ] field(s) name of the field(s) to reset
144
+ def reset_bitfields column_name, *fields
145
+ mask = self.class.reset_mask_for column_name, *fields
146
+ self[column_name] = self[column_name] & mask
147
+ save
148
+ end
149
+ alias :reset_bitfield :reset_bitfields
150
+
151
+ # Increases one or more bitfields by 1 value
152
+ #
153
+ # +increment_bitfield :column, :fields
154
+ #
155
+ # @example
156
+ # user.increment_bitfield :column, :daily, :monthly
157
+ #
158
+ # @param [ Symbol ] column name of the column these fields are in
159
+ # @param [ Symbol ] field(s) name of the field(s) to reset
160
+ def increment_bitfields column_name, *fields
161
+ mask = self.class.increment_mask_for column_name, *fields
162
+ self[column_name] = self[column_name] += mask
163
+ save
164
+ end
165
+ alias :increment_bitfield :increment_bitfields
166
+
50
167
  private
51
168
 
52
169
  # self[column_name].to_s(2) -- converts integer to binary string
@@ -54,14 +171,14 @@ module MultiBitField
54
171
  # self[column_name].to_s(2)[filum].to_i(2) -- converts it back to an integer
55
172
  def get_bits_for(column, filum)
56
173
  return nil if self[column].nil?
57
- length = self.class.bitfields[column]
174
+ length = self.class.bitfield_size column
58
175
  bit_string = self[column].to_i.to_s(2)
59
176
 
60
177
  sprintf("%0#{length}d", bit_string)[filum].to_i(2)
61
178
  end
62
179
 
63
180
  def set_bits_for(column, filum, value)
64
- length = self.class.bitfields[column]
181
+ length = self.class.bitfield_size column
65
182
  bit_string = self[column].to_i.to_s(2)
66
183
  temp_field = sprintf("%0#{length}d", bit_string)
67
184
 
Binary file
Binary file
@@ -40,3 +40,19 @@ Migrating to CreatePeople (20120225171132)
40
40
   (0.4ms) select sqlite_version(*)
41
41
   (0.1ms) SELECT "schema_migrations"."version" FROM "schema_migrations"
42
42
   (0.0ms) PRAGMA index_list("people")
43
+  (0.1ms) SELECT "schema_migrations"."version" FROM "schema_migrations" 
44
+  (0.3ms) select sqlite_version(*)
45
+  (1.9ms) CREATE TABLE "people" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255), "birthday" integer, "created_at" datetime NOT NULL, "updated_at" datetime NOT NULL) 
46
+  (1.9ms) CREATE TABLE "schema_migrations" ("version" varchar(255) NOT NULL)
47
+  (0.0ms) PRAGMA index_list("schema_migrations")
48
+  (1.7ms) CREATE UNIQUE INDEX "unique_schema_migrations" ON "schema_migrations" ("version")
49
+  (0.1ms) SELECT version FROM "schema_migrations"
50
+  (1.6ms) INSERT INTO "schema_migrations" (version) VALUES ('20120225171132')
51
+  (0.1ms) SELECT "schema_migrations"."version" FROM "schema_migrations" 
52
+  (0.3ms) select sqlite_version(*)
53
+  (4.1ms) CREATE TABLE "people" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255), "birthday" integer, "created_at" datetime NOT NULL, "updated_at" datetime NOT NULL) 
54
+  (2.0ms) CREATE TABLE "schema_migrations" ("version" varchar(255) NOT NULL)
55
+  (0.2ms) PRAGMA index_list("schema_migrations")
56
+  (2.7ms) CREATE UNIQUE INDEX "unique_schema_migrations" ON "schema_migrations" ("version")
57
+  (0.2ms) SELECT version FROM "schema_migrations"
58
+  (9.2ms) INSERT INTO "schema_migrations" (version) VALUES ('20120225171132')