dumbo 0.0.1 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/Gemfile +1 -1
- data/bin/dumbo +65 -24
- data/config/boot.rb +2 -3
- data/dumbo.gemspec +1 -2
- data/lib/dumbo.rb +17 -16
- data/lib/dumbo/aggregate.rb +3 -5
- data/lib/dumbo/base_type.rb +17 -17
- data/lib/dumbo/binding_loader.rb +50 -0
- data/lib/dumbo/cast.rb +5 -5
- data/lib/dumbo/composite_type.rb +4 -4
- data/lib/dumbo/db_task.rb +6 -5
- data/lib/dumbo/dependency_resolver.rb +17 -18
- data/lib/dumbo/enum_type.rb +3 -4
- data/lib/dumbo/extension.rb +64 -20
- data/lib/dumbo/extension_migrator.rb +11 -11
- data/lib/dumbo/extension_version.rb +29 -11
- data/lib/dumbo/function.rb +21 -21
- data/lib/dumbo/operator.rb +4 -6
- data/lib/dumbo/pg_object.rb +17 -21
- data/lib/dumbo/rake_task.rb +63 -53
- data/lib/dumbo/range_type.rb +6 -9
- data/lib/dumbo/test.rb +8 -0
- data/lib/dumbo/test/fixture.rb +51 -0
- data/lib/dumbo/test/helper.rb +64 -0
- data/lib/dumbo/test/matchers.rb +76 -0
- data/lib/dumbo/test/regression_helper.rb +20 -0
- data/lib/dumbo/test/silence_unknown_oid.rb +12 -0
- data/lib/dumbo/type.rb +3 -4
- data/lib/dumbo/version.rb +1 -1
- data/spec/aggregate_spec.rb +9 -10
- data/spec/cast_spec.rb +5 -5
- data/spec/{Makefile → dumbo_sample/Makefile} +4 -0
- data/spec/{dumbo_sample--0.0.1.sql → dumbo_sample/dumbo_sample--0.0.1.sql} +0 -0
- data/spec/{dumbo_sample--0.0.2.sql → dumbo_sample/dumbo_sample--0.0.2.sql} +0 -0
- data/spec/dumbo_sample/dumbo_sample--0.0.3.sql +7 -0
- data/spec/dumbo_sample/dumbo_sample--0.0.4.sql +13 -0
- data/spec/{dumbo_sample.control → dumbo_sample/dumbo_sample.control} +0 -0
- data/spec/dumbo_sample/src/dumbo_sample.c +13 -0
- data/spec/dumbo_sample/src/dumbo_sample.h +17 -0
- data/spec/extension_migrator_spec.rb +12 -11
- data/spec/extension_spec.rb +41 -12
- data/spec/extension_version_spec.rb +27 -0
- data/spec/operator_spec.rb +6 -7
- data/spec/spec_helper.rb +15 -9
- data/spec/support/extension_helper.rb +31 -0
- data/spec/support/silence_unknown_oid.rb +12 -0
- data/spec/type_spec.rb +59 -55
- data/template/Rakefile +6 -6
- data/template/spec/sample_spec.rb.erb +1 -1
- metadata +33 -31
- data/config/database.yml +0 -31
- data/lib/tasks/db.rake +0 -52
- data/lib/tasks/dumbo.rake +0 -23
- data/spec/support/sql_helper.rb +0 -23
data/lib/dumbo/rake_task.rb
CHANGED
@@ -1,24 +1,26 @@
|
|
1
|
-
require
|
1
|
+
require 'rake'
|
2
2
|
require 'rake/tasklib'
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require 'erubis'
|
4
|
+
require 'pathname'
|
5
5
|
require 'yaml'
|
6
6
|
require 'logger'
|
7
7
|
require 'active_record'
|
8
|
-
require
|
9
|
-
require
|
8
|
+
require 'dumbo/extension'
|
9
|
+
require 'dumbo/dependency_resolver'
|
10
|
+
require 'rspec/core'
|
10
11
|
|
11
12
|
module Dumbo
|
12
13
|
class RakeTask < ::Rake::TaskLib
|
13
14
|
attr_accessor :name
|
14
15
|
|
15
16
|
def initialize(name = 'dumbo')
|
16
|
-
|
17
17
|
@name = name
|
18
18
|
|
19
19
|
namespace name do
|
20
|
+
Dumbo::DbTask.new(:db)
|
21
|
+
|
20
22
|
desc 'creates and installs extension'
|
21
|
-
task :
|
23
|
+
task all: [:src, Extension.file_name, :install]
|
22
24
|
|
23
25
|
desc 'installs the extension'
|
24
26
|
task :install do
|
@@ -26,69 +28,83 @@ module Dumbo
|
|
26
28
|
end
|
27
29
|
|
28
30
|
desc 'concatenates files'
|
29
|
-
file
|
31
|
+
file Extension.file_name => file_list do |t|
|
30
32
|
sql = t.prerequisites.map do |file|
|
31
|
-
["--source file #{file}"] + get_sql(file) + [
|
33
|
+
["--source file #{file}"] + get_sql(file) + [' ']
|
32
34
|
end.flatten
|
33
35
|
concatenate sql, t.name
|
34
36
|
end
|
35
37
|
|
38
|
+
desc 'prepare source files'
|
39
|
+
task :src do
|
40
|
+
Dir.glob('src/**/*.erb').each do |file|
|
41
|
+
src = convert_template(file)
|
42
|
+
out = Pathname.new(file).sub_ext('')
|
43
|
+
File.open(out.to_s, 'w') do |f|
|
44
|
+
f.puts src.join("\n")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
36
49
|
desc 'creates migration files for the last two versions'
|
37
50
|
task :migrations do
|
38
|
-
old_version, new_version =
|
51
|
+
old_version, new_version = Extension.versions.last(2).map(&:to_s)
|
52
|
+
|
39
53
|
if new_version
|
40
|
-
|
54
|
+
ExtensionMigrator.new(Extension.name, old_version, new_version)
|
55
|
+
.create
|
41
56
|
end
|
42
57
|
end
|
43
58
|
|
44
|
-
desc '
|
59
|
+
desc 'upgrate .control file to a new version'
|
45
60
|
task :new_version, :level do |t, args|
|
46
|
-
args.with_defaults(:
|
47
|
-
|
48
|
-
|
61
|
+
args.with_defaults(level: 'patch')
|
62
|
+
|
63
|
+
v = new_version(args[:level])
|
64
|
+
Extension.version!(v)
|
49
65
|
|
50
66
|
Rake::Task["#{name}:all"].invoke
|
51
67
|
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
def set_version(new_version)
|
56
|
-
content = File.read("#{extension}.control")
|
57
|
-
new_content = content.gsub(version, new_version)
|
58
|
-
File.open("#{extension}.control", "w") {|file| file.puts new_content}
|
59
|
-
end
|
60
68
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
69
|
+
namespace :test do
|
70
|
+
desc 'creates regression tests from specs and runs them'
|
71
|
+
task regression: ['all', 'db:test:prepare'] do
|
72
|
+
ENV['DUMBO_REGRESSION'] = 'true'
|
73
|
+
RSpec::Core::RakeTask.new(:spec).run_task(false)
|
74
|
+
|
75
|
+
if $?.success?
|
76
|
+
test_files = Rake::FileList.new("test/sql/**/*.sql")
|
77
|
+
out_files = test_files.pathmap("%{^test/sql/,test/expected/}X.out")
|
78
|
+
out_files.each{|f| FileUtils.touch(f)}
|
79
|
+
system('make installcheck &> /dev/null')
|
80
|
+
|
81
|
+
out_files.pathmap("%{^test/expected/,results/}p").each do |f|
|
82
|
+
FileUtils.cp(f,'test/expected/')
|
83
|
+
File.delete(f)
|
84
|
+
end
|
85
|
+
|
86
|
+
system('make installcheck')
|
87
|
+
else
|
88
|
+
Dir.glob('test/*').each{|f| File.delete(f)}
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
77
93
|
end
|
78
94
|
|
79
|
-
def
|
80
|
-
|
95
|
+
def new_version(level = :patch)
|
96
|
+
ExtensionVersion.new_from_string(Extension.version).bump(level).to_s
|
81
97
|
end
|
82
98
|
|
83
|
-
|
99
|
+
# source sql file list
|
84
100
|
def file_list
|
85
|
-
|
101
|
+
DependencyResolver.new(Dir.glob('sql/**/*.{sql,erb}')).resolve
|
86
102
|
end
|
87
103
|
|
88
104
|
def concatenate(lines, target)
|
89
|
-
File.open(target,'w') do |f|
|
105
|
+
File.open(target, 'w') do |f|
|
90
106
|
lines.each do |line|
|
91
|
-
f.puts line unless line =~
|
107
|
+
f.puts line unless line =~ DependencyResolver.depends_pattern
|
92
108
|
end
|
93
109
|
end
|
94
110
|
end
|
@@ -109,13 +125,7 @@ module Dumbo
|
|
109
125
|
end
|
110
126
|
|
111
127
|
def get_bindings(file)
|
112
|
-
|
113
|
-
yaml = Pathname.new('config').join(base)
|
114
|
-
if yaml.exist?
|
115
|
-
YAML.load_file(yaml)
|
116
|
-
else
|
117
|
-
{}
|
118
|
-
end
|
128
|
+
BindingLoader.new(file).load
|
119
129
|
end
|
120
130
|
end
|
121
|
-
end
|
131
|
+
end
|
data/lib/dumbo/range_type.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Dumbo
|
2
2
|
class RangeType < Type
|
3
3
|
attr_accessor :subtype, :subtype_opclass, :collation,
|
4
|
-
:canonical
|
4
|
+
:canonical, :subtype_diff
|
5
5
|
|
6
6
|
def load_attributes
|
7
7
|
super
|
@@ -19,19 +19,16 @@ module Dumbo
|
|
19
19
|
WHERE rngtypid=#{oid}
|
20
20
|
SQL
|
21
21
|
|
22
|
-
result.first.each do |k,v|
|
23
|
-
send("#{k}=",v) rescue nil
|
22
|
+
result.first.each do |k, v|
|
23
|
+
send("#{k}=", v) rescue nil
|
24
24
|
end
|
25
25
|
result.first
|
26
26
|
end
|
27
27
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
28
|
def to_sql
|
32
|
-
attr_str = [:subtype, :subtype_opclass, :collation, :canonical
|
29
|
+
attr_str = [:subtype, :subtype_opclass, :collation, :canonical, :subtype_diff].map do |a|
|
33
30
|
[a, public_send(a)]
|
34
|
-
end.select{|k,v| v && v != '-' }.map{|k,v| "#{k.upcase}=#{v}"}.join(",\n ")
|
31
|
+
end.select { |k, v| v && v != '-' }.map { |k, v| "#{k.upcase}=#{v}" }.join(",\n ")
|
35
32
|
|
36
33
|
<<-SQL.gsub(/^ {6}/, '')
|
37
34
|
CREATE TYPE #{name} AS RANGE (
|
@@ -40,4 +37,4 @@ module Dumbo
|
|
40
37
|
SQL
|
41
38
|
end
|
42
39
|
end
|
43
|
-
end
|
40
|
+
end
|
data/lib/dumbo/test.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
module Dumbo
|
3
|
+
module Test
|
4
|
+
class Fixture
|
5
|
+
include Singleton
|
6
|
+
|
7
|
+
attr_reader :fixtures
|
8
|
+
|
9
|
+
def self.fixtures
|
10
|
+
instance.fixtures
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@fixtures = {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def eval_fixture(file,contents=nil)
|
18
|
+
contents ||= File.read(file.to_s)
|
19
|
+
instance_eval(contents)
|
20
|
+
@fixtures
|
21
|
+
rescue SyntaxError => e
|
22
|
+
syntax_msg = e.message.gsub("#{file}:", 'on line ')
|
23
|
+
raise "Fixture syntax error #{syntax_msg}"
|
24
|
+
rescue ScriptError, RegexpError, NameError, ArgumentError => e
|
25
|
+
e.backtrace[0] = "#{e.backtrace[0]}: #{e.message} (#{e.class})"
|
26
|
+
puts e.backtrace.join("\n ")
|
27
|
+
raise "There was an error in your Fixture," \
|
28
|
+
+ e.message
|
29
|
+
end
|
30
|
+
|
31
|
+
def fixture(name, *args)
|
32
|
+
opts = extract_options!(args)
|
33
|
+
table_name = args.first || name
|
34
|
+
|
35
|
+
@fixtures[name] = [table_name, opts]
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def extract_options!(arr)
|
42
|
+
if arr.last.is_a? Hash
|
43
|
+
arr.pop
|
44
|
+
else
|
45
|
+
{}
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'dumbo/test/fixture'
|
3
|
+
module Dumbo
|
4
|
+
module Test
|
5
|
+
module Helper
|
6
|
+
class Fixture
|
7
|
+
def initialize(table_name, values)
|
8
|
+
@table_name, @values = table_name, values
|
9
|
+
end
|
10
|
+
|
11
|
+
def values
|
12
|
+
(read_fixture.last || {}).merge(@values)
|
13
|
+
end
|
14
|
+
|
15
|
+
def table_name
|
16
|
+
read_fixture.first || @table_name
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def read_fixture
|
22
|
+
@fixtures ||= (Dumbo::Test::Fixture.fixtures[@table_name] || [])
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class SqlLogger < Logger
|
27
|
+
def format_message(severity, timestamp, progname, msg)
|
28
|
+
"#{msg.gsub(/\e\[(\d+)m/, '').gsub(/.*?\(.*?ms\)/,'').gsub(/^ +/,'')};\n"
|
29
|
+
end
|
30
|
+
|
31
|
+
def add(*args)
|
32
|
+
return unless args.first == DEBUG
|
33
|
+
super
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
def install_extension
|
39
|
+
query "CREATE EXTENSION #{Dumbo::Extension.new.name}"
|
40
|
+
end
|
41
|
+
|
42
|
+
def query(sql)
|
43
|
+
ActiveRecord::Base.connection.select_all(sql, 'SQL', [])
|
44
|
+
end
|
45
|
+
|
46
|
+
def create(table_name, values)
|
47
|
+
fix = Fixture.new(table_name, values)
|
48
|
+
table_name, values = fix.table_name, fix.values
|
49
|
+
|
50
|
+
ActiveRecord::Base.connection.insert_fixture(values, table_name)
|
51
|
+
end
|
52
|
+
|
53
|
+
def create_list(num, table_name, &block)
|
54
|
+
num.times do |i|
|
55
|
+
block_val = block.call(i)
|
56
|
+
fix = Fixture.new(table_name, block_val)
|
57
|
+
table_name, values = fix.table_name, fix.values
|
58
|
+
|
59
|
+
ActiveRecord::Base.connection.insert_fixture(values, table_name)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Dumbo
|
2
|
+
module Matchers
|
3
|
+
|
4
|
+
# test a query result against an expectation
|
5
|
+
# e.g.
|
6
|
+
# query("SELECT COUNT(*) FROM users").should match '3'
|
7
|
+
# query("SELECT id, name FROM users").should match ['1', 'foo'] ,['2', 'bar'] ,['3', 'baz']
|
8
|
+
# query("SELECT id, name FROM users").should match_with_header ['id', 'name'], ['1', 'foo'] ,['2', 'bar'] ,['3', 'baz']
|
9
|
+
# query("SELECT id, name FROM users ORDER BY id").should match_ordered ['id', 'name'], ['1', 'foo'] ,['2', 'bar'] ,['3', 'baz']
|
10
|
+
|
11
|
+
def match(*expected)
|
12
|
+
return super if expected.first.is_a? Regexp
|
13
|
+
QueryMatcher.new(flat_expected(expected))
|
14
|
+
end
|
15
|
+
|
16
|
+
def match_with_header(*expected)
|
17
|
+
QueryMatcher.new(flat_expected(expected), header: true)
|
18
|
+
end
|
19
|
+
|
20
|
+
def match_ordered(*expected)
|
21
|
+
QueryMatcher.new(flat_expected(expected), ordered: true )
|
22
|
+
end
|
23
|
+
|
24
|
+
def flat_expected(expected)
|
25
|
+
expected.size == 1 ? expected.first.to_s : expected.map(&:to_s)
|
26
|
+
end
|
27
|
+
|
28
|
+
class QueryMatcher < RSpec::Matchers::BuiltIn::ContainExactly
|
29
|
+
attr_reader :actual, :expected, :options
|
30
|
+
|
31
|
+
def initialize(expected = UNDEFINED, options={})
|
32
|
+
@expected = expected unless UNDEFINED.equal?(expected)
|
33
|
+
@options = options
|
34
|
+
end
|
35
|
+
|
36
|
+
def matches?(actual)
|
37
|
+
@actual = actual
|
38
|
+
convert_actual
|
39
|
+
convert_expected
|
40
|
+
match = match(expected, self.actual)
|
41
|
+
options[:ordered] ? expected == self.actual : match
|
42
|
+
end
|
43
|
+
|
44
|
+
def failure_message_for_should
|
45
|
+
if @missing_items.empty? && @extra_items.empty?
|
46
|
+
message = "actual collection is in the wrong order\n"
|
47
|
+
message += "expected collection contained: #{expected.inspect}\n"
|
48
|
+
message += "actual collection contained: #{actual.inspect}\n"
|
49
|
+
message
|
50
|
+
else
|
51
|
+
super
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
def convert_expected
|
57
|
+
@expected = [expected] unless expected.is_a?(Array)
|
58
|
+
end
|
59
|
+
|
60
|
+
def convert_actual
|
61
|
+
@actual = if options[:header]
|
62
|
+
[actual.columns] + actual.rows
|
63
|
+
else
|
64
|
+
case actual.rows.size
|
65
|
+
when 0
|
66
|
+
[]
|
67
|
+
when 1
|
68
|
+
actual.rows.first
|
69
|
+
else
|
70
|
+
actual.rows
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
RSpec.configure do |config|
|
2
|
+
config.before(:all) do |e|
|
3
|
+
path = self.class.metadata[:file_path]
|
4
|
+
test_file = Pathname.new(path).basename.sub_ext('.sql').sub('_spec','')
|
5
|
+
test_path = test_file.realdirpath File.expand_path('test/sql')
|
6
|
+
FileUtils.touch(test_path)
|
7
|
+
|
8
|
+
ActiveRecord::Base.logger = Dumbo::Test::Helper::SqlLogger.new(test_path)
|
9
|
+
ActiveRecord::Base.logger.level = 0
|
10
|
+
end
|
11
|
+
|
12
|
+
config.before(:suite) do |s|
|
13
|
+
FileUtils.rm_r Dir.glob('test/sql/*'), force: true
|
14
|
+
end
|
15
|
+
|
16
|
+
config.before(:each) do |example|
|
17
|
+
ActiveRecord::Base.logger.debug "-- " + example.metadata[:full_description]
|
18
|
+
ActiveRecord::Base.logger.debug "-- " + example.metadata[:location]
|
19
|
+
end
|
20
|
+
end
|