bitfields 0.1.4 → 0.1.5
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 +4 -0
- data/Rakefile +6 -11
- data/VERSION +1 -1
- data/benchmark/1.rb +144 -0
- data/bitfields.gemspec +16 -17
- data/lib/bitfields.rb +7 -7
- metadata +14 -8
data/README.markdown
CHANGED
@@ -38,6 +38,10 @@ Update all users
|
|
38
38
|
Delete the shop when a user is no longer a seller
|
39
39
|
before_save :delete_shop, :if => lambda{|u| u.changes['seller'] == [true, false]}
|
40
40
|
|
41
|
+
TODO
|
42
|
+
====
|
43
|
+
- convenient named scope `User.with_bitfields(:xxx=>true, :yy=>false)`
|
44
|
+
|
41
45
|
Author
|
42
46
|
======
|
43
47
|
[Michael Grosser](http://pragmatig.wordpress.com)
|
data/Rakefile
CHANGED
@@ -1,16 +1,11 @@
|
|
1
|
-
require
|
2
|
-
|
1
|
+
require "rspec/core/rake_task"
|
2
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
3
|
+
t.rspec_opts = '--backtrace --color'
|
4
|
+
end
|
3
5
|
|
4
6
|
task :default do
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
# gem 'activerecord', '>=3' did not work for me, but just require gets the right version...
|
9
|
-
require 'active_record'
|
10
|
-
if ActiveRecord::VERSION::MAJOR >= 3
|
11
|
-
puts `rake spec`
|
12
|
-
else
|
13
|
-
'install rails 3 to get full test coverage...'
|
7
|
+
[2,3].each do |version|
|
8
|
+
sh "VERSION='~>#{version}' rake spec" rescue nil
|
14
9
|
end
|
15
10
|
end
|
16
11
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.5
|
data/benchmark/1.rb
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
bit_counts = [2,3,4,5,6,7,8,9,10,11,12,13,14]
|
2
|
+
record_counts = (1..20).to_a.map{|i| i * 50_000 }
|
3
|
+
use_index = true
|
4
|
+
database = ARGV[0]
|
5
|
+
|
6
|
+
puts "running#{' with index' if use_index} on #{database}"
|
7
|
+
|
8
|
+
$LOAD_PATH.unshift File.expand_path('lib')
|
9
|
+
require 'rubygems'
|
10
|
+
gem 'gchartrb'
|
11
|
+
require 'google_chart'
|
12
|
+
require 'active_record'
|
13
|
+
require 'bitfields'
|
14
|
+
|
15
|
+
def benchmark
|
16
|
+
t = Time.now.to_f
|
17
|
+
yield
|
18
|
+
Time.now.to_f - t
|
19
|
+
end
|
20
|
+
|
21
|
+
def create(bit_counts, count)
|
22
|
+
count.times do |i|
|
23
|
+
columns = bit_counts.map do |bits_count|
|
24
|
+
["bit_#{bits_count}", rand(2**(bits_count-1))]
|
25
|
+
end
|
26
|
+
User.create!(Hash[columns])
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def connect(db)
|
31
|
+
if db == 'mysql'
|
32
|
+
puts 'using mysql'
|
33
|
+
ActiveRecord::Base.establish_connection(
|
34
|
+
:adapter => "mysql",
|
35
|
+
:database => "bitfields_benchmark"
|
36
|
+
)
|
37
|
+
else
|
38
|
+
puts 'using sqlite'
|
39
|
+
ActiveRecord::Base.establish_connection(
|
40
|
+
:adapter => "sqlite3",
|
41
|
+
:database => ":memory:"
|
42
|
+
)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def create_model_table(bit_counts, use_index)
|
47
|
+
ActiveRecord::Schema.define(:version => 2) do
|
48
|
+
drop_table :users rescue nil
|
49
|
+
create_table :users do |t|
|
50
|
+
bit_counts.each do |bit_count|
|
51
|
+
t.integer "bit_#{bit_count}", :default => 0, :null => false
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
if use_index
|
56
|
+
bit_counts.each do |bit_count|
|
57
|
+
add_index :users, "bit_#{bit_count}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def create_model_fields(bit_counts)
|
64
|
+
puts "creating model"
|
65
|
+
User.class_eval do
|
66
|
+
include Bitfields
|
67
|
+
|
68
|
+
# this takes long for 15/20 bits, maybe needs to be optimized..
|
69
|
+
bit_counts.each do |bits_count|
|
70
|
+
bits = {}
|
71
|
+
0.upto(bits_count-1) do |bit|
|
72
|
+
bits[2**bit] = "bit_#{bits_count}_#{bit}"
|
73
|
+
end
|
74
|
+
|
75
|
+
bitfield "bit_#{bits_count}", bits
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_speed(bit_counts, query_mode)
|
81
|
+
result = bit_counts.map do |bit_count|
|
82
|
+
sql = User.bitfield_sql({"bit_#{bit_count}_1" => true}, :query_mode => query_mode)
|
83
|
+
# puts sql[0..100]
|
84
|
+
time = benchmark do
|
85
|
+
User.count sql
|
86
|
+
end
|
87
|
+
puts "#{bit_count} -> #{time}"
|
88
|
+
[bit_count, time]
|
89
|
+
end
|
90
|
+
Hash[result]
|
91
|
+
end
|
92
|
+
|
93
|
+
#determines a unique color based on a name
|
94
|
+
def color_for(name)
|
95
|
+
name = name.inspect + "some randomness for fun"
|
96
|
+
hash = (name.hash % 1_000_000_000).to_s #get a hash of a constant size
|
97
|
+
colors = [hash[0..1], hash[2..3], hash[4..5]].map{|c| c.to_f / 100.0 * 16} #use 3 parts of the hash to get numbers from 0 to 15.99
|
98
|
+
palette = ('0'..'9').to_a + ('a'..'f').to_a #hex values 0..f
|
99
|
+
colors.map{|c| palette[c.floor].to_s * 2} * '' #each color is duplicated and the joined
|
100
|
+
end
|
101
|
+
|
102
|
+
connect(database)
|
103
|
+
create_model_table(bit_counts, use_index)
|
104
|
+
class User < ActiveRecord::Base
|
105
|
+
end
|
106
|
+
create_model_fields(bit_counts)
|
107
|
+
|
108
|
+
puts "creating test data"
|
109
|
+
last = 0
|
110
|
+
|
111
|
+
graphs = {:bit => {}, :in => {}}
|
112
|
+
data = record_counts.map do |record_count|
|
113
|
+
create(bit_counts, record_count-last)
|
114
|
+
puts User.count
|
115
|
+
last = record_count
|
116
|
+
|
117
|
+
puts "testing with #{record_count} records -- bit_operator"
|
118
|
+
graphs[:bit][record_count] = test_speed(bit_counts, :bit_operator)
|
119
|
+
|
120
|
+
puts "testing with #{record_count} records -- in_list"
|
121
|
+
graphs[:in][record_count] = test_speed(bit_counts, :in_list)
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
colors = {:bit => '00xx00', :in => 'xx0000'}
|
126
|
+
alpha_num = (('0'..'9').to_a + ('a'..'f').to_a).reverse
|
127
|
+
title = "bit-operator vs IN() -- #{use_index ? 'with' : 'without'} index"
|
128
|
+
url = GoogleChart::LineChart.new('600x500', title, false) do |line|
|
129
|
+
max = 0
|
130
|
+
graphs.each do |type, line_data|
|
131
|
+
bit_counts.each do |bit_count|
|
132
|
+
data = record_counts.map{|rc| line_data[rc][bit_count] }
|
133
|
+
name = "#{bit_count}bits (#{type})"
|
134
|
+
color = colors[type].sub('xx', alpha_num[bit_count]*2)
|
135
|
+
line.data(name, data, color)
|
136
|
+
max = [data.max, max].max
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
line.axis :x, :labels => record_counts.map{|c|"#{c/1000}K"}
|
141
|
+
line.axis :y, :labels => ['0', "%.3f" % [max*100]]
|
142
|
+
end.to_url
|
143
|
+
|
144
|
+
puts url
|
data/bitfields.gemspec
CHANGED
@@ -1,45 +1,44 @@
|
|
1
1
|
# Generated by jeweler
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{bitfields}
|
8
|
-
s.version = "0.1.
|
8
|
+
s.version = "0.1.5"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Michael Grosser"]
|
12
|
-
s.date = %q{
|
12
|
+
s.date = %q{2011-01-19}
|
13
13
|
s.email = %q{grosser.michael@gmail.com}
|
14
14
|
s.extra_rdoc_files = [
|
15
15
|
"README.markdown"
|
16
16
|
]
|
17
17
|
s.files = [
|
18
18
|
"README.markdown",
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
19
|
+
"Rakefile",
|
20
|
+
"VERSION",
|
21
|
+
"benchmark/1.rb",
|
22
|
+
"bitfields.gemspec",
|
23
|
+
"lib/bitfields.rb",
|
24
|
+
"spec/bitfields_spec.rb",
|
25
|
+
"spec/database.rb",
|
26
|
+
"spec/spec_helper.rb"
|
26
27
|
]
|
27
28
|
s.homepage = %q{http://github.com/grosser/bitfields}
|
28
|
-
s.rdoc_options = ["--charset=UTF-8"]
|
29
29
|
s.require_paths = ["lib"]
|
30
|
-
s.rubygems_version = %q{1.
|
30
|
+
s.rubygems_version = %q{1.4.2}
|
31
31
|
s.summary = %q{Save migrations and columns by storing multiple booleans in a single integer.}
|
32
32
|
s.test_files = [
|
33
|
-
"spec/
|
34
|
-
|
35
|
-
|
33
|
+
"spec/bitfields_spec.rb",
|
34
|
+
"spec/database.rb",
|
35
|
+
"spec/spec_helper.rb"
|
36
36
|
]
|
37
37
|
|
38
38
|
if s.respond_to? :specification_version then
|
39
|
-
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
40
39
|
s.specification_version = 3
|
41
40
|
|
42
|
-
if Gem::Version.new(Gem::
|
41
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
43
42
|
else
|
44
43
|
end
|
45
44
|
else
|
data/lib/bitfields.rb
CHANGED
@@ -58,20 +58,20 @@ module Bitfields
|
|
58
58
|
found.first
|
59
59
|
end
|
60
60
|
|
61
|
-
def bitfield_sql(bit_values)
|
62
|
-
bits = group_bits_by_column(bit_values)
|
63
|
-
bits.map{|column, bit_values| bitfield_sql_by_column(column, bit_values) } * ' AND '
|
61
|
+
def bitfield_sql(bit_values, options={})
|
62
|
+
bits = group_bits_by_column(bit_values).sort_by{|c,v| c.to_s }
|
63
|
+
bits.map{|column, bit_values| bitfield_sql_by_column(column, bit_values, options) } * ' AND '
|
64
64
|
end
|
65
65
|
|
66
66
|
def set_bitfield_sql(bit_values)
|
67
|
-
|
68
|
-
|
67
|
+
bits = group_bits_by_column(bit_values).sort_by{|c,v| c.to_s }
|
68
|
+
bits.map{|column, bit_values| set_bitfield_sql_by_column(column, bit_values) } * ', '
|
69
69
|
end
|
70
70
|
|
71
71
|
private
|
72
72
|
|
73
|
-
def bitfield_sql_by_column(column, bit_values)
|
74
|
-
mode = (bitfield_options[column][:query_mode] || :in_list)
|
73
|
+
def bitfield_sql_by_column(column, bit_values, options={})
|
74
|
+
mode = options[:query_mode] || (bitfield_options[column][:query_mode] || :in_list)
|
75
75
|
case mode
|
76
76
|
when :in_list then
|
77
77
|
max = (bitfields[column].values.max * 2) - 1
|
metadata
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bitfields
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
hash: 17
|
5
|
+
prerelease:
|
5
6
|
segments:
|
6
7
|
- 0
|
7
8
|
- 1
|
8
|
-
-
|
9
|
-
version: 0.1.
|
9
|
+
- 5
|
10
|
+
version: 0.1.5
|
10
11
|
platform: ruby
|
11
12
|
authors:
|
12
13
|
- Michael Grosser
|
@@ -14,7 +15,7 @@ autorequire:
|
|
14
15
|
bindir: bin
|
15
16
|
cert_chain: []
|
16
17
|
|
17
|
-
date:
|
18
|
+
date: 2011-01-19 00:00:00 +01:00
|
18
19
|
default_executable:
|
19
20
|
dependencies: []
|
20
21
|
|
@@ -30,6 +31,7 @@ files:
|
|
30
31
|
- README.markdown
|
31
32
|
- Rakefile
|
32
33
|
- VERSION
|
34
|
+
- benchmark/1.rb
|
33
35
|
- bitfields.gemspec
|
34
36
|
- lib/bitfields.rb
|
35
37
|
- spec/bitfields_spec.rb
|
@@ -40,32 +42,36 @@ homepage: http://github.com/grosser/bitfields
|
|
40
42
|
licenses: []
|
41
43
|
|
42
44
|
post_install_message:
|
43
|
-
rdoc_options:
|
44
|
-
|
45
|
+
rdoc_options: []
|
46
|
+
|
45
47
|
require_paths:
|
46
48
|
- lib
|
47
49
|
required_ruby_version: !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
48
51
|
requirements:
|
49
52
|
- - ">="
|
50
53
|
- !ruby/object:Gem::Version
|
54
|
+
hash: 3
|
51
55
|
segments:
|
52
56
|
- 0
|
53
57
|
version: "0"
|
54
58
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
59
|
+
none: false
|
55
60
|
requirements:
|
56
61
|
- - ">="
|
57
62
|
- !ruby/object:Gem::Version
|
63
|
+
hash: 3
|
58
64
|
segments:
|
59
65
|
- 0
|
60
66
|
version: "0"
|
61
67
|
requirements: []
|
62
68
|
|
63
69
|
rubyforge_project:
|
64
|
-
rubygems_version: 1.
|
70
|
+
rubygems_version: 1.4.2
|
65
71
|
signing_key:
|
66
72
|
specification_version: 3
|
67
73
|
summary: Save migrations and columns by storing multiple booleans in a single integer.
|
68
74
|
test_files:
|
69
|
-
- spec/spec_helper.rb
|
70
75
|
- spec/bitfields_spec.rb
|
71
76
|
- spec/database.rb
|
77
|
+
- spec/spec_helper.rb
|