bitfields 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|