slacker 1.0.7 → 1.0.8

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
- source "http://rubygems.org"
2
-
3
- # Specify your gem's dependencies in slacker.gemspec
4
- gemspec
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in slacker.gemspec
4
+ gemspec
@@ -1,22 +1,22 @@
1
- # Slacker
2
-
3
- Behavior Driven Development for SQL Server
4
-
5
- Slacker is a transacted RSpec-based framework for developing automated tests for SQL Server 2005 and 2008 programmable objects such as stored procedures, scalar/table functions, triggers, etc.
6
-
7
- # Resources
8
-
9
- * [__Documentation__](https://github.com/vassilvk/slacker/wiki)
10
- * [__Mailing List__](https://groups.google.com/forum/#!forum/ruby_slacker)
11
-
12
-
13
- # LICENSE
14
- (The MIT License)
15
-
16
- Copyright (c) 2011 Vassil Kovatchev
17
-
18
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
19
-
20
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
21
-
22
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1
+ # Slacker
2
+
3
+ Behavior Driven Development for SQL Server
4
+
5
+ Slacker is a transacted RSpec-based framework for developing automated tests for SQL Server 2005 and 2008 programmable objects such as stored procedures, scalar/table functions, triggers, etc.
6
+
7
+ # Resources
8
+
9
+ * [__Documentation__](https://github.com/vassilvk/slacker/wiki)
10
+ * [__Mailing List__](https://groups.google.com/forum/#!forum/ruby_slacker)
11
+
12
+
13
+ # LICENSE
14
+ (The MIT License)
15
+
16
+ Copyright (c) 2012 Vassil Kovatchev
17
+
18
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
19
+
20
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
21
+
22
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile CHANGED
@@ -1,11 +1,11 @@
1
- require 'rubygems'
2
- require 'rake'
3
- require 'rake/clean'
4
- require 'rubygems/package_task'
5
- require 'bundler/gem_tasks'
6
- require 'rspec/core/rake_task'
7
-
8
- RSpec::Core::RakeTask.new do |t|
9
- t.fail_on_error = false
10
- t.pattern = 'spec/**/*.rb'
11
- end
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/clean'
4
+ require 'rubygems/package_task'
5
+ require 'bundler/gem_tasks'
6
+ require 'rspec/core/rake_task'
7
+
8
+ RSpec::Core::RakeTask.new do |t|
9
+ t.fail_on_error = false
10
+ t.pattern = 'spec/**/*.rb'
11
+ end
@@ -1,175 +1,175 @@
1
- require 'bundler/setup'
2
- require "slacker/version"
3
- require 'slacker/application'
4
- require 'slacker/configuration'
5
- require 'slacker/sql'
6
- require 'slacker/formatter'
7
- require 'slacker/sql_preprocessor'
8
- require 'csv'
9
- require 'erb'
10
-
11
- module Slacker
12
- class << self
13
- def application
14
- @application ||= Slacker::Application.new(configuration)
15
- end
16
-
17
- def sql(rspec_ext)
18
- Slacker::Sql.new(configuration.expand_path('sql'), rspec_ext)
19
- end
20
-
21
- def configuration
22
- @configuration ||= Slacker::Configuration.new
23
- end
24
-
25
- def configure
26
- yield configuration
27
- end
28
-
29
- def sql_template_path_stack
30
- if @sql_template_path_stack.nil?
31
- @sql_template_path_stack = []
32
- @sql_template_path_stack.push(configuration.expand_path('sql'))
33
- end
34
- @sql_template_path_stack
35
- end
36
-
37
- # Given a template name produce the path to that template
38
- def get_sql_template_path(template_name)
39
- template_base_dir = template_name[0].chr == '/' ? sql_template_path_stack.first : sql_template_path_stack.last
40
- File.expand_path(template_base_dir + '/' + template_name)
41
- end
42
-
43
- # Render a template file and return the result
44
- def render(template_name, options = {})
45
- template_file_path = get_sql_template_path(template_name)
46
-
47
- if !File.exists?(template_file_path)
48
- raise "File #{template_file_path} does not exist"
49
- end
50
-
51
- begin
52
- sql_template_path_stack.push(File.dirname(template_file_path))
53
- result = render_text(IO.read(template_file_path), options)
54
- rescue => detail
55
- # Report errors in the template
56
- if detail.backtrace[0] =~ /^\(erb\)/
57
- raise "Template error in #{template_name}:\n#{detail.backtrace[0]} : #{detail.message}\n"
58
- else
59
- raise detail
60
- end
61
- ensure
62
- sql_template_path_stack.pop
63
- end
64
-
65
- result
66
- end
67
-
68
- # Render a template test and return the result
69
- def render_text(template_text, options)
70
- ERB.new(template_text, 0, '%<>').result(binding)
71
- end
72
-
73
- def filter_golden_master(golden_master)
74
- golden_master = case golden_master
75
- when String
76
- golden_master =~ /\.csv$/ ? get_csv(golden_master) : golden_master
77
- else
78
- golden_master
79
- end
80
- end
81
-
82
- def sql_from_query_string(query_string, options = {})
83
- case query_string
84
- when /\.sql$/i,/\.erb$/i
85
- #Pass the file through an ERb template engine
86
- render(query_string, options)
87
- else
88
- query_string
89
- end
90
- end
91
-
92
- def get_csv(csv_file_path)
93
- CSV.read(configuration.expand_path("data/#{csv_file_path}"), {:headers => true, :encoding => 'Windows-1252', :header_converters => :symbol})
94
- end
95
-
96
- def hash_array_to_csv(raw_array)
97
- csv_array = []
98
- raw_array.each do |raw_row|
99
- csv_array << CSV::Row.new(raw_row.keys, raw_row.values)
100
- end
101
- CSV::Table.new(csv_array)
102
- end
103
-
104
- def sql_file_from_method_name(base_folder, method_name)
105
- file_name = File.join(base_folder, method_name)
106
-
107
- file_name = case
108
- when File.exists?("#{file_name}.sql") then "#{file_name}.sql"
109
- when File.exists?("#{file_name}.sql.erb") then "#{file_name}.sql.erb"
110
- else nil
111
- end
112
-
113
- file_name.nil? ? nil : file_name.gsub(/#{Regexp.escape(configuration.expand_path('sql'))}/i, '')
114
- end
115
-
116
- def construct_log_name(entry_point, query_string, options)
117
- "#{entry_point} '#{query_string}'" + (options.empty? ? '': ", options = #{options.inspect}")
118
- end
119
-
120
- # Run a SQL query against an example
121
- def query_script(example, sql, log_name=nil)
122
- log_name ||= 'Run SQL Script'
123
-
124
- debuggable_sql = SqlPreprocessor.debuggable_sql(sql)
125
- executable_sql = SqlPreprocessor.executable_sql(sql, example)
126
-
127
- example.metadata[:sql] += ((example.metadata[:sql] == '' ? '' : "\n\n") + "-- #{log_name.split(/\r\n|\n/).join("\n-- ")}\n#{debuggable_sql}")
128
- application.query_script(executable_sql)
129
- end
130
-
131
- def load_csv(example, csv, table_name, log_name = nil)
132
- csv_a = csv.to_a
133
- sql = nil
134
- csv_a.each_with_index do |row, index|
135
- if index == 0
136
- sql = "INSERT INTO #{table_name}(#{row.map{|header| "[#{header}]"}.join(',')})"
137
- else
138
- sql += ("\nSELECT #{row.map{|val| val.nil? ? 'NULL': "'#{val}'"}.join(',')}" + (index < (csv_a.count - 1) ? ' UNION ALL' : ''))
139
- end
140
- end
141
- query_script(example, sql, log_name) unless sql.nil?
142
- end
143
-
144
- def touch_csv(csv_file_or_object, fields, field_generators = {})
145
- csv_obj = csv_file_or_object.kind_of?(String) ? get_csv(csv_file_or_object) : csv_file_or_object
146
- fields = fields.is_a?(Array) ? fields : [fields]
147
-
148
- # Adjust the csv if we are providing more records than there are in the csv
149
- csv_row_count = csv_obj.to_a.count - 1
150
-
151
- (fields.count - csv_row_count).times do |index|
152
- csv_obj << csv_obj[index % csv_row_count].fields
153
- end
154
-
155
- # Add the field generators to the hard-coded fields
156
- field_generators.each do |key, value|
157
- fields.each_with_index do |record, index|
158
- record[key] = value.to_s + index.to_s
159
- end
160
- end
161
-
162
- fields.each_with_index do |record, index|
163
- record.each do |key, value|
164
- csv_obj[index][key.to_sym.downcase] = value
165
- end
166
- end
167
- csv_obj
168
- end
169
-
170
- def mixin_module(module_class)
171
- extend module_class
172
- end
173
-
174
- end
175
- end
1
+ require 'bundler/setup'
2
+ require "slacker/version"
3
+ require 'slacker/application'
4
+ require 'slacker/configuration'
5
+ require 'slacker/sql'
6
+ require 'slacker/formatter'
7
+ require 'slacker/sql_preprocessor'
8
+ require 'csv'
9
+ require 'erb'
10
+
11
+ module Slacker
12
+ class << self
13
+ def application
14
+ @application ||= Slacker::Application.new(configuration)
15
+ end
16
+
17
+ def sql(rspec_ext)
18
+ Slacker::Sql.new(configuration.expand_path('sql'), rspec_ext)
19
+ end
20
+
21
+ def configuration
22
+ @configuration ||= Slacker::Configuration.new
23
+ end
24
+
25
+ def configure
26
+ yield configuration
27
+ end
28
+
29
+ def sql_template_path_stack
30
+ if @sql_template_path_stack.nil?
31
+ @sql_template_path_stack = []
32
+ @sql_template_path_stack.push(configuration.expand_path('sql'))
33
+ end
34
+ @sql_template_path_stack
35
+ end
36
+
37
+ # Given a template name produce the path to that template
38
+ def get_sql_template_path(template_name)
39
+ template_base_dir = template_name[0].chr == '/' ? sql_template_path_stack.first : sql_template_path_stack.last
40
+ File.expand_path(template_base_dir + '/' + template_name)
41
+ end
42
+
43
+ # Render a template file and return the result
44
+ def render(template_name, options = {})
45
+ template_file_path = get_sql_template_path(template_name)
46
+
47
+ if !File.exists?(template_file_path)
48
+ raise "File #{template_file_path} does not exist"
49
+ end
50
+
51
+ begin
52
+ sql_template_path_stack.push(File.dirname(template_file_path))
53
+ result = render_text(IO.read(template_file_path), options)
54
+ rescue => detail
55
+ # Report errors in the template
56
+ if detail.backtrace[0] =~ /^\(erb\)/
57
+ raise "Template error in #{template_name}:\n#{detail.backtrace[0]} : #{detail.message}\n"
58
+ else
59
+ raise detail
60
+ end
61
+ ensure
62
+ sql_template_path_stack.pop
63
+ end
64
+
65
+ result
66
+ end
67
+
68
+ # Render a template test and return the result
69
+ def render_text(template_text, options)
70
+ ERB.new(template_text, 0, '%<>').result(binding)
71
+ end
72
+
73
+ def filter_golden_master(golden_master)
74
+ golden_master = case golden_master
75
+ when String
76
+ golden_master =~ /\.csv$/ ? get_csv(golden_master) : golden_master
77
+ else
78
+ golden_master
79
+ end
80
+ end
81
+
82
+ def sql_from_query_string(query_string, options = {})
83
+ case query_string
84
+ when /\.sql$/i,/\.erb$/i
85
+ #Pass the file through an ERb template engine
86
+ render(query_string, options)
87
+ else
88
+ query_string
89
+ end
90
+ end
91
+
92
+ def get_csv(csv_file_path)
93
+ CSV.read(configuration.expand_path("data/#{csv_file_path}"), {:headers => true, :encoding => 'Windows-1252', :header_converters => :symbol})
94
+ end
95
+
96
+ def hash_array_to_csv(raw_array)
97
+ csv_array = []
98
+ raw_array.each do |raw_row|
99
+ csv_array << CSV::Row.new(raw_row.keys, raw_row.values)
100
+ end
101
+ CSV::Table.new(csv_array)
102
+ end
103
+
104
+ def sql_file_from_method_name(base_folder, method_name)
105
+ file_name = File.join(base_folder, method_name)
106
+
107
+ file_name = case
108
+ when File.exists?("#{file_name}.sql") then "#{file_name}.sql"
109
+ when File.exists?("#{file_name}.sql.erb") then "#{file_name}.sql.erb"
110
+ else nil
111
+ end
112
+
113
+ file_name.nil? ? nil : file_name.gsub(/#{Regexp.escape(configuration.expand_path('sql'))}/i, '')
114
+ end
115
+
116
+ def construct_log_name(entry_point, query_string, options)
117
+ "#{entry_point} '#{query_string}'" + (options.empty? ? '': ", options = #{options.inspect}")
118
+ end
119
+
120
+ # Run a SQL query against an example
121
+ def query_script(example, sql, log_name=nil)
122
+ log_name ||= 'Run SQL Script'
123
+
124
+ debuggable_sql = SqlPreprocessor.debuggable_sql(sql)
125
+ executable_sql = SqlPreprocessor.executable_sql(sql, example)
126
+
127
+ example.metadata[:sql] += ((example.metadata[:sql] == '' ? '' : "\n\n") + "-- #{log_name.split(/\r\n|\n/).join("\n-- ")}\n#{debuggable_sql}")
128
+ application.query_script(executable_sql)
129
+ end
130
+
131
+ def load_csv(example, csv, table_name, log_name = nil)
132
+ csv_a = csv.to_a
133
+ sql = nil
134
+ csv_a.each_with_index do |row, index|
135
+ if index == 0
136
+ sql = "INSERT INTO #{table_name}(#{row.map{|header| "[#{header}]"}.join(',')})"
137
+ else
138
+ sql += ("\nSELECT #{row.map{|val| val.nil? ? 'NULL': "'#{val}'"}.join(',')}" + (index < (csv_a.count - 1) ? ' UNION ALL' : ''))
139
+ end
140
+ end
141
+ query_script(example, sql, log_name) unless sql.nil?
142
+ end
143
+
144
+ def touch_csv(csv_file_or_object, fields, field_generators = {})
145
+ csv_obj = csv_file_or_object.kind_of?(String) ? get_csv(csv_file_or_object) : csv_file_or_object
146
+ fields = fields.is_a?(Array) ? fields : [fields]
147
+
148
+ # Adjust the csv if we are providing more records than there are in the csv
149
+ csv_row_count = csv_obj.to_a.count - 1
150
+
151
+ (fields.count - csv_row_count).times do |index|
152
+ csv_obj << csv_obj[index % csv_row_count].fields
153
+ end
154
+
155
+ # Add the field generators to the hard-coded fields
156
+ field_generators.each do |key, value|
157
+ fields.each_with_index do |record, index|
158
+ record[key] = value.to_s + index.to_s
159
+ end
160
+ end
161
+
162
+ fields.each_with_index do |record, index|
163
+ record.each do |key, value|
164
+ csv_obj[index][key.to_sym.downcase] = value
165
+ end
166
+ end
167
+ csv_obj
168
+ end
169
+
170
+ def mixin_module(module_class)
171
+ extend module_class
172
+ end
173
+
174
+ end
175
+ end
@@ -1,207 +1,207 @@
1
- require 'logger'
2
- require 'rspec/core'
3
- require 'slacker/rspec_monkey'
4
- require 'slacker/rspec_ext'
5
- require 'slacker/string_helper'
6
- require 'odbc'
7
-
8
- module Slacker
9
- class Application
10
- attr_reader :target_folder_structure
11
-
12
- SQL_OPTIONS = <<EOF
13
- set textsize 2147483647;
14
- set language us_english;
15
- set dateformat mdy;
16
- set datefirst 7;
17
- set lock_timeout -1;
18
- set quoted_identifier on;
19
- set arithabort on;
20
- set ansi_null_dflt_on on;
21
- set ansi_warnings on;
22
- set ansi_padding on;
23
- set ansi_nulls on;
24
- set concat_null_yields_null on;
25
- EOF
26
-
27
- def initialize(configuration)
28
- @configuration = configuration
29
- @target_folder_structure = ['data', 'debug', 'debug/passed_examples', 'debug/failed_examples', 'sql', 'spec', 'lib', 'lib/helpers']
30
- @error_message = ''
31
- @database = ODBC::Database.new
32
- end
33
-
34
- def print_connection_message
35
- puts "#{@configuration.db_name} (#{@configuration.db_server})" if @configuration.console_enabled
36
- end
37
-
38
- # Customize RSpec and run it
39
- def run
40
- begin
41
- error = catch :error_exit do
42
- print_connection_message
43
- test_folder_structure
44
- cleanup_folders
45
- configure
46
- run_rspec
47
- false #Return false to error
48
- end
49
- ensure
50
- cleanup_after_run
51
- end
52
-
53
- if @configuration.console_enabled
54
- puts @error_message if error
55
- else
56
- raise @error_message if error
57
- end
58
- end
59
-
60
- def run_rspec
61
- RSpec::Core::Runner.disable_autorun!
62
-
63
- RSpec::Core::Runner.run(@configuration.rspec_args,
64
- @configuration.error_stream,
65
- @configuration.output_stream)
66
- end
67
-
68
- # Configure Slacker
69
- def configure
70
- configure_db
71
- configure_rspec
72
- configure_misc
73
- end
74
-
75
- def cleanup_after_run
76
- @database.disconnect if (@database && @database.connected?)
77
- end
78
-
79
- def cleanup_folders
80
- cleanup_folder('debug/passed_examples')
81
- cleanup_folder('debug/failed_examples')
82
- end
83
-
84
- def cleanup_folder(folder)
85
- folder_path = get_path(folder)
86
- Dir.new(folder_path).each{|file_name| File.delete("#{folder_path}/#{file_name}") if File.file?("#{folder_path}/#{file_name}")}
87
- end
88
-
89
- # Get a path relative to the current path
90
- def get_path(path)
91
- @configuration.expand_path(path)
92
- end
93
-
94
- def configure_misc
95
- # Add the lib folder to the load path
96
- $:.push get_path('lib')
97
- # Mixin the helper modules
98
- mixin_helpers
99
- end
100
-
101
- # Mix in the helper modules
102
- def mixin_helpers
103
- helpers_dir = get_path('lib/helpers')
104
- $:.push helpers_dir
105
- Dir.new(helpers_dir).each do |file_name|
106
- if file_name =~ /\.rb$/
107
- require file_name
108
- module_class = Slacker::StringHelper.constantize(Slacker::StringHelper.camelize(file_name.gsub(/\.rb$/,'')))
109
- RSpec.configure do |config|
110
- config.include(module_class )
111
- end
112
- Slacker.mixin_module(module_class )
113
- end
114
- end
115
- end
116
-
117
- # Configure database connection
118
- def configure_db
119
- drv = ODBC::Driver.new
120
- drv.name = 'Driver1'
121
- drv.attrs.tap do |a|
122
- a['Driver'] = '{SQL Server}'
123
- a['Server']= @configuration.db_server
124
- a['Database']= @configuration.db_name
125
- a['Uid'] = @configuration.db_user
126
- a['Pwd'] = @configuration.db_password
127
- a['TDS_Version'] = '7.0' #Used by the linux driver
128
- end
129
-
130
- begin
131
- @database.drvconnect(drv)
132
- rescue ODBC::Error => e
133
- throw_error("#{e.class}: #{e.message}")
134
- end
135
- end
136
-
137
- # Run a script against the currently configured database
138
- def query_script(sql)
139
- results = []
140
- begin
141
- st = @database.run(sql)
142
- begin
143
- if st.ncols > 0
144
- rows = []
145
- st.each_hash(false, true){|row| rows << row}
146
- results << rows
147
- end
148
- end while(st.more_results)
149
- ensure
150
- st.drop unless st.nil?
151
- end
152
- results.count > 1 ? results : results.first
153
- end
154
-
155
- # Customize RSpec
156
- def configure_rspec
157
- before_proc = lambda do |example|
158
- # Initialize the example's SQL
159
- example.metadata[:sql] = ''
160
- Slacker.query_script(example, 'begin transaction;', 'Initiate the example script')
161
- Slacker.query_script(example, SQL_OPTIONS, 'Set default options')
162
- end
163
-
164
- after_proc = lambda do |example|
165
- Slacker.query_script(example, 'rollback transaction;', 'Rollback the changes made by the example script')
166
- end
167
-
168
- # Reset RSpec through a monkey-patched method
169
- RSpec.slacker_reset
170
-
171
- RSpec.configure do |config|
172
- # Global "before" hooks to begin a transaction
173
- config.before(:each) do
174
- before_proc.call(example)
175
- end
176
-
177
- # Global "after" hooks to rollback a transaction
178
- config.after(:each) do
179
- after_proc.call(example)
180
- end
181
-
182
- # Slacker's RSpec extension module
183
- config.include(Slacker::RSpecExt)
184
- config.extend(Slacker::RSpecExt)
185
-
186
- config.output_stream = @configuration.output_stream
187
- config.error_stream = @configuration.error_stream
188
-
189
- config.formatters << @configuration.formatter unless @configuration.formatter.nil?
190
- end
191
- end
192
-
193
- # Tests the current folder's structure
194
- def test_folder_structure()
195
- target_folder_structure.each do |dir|
196
- if !File.directory?(get_path(dir))
197
- throw_error("Cannot find directory \"#{get_path(dir)}\"")
198
- end
199
- end
200
- end
201
-
202
- def throw_error(msg)
203
- @error_message = msg
204
- throw :error_exit, true
205
- end
206
- end
1
+ require 'logger'
2
+ require 'rspec/core'
3
+ require 'slacker/rspec_monkey'
4
+ require 'slacker/rspec_ext'
5
+ require 'slacker/string_helper'
6
+ require 'odbc'
7
+
8
+ module Slacker
9
+ class Application
10
+ attr_reader :target_folder_structure
11
+
12
+ SQL_OPTIONS = <<EOF
13
+ set textsize 2147483647;
14
+ set language us_english;
15
+ set dateformat mdy;
16
+ set datefirst 7;
17
+ set lock_timeout -1;
18
+ set quoted_identifier on;
19
+ set arithabort on;
20
+ set ansi_null_dflt_on on;
21
+ set ansi_warnings on;
22
+ set ansi_padding on;
23
+ set ansi_nulls on;
24
+ set concat_null_yields_null on;
25
+ EOF
26
+
27
+ def initialize(configuration)
28
+ @configuration = configuration
29
+ @target_folder_structure = ['data', 'debug', 'debug/passed_examples', 'debug/failed_examples', 'sql', 'spec', 'lib', 'lib/helpers']
30
+ @error_message = ''
31
+ @database = ODBC::Database.new
32
+ end
33
+
34
+ def print_connection_message
35
+ puts "#{@configuration.db_name} (#{@configuration.db_server})" if @configuration.console_enabled
36
+ end
37
+
38
+ # Customize RSpec and run it
39
+ def run
40
+ begin
41
+ error = catch :error_exit do
42
+ print_connection_message
43
+ test_folder_structure
44
+ cleanup_folders
45
+ configure
46
+ run_rspec
47
+ false #Return false to error
48
+ end
49
+ ensure
50
+ cleanup_after_run
51
+ end
52
+
53
+ if @configuration.console_enabled
54
+ puts @error_message if error
55
+ else
56
+ raise @error_message if error
57
+ end
58
+ end
59
+
60
+ def run_rspec
61
+ RSpec::Core::Runner.disable_autorun!
62
+
63
+ RSpec::Core::Runner.run(@configuration.rspec_args,
64
+ @configuration.error_stream,
65
+ @configuration.output_stream)
66
+ end
67
+
68
+ # Configure Slacker
69
+ def configure
70
+ configure_db
71
+ configure_rspec
72
+ configure_misc
73
+ end
74
+
75
+ def cleanup_after_run
76
+ @database.disconnect if (@database && @database.connected?)
77
+ end
78
+
79
+ def cleanup_folders
80
+ cleanup_folder('debug/passed_examples')
81
+ cleanup_folder('debug/failed_examples')
82
+ end
83
+
84
+ def cleanup_folder(folder)
85
+ folder_path = get_path(folder)
86
+ Dir.new(folder_path).each{|file_name| File.delete("#{folder_path}/#{file_name}") if File.file?("#{folder_path}/#{file_name}")}
87
+ end
88
+
89
+ # Get a path relative to the current path
90
+ def get_path(path)
91
+ @configuration.expand_path(path)
92
+ end
93
+
94
+ def configure_misc
95
+ # Add the lib folder to the load path
96
+ $:.push get_path('lib')
97
+ # Mixin the helper modules
98
+ mixin_helpers
99
+ end
100
+
101
+ # Mix in the helper modules
102
+ def mixin_helpers
103
+ helpers_dir = get_path('lib/helpers')
104
+ $:.push helpers_dir
105
+ Dir.new(helpers_dir).each do |file_name|
106
+ if file_name =~ /\.rb$/
107
+ require file_name
108
+ module_class = Slacker::StringHelper.constantize(Slacker::StringHelper.camelize(file_name.gsub(/\.rb$/,'')))
109
+ RSpec.configure do |config|
110
+ config.include(module_class)
111
+ end
112
+ Slacker.mixin_module(module_class)
113
+ end
114
+ end
115
+ end
116
+
117
+ # Configure database connection
118
+ def configure_db
119
+ drv = ODBC::Driver.new
120
+ drv.name = 'Driver1'
121
+ drv.attrs.tap do |a|
122
+ a['Driver'] = '{SQL Server}'
123
+ a['Server']= @configuration.db_server
124
+ a['Database']= @configuration.db_name
125
+ a['Uid'] = @configuration.db_user
126
+ a['Pwd'] = @configuration.db_password
127
+ a['TDS_Version'] = '7.0' #Used by the linux driver
128
+ end
129
+
130
+ begin
131
+ @database.drvconnect(drv)
132
+ rescue ODBC::Error => e
133
+ throw_error("#{e.class}: #{e.message}")
134
+ end
135
+ end
136
+
137
+ # Run a script against the currently configured database
138
+ def query_script(sql)
139
+ results = []
140
+ begin
141
+ st = @database.run(sql)
142
+ begin
143
+ if st.ncols > 0
144
+ rows = []
145
+ st.each_hash(false, true){|row| rows << row}
146
+ results << rows
147
+ end
148
+ end while(st.more_results)
149
+ ensure
150
+ st.drop unless st.nil?
151
+ end
152
+ results.count > 1 ? results : results.first
153
+ end
154
+
155
+ # Customize RSpec
156
+ def configure_rspec
157
+ before_proc = lambda do |example|
158
+ # Initialize the example's SQL
159
+ example.metadata[:sql] = ''
160
+ Slacker.query_script(example, 'begin transaction;', 'Initiate the example script')
161
+ Slacker.query_script(example, SQL_OPTIONS, 'Set default options')
162
+ end
163
+
164
+ after_proc = lambda do |example|
165
+ Slacker.query_script(example, 'rollback transaction;', 'Rollback the changes made by the example script')
166
+ end
167
+
168
+ # Reset RSpec through a monkey-patched method
169
+ RSpec.slacker_reset
170
+
171
+ RSpec.configure do |config|
172
+ # Global "before" hooks to begin a transaction
173
+ config.before(:each) do
174
+ before_proc.call(example)
175
+ end
176
+
177
+ # Global "after" hooks to rollback a transaction
178
+ config.after(:each) do
179
+ after_proc.call(example)
180
+ end
181
+
182
+ # Slacker's RSpec extension module
183
+ config.include(Slacker::RSpecExt)
184
+ config.extend(Slacker::RSpecExt)
185
+
186
+ config.output_stream = @configuration.output_stream
187
+ config.error_stream = @configuration.error_stream
188
+
189
+ config.formatters << @configuration.formatter unless @configuration.formatter.nil?
190
+ end
191
+ end
192
+
193
+ # Tests the current folder's structure
194
+ def test_folder_structure()
195
+ target_folder_structure.each do |dir|
196
+ if !File.directory?(get_path(dir))
197
+ throw_error("Cannot find directory \"#{get_path(dir)}\"")
198
+ end
199
+ end
200
+ end
201
+
202
+ def throw_error(msg)
203
+ @error_message = msg
204
+ throw :error_exit, true
205
+ end
206
+ end
207
207
  end