surus 0.4.2 → 0.5.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.
- checksums.yaml +4 -4
- data/README.md +15 -82
- data/bench/benchmark_helper.rb +0 -9
- data/bench/database_structure.sql +0 -18
- data/bench/hstore_find.rb +7 -7
- data/bench/json_generation.rb +1 -1
- data/lib/surus.rb +0 -6
- data/lib/surus/array/scope.rb +1 -1
- data/lib/surus/hstore/serializer.rb +23 -47
- data/lib/surus/json/belongs_to_scope_builder.rb +3 -3
- data/lib/surus/json/has_and_belongs_to_many_scope_builder.rb +4 -5
- data/lib/surus/json/has_many_scope_builder.rb +3 -4
- data/lib/surus/json/model.rb +1 -1
- data/lib/surus/version.rb +1 -1
- data/spec/array/scope_spec.rb +6 -6
- data/spec/hstore/scope_spec.rb +21 -21
- data/spec/spec_helper.rb +6 -20
- data/surus.gemspec +1 -1
- metadata +5 -24
- data/bench/array_create.rb +0 -46
- data/bench/array_find.rb +0 -79
- data/bench/array_serialize.rb +0 -34
- data/bench/hstore_create.rb +0 -57
- data/bench/hstore_serialize.rb +0 -42
- data/lib/surus/array/decimal_serializer.rb +0 -28
- data/lib/surus/array/float_serializer.rb +0 -28
- data/lib/surus/array/integer_serializer.rb +0 -28
- data/lib/surus/array/text_serializer.rb +0 -47
- data/lib/surus/hstore/connection_adapters.rb +0 -46
- data/lib/surus/json/connection_adapters.rb +0 -46
- data/spec/array/decimal_serializer_spec.rb +0 -23
- data/spec/array/float_serializer_spec.rb +0 -23
- data/spec/array/integer_serializer_spec.rb +0 -22
- data/spec/array/text_serializer_spec.rb +0 -29
data/bench/array_serialize.rb
DELETED
@@ -1,34 +0,0 @@
|
|
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
|
data/bench/hstore_create.rb
DELETED
@@ -1,57 +0,0 @@
|
|
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
|
data/bench/hstore_serialize.rb
DELETED
@@ -1,42 +0,0 @@
|
|
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
|
@@ -1,28 +0,0 @@
|
|
1
|
-
module Surus
|
2
|
-
module Array
|
3
|
-
class DecimalSerializer
|
4
|
-
ARRAY_REGEX = %r{
|
5
|
-
(?<=[\{,]) (?# All elements are prefixed with either the opening brace or a comma)
|
6
|
-
\-?\d+\.?\d*
|
7
|
-
|
|
8
|
-
NULL
|
9
|
-
}x
|
10
|
-
|
11
|
-
def load(string)
|
12
|
-
return unless string
|
13
|
-
string.scan(ARRAY_REGEX).map do |match|
|
14
|
-
match == "NULL" ? nil : BigDecimal(match)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
def dump(array)
|
19
|
-
return unless array
|
20
|
-
'{' + array.map { |s| format(s) }.join(",") + '}'
|
21
|
-
end
|
22
|
-
|
23
|
-
def format(value)
|
24
|
-
value == nil ? "NULL" : value
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
@@ -1,28 +0,0 @@
|
|
1
|
-
module Surus
|
2
|
-
module Array
|
3
|
-
class FloatSerializer
|
4
|
-
ARRAY_REGEX = %r{
|
5
|
-
(?<=[\{,]) (?# All elements are prefixed with either the opening brace or a comma)
|
6
|
-
\-?\d+(?:\.\d+)?(?:e[+-])?\d*
|
7
|
-
|
|
8
|
-
NULL
|
9
|
-
}x
|
10
|
-
|
11
|
-
def load(string)
|
12
|
-
return unless string
|
13
|
-
string.scan(ARRAY_REGEX).map do |match|
|
14
|
-
match == "NULL" ? nil : Float(match)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
def dump(array)
|
19
|
-
return unless array
|
20
|
-
'{' + array.map { |s| format(s) }.join(",") + '}'
|
21
|
-
end
|
22
|
-
|
23
|
-
def format(value)
|
24
|
-
value == nil ? "NULL" : value
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
@@ -1,28 +0,0 @@
|
|
1
|
-
module Surus
|
2
|
-
module Array
|
3
|
-
class IntegerSerializer
|
4
|
-
ARRAY_REGEX = %r{
|
5
|
-
(?<=[\{,]) (?# All elements are prefixed with either the opening brace or a comma)
|
6
|
-
\-?\d+
|
7
|
-
|
|
8
|
-
NULL
|
9
|
-
}x
|
10
|
-
|
11
|
-
def load(string)
|
12
|
-
return unless string
|
13
|
-
string.scan(ARRAY_REGEX).map do |match|
|
14
|
-
match == "NULL" ? nil : Integer(match)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
def dump(array)
|
19
|
-
return unless array
|
20
|
-
'{' + array.map { |s| format(s) }.join(",") + '}'
|
21
|
-
end
|
22
|
-
|
23
|
-
def format(value)
|
24
|
-
value == nil ? "NULL" : value
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
@@ -1,47 +0,0 @@
|
|
1
|
-
module Surus
|
2
|
-
module Array
|
3
|
-
class TextSerializer
|
4
|
-
ARRAY_REGEX = %r{
|
5
|
-
[{,] (?# All elements are prefixed with either the opening brace or a comma)
|
6
|
-
(?:
|
7
|
-
"
|
8
|
-
(?<quoted_string>(?:[^"\\]|\\.)*)
|
9
|
-
"
|
10
|
-
|
|
11
|
-
(?<null>NULL)
|
12
|
-
|
|
13
|
-
(?<unquoted_string>[^,}]+)
|
14
|
-
)
|
15
|
-
}x
|
16
|
-
|
17
|
-
def load(string)
|
18
|
-
return unless string
|
19
|
-
string.scan(ARRAY_REGEX).map do |quoted_string, null, unquoted_string|
|
20
|
-
element = quoted_string || unquoted_string
|
21
|
-
element ? unescape(element) : nil
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def dump(array)
|
26
|
-
return unless array
|
27
|
-
'{' + array.map { |s| format(s) }.join(",") + '}'
|
28
|
-
end
|
29
|
-
|
30
|
-
def format(value)
|
31
|
-
value == nil ? "NULL" : '"' + escape(value) + '"'
|
32
|
-
end
|
33
|
-
|
34
|
-
def escape(value)
|
35
|
-
value
|
36
|
-
.gsub('\\', '\\\\\\')
|
37
|
-
.gsub('"', '\\"')
|
38
|
-
end
|
39
|
-
|
40
|
-
def unescape(value)
|
41
|
-
value
|
42
|
-
.gsub('\\\\', '\\')
|
43
|
-
.gsub('\\"', '"')
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
@@ -1,46 +0,0 @@
|
|
1
|
-
require 'active_record/connection_adapters/postgresql_adapter'
|
2
|
-
|
3
|
-
ActiveRecord::ConnectionAdapters::PostgreSQLColumn.class_eval do
|
4
|
-
def simplified_type_with_hstore(field_type)
|
5
|
-
field_type == 'hstore' ? :hstore : simplified_type_without_hstore(field_type)
|
6
|
-
end
|
7
|
-
|
8
|
-
alias_method_chain :simplified_type, :hstore
|
9
|
-
end
|
10
|
-
|
11
|
-
|
12
|
-
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.class_eval do
|
13
|
-
def native_database_types_with_hstore
|
14
|
-
native_database_types_without_hstore.merge({:hstore => { :name => "hstore" }})
|
15
|
-
end
|
16
|
-
|
17
|
-
alias_method_chain :native_database_types, :hstore
|
18
|
-
end
|
19
|
-
|
20
|
-
ActiveRecord::ConnectionAdapters::TableDefinition.class_eval do
|
21
|
-
# Adds hstore type for migrations. So you can add columns to a table like:
|
22
|
-
# create_table :people do |t|
|
23
|
-
# ...
|
24
|
-
# t.hstore :info
|
25
|
-
# ...
|
26
|
-
# end
|
27
|
-
def hstore(*args)
|
28
|
-
options = args.extract_options!
|
29
|
-
column_names = args
|
30
|
-
column_names.each { |name| column(name, 'hstore', options) }
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
ActiveRecord::ConnectionAdapters::Table.class_eval do
|
35
|
-
# Adds hstore type for migrations. So you can add columns to a table like:
|
36
|
-
# change_table :people do |t|
|
37
|
-
# ...
|
38
|
-
# t.hstore :info
|
39
|
-
# ...
|
40
|
-
# end
|
41
|
-
def hstore(*args)
|
42
|
-
options = args.extract_options!
|
43
|
-
column_names = args
|
44
|
-
column_names.each { |name| column(name, 'hstore', options) }
|
45
|
-
end
|
46
|
-
end
|
@@ -1,46 +0,0 @@
|
|
1
|
-
require 'active_record/connection_adapters/postgresql_adapter'
|
2
|
-
|
3
|
-
ActiveRecord::ConnectionAdapters::PostgreSQLColumn.class_eval do
|
4
|
-
def simplified_type_with_json(field_type)
|
5
|
-
field_type == 'json' ? :json : simplified_type_without_json(field_type)
|
6
|
-
end
|
7
|
-
|
8
|
-
alias_method_chain :simplified_type, :json
|
9
|
-
end
|
10
|
-
|
11
|
-
|
12
|
-
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.class_eval do
|
13
|
-
def native_database_types_with_json
|
14
|
-
native_database_types_without_json.merge({:json => { :name => "json" }})
|
15
|
-
end
|
16
|
-
|
17
|
-
alias_method_chain :native_database_types, :json
|
18
|
-
end
|
19
|
-
|
20
|
-
ActiveRecord::ConnectionAdapters::TableDefinition.class_eval do
|
21
|
-
# Adds json type for migrations. So you can add columns to a table like:
|
22
|
-
# create_table :people do |t|
|
23
|
-
# ...
|
24
|
-
# t.json :info
|
25
|
-
# ...
|
26
|
-
# end
|
27
|
-
def json(*args)
|
28
|
-
options = args.extract_options!
|
29
|
-
column_names = args
|
30
|
-
column_names.each { |name| column(name, 'json', options) }
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
ActiveRecord::ConnectionAdapters::Table.class_eval do
|
35
|
-
# Adds json type for migrations. So you can add columns to a table like:
|
36
|
-
# change_table :people do |t|
|
37
|
-
# ...
|
38
|
-
# t.json :info
|
39
|
-
# ...
|
40
|
-
# end
|
41
|
-
def json(*args)
|
42
|
-
options = args.extract_options!
|
43
|
-
column_names = args
|
44
|
-
column_names.each { |name| column(name, 'json', options) }
|
45
|
-
end
|
46
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Surus::Array::DecimalSerializer do
|
4
|
-
round_trip_examples = [
|
5
|
-
[nil, "nil"],
|
6
|
-
[[], "empty array"],
|
7
|
-
[[BigDecimal("0.0")], "single element"],
|
8
|
-
[[BigDecimal("1.0"), BigDecimal("2.0")], "multiple elements"],
|
9
|
-
[[BigDecimal("-1.0"), BigDecimal("-2.0")], "negative element"],
|
10
|
-
[[BigDecimal("1232493289348929843.323422349274382923")], "high magnitude element"],
|
11
|
-
[[BigDecimal("1.0"), BigDecimal("2.0"), BigDecimal("2.0")], "duplicated elements"],
|
12
|
-
[[BigDecimal("1.0"), nil], "an element is nil"],
|
13
|
-
[(10_000.times.map { |n| n * BigDecimal("1.13312") }).to_a, "huge array"]
|
14
|
-
]
|
15
|
-
|
16
|
-
round_trip_examples.each do |value, description|
|
17
|
-
it "round trips when #{description}" do
|
18
|
-
r = DecimalArrayRecord.create! :decimals => value
|
19
|
-
r.reload
|
20
|
-
expect(r.decimals).to eq(value)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Surus::Array::FloatSerializer do
|
4
|
-
round_trip_examples = [
|
5
|
-
[nil, "nil"],
|
6
|
-
[[], "empty array"],
|
7
|
-
[[0.0], "single element"],
|
8
|
-
[[1.0, 2.0], "multiple elements"],
|
9
|
-
[[-1.0, -2.0], "negative element"],
|
10
|
-
[[4.4325349e+45, 1.2324323e+77, 1.1242342e-57, 3e99], "high magnitude elements"],
|
11
|
-
[[1.0, 2.0, 2.0, 2.0], "duplicated elements"],
|
12
|
-
[[1.0, nil], "an element is nil"],
|
13
|
-
[(10_000.times.map { |n| n * 1.0 }).to_a, "huge array"]
|
14
|
-
]
|
15
|
-
|
16
|
-
round_trip_examples.each do |value, description|
|
17
|
-
it "round trips when #{description}" do
|
18
|
-
r = FloatArrayRecord.create! :floats => value
|
19
|
-
r.reload
|
20
|
-
expect(r.floats).to eq(value)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
@@ -1,22 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Surus::Array::IntegerSerializer do
|
4
|
-
round_trip_examples = [
|
5
|
-
[nil, "nil"],
|
6
|
-
[[], "empty array"],
|
7
|
-
[[0], "single element"],
|
8
|
-
[[1, 2], "multiple elements"],
|
9
|
-
[[-1, -2], "negative element"],
|
10
|
-
[[1, 2, 2, 2], "duplicated elements"],
|
11
|
-
[[1, nil], "an element is nil"],
|
12
|
-
[(1..10_000).to_a, "huge array"]
|
13
|
-
]
|
14
|
-
|
15
|
-
round_trip_examples.each do |value, description|
|
16
|
-
it "round trips when #{description}" do
|
17
|
-
r = IntegerArrayRecord.create! :integers => value
|
18
|
-
r.reload
|
19
|
-
expect(r.integers).to eq(value)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
@@ -1,29 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Surus::Array::TextSerializer do
|
4
|
-
round_trip_examples = [
|
5
|
-
[nil, "nil"],
|
6
|
-
[[], "empty array"],
|
7
|
-
[["foo"], "single element"],
|
8
|
-
[["foo", "bar"], "multiple elements"],
|
9
|
-
[["foo", "bar", "bar", "bar"], "duplicated elements"],
|
10
|
-
[["foo bar", nil], "an element is nil"],
|
11
|
-
[["foo bar", 'NULL'], "an element is the string 'NULL'"],
|
12
|
-
[["foo bar", "baz"], "an element has a space"],
|
13
|
-
[["foo,bar", "baz"], "an element has a comma (,)"],
|
14
|
-
[["foo'bar", "baz"], "an element has a single quote (')"],
|
15
|
-
[['foo"bar', "baz"], "an element has a double quote (\")"],
|
16
|
-
[['foo\\bar', "baz"], "an element has a backslash (\\)"],
|
17
|
-
[['foo{}bar', "{baz}"], "an element has a braces ({})"],
|
18
|
-
[[%q~foo \\ / " ; ' ( ) {}bar \\'~, "bar"], "an element many special characters"],
|
19
|
-
[("aaa".."zzz").to_a, "huge array"]
|
20
|
-
]
|
21
|
-
|
22
|
-
round_trip_examples.each do |value, description|
|
23
|
-
it "round trips when #{description}" do
|
24
|
-
r = TextArrayRecord.create! :texts => value
|
25
|
-
r.reload
|
26
|
-
expect(r.texts).to eq(value)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|