dumbo 0.0.1 → 0.0.3

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.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/Gemfile +1 -1
  4. data/bin/dumbo +65 -24
  5. data/config/boot.rb +2 -3
  6. data/dumbo.gemspec +1 -2
  7. data/lib/dumbo.rb +17 -16
  8. data/lib/dumbo/aggregate.rb +3 -5
  9. data/lib/dumbo/base_type.rb +17 -17
  10. data/lib/dumbo/binding_loader.rb +50 -0
  11. data/lib/dumbo/cast.rb +5 -5
  12. data/lib/dumbo/composite_type.rb +4 -4
  13. data/lib/dumbo/db_task.rb +6 -5
  14. data/lib/dumbo/dependency_resolver.rb +17 -18
  15. data/lib/dumbo/enum_type.rb +3 -4
  16. data/lib/dumbo/extension.rb +64 -20
  17. data/lib/dumbo/extension_migrator.rb +11 -11
  18. data/lib/dumbo/extension_version.rb +29 -11
  19. data/lib/dumbo/function.rb +21 -21
  20. data/lib/dumbo/operator.rb +4 -6
  21. data/lib/dumbo/pg_object.rb +17 -21
  22. data/lib/dumbo/rake_task.rb +63 -53
  23. data/lib/dumbo/range_type.rb +6 -9
  24. data/lib/dumbo/test.rb +8 -0
  25. data/lib/dumbo/test/fixture.rb +51 -0
  26. data/lib/dumbo/test/helper.rb +64 -0
  27. data/lib/dumbo/test/matchers.rb +76 -0
  28. data/lib/dumbo/test/regression_helper.rb +20 -0
  29. data/lib/dumbo/test/silence_unknown_oid.rb +12 -0
  30. data/lib/dumbo/type.rb +3 -4
  31. data/lib/dumbo/version.rb +1 -1
  32. data/spec/aggregate_spec.rb +9 -10
  33. data/spec/cast_spec.rb +5 -5
  34. data/spec/{Makefile → dumbo_sample/Makefile} +4 -0
  35. data/spec/{dumbo_sample--0.0.1.sql → dumbo_sample/dumbo_sample--0.0.1.sql} +0 -0
  36. data/spec/{dumbo_sample--0.0.2.sql → dumbo_sample/dumbo_sample--0.0.2.sql} +0 -0
  37. data/spec/dumbo_sample/dumbo_sample--0.0.3.sql +7 -0
  38. data/spec/dumbo_sample/dumbo_sample--0.0.4.sql +13 -0
  39. data/spec/{dumbo_sample.control → dumbo_sample/dumbo_sample.control} +0 -0
  40. data/spec/dumbo_sample/src/dumbo_sample.c +13 -0
  41. data/spec/dumbo_sample/src/dumbo_sample.h +17 -0
  42. data/spec/extension_migrator_spec.rb +12 -11
  43. data/spec/extension_spec.rb +41 -12
  44. data/spec/extension_version_spec.rb +27 -0
  45. data/spec/operator_spec.rb +6 -7
  46. data/spec/spec_helper.rb +15 -9
  47. data/spec/support/extension_helper.rb +31 -0
  48. data/spec/support/silence_unknown_oid.rb +12 -0
  49. data/spec/type_spec.rb +59 -55
  50. data/template/Rakefile +6 -6
  51. data/template/spec/sample_spec.rb.erb +1 -1
  52. metadata +33 -31
  53. data/config/database.yml +0 -31
  54. data/lib/tasks/db.rake +0 -52
  55. data/lib/tasks/dumbo.rake +0 -23
  56. data/spec/support/sql_helper.rb +0 -23
@@ -1,24 +1,26 @@
1
- require "rake"
1
+ require 'rake'
2
2
  require 'rake/tasklib'
3
- require "erubis"
4
- require "pathname"
3
+ require 'erubis'
4
+ require 'pathname'
5
5
  require 'yaml'
6
6
  require 'logger'
7
7
  require 'active_record'
8
- require "dumbo/extension"
9
- require "dumbo/dependency_resolver"
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 :all => ["#{extension}--#{version}.sql", :install]
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 "#{extension}--#{version}.sql" => file_list do |t|
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 = Dumbo::Extension.new.available_versions.last(2).map(&:to_s)
51
+ old_version, new_version = Extension.versions.last(2).map(&:to_s)
52
+
39
53
  if new_version
40
- Dumbo::ExtensionMigrator.new(Dumbo::Extension.new.name, old_version, new_version).create
54
+ ExtensionMigrator.new(Extension.name, old_version, new_version)
55
+ .create
41
56
  end
42
57
  end
43
58
 
44
- desc 'release a new version'
59
+ desc 'upgrate .control file to a new version'
45
60
  task :new_version, :level do |t, args|
46
- args.with_defaults(:level => 'patch')
47
- v = version_bump args[:level]
48
- set_version v
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
- def version_bump(level='patch')
62
- levels = {'patch' => 2, 'minor' => 1, 'major' => 0}
63
- parts = version.split('.').map(&:to_i)
64
- l = levels[level]
65
- parts[l] += 1
66
- (l+1..2).each {|l| parts[l]=0}
67
-
68
- parts.join('.')
69
- end
70
-
71
- def version
72
- Dumbo::Extension.new.version
73
- end
74
-
75
- def extension
76
- Dumbo::Extension.new.name
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 available_versions
80
- Dumbo::Extension.new.name.available_versions
95
+ def new_version(level = :patch)
96
+ ExtensionVersion.new_from_string(Extension.version).bump(level).to_s
81
97
  end
82
98
 
83
- # source sql file list
99
+ # source sql file list
84
100
  def file_list
85
- Dumbo::DependencyResolver.new(Dir.glob("sql/**/*.{sql,erb}")).resolve
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 =~ Dumbo::DependencyResolver.depends_pattern
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
- base = Pathname.new(file).sub_ext('.yml').basename
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
@@ -1,7 +1,7 @@
1
1
  module Dumbo
2
2
  class RangeType < Type
3
3
  attr_accessor :subtype, :subtype_opclass, :collation,
4
- :canonical,:subtype_diff
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,:subtype_diff].map do |a|
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
@@ -0,0 +1,8 @@
1
+ require 'dumbo/test/fixture'
2
+ require 'dumbo/test/helper'
3
+ require 'dumbo/test/matchers'
4
+
5
+ module Dumbo
6
+ module Test
7
+ end
8
+ end
@@ -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
@@ -0,0 +1,12 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ class PostgreSQLAdapter
4
+ module DatabaseStatements
5
+ def warn(msg)
6
+ return if msg =~ /^unknown OID/
7
+ super
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end