surus 0.1.0 → 0.2.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.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: []
|