bitfields 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.
- data/README.markdown +45 -0
- data/Rakefile +19 -0
- data/VERSION +1 -0
- data/lib/bitfields.rb +139 -0
- data/spec/bitfields_spec.rb +277 -0
- data/spec/database.rb +15 -0
- data/spec/spec_helper.rb +4 -0
- metadata +70 -0
data/README.markdown
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
Save migrations and columns by storing multiple booleans in a single integer.
|
2
|
+
e.g. 3 = 1->true 2->true 4->false, 4 = 1->false 2->false 4->true, 5 = 1->true 2->false 4->true
|
3
|
+
|
4
|
+
class User < ActiveRecord::Base
|
5
|
+
include Bitfields
|
6
|
+
bitfield :my_bits, 1 => :seller, 2 => :insane, 4 => :stupid
|
7
|
+
end
|
8
|
+
|
9
|
+
user = User.new(:seller => true, :insane => true)
|
10
|
+
user.seller == true
|
11
|
+
user.stupid? == false
|
12
|
+
user.my_bits == 9
|
13
|
+
|
14
|
+
- records changes `user.chamges == {:seller => [false, true]}`
|
15
|
+
- adds scopes `User.seller.stupid.first` (deactivate with `bitfield ..., :scopes => false`)
|
16
|
+
- builds sql `User.bitfield_sql(:insane => true, :stupid => false) == 'users.my_bits IN (2, 3)'` (2 and 1+2)
|
17
|
+
- builds not-index-using sql with `bitfield ... ,:query_mode => :bit_operator` and `User.bitfield_sql(:insane => true, :stupid => false) == '(users.my_bits & 3) = 1'`, always slower than IN() sql, since it will not use an existing index (tested for up to 64 values)
|
18
|
+
- builds update sql `User.set_bitfield_sql(:insane => true, :stupid => false) == 'my_bits = (my_bits | 6) - 4'`
|
19
|
+
- gives access to bits `User.bitfields[:my_bits][:stupid] == 4`
|
20
|
+
|
21
|
+
Install
|
22
|
+
=======
|
23
|
+
As Gem: ` sudo gem install bitfields `
|
24
|
+
Or as Rails plugin: ` script/plugins install git://github.com/grosser/bitfields.git `
|
25
|
+
|
26
|
+
### Migration
|
27
|
+
ALWAYS set a default, bitfield queries will not work for NULL
|
28
|
+
t.integer :my_bits, :default => 0, :null => false
|
29
|
+
OR
|
30
|
+
add_column :users, :my_bits, :integer, :default => 0, :null => false
|
31
|
+
|
32
|
+
Usage
|
33
|
+
=====
|
34
|
+
|
35
|
+
# update all users
|
36
|
+
User.seller.not_stupid.update_all(User.set_bitfield_sql(:seller => true, :insane => true))
|
37
|
+
|
38
|
+
# delete the shop when a user is no longer a seller
|
39
|
+
before_save :delete_shop, :if => lambda{|u| u.changes['seller'] == [true, false]}
|
40
|
+
|
41
|
+
Author
|
42
|
+
======
|
43
|
+
[Michael Grosser](http://pragmatig.wordpress.com)
|
44
|
+
grosser.michael@gmail.com
|
45
|
+
Hereby placed under public domain, do what you want, just do not hold me accountable...
|
data/Rakefile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
task :default => :spec
|
2
|
+
require 'spec/rake/spectask'
|
3
|
+
Spec::Rake::SpecTask.new {|t| t.spec_opts = ['--color']}
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'jeweler'
|
7
|
+
project_name = 'bitfields'
|
8
|
+
Jeweler::Tasks.new do |gem|
|
9
|
+
gem.name = project_name
|
10
|
+
gem.summary = "Save migrations and columns by storing multiple booleans in a single integer."
|
11
|
+
gem.email = "grosser.michael@gmail.com"
|
12
|
+
gem.homepage = "http://github.com/grosser/#{project_name}"
|
13
|
+
gem.authors = ["Michael Grosser"]
|
14
|
+
end
|
15
|
+
|
16
|
+
Jeweler::GemcutterTasks.new
|
17
|
+
rescue LoadError
|
18
|
+
puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install jeweler"
|
19
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/lib/bitfields.rb
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
|
3
|
+
module Bitfields
|
4
|
+
VERSION = File.read( File.join(File.dirname(__FILE__),'..','VERSION') ).strip
|
5
|
+
TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE'] # taken from ActiveRecord::ConnectionAdapters::Column
|
6
|
+
|
7
|
+
def self.included(base)
|
8
|
+
base.class_inheritable_accessor :bitfields, :bitfield_options
|
9
|
+
base.extend Bitfields::ClassMethods
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.extract_bits(options)
|
13
|
+
bitfields = {}
|
14
|
+
options.keys.select{|key| key.is_a?(Numeric) }.each do |bit|
|
15
|
+
bit_name = options.delete(bit).to_sym
|
16
|
+
bitfields[bit_name] = bit
|
17
|
+
end
|
18
|
+
bitfields
|
19
|
+
end
|
20
|
+
|
21
|
+
module ClassMethods
|
22
|
+
def bitfield(column, options)
|
23
|
+
# prepare ...
|
24
|
+
column = column.to_sym
|
25
|
+
options = options.dup # since we will modify them...
|
26
|
+
|
27
|
+
# extract options
|
28
|
+
self.bitfields ||= {}
|
29
|
+
self.bitfield_options ||= {}
|
30
|
+
bitfields[column] = Bitfields.extract_bits(options)
|
31
|
+
bitfield_options[column] = options
|
32
|
+
|
33
|
+
# add instance methods and scopes
|
34
|
+
bitfields[column].keys.each do |bit_name|
|
35
|
+
define_method(bit_name){ bitfield_value(bit_name) }
|
36
|
+
define_method("#{bit_name}?"){ bitfield_value(bit_name) }
|
37
|
+
define_method("#{bit_name}="){|value| set_bitfield_value(bit_name, value) }
|
38
|
+
if options[:scopes] != false
|
39
|
+
scoping_method = (respond_to?(:scope) ? :scope : :named_scope) # AR 3.0+ uses scope
|
40
|
+
send scoping_method, bit_name, :conditions => bitfield_sql(bit_name => true)
|
41
|
+
send scoping_method, "not_#{bit_name}", :conditions => bitfield_sql(bit_name => false)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
include Bitfields::InstanceMethods
|
46
|
+
end
|
47
|
+
|
48
|
+
def bitfield_column(bit_name)
|
49
|
+
bitfields.detect{|c, bits| bits.keys.include?(bit_name.to_sym) }.first
|
50
|
+
end
|
51
|
+
|
52
|
+
def bitfield_sql(bit_values)
|
53
|
+
bits = group_bits_by_column(bit_values)
|
54
|
+
bits.map{|column, bit_values| bitfield_sql_by_column(column, bit_values) } * ' AND '
|
55
|
+
end
|
56
|
+
|
57
|
+
def set_bitfield_sql(bit_values)
|
58
|
+
columns = group_bits_by_column(bit_values)
|
59
|
+
columns.map{|column, bit_values| set_bitfield_sql_by_column(column, bit_values) } * ', '
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def bitfield_sql_by_column(column, bit_values)
|
65
|
+
mode = (bitfield_options[column][:query_mode] || :in_list)
|
66
|
+
case mode
|
67
|
+
when :in_list then
|
68
|
+
max = (bitfields[column].values.max * 2) - 1
|
69
|
+
bits = (0..max).to_a # all possible bits
|
70
|
+
bit_values.each do |bit_name, value|
|
71
|
+
bit = bitfields[column][bit_name]
|
72
|
+
# reject values with: bit off for true, bit on for false
|
73
|
+
bits.reject!{|i| i & bit == (value ? 0 : bit) }
|
74
|
+
end
|
75
|
+
"#{table_name}.#{column} IN (#{bits * ','})"
|
76
|
+
when :bit_operator
|
77
|
+
on, off = bit_values_to_on_off(column, bit_values)
|
78
|
+
"(#{table_name}.#{column} & #{on+off}) = #{on}"
|
79
|
+
else raise("bitfields: unknown query mode #{mode.inspect}")
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def set_bitfield_sql_by_column(column, bit_values)
|
84
|
+
on, off = bit_values_to_on_off(column, bit_values)
|
85
|
+
"#{column} = (#{column} | #{on+off}) - #{off}"
|
86
|
+
end
|
87
|
+
|
88
|
+
def group_bits_by_column(bit_values)
|
89
|
+
columns = {}
|
90
|
+
bit_values.each do |bit_name, value|
|
91
|
+
column = bitfield_column(bit_name.to_sym)
|
92
|
+
columns[column] ||= {}
|
93
|
+
columns[column][bit_name.to_sym] = value
|
94
|
+
end
|
95
|
+
columns
|
96
|
+
end
|
97
|
+
|
98
|
+
def bit_values_to_on_off(column, bit_values)
|
99
|
+
on = off = 0
|
100
|
+
bit_values.each do |bit_name, value|
|
101
|
+
bit = bitfields[column][bit_name]
|
102
|
+
value ? on += bit : off += bit
|
103
|
+
end
|
104
|
+
[on, off]
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
module InstanceMethods
|
109
|
+
private
|
110
|
+
def bitfield_value(bit_name)
|
111
|
+
column, bit, current_value = bitfield_info(bit_name)
|
112
|
+
current_value & bit != 0
|
113
|
+
end
|
114
|
+
|
115
|
+
def set_bitfield_value(bit_name, value)
|
116
|
+
column, bit, current_value = bitfield_info(bit_name)
|
117
|
+
new_value = TRUE_VALUES.include?(value)
|
118
|
+
old_value = bitfield_value(bit_name)
|
119
|
+
return if new_value == old_value
|
120
|
+
|
121
|
+
if defined? changed_attributes
|
122
|
+
send(:changed_attributes).merge!(bit_name.to_s => old_value)
|
123
|
+
end
|
124
|
+
|
125
|
+
# 8 + 1 == 9 // 8 + 8 == 8 // 1 - 8 == 1 // 8 - 8 == 0
|
126
|
+
new_bits = if new_value then current_value | bit else (current_value | bit) - bit end
|
127
|
+
send("#{column}=", new_bits)
|
128
|
+
end
|
129
|
+
|
130
|
+
def bitfield_info(bit_name)
|
131
|
+
column = self.class.bitfield_column(bit_name)
|
132
|
+
[
|
133
|
+
column,
|
134
|
+
self.class.bitfields[column][bit_name], # bit
|
135
|
+
(send(column)||0) # current value
|
136
|
+
]
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,277 @@
|
|
1
|
+
require 'spec/spec_helper'
|
2
|
+
|
3
|
+
class User < ActiveRecord::Base
|
4
|
+
include Bitfields
|
5
|
+
bitfield :bits, 1 => :seller, 2 => :insane, 4 => :stupid
|
6
|
+
end
|
7
|
+
|
8
|
+
class UserWithBitfieldOptions < ActiveRecord::Base
|
9
|
+
include Bitfields
|
10
|
+
bitfield :bits, 1 => :seller, 2 => :insane, 4 => :stupid, :scopes => false
|
11
|
+
end
|
12
|
+
|
13
|
+
class MultiBitUser < ActiveRecord::Base
|
14
|
+
set_table_name 'users'
|
15
|
+
include Bitfields
|
16
|
+
bitfield :bits, 1 => :seller, 2 => :insane, 4 => :stupid
|
17
|
+
bitfield :more_bits, 1 => :one, 2 => :two, 4 => :four
|
18
|
+
end
|
19
|
+
|
20
|
+
class UserWithoutScopes < ActiveRecord::Base
|
21
|
+
set_table_name 'users'
|
22
|
+
include Bitfields
|
23
|
+
bitfield :bits, 1 => :seller, 2 => :insane, 4 => :stupid, :scopes => false
|
24
|
+
end
|
25
|
+
|
26
|
+
class InheritedUser < User
|
27
|
+
end
|
28
|
+
|
29
|
+
class OverwrittenUser < User
|
30
|
+
bitfield :bits, 1 => :seller_inherited
|
31
|
+
end
|
32
|
+
|
33
|
+
class BitOperatorMode < ActiveRecord::Base
|
34
|
+
set_table_name 'users'
|
35
|
+
include Bitfields
|
36
|
+
bitfield :bits, 1 => :seller, 2 => :insane, :query_mode => :bit_operator
|
37
|
+
end
|
38
|
+
|
39
|
+
describe Bitfields do
|
40
|
+
before do
|
41
|
+
User.delete_all
|
42
|
+
end
|
43
|
+
|
44
|
+
describe :bitfields do
|
45
|
+
it "parses them correctly" do
|
46
|
+
User.bitfields.should == {:bits => {:seller => 1, :insane => 2, :stupid => 4}}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe :bitfield_options do
|
51
|
+
it "parses them correctly when not set" do
|
52
|
+
User.bitfield_options.should == {:bits => {}}
|
53
|
+
end
|
54
|
+
|
55
|
+
it "parses them correctly when set" do
|
56
|
+
UserWithBitfieldOptions.bitfield_options.should == {:bits => {:scopes => false}}
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe 'attribute accessors' do
|
61
|
+
it "has everything on false by default" do
|
62
|
+
User.new.seller.should == false
|
63
|
+
User.new.seller?.should == false
|
64
|
+
end
|
65
|
+
|
66
|
+
it "is true when set to true" do
|
67
|
+
User.new(:seller => true).seller.should == true
|
68
|
+
end
|
69
|
+
|
70
|
+
it "is true when set to truthy" do
|
71
|
+
User.new(:seller => 1).seller.should == true
|
72
|
+
end
|
73
|
+
|
74
|
+
it "is false when set to false" do
|
75
|
+
User.new(:seller => false).seller.should == false
|
76
|
+
end
|
77
|
+
|
78
|
+
it "is false when set to falsy" do
|
79
|
+
User.new(:seller => 'false').seller.should == false
|
80
|
+
end
|
81
|
+
|
82
|
+
it "stays true when set to true twice" do
|
83
|
+
u = User.new
|
84
|
+
u.seller = true
|
85
|
+
u.seller = true
|
86
|
+
u.seller.should == true
|
87
|
+
u.bits.should == 1
|
88
|
+
end
|
89
|
+
|
90
|
+
it "stays false when set to false twice" do
|
91
|
+
u = User.new(:bits => 3)
|
92
|
+
u.seller = false
|
93
|
+
u.seller = false
|
94
|
+
u.seller.should == false
|
95
|
+
u.bits.should == 2
|
96
|
+
end
|
97
|
+
|
98
|
+
it "changes the bits when setting to false" do
|
99
|
+
user = User.new(:bits => 7)
|
100
|
+
user.seller = false
|
101
|
+
user.bits.should == 6
|
102
|
+
end
|
103
|
+
|
104
|
+
it "does not get negative when unsetting high bits" do
|
105
|
+
user = User.new(:seller => true)
|
106
|
+
user.stupid = false
|
107
|
+
user.bits.should == 1
|
108
|
+
end
|
109
|
+
|
110
|
+
it "changes the bits when setting to true" do
|
111
|
+
user = User.new(:bits => 2)
|
112
|
+
user.seller = true
|
113
|
+
user.bits.should == 3
|
114
|
+
end
|
115
|
+
|
116
|
+
it "does not get too high when setting high bits" do
|
117
|
+
user = User.new(:bits => 7)
|
118
|
+
user.seller = true
|
119
|
+
user.bits.should == 7
|
120
|
+
end
|
121
|
+
|
122
|
+
describe 'changes' do
|
123
|
+
it "has no changes by defaut" do
|
124
|
+
User.new.changes.should == {}
|
125
|
+
end
|
126
|
+
|
127
|
+
it "records a change when setting" do
|
128
|
+
User.new(:seller => true).changes.should == {'seller' => [false, true], 'bits' => [0,1]}
|
129
|
+
end
|
130
|
+
|
131
|
+
it "records a change when unsetting" do
|
132
|
+
u = User.create!(:seller => true)
|
133
|
+
u.seller = false
|
134
|
+
u.changes.should == {'seller' => [true, false], 'bits' => [1,0]}
|
135
|
+
end
|
136
|
+
|
137
|
+
it "does not track duplicate changes" do
|
138
|
+
User.create!(:seller => false).changes.should == {}
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
describe :bitfield_sql do
|
144
|
+
it "includes true states" do
|
145
|
+
User.bitfield_sql(:insane => true).should == 'users.bits IN (2,3,6,7)' # 2, 1+2, 2+4, 1+2+4
|
146
|
+
end
|
147
|
+
|
148
|
+
it "includes invalid states" do
|
149
|
+
User.bitfield_sql(:insane => false).should == 'users.bits IN (0,1,4,5)' # 0, 1, 4, 4+1
|
150
|
+
end
|
151
|
+
|
152
|
+
it "can combine multiple fields" do
|
153
|
+
User.bitfield_sql(:seller => true, :insane => true).should == 'users.bits IN (3,7)' # 1+2, 1+2+4
|
154
|
+
end
|
155
|
+
|
156
|
+
it "can combine multiple fields with different values" do
|
157
|
+
User.bitfield_sql(:seller => true, :insane => false).should == 'users.bits IN (1,5)' # 1, 1+4
|
158
|
+
end
|
159
|
+
|
160
|
+
it "combines multiple columns into one sql" do
|
161
|
+
sql = MultiBitUser.bitfield_sql(:seller => true, :insane => false, :one => true, :four => true)
|
162
|
+
sql.should == 'users.bits IN (1,5) AND users.more_bits IN (5,7)' # 1, 1+4 AND 1+4, 1+2+4
|
163
|
+
end
|
164
|
+
|
165
|
+
it "produces working sql" do
|
166
|
+
u1 = MultiBitUser.create!(:seller => true, :one => true)
|
167
|
+
u2 = MultiBitUser.create!(:seller => true, :one => false)
|
168
|
+
u3 = MultiBitUser.create!(:seller => false, :one => false)
|
169
|
+
MultiBitUser.all(:conditions => MultiBitUser.bitfield_sql(:seller => true, :one => false)).should == [u2]
|
170
|
+
end
|
171
|
+
|
172
|
+
describe 'with bit operator mode' do
|
173
|
+
it "generates bit-operator sql" do
|
174
|
+
BitOperatorMode.bitfield_sql(:seller => true).should == '(users.bits & 1) = 1'
|
175
|
+
end
|
176
|
+
|
177
|
+
it "generates sql for each bit" do
|
178
|
+
BitOperatorMode.bitfield_sql(:seller => true, :insane => false).should == '(users.bits & 3) = 1'
|
179
|
+
end
|
180
|
+
|
181
|
+
it "generates working sql" do
|
182
|
+
u1 = BitOperatorMode.create!(:seller => true, :insane => true)
|
183
|
+
u2 = BitOperatorMode.create!(:seller => true, :insane => false)
|
184
|
+
u3 = BitOperatorMode.create!(:seller => false, :insane => false)
|
185
|
+
BitOperatorMode.all(:conditions => MultiBitUser.bitfield_sql(:seller => true, :insane => false)).should == [u2]
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
describe :set_bitfield_sql do
|
191
|
+
it "sets a single bit" do
|
192
|
+
User.set_bitfield_sql(:seller => true).should == 'bits = (bits | 1) - 0'
|
193
|
+
end
|
194
|
+
|
195
|
+
it "unsets a single bit" do
|
196
|
+
User.set_bitfield_sql(:seller => false).should == 'bits = (bits | 1) - 1'
|
197
|
+
end
|
198
|
+
|
199
|
+
it "sets multiple bits" do
|
200
|
+
User.set_bitfield_sql(:seller => true, :insane => true).should == 'bits = (bits | 3) - 0'
|
201
|
+
end
|
202
|
+
|
203
|
+
it "unsets multiple bits" do
|
204
|
+
User.set_bitfield_sql(:seller => false, :insane => false).should == 'bits = (bits | 3) - 3'
|
205
|
+
end
|
206
|
+
|
207
|
+
it "sets and unsets in one command" do
|
208
|
+
User.set_bitfield_sql(:seller => false, :insane => true).should == 'bits = (bits | 3) - 1'
|
209
|
+
end
|
210
|
+
|
211
|
+
it "sets and unsets for multiple columns in one sql" do
|
212
|
+
sql = MultiBitUser.set_bitfield_sql(:seller => false, :insane => true, :one => true, :two => false)
|
213
|
+
sql.should == "bits = (bits | 3) - 1, more_bits = (more_bits | 3) - 2"
|
214
|
+
end
|
215
|
+
|
216
|
+
it "produces working sql" do
|
217
|
+
u = MultiBitUser.create!(:seller => true, :insane => true, :stupid => false, :one => true, :two => false, :four => false)
|
218
|
+
sql = MultiBitUser.set_bitfield_sql(:seller => false, :insane => true, :one => true, :two => false)
|
219
|
+
MultiBitUser.update_all(sql)
|
220
|
+
u.reload
|
221
|
+
u.seller.should == false
|
222
|
+
u.insane.should == true
|
223
|
+
u.stupid.should == false
|
224
|
+
u.one.should == true
|
225
|
+
u.two.should == false
|
226
|
+
u.four.should == false
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
describe 'named scopes' do
|
231
|
+
before do
|
232
|
+
@u1 = User.create!(:seller => true, :insane => false)
|
233
|
+
@u2 = User.create!(:seller => true, :insane => true)
|
234
|
+
end
|
235
|
+
|
236
|
+
it "creates them when nothing was passed" do
|
237
|
+
User.respond_to?(:seller).should == true
|
238
|
+
User.respond_to?(:not_seller).should == true
|
239
|
+
end
|
240
|
+
|
241
|
+
it "does not create them when false was passed" do
|
242
|
+
UserWithoutScopes.respond_to?(:seller).should == false
|
243
|
+
UserWithoutScopes.respond_to?(:not_seller).should == false
|
244
|
+
end
|
245
|
+
|
246
|
+
it "produces working positive scopes" do
|
247
|
+
User.insane.seller.to_a.should == [@u2]
|
248
|
+
end
|
249
|
+
|
250
|
+
it "produces working negative scopes" do
|
251
|
+
User.not_insane.seller.to_a.should == [@u1]
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
describe 'overwriting' do
|
256
|
+
it "does not change base class" do
|
257
|
+
OverwrittenUser.bitfields[:bits][:seller_inherited].should_not == nil
|
258
|
+
User.bitfields[:bits][:seller_inherited].should == nil
|
259
|
+
end
|
260
|
+
|
261
|
+
it "has inherited methods" do
|
262
|
+
User.respond_to?(:seller).should == true
|
263
|
+
OverwrittenUser.respond_to?(:seller).should == true
|
264
|
+
end
|
265
|
+
|
266
|
+
it "knows inherited values" do
|
267
|
+
pending
|
268
|
+
OverwrittenUser.bitfield_column(:seller).should == :bits
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
describe 'inheritance' do
|
273
|
+
it "knows inherited values" do
|
274
|
+
InheritedUser.bitfield_column(:seller).should == :bits
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
data/spec/database.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
# connect
|
4
|
+
ActiveRecord::Base.establish_connection(
|
5
|
+
:adapter => "sqlite3",
|
6
|
+
:database => ":memory:"
|
7
|
+
)
|
8
|
+
|
9
|
+
# create tables
|
10
|
+
ActiveRecord::Schema.define(:version => 1) do
|
11
|
+
create_table :users do |t|
|
12
|
+
t.integer :bits, :default => 0, :null => false
|
13
|
+
t.integer :more_bits, :default => 0, :null => false
|
14
|
+
end
|
15
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bitfields
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: 0.1.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Michael Grosser
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-03-06 00:00:00 +01:00
|
18
|
+
default_executable:
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description:
|
22
|
+
email: grosser.michael@gmail.com
|
23
|
+
executables: []
|
24
|
+
|
25
|
+
extensions: []
|
26
|
+
|
27
|
+
extra_rdoc_files:
|
28
|
+
- README.markdown
|
29
|
+
files:
|
30
|
+
- README.markdown
|
31
|
+
- Rakefile
|
32
|
+
- VERSION
|
33
|
+
- lib/bitfields.rb
|
34
|
+
- spec/bitfields_spec.rb
|
35
|
+
- spec/database.rb
|
36
|
+
- spec/spec_helper.rb
|
37
|
+
has_rdoc: true
|
38
|
+
homepage: http://github.com/grosser/bitfields
|
39
|
+
licenses: []
|
40
|
+
|
41
|
+
post_install_message:
|
42
|
+
rdoc_options:
|
43
|
+
- --charset=UTF-8
|
44
|
+
require_paths:
|
45
|
+
- lib
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
segments:
|
51
|
+
- 0
|
52
|
+
version: "0"
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - ">="
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
segments:
|
58
|
+
- 0
|
59
|
+
version: "0"
|
60
|
+
requirements: []
|
61
|
+
|
62
|
+
rubyforge_project:
|
63
|
+
rubygems_version: 1.3.6
|
64
|
+
signing_key:
|
65
|
+
specification_version: 3
|
66
|
+
summary: Save migrations and columns by storing multiple booleans in a single integer.
|
67
|
+
test_files:
|
68
|
+
- spec/spec_helper.rb
|
69
|
+
- spec/bitfields_spec.rb
|
70
|
+
- spec/database.rb
|