surus 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +97 -3
- data/bench/array_create.rb +46 -0
- data/bench/array_find.rb +79 -0
- data/bench/array_serialize.rb +34 -0
- data/bench/benchmark_helper.rb +65 -0
- data/bench/database.yml +4 -0
- data/bench/database_structure.sql +92 -0
- data/bench/hstore_create.rb +57 -0
- data/bench/hstore_find.rb +105 -0
- data/bench/hstore_serialize.rb +42 -0
- data/bench/synchronous_commit.rb +90 -0
- data/lib/surus.rb +2 -0
- data/lib/surus/array/scope.rb +12 -0
- data/lib/surus/hstore/scope.rb +18 -0
- data/lib/surus/hstore/serializer.rb +4 -0
- data/lib/surus/synchronous_commit/connection.rb +34 -0
- data/lib/surus/synchronous_commit/model.rb +11 -0
- data/lib/surus/version.rb +1 -1
- data/spec/hstore/serializer_spec.rb +1 -0
- data/spec/spec_helper.rb +1 -1
- data/spec/synchronous_commit/connection_spec.rb +95 -0
- data/spec/synchronous_commit/model_spec.rb +19 -0
- data/surus.gemspec +1 -1
- metadata +28 -13
data/README.md
CHANGED
@@ -4,24 +4,28 @@ Surus
|
|
4
4
|
# Description
|
5
5
|
|
6
6
|
Surus extends ActiveRecord with PostgreSQL specific functionality. It includes
|
7
|
-
hstore and array serializers and helper scopes.
|
7
|
+
hstore and array serializers and helper scopes. It also includes a helper to
|
8
|
+
control PostgreSQL synchronous commit behavior.
|
8
9
|
|
9
10
|
# Installation
|
10
11
|
|
11
12
|
gem install surus
|
12
13
|
|
13
|
-
|
14
|
+
Or add to your Gemfile.
|
14
15
|
|
15
16
|
gem 'surus'
|
16
17
|
|
17
18
|
# Hstore
|
18
19
|
|
19
|
-
Hashes can be serialized to an hstore column.
|
20
|
+
Hashes can be serialized to an hstore column. hstore is a PostgreSQL key/value
|
21
|
+
type that can be indexed for fast searching.
|
20
22
|
|
21
23
|
class User < ActiveRecord::Base
|
22
24
|
serialize :properties, Surus::Hstore::Serializer.new
|
23
25
|
end
|
24
26
|
|
27
|
+
User.create :properties => { :favorite_color => "green", :results_per_page => 20 }
|
28
|
+
|
25
29
|
Even though the underlying hstore can only use strings for keys and values
|
26
30
|
(and NULL for values) Surus can successfully maintain type for integers,
|
27
31
|
floats, bigdecimals, and dates. It does this by storing an extra key
|
@@ -34,6 +38,9 @@ Hstores can be searched with helper scopes.
|
|
34
38
|
User.hstore_has_all_keys(:properties, "favorite_color", "gender")
|
35
39
|
User.hstore_has_any_keys(:properties, "favorite_color", "favorite_artist")
|
36
40
|
|
41
|
+
|
42
|
+
Read more in the [PostgreSQL hstore documentation](http://www.postgresql.org/docs/9.1/static/hstore.html).
|
43
|
+
|
37
44
|
# Array
|
38
45
|
|
39
46
|
Ruby arrays can be serialized to PostgreSQL arrays. Surus includes support
|
@@ -46,12 +53,99 @@ for text, integer, float, and decimal arrays.
|
|
46
53
|
serialize :favorite_decimals, Surus::Array::DecimalSerializer.new
|
47
54
|
end
|
48
55
|
|
56
|
+
User.create :permissions => %w{ read_notes write_notes, manage_topics },
|
57
|
+
:favorite_integers => [1, 2, 3],
|
58
|
+
:favorite_floats => [1.3, 2.2, 3.1],
|
59
|
+
:favorite_decimals => [BigDecimal("3.14"), BigDecimal("4.23"]
|
60
|
+
|
49
61
|
Arrays can be searched with helper scopes.
|
50
62
|
|
51
63
|
User.array_has(:permissions, "admin")
|
52
64
|
User.array_has(:permissions, "manage_accounts", "manage_users")
|
53
65
|
User.array_has_any(:favorite_integers, 7, 11, 42)
|
54
66
|
|
67
|
+
# Synchronous Commit
|
68
|
+
|
69
|
+
PostgreSQL can trade durability for speed. By disabling synchronous commit,
|
70
|
+
transactions will return before the data is actually stored on the disk. This
|
71
|
+
can be substantially faster, but it entails a short window where a crash
|
72
|
+
could cause data loss (but not data corruption). This can be enabled for an
|
73
|
+
entire session or per transaction.
|
74
|
+
|
75
|
+
User.synchronous_commit # -> true
|
76
|
+
|
77
|
+
User.transaction do
|
78
|
+
User.synchronous_commit false
|
79
|
+
@user.save
|
80
|
+
end # This transaction can return before the data is written to the drive
|
81
|
+
|
82
|
+
# synchronous_commit returns to its former value outside of the transaction
|
83
|
+
User.synchronous_commit # -> true
|
84
|
+
|
85
|
+
# synchronous_commit can be turned off permanently
|
86
|
+
User.synchronous_commit false
|
87
|
+
|
88
|
+
Read more in the [PostgreSQL asynchronous commit documentation](http://www.postgresql.org/docs/9.1/interactive/wal-async-commit.html).
|
89
|
+
|
90
|
+
# Benchmarks
|
91
|
+
|
92
|
+
Using PostgreSQL's hstore enables searches to be done quickly in the database.
|
93
|
+
|
94
|
+
jack@moya:~/work/surus$ ruby -I lib -I bench bench/hstore_find.rb
|
95
|
+
Skipping EAV test. Use -e to enable (VERY SLOW!)
|
96
|
+
Skipping YAML test. Use -y to enable (VERY SLOW!)
|
97
|
+
Creating Surus test data... Done.
|
98
|
+
|
99
|
+
2000 records with 5 string key/value pairs
|
100
|
+
Finding all by inclusion of a key 200 times
|
101
|
+
user system total real
|
102
|
+
Surus 0.120000 0.030000 0.150000 ( 0.356240)
|
103
|
+
|
104
|
+
Arrays are also searchable.
|
105
|
+
|
106
|
+
jack@moya:~/work/surus$ ruby -I lib -I bench bench/array_find.rb
|
107
|
+
Skipping YAML test. Use -y to enable (VERY SLOW!)
|
108
|
+
Creating Surus test data... Done.
|
109
|
+
|
110
|
+
2000 records with 10 element arrays
|
111
|
+
Finding all where array includes value 200 times
|
112
|
+
user system total real
|
113
|
+
Surus 0.120000 0.040000 0.160000 ( 0.531735)
|
114
|
+
|
115
|
+
Disabling synchronous commit can improve commit speed by 50% or more.
|
116
|
+
|
117
|
+
jack@moya:~/work/surus$ ruby -I lib -I bench bench/synchronous_commit.rb
|
118
|
+
Generating random data before test to avoid bias... Done.
|
119
|
+
|
120
|
+
Writing 1000 narrow records
|
121
|
+
user system total real
|
122
|
+
enabled 0.550000 0.870000 1.420000 ( 3.025896)
|
123
|
+
disabled 0.700000 0.580000 1.280000 ( 1.788585)
|
124
|
+
disabled per transaction 0.870000 0.580000 1.450000 ( 2.072150)
|
125
|
+
enabled / single transaction 0.700000 0.330000 1.030000 ( 1.280455)
|
126
|
+
disabled / single transaction 0.660000 0.340000 1.000000 ( 1.252301)
|
127
|
+
|
128
|
+
Writing 1000 wide records
|
129
|
+
user system total real
|
130
|
+
enabled 1.030000 0.870000 1.900000 ( 3.559709)
|
131
|
+
disabled 0.930000 0.780000 1.710000 ( 2.259340)
|
132
|
+
disabled per transaction 0.970000 0.850000 1.820000 ( 2.478290)
|
133
|
+
enabled / single transaction 0.890000 0.500000 1.390000 ( 1.693629)
|
134
|
+
disabled / single transaction 0.820000 0.450000 1.270000 ( 1.554767)
|
135
|
+
|
136
|
+
Many more benchmarks are in the bench directory. Most accept parameters to
|
137
|
+
adjust the amount of test data.
|
138
|
+
|
139
|
+
## Running the benchmarks
|
140
|
+
|
141
|
+
1. Create a database
|
142
|
+
2. Configure bench/database.yml to connect to it.
|
143
|
+
3. Load bench/database_structure.sql into your bench database.
|
144
|
+
4. Run benchmark scripts from root of gem directory (remember pass ruby
|
145
|
+
the include paths for lib and bench)
|
146
|
+
|
147
|
+
|
148
|
+
|
55
149
|
# License
|
56
150
|
|
57
151
|
MIT
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'benchmark_helper'
|
2
|
+
|
3
|
+
options = {}
|
4
|
+
optparse = OptionParser.new do |opts|
|
5
|
+
options[:records] = 1000
|
6
|
+
opts.on '-r NUM', '--records NUM', Integer, 'Number of records to create' do |n|
|
7
|
+
options[:records] = n
|
8
|
+
end
|
9
|
+
|
10
|
+
options[:elements] = 10
|
11
|
+
opts.on '-e NUM', '--elements NUM', Integer, 'Number of elements' do |n|
|
12
|
+
options[:elements] = n
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
optparse.parse!
|
17
|
+
|
18
|
+
clean_database
|
19
|
+
|
20
|
+
num_records = options[:records]
|
21
|
+
num_elements = options[:elements]
|
22
|
+
|
23
|
+
arrays = num_records.times.map do
|
24
|
+
num_elements.times.map { SecureRandom.hex(4) }
|
25
|
+
end
|
26
|
+
|
27
|
+
puts
|
28
|
+
puts "Writing #{num_records} records with a #{num_elements} element string array"
|
29
|
+
|
30
|
+
Benchmark.bm(8) do |x|
|
31
|
+
x.report("Surus") do
|
32
|
+
SurusKeyValueRecord.transaction do
|
33
|
+
num_records.times do |i|
|
34
|
+
SurusTextArrayRecord.create! :names => arrays[i]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
x.report("YAML") do
|
40
|
+
YamlKeyValueRecord.transaction do
|
41
|
+
num_records.times do |i|
|
42
|
+
YamlArrayRecord.create! :names => arrays[i]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/bench/array_find.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'benchmark_helper'
|
2
|
+
|
3
|
+
options = {}
|
4
|
+
optparse = OptionParser.new do |opts|
|
5
|
+
options[:records] = 2_000
|
6
|
+
opts.on '-r NUM', '--records NUM', Integer, 'Number of records to create' do |n|
|
7
|
+
options[:records] = n
|
8
|
+
end
|
9
|
+
|
10
|
+
options[:elements] = 10
|
11
|
+
opts.on '-e NUM', '--elements NUM', Integer, 'Number of elements per array' do |n|
|
12
|
+
options[:elements] = n
|
13
|
+
end
|
14
|
+
|
15
|
+
options[:finds] = 200
|
16
|
+
opts.on '-f NUM', '--finds NUM', Integer, 'Number of finds to perform' do |n|
|
17
|
+
options[:finds] = n
|
18
|
+
end
|
19
|
+
|
20
|
+
options[:yaml] = false
|
21
|
+
opts.on '-y', '--yaml', 'Include YAML in benchmark (VERY SLOW!)' do
|
22
|
+
options[:yaml] = true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
optparse.parse!
|
27
|
+
|
28
|
+
clean_database
|
29
|
+
|
30
|
+
num_records = options[:records]
|
31
|
+
num_elements = options[:elements]
|
32
|
+
num_finds = options[:finds]
|
33
|
+
yaml = options[:yaml]
|
34
|
+
|
35
|
+
puts "Skipping YAML test. Use -y to enable (VERY SLOW!)" unless yaml
|
36
|
+
|
37
|
+
arrays = num_records.times.map do
|
38
|
+
num_elements.times.map { SecureRandom.hex(4) }
|
39
|
+
end
|
40
|
+
|
41
|
+
print "Creating Surus test data... "
|
42
|
+
SurusKeyValueRecord.transaction do
|
43
|
+
num_records.times do |i|
|
44
|
+
SurusTextArrayRecord.create! :names => arrays[i]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
puts "Done."
|
48
|
+
|
49
|
+
if yaml
|
50
|
+
print "Creating YAML test data... "
|
51
|
+
YamlArrayRecord.transaction do
|
52
|
+
num_records.times do |i|
|
53
|
+
YamlArrayRecord.create! :names => arrays[i]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
puts "Done."
|
57
|
+
end
|
58
|
+
|
59
|
+
puts
|
60
|
+
puts "#{num_records} records with #{num_elements} element arrays"
|
61
|
+
puts "Finding all where array includes value #{num_finds} times"
|
62
|
+
|
63
|
+
values_to_find = arrays.sample(num_finds).map { |a| a.sample }
|
64
|
+
|
65
|
+
Benchmark.bm(8) do |x|
|
66
|
+
x.report("Surus") do
|
67
|
+
values_to_find.each do |value_to_find|
|
68
|
+
SurusTextArrayRecord.array_has(:names, value_to_find).all
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
if yaml
|
73
|
+
x.report("YAML") do
|
74
|
+
values_to_find.each do |value_to_find|
|
75
|
+
YamlArrayRecord.all.select { |r| r.names.include?(value_to_find) }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'benchmark_helper'
|
2
|
+
|
3
|
+
options = {}
|
4
|
+
optparse = OptionParser.new do |opts|
|
5
|
+
options[:elements] = 10
|
6
|
+
opts.on '-e NUM', '--elements NUM', Integer, 'Number of elements per array' do |n|
|
7
|
+
options[:elements] = n
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
optparse.parse!
|
12
|
+
|
13
|
+
clean_database
|
14
|
+
|
15
|
+
num_elements = options[:elements]
|
16
|
+
array = num_elements.times.map { SecureRandom.hex(4) }
|
17
|
+
|
18
|
+
SurusTextArrayRecord.create! :names => array
|
19
|
+
YamlArrayRecord.create! :names => array
|
20
|
+
|
21
|
+
num_reads = 3_000
|
22
|
+
|
23
|
+
puts
|
24
|
+
puts "Reading a single record with a #{num_elements} element array #{num_reads} times"
|
25
|
+
|
26
|
+
Benchmark.bm(8) do |x|
|
27
|
+
x.report("Surus") do
|
28
|
+
num_reads.times { SurusTextArrayRecord.first.names }
|
29
|
+
end
|
30
|
+
|
31
|
+
x.report("YAML") do
|
32
|
+
num_reads.times { YamlArrayRecord.first.names }
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'surus'
|
2
|
+
require 'benchmark'
|
3
|
+
require 'securerandom'
|
4
|
+
require 'optparse'
|
5
|
+
|
6
|
+
database_config = YAML.load_file(File.expand_path("../database.yml", __FILE__))
|
7
|
+
ActiveRecord::Base.establish_connection database_config["bench"]
|
8
|
+
|
9
|
+
class YamlKeyValueRecord < ActiveRecord::Base
|
10
|
+
serialize :properties
|
11
|
+
end
|
12
|
+
|
13
|
+
class SurusKeyValueRecord < ActiveRecord::Base
|
14
|
+
serialize :properties, Surus::Hstore::Serializer.new
|
15
|
+
end
|
16
|
+
|
17
|
+
class EavMasterRecord < ActiveRecord::Base
|
18
|
+
has_many :eav_detail_records
|
19
|
+
|
20
|
+
def properties
|
21
|
+
@properties ||= eav_detail_records.each_with_object({}) do |d, hash|
|
22
|
+
hash[d.key] = d.value
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def properties=(value)
|
27
|
+
@properties = value
|
28
|
+
end
|
29
|
+
|
30
|
+
after_save :persist_properties
|
31
|
+
def persist_properties
|
32
|
+
eav_detail_records.clear
|
33
|
+
@properties.each do |k,v|
|
34
|
+
eav_detail_records.create! :key => k, :value => v
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class EavDetailRecord < ActiveRecord::Base
|
40
|
+
belongs_to :eav_master_record
|
41
|
+
end
|
42
|
+
|
43
|
+
class YamlArrayRecord < ActiveRecord::Base
|
44
|
+
serialize :names
|
45
|
+
end
|
46
|
+
|
47
|
+
class SurusTextArrayRecord < ActiveRecord::Base
|
48
|
+
serialize :names, Surus::Array::TextSerializer.new
|
49
|
+
end
|
50
|
+
|
51
|
+
class WideRecord < ActiveRecord::Base
|
52
|
+
end
|
53
|
+
|
54
|
+
class NarrowRecord < ActiveRecord::Base
|
55
|
+
end
|
56
|
+
|
57
|
+
def clean_database
|
58
|
+
YamlKeyValueRecord.delete_all
|
59
|
+
SurusKeyValueRecord.delete_all
|
60
|
+
EavDetailRecord.delete_all
|
61
|
+
EavMasterRecord.delete_all
|
62
|
+
YamlArrayRecord.delete_all
|
63
|
+
WideRecord.delete_all
|
64
|
+
NarrowRecord.delete_all
|
65
|
+
end
|
data/bench/database.yml
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
CREATE EXTENSION IF NOT EXISTS hstore;
|
2
|
+
|
3
|
+
DROP TABLE IF EXISTS yaml_key_value_records;
|
4
|
+
|
5
|
+
CREATE TABLE yaml_key_value_records(
|
6
|
+
id serial PRIMARY KEY,
|
7
|
+
properties varchar
|
8
|
+
);
|
9
|
+
|
10
|
+
|
11
|
+
DROP TABLE IF EXISTS surus_key_value_records;
|
12
|
+
|
13
|
+
CREATE TABLE surus_key_value_records(
|
14
|
+
id serial PRIMARY KEY,
|
15
|
+
properties hstore
|
16
|
+
);
|
17
|
+
|
18
|
+
CREATE INDEX ON surus_key_value_records USING GIN (properties);
|
19
|
+
|
20
|
+
|
21
|
+
DROP TABLE IF EXISTS eav_detail_records;
|
22
|
+
DROP TABLE IF EXISTS eav_key_value_records;
|
23
|
+
|
24
|
+
CREATE TABLE eav_master_records(
|
25
|
+
id serial PRIMARY KEY
|
26
|
+
);
|
27
|
+
|
28
|
+
CREATE TABLE eav_detail_records(
|
29
|
+
id serial PRIMARY KEY,
|
30
|
+
eav_master_record_id integer REFERENCES eav_master_records,
|
31
|
+
"key" varchar NOT NULL,
|
32
|
+
"value" varchar NOT NULL
|
33
|
+
);
|
34
|
+
|
35
|
+
CREATE INDEX ON eav_detail_records (eav_master_record_id);
|
36
|
+
CREATE INDEX ON eav_detail_records ("key");
|
37
|
+
CREATE UNIQUE INDEX ON eav_detail_records(eav_master_record_id, "key");
|
38
|
+
CREATE INDEX ON eav_detail_records ("value");
|
39
|
+
|
40
|
+
|
41
|
+
DROP TABLE IF EXISTS yaml_array_records;
|
42
|
+
|
43
|
+
CREATE TABLE yaml_array_records(
|
44
|
+
id serial PRIMARY KEY,
|
45
|
+
names text
|
46
|
+
);
|
47
|
+
|
48
|
+
|
49
|
+
DROP TABLE IF EXISTS surus_text_array_records;
|
50
|
+
|
51
|
+
CREATE TABLE surus_text_array_records(
|
52
|
+
id serial PRIMARY KEY,
|
53
|
+
names text[]
|
54
|
+
);
|
55
|
+
|
56
|
+
CREATE INDEX ON surus_text_array_records USING GIN (names);
|
57
|
+
|
58
|
+
|
59
|
+
|
60
|
+
DROP TABLE IF EXISTS wide_records;
|
61
|
+
|
62
|
+
-- a couple indexed fields and a number of unindexed fields
|
63
|
+
CREATE TABLE wide_records(
|
64
|
+
id serial PRIMARY KEY,
|
65
|
+
a varchar NOT NULL,
|
66
|
+
b varchar NOT NULL,
|
67
|
+
c varchar NOT NULL,
|
68
|
+
d varchar NOT NULL,
|
69
|
+
e varchar NOT NULL,
|
70
|
+
f varchar NOT NULL,
|
71
|
+
g varchar NOT NULL,
|
72
|
+
h varchar NOT NULL,
|
73
|
+
i varchar NOT NULL,
|
74
|
+
j varchar NOT NULL
|
75
|
+
);
|
76
|
+
|
77
|
+
CREATE INDEX ON wide_records (a);
|
78
|
+
CREATE INDEX ON wide_records (b);
|
79
|
+
|
80
|
+
|
81
|
+
|
82
|
+
DROP TABLE IF EXISTS narrow_records;
|
83
|
+
|
84
|
+
-- a one indexed fields and a couple unindexed fields
|
85
|
+
CREATE TABLE narrow_records(
|
86
|
+
id serial PRIMARY KEY,
|
87
|
+
a varchar NOT NULL,
|
88
|
+
b varchar NOT NULL,
|
89
|
+
c varchar NOT NULL
|
90
|
+
);
|
91
|
+
|
92
|
+
CREATE INDEX ON narrow_records (a);
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'benchmark_helper'
|
2
|
+
|
3
|
+
options = {}
|
4
|
+
optparse = OptionParser.new do |opts|
|
5
|
+
options[:records] = 200
|
6
|
+
opts.on '-r NUM', '--records NUM', Integer, 'Number of records to create' do |n|
|
7
|
+
options[:records] = n
|
8
|
+
end
|
9
|
+
|
10
|
+
options[:pairs] = 5
|
11
|
+
opts.on '-p NUM', '--pairs NUM', Integer, 'Number of key/value pairs' do |n|
|
12
|
+
options[:pairs] = n
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
optparse.parse!
|
17
|
+
|
18
|
+
clean_database
|
19
|
+
GC.disable
|
20
|
+
|
21
|
+
num_records = options[:records]
|
22
|
+
num_key_value_pairs = options[:pairs]
|
23
|
+
|
24
|
+
key_value_pairs = num_records.times.map do
|
25
|
+
num_key_value_pairs.times.each_with_object({}) do |n, hash|
|
26
|
+
hash[SecureRandom.hex(4)] = SecureRandom.hex(4)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
puts
|
31
|
+
puts "Writing #{num_records} records with #{num_key_value_pairs} string key/value pairs"
|
32
|
+
|
33
|
+
Benchmark.bm(8) do |x|
|
34
|
+
x.report("EAV") do
|
35
|
+
EavMasterRecord.transaction do
|
36
|
+
num_records.times do |i|
|
37
|
+
EavMasterRecord.create! :properties => key_value_pairs[i]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
x.report("Surus") do
|
43
|
+
SurusKeyValueRecord.transaction do
|
44
|
+
num_records.times do |i|
|
45
|
+
SurusKeyValueRecord.create! :properties => key_value_pairs[i]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
x.report("YAML") do
|
51
|
+
YamlKeyValueRecord.transaction do
|
52
|
+
num_records.times do |i|
|
53
|
+
YamlKeyValueRecord.create! :properties => key_value_pairs[i]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'benchmark_helper'
|
2
|
+
|
3
|
+
options = {}
|
4
|
+
optparse = OptionParser.new do |opts|
|
5
|
+
options[:records] = 2_000
|
6
|
+
opts.on '-r NUM', '--records NUM', Integer, 'Number of records to create' do |n|
|
7
|
+
options[:records] = n
|
8
|
+
end
|
9
|
+
|
10
|
+
options[:pairs] = 5
|
11
|
+
opts.on '-p NUM', '--pairs NUM', Integer, 'Number of key/value pairs' do |n|
|
12
|
+
options[:pairs] = n
|
13
|
+
end
|
14
|
+
|
15
|
+
options[:eav] = false
|
16
|
+
opts.on '-e', '--eav', 'Include EAV in benchmark (VERY SLOW!)' do
|
17
|
+
options[:eav] = true
|
18
|
+
end
|
19
|
+
|
20
|
+
options[:yaml] = false
|
21
|
+
opts.on '-y', '--yaml', 'Include YAML in benchmark (VERY SLOW!)' do
|
22
|
+
options[:yaml] = true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
optparse.parse!
|
27
|
+
|
28
|
+
clean_database
|
29
|
+
|
30
|
+
num_records = options[:records]
|
31
|
+
num_key_value_pairs = options[:pairs]
|
32
|
+
eav = options[:eav]
|
33
|
+
yaml = options[:yaml]
|
34
|
+
|
35
|
+
puts "Skipping EAV test. Use -e to enable (VERY SLOW!)" unless eav
|
36
|
+
puts "Skipping YAML test. Use -y to enable (VERY SLOW!)" unless yaml
|
37
|
+
|
38
|
+
key_value_pairs = num_records.times.map do
|
39
|
+
num_key_value_pairs.times.each_with_object({}) do |n, hash|
|
40
|
+
hash[SecureRandom.hex(2)] = SecureRandom.hex(4)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
if eav
|
45
|
+
print "Creating EAV test data... "
|
46
|
+
EavMasterRecord.transaction do
|
47
|
+
num_records.times do |i|
|
48
|
+
EavMasterRecord.create! :properties => key_value_pairs[i]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
puts "Done."
|
52
|
+
end
|
53
|
+
|
54
|
+
print "Creating Surus test data... "
|
55
|
+
SurusKeyValueRecord.transaction do
|
56
|
+
num_records.times do |i|
|
57
|
+
SurusKeyValueRecord.create! :properties => key_value_pairs[i]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
puts "Done."
|
61
|
+
|
62
|
+
if yaml
|
63
|
+
print "Creating YAML test data... "
|
64
|
+
YamlKeyValueRecord.transaction do
|
65
|
+
num_records.times do |i|
|
66
|
+
YamlKeyValueRecord.create! :properties => key_value_pairs[i]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
puts "Done."
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
num_finds = 200
|
74
|
+
keys_to_find = key_value_pairs.sample(num_finds).map { |h| h.keys.sample }
|
75
|
+
|
76
|
+
puts
|
77
|
+
puts "#{num_records} records with #{num_key_value_pairs} string key/value pairs"
|
78
|
+
puts "Finding all by inclusion of a key #{num_finds} times"
|
79
|
+
|
80
|
+
Benchmark.bm(8) do |x|
|
81
|
+
if eav
|
82
|
+
x.report("EAV") do
|
83
|
+
keys_to_find.each do |key_to_find|
|
84
|
+
EavMasterRecord
|
85
|
+
.includes(:eav_detail_records)
|
86
|
+
.where("EXISTS(SELECT 1 FROM eav_detail_records WHERE eav_master_records.id=eav_detail_records.eav_master_record_id AND key=?)", key_to_find)
|
87
|
+
.all
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
x.report("Surus") do
|
93
|
+
keys_to_find.each do |key_to_find|
|
94
|
+
SurusKeyValueRecord.hstore_has_key(:properties, key_to_find).all
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
if yaml
|
99
|
+
x.report("YAML") do
|
100
|
+
keys_to_find.each do |key_to_find|
|
101
|
+
YamlKeyValueRecord.all.select { |r| r.properties.key?(key_to_find) }
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'benchmark_helper'
|
2
|
+
|
3
|
+
options = {}
|
4
|
+
optparse = OptionParser.new do |opts|
|
5
|
+
options[:pairs] = 5
|
6
|
+
opts.on '-p NUM', '--pairs NUM', Integer, 'Number of key/value pairs' do |n|
|
7
|
+
options[:pairs] = n
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
optparse.parse!
|
12
|
+
|
13
|
+
clean_database
|
14
|
+
|
15
|
+
num_key_value_pairs = options[:pairs]
|
16
|
+
|
17
|
+
key_value_pair = num_key_value_pairs.times.each_with_object({}) do |n, hash|
|
18
|
+
hash[SecureRandom.hex(4)] = SecureRandom.hex(4)
|
19
|
+
end
|
20
|
+
|
21
|
+
EavMasterRecord.create! :properties => key_value_pair
|
22
|
+
SurusKeyValueRecord.create! :properties => key_value_pair
|
23
|
+
YamlKeyValueRecord.create! :properties => key_value_pair
|
24
|
+
|
25
|
+
num_reads = 3_000
|
26
|
+
|
27
|
+
puts
|
28
|
+
puts "Reading a single record with #{num_key_value_pairs} string key/value pairs #{num_reads} times"
|
29
|
+
|
30
|
+
Benchmark.bm(8) do |x|
|
31
|
+
x.report("EAV") do
|
32
|
+
num_reads.times { EavMasterRecord.first.properties }
|
33
|
+
end
|
34
|
+
|
35
|
+
x.report("Surus") do
|
36
|
+
num_reads.times { SurusKeyValueRecord.first.properties }
|
37
|
+
end
|
38
|
+
|
39
|
+
x.report("YAML") do
|
40
|
+
num_reads.times { YamlKeyValueRecord.first.properties }
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'benchmark_helper'
|
2
|
+
|
3
|
+
options = {}
|
4
|
+
optparse = OptionParser.new do |opts|
|
5
|
+
options[:records] = 1000
|
6
|
+
opts.on '-r NUM', '--records NUM', Integer, 'Number of records to create' do |n|
|
7
|
+
options[:records] = n
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
optparse.parse!
|
12
|
+
num_records = options[:records]
|
13
|
+
narrow_field_width = 8
|
14
|
+
wide_field_width = 25
|
15
|
+
|
16
|
+
print "Generating random data before test to avoid bias... "
|
17
|
+
random_string = SecureRandom.base64(num_records * 10 * wide_field_width)
|
18
|
+
puts "Done."
|
19
|
+
|
20
|
+
random_io = StringIO.new(random_string)
|
21
|
+
|
22
|
+
create_wide_record = lambda do
|
23
|
+
WideRecord.create! :a => random_io.read(wide_field_width),
|
24
|
+
:b => random_io.read(wide_field_width),
|
25
|
+
:c => random_io.read(wide_field_width),
|
26
|
+
:d => random_io.read(wide_field_width),
|
27
|
+
:e => random_io.read(wide_field_width),
|
28
|
+
:f => random_io.read(wide_field_width),
|
29
|
+
:g => random_io.read(wide_field_width),
|
30
|
+
:h => random_io.read(wide_field_width),
|
31
|
+
:i => random_io.read(wide_field_width),
|
32
|
+
:j => random_io.read(wide_field_width)
|
33
|
+
end
|
34
|
+
|
35
|
+
create_narrow_record = lambda do
|
36
|
+
NarrowRecord.create! :a => random_io.read(narrow_field_width),
|
37
|
+
:b => random_io.read(narrow_field_width),
|
38
|
+
:c => random_io.read(narrow_field_width)
|
39
|
+
end
|
40
|
+
|
41
|
+
{ "narrow" => create_narrow_record, "wide" => create_wide_record }.each do |text, create_record|
|
42
|
+
puts
|
43
|
+
puts "Writing #{num_records} #{text} records"
|
44
|
+
|
45
|
+
Benchmark.bm(30) do |x|
|
46
|
+
clean_database
|
47
|
+
random_io.rewind
|
48
|
+
WideRecord.synchronous_commit true
|
49
|
+
x.report("enabled") do
|
50
|
+
num_records.times { create_record.call }
|
51
|
+
end
|
52
|
+
|
53
|
+
clean_database
|
54
|
+
random_io.rewind
|
55
|
+
WideRecord.synchronous_commit false
|
56
|
+
x.report("disabled") do
|
57
|
+
num_records.times { create_record.call }
|
58
|
+
end
|
59
|
+
|
60
|
+
clean_database
|
61
|
+
random_io.rewind
|
62
|
+
WideRecord.synchronous_commit true
|
63
|
+
x.report("disabled per transaction") do
|
64
|
+
num_records.times do
|
65
|
+
WideRecord.transaction do
|
66
|
+
WideRecord.synchronous_commit false
|
67
|
+
create_record.call
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
clean_database
|
73
|
+
random_io.rewind
|
74
|
+
WideRecord.synchronous_commit true
|
75
|
+
x.report("enabled / single transaction") do
|
76
|
+
WideRecord.transaction do
|
77
|
+
num_records.times { create_record.call }
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
clean_database
|
82
|
+
random_io.rewind
|
83
|
+
WideRecord.synchronous_commit false
|
84
|
+
x.report("disabled / single transaction") do
|
85
|
+
WideRecord.transaction do
|
86
|
+
num_records.times { create_record.call }
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
data/lib/surus.rb
CHANGED
data/lib/surus/array/scope.rb
CHANGED
@@ -1,10 +1,22 @@
|
|
1
1
|
module Surus
|
2
2
|
module Array
|
3
3
|
module Scope
|
4
|
+
# Adds where condition that requires column to contain all values
|
5
|
+
#
|
6
|
+
# Examples:
|
7
|
+
# User.array_has(:permissions, "manage_users")
|
8
|
+
# User.array_has(:permissions, "manage_users", "manage_roles")
|
9
|
+
# User.array_has(:permissions, ["manage_users", "manage_roles"])
|
4
10
|
def array_has(column, *values)
|
5
11
|
where("#{connection.quote_column_name(column)} @> ARRAY[?]", values.flatten)
|
6
12
|
end
|
7
13
|
|
14
|
+
# Adds where condition that requires column to contain any values
|
15
|
+
#
|
16
|
+
# Examples:
|
17
|
+
# User.array_has_any(:permissions, "manage_users")
|
18
|
+
# User.array_has_any(:permissions, "manage_users", "manage_roles")
|
19
|
+
# User.array_has_any(:permissions, ["manage_users", "manage_roles"])
|
8
20
|
def array_has_any(column, *values)
|
9
21
|
where("#{connection.quote_column_name(column)} && ARRAY[?]", values.flatten)
|
10
22
|
end
|
data/lib/surus/hstore/scope.rb
CHANGED
@@ -1,18 +1,36 @@
|
|
1
1
|
module Surus
|
2
2
|
module Hstore
|
3
3
|
module Scope
|
4
|
+
# Adds a where condition that requires column to contain hash
|
5
|
+
#
|
6
|
+
# Example:
|
7
|
+
# User.hstore_has_pairs(:properties, "favorite_color" => "green")
|
4
8
|
def hstore_has_pairs(column, hash)
|
5
9
|
where("#{connection.quote_column_name(column)} @> ?", Serializer.new.dump(hash))
|
6
10
|
end
|
7
11
|
|
12
|
+
# Adds a where condition that requires column to contain key
|
13
|
+
#
|
14
|
+
# Example:
|
15
|
+
# User.hstore_has_key(:properties, "favorite_color")
|
8
16
|
def hstore_has_key(column, key)
|
9
17
|
where("#{connection.quote_column_name(column)} ? :key", :key => key)
|
10
18
|
end
|
11
19
|
|
20
|
+
# Adds a where condition that requires column to contain all keys.
|
21
|
+
#
|
22
|
+
# Example:
|
23
|
+
# User.hstore_has_all_keys(:properties, "favorite_color", "favorite_song")
|
24
|
+
# User.hstore_has_all_keys(:properties, ["favorite_color", "favorite_song"])
|
12
25
|
def hstore_has_all_keys(column, *keys)
|
13
26
|
where("#{connection.quote_column_name(column)} ?& ARRAY[:keys]", :keys => keys.flatten)
|
14
27
|
end
|
15
28
|
|
29
|
+
# Adds a where condition that requires column to contain any keys.
|
30
|
+
#
|
31
|
+
# Example:
|
32
|
+
# User.hstore_has_any_keys(:properties, "favorite_color", "favorite_song")
|
33
|
+
# User.hstore_has_any_keys(:properties, ["favorite_color", "favorite_song"])
|
16
34
|
def hstore_has_any_keys(column, *keys)
|
17
35
|
where("#{connection.quote_column_name(column)} ?| ARRAY[:keys]", :keys => keys.flatten)
|
18
36
|
end
|
@@ -108,6 +108,8 @@ module Surus
|
|
108
108
|
def stringify(value)
|
109
109
|
if value.kind_of?(String)
|
110
110
|
[value, "String"]
|
111
|
+
elsif value.kind_of?(Symbol)
|
112
|
+
[value.to_s, "Symbol"]
|
111
113
|
elsif value.kind_of?(Integer)
|
112
114
|
[value.to_s, "Integer"]
|
113
115
|
elsif value.kind_of?(Float)
|
@@ -129,6 +131,8 @@ module Surus
|
|
129
131
|
|
130
132
|
def typecast(value, type)
|
131
133
|
case type
|
134
|
+
when "Symbol"
|
135
|
+
value.to_sym
|
132
136
|
when "Integer"
|
133
137
|
Integer(value)
|
134
138
|
when "Float"
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Surus
|
2
|
+
module SynchronousCommit
|
3
|
+
module Connection
|
4
|
+
# When called without any value returns the current synchronous_commit
|
5
|
+
# value.
|
6
|
+
#
|
7
|
+
# When called with a value it is delegated to #synchronous_commit=
|
8
|
+
def synchronous_commit(value=:not_passed_param)
|
9
|
+
if value == :not_passed_param
|
10
|
+
select_value("SHOW synchronous_commit") == "on"
|
11
|
+
else
|
12
|
+
self.synchronous_commit = value
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Changes current synchronous_commit state. If a transaction is currently
|
17
|
+
# in progress the change will be reverted at the end of the transaction.
|
18
|
+
#
|
19
|
+
# Requires true or false to be passed exactly -- not merely truthy or falsy
|
20
|
+
def synchronous_commit=(value)
|
21
|
+
raise ArgumentError, "argument must be true or false" unless value == true || value == false
|
22
|
+
|
23
|
+
execute "SET #{'LOCAL' if open_transactions > 0} synchronous_commit TO #{value ? 'ON' : 'OFF'}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# If Surus is loaded before establish_connection is called then
|
30
|
+
# PostgreSQLAdapter will not be loaded yet. require it to ensure
|
31
|
+
# it is available
|
32
|
+
require "active_record/connection_adapters/postgresql_adapter"
|
33
|
+
|
34
|
+
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.send :include, Surus::SynchronousCommit::Connection
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Surus
|
2
|
+
module SynchronousCommit
|
3
|
+
# synchronous_commit and synchronous_commit= are delegated to the underlying
|
4
|
+
# connection object
|
5
|
+
module Model
|
6
|
+
delegate :synchronous_commit, :synchronous_commit=, :to => :connection
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
ActiveRecord::Base.extend Surus::SynchronousCommit::Model
|
data/lib/surus/version.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Surus::SynchronousCommit::Connection, :disable_transactions => true do
|
4
|
+
let(:conn) { ActiveRecord::Base.connection }
|
5
|
+
before { conn.execute "SET synchronous_commit TO ON;" }
|
6
|
+
after { conn.execute "SET synchronous_commit TO ON;" }
|
7
|
+
|
8
|
+
describe "synchronous_commit" do
|
9
|
+
context "without parameter" do
|
10
|
+
subject { conn.synchronous_commit }
|
11
|
+
|
12
|
+
context "when synchronous_commit commit is off" do
|
13
|
+
before { conn.execute "SET synchronous_commit TO OFF;" }
|
14
|
+
it { should == false }
|
15
|
+
end
|
16
|
+
|
17
|
+
context "when synchronous_commit commit is on" do
|
18
|
+
before { conn.execute "SET synchronous_commit TO ON;" }
|
19
|
+
it { should == true }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context "with parameter" do
|
24
|
+
context "true" do
|
25
|
+
before { conn.execute "SET synchronous_commit TO OFF;" }
|
26
|
+
it "sets synchronous_commit to on" do
|
27
|
+
conn.synchronous_commit true
|
28
|
+
conn.select_value("SHOW synchronous_commit").should == "on"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context "false" do
|
33
|
+
before { conn.execute "SET synchronous_commit TO ON;" }
|
34
|
+
it "sets synchronous_commit to off" do
|
35
|
+
conn.synchronous_commit false
|
36
|
+
conn.select_value("SHOW synchronous_commit").should == "off"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context "invalid value" do
|
41
|
+
it "raises ArgumentError" do
|
42
|
+
expect{conn.synchronous_commit "foo"}.to raise_error(ArgumentError)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context "inside transaction" do
|
47
|
+
before { conn.execute "SET synchronous_commit TO OFF;" }
|
48
|
+
|
49
|
+
it "only persists for duration of transaction" do
|
50
|
+
conn.transaction do
|
51
|
+
conn.synchronous_commit true
|
52
|
+
conn.select_value("SHOW synchronous_commit").should == "on"
|
53
|
+
end
|
54
|
+
conn.select_value("SHOW synchronous_commit").should == "off"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "synchronous_commit=" do
|
61
|
+
context "true" do
|
62
|
+
before { conn.execute "SET synchronous_commit TO OFF;" }
|
63
|
+
it "sets synchronous_commit to on" do
|
64
|
+
conn.synchronous_commit true
|
65
|
+
conn.select_value("SHOW synchronous_commit").should == "on"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context "false" do
|
70
|
+
before { conn.execute "SET synchronous_commit TO ON;" }
|
71
|
+
it "sets synchronous_commit to off" do
|
72
|
+
conn.synchronous_commit false
|
73
|
+
conn.select_value("SHOW synchronous_commit").should == "off"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context "invalid value" do
|
78
|
+
it "raises ArgumentError" do
|
79
|
+
expect{conn.synchronous_commit "foo"}.to raise_error(ArgumentError)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
context "inside transaction" do
|
84
|
+
before { conn.execute "SET synchronous_commit TO OFF;" }
|
85
|
+
|
86
|
+
it "only persists for duration of transaction" do
|
87
|
+
conn.transaction do
|
88
|
+
conn.synchronous_commit true
|
89
|
+
conn.select_value("SHOW synchronous_commit").should == "on"
|
90
|
+
end
|
91
|
+
conn.select_value("SHOW synchronous_commit").should == "off"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Surus::SynchronousCommit::Model do
|
4
|
+
let(:conn) { ActiveRecord::Base.connection }
|
5
|
+
|
6
|
+
describe "synchronous_commit" do
|
7
|
+
it "is delegated to connection" do
|
8
|
+
conn.should_receive(:synchronous_commit)
|
9
|
+
ActiveRecord::Base.synchronous_commit
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "synchronous_commit=" do
|
14
|
+
it "is delegated to connection" do
|
15
|
+
conn.should_receive(:synchronous_commit=)
|
16
|
+
ActiveRecord::Base.synchronous_commit = true
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/surus.gemspec
CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |s|
|
|
9
9
|
s.email = ["jack@jackchristensen.com"]
|
10
10
|
s.homepage = "https://github.com/JackC/surus"
|
11
11
|
s.summary = %q{PostgreSQL extensions for ActiveRecord}
|
12
|
-
s.description = %q{
|
12
|
+
s.description = %q{Includes serializers and search scopes for hstore and array. Also includes control over synchronous_commit to boost insert and update speed.}
|
13
13
|
|
14
14
|
s.rubyforge_project = ""
|
15
15
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: surus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-02-
|
12
|
+
date: 2012-02-05 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: pg
|
16
|
-
requirement: &
|
16
|
+
requirement: &13148960 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *13148960
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: activerecord
|
27
|
-
requirement: &
|
27
|
+
requirement: &13161940 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: 3.1.0
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *13161940
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: rspec
|
38
|
-
requirement: &
|
38
|
+
requirement: &13159120 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ~>
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: 2.8.0
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *13159120
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: guard
|
49
|
-
requirement: &
|
49
|
+
requirement: &13155700 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ! '>='
|
@@ -54,10 +54,10 @@ dependencies:
|
|
54
54
|
version: 0.10.0
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *13155700
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: guard-rspec
|
60
|
-
requirement: &
|
60
|
+
requirement: &13173200 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
63
|
- - ! '>='
|
@@ -65,8 +65,9 @@ dependencies:
|
|
65
65
|
version: 0.6.0
|
66
66
|
type: :development
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
69
|
-
description:
|
68
|
+
version_requirements: *13173200
|
69
|
+
description: Includes serializers and search scopes for hstore and array. Also includes
|
70
|
+
control over synchronous_commit to boost insert and update speed.
|
70
71
|
email:
|
71
72
|
- jack@jackchristensen.com
|
72
73
|
executables: []
|
@@ -81,6 +82,16 @@ files:
|
|
81
82
|
- LICENSE
|
82
83
|
- README.md
|
83
84
|
- Rakefile
|
85
|
+
- bench/array_create.rb
|
86
|
+
- bench/array_find.rb
|
87
|
+
- bench/array_serialize.rb
|
88
|
+
- bench/benchmark_helper.rb
|
89
|
+
- bench/database.yml
|
90
|
+
- bench/database_structure.sql
|
91
|
+
- bench/hstore_create.rb
|
92
|
+
- bench/hstore_find.rb
|
93
|
+
- bench/hstore_serialize.rb
|
94
|
+
- bench/synchronous_commit.rb
|
84
95
|
- lib/surus.rb
|
85
96
|
- lib/surus/array/decimal_serializer.rb
|
86
97
|
- lib/surus/array/float_serializer.rb
|
@@ -89,6 +100,8 @@ files:
|
|
89
100
|
- lib/surus/array/text_serializer.rb
|
90
101
|
- lib/surus/hstore/scope.rb
|
91
102
|
- lib/surus/hstore/serializer.rb
|
103
|
+
- lib/surus/synchronous_commit/connection.rb
|
104
|
+
- lib/surus/synchronous_commit/model.rb
|
92
105
|
- lib/surus/version.rb
|
93
106
|
- spec/array/decimal_serializer_spec.rb
|
94
107
|
- spec/array/float_serializer_spec.rb
|
@@ -100,6 +113,8 @@ files:
|
|
100
113
|
- spec/hstore/scope_spec.rb
|
101
114
|
- spec/hstore/serializer_spec.rb
|
102
115
|
- spec/spec_helper.rb
|
116
|
+
- spec/synchronous_commit/connection_spec.rb
|
117
|
+
- spec/synchronous_commit/model_spec.rb
|
103
118
|
- surus.gemspec
|
104
119
|
homepage: https://github.com/JackC/surus
|
105
120
|
licenses: []
|