slacker 1.0.14 → 1.0.15
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/Gemfile +4 -4
- data/README.markdown +22 -22
- data/Rakefile +11 -11
- data/bin/slacker +32 -32
- data/bin/slacker_new +34 -34
- data/lib/slacker.rb +180 -175
- data/lib/slacker/application.rb +224 -214
- data/lib/slacker/command_line_formatter.rb +61 -61
- data/lib/slacker/configuration.rb +59 -59
- data/lib/slacker/formatter.rb +14 -14
- data/lib/slacker/query_result_matcher.rb +178 -178
- data/lib/slacker/rspec_ext.rb +57 -57
- data/lib/slacker/rspec_monkey.rb +7 -7
- data/lib/slacker/sql.rb +39 -39
- data/lib/slacker/sql_preprocessor.rb +23 -23
- data/lib/slacker/string_helper.rb +16 -16
- data/lib/slacker/version.rb +3 -3
- data/lib/slacker_new/project/data/sample_1/my_table_expected_power_results.csv +11 -11
- data/lib/slacker_new/project/data/sample_1/my_table_initial_data.csv +11 -11
- data/lib/slacker_new/project/data/sample_1/numbers_expected_output.csv +3 -3
- data/lib/slacker_new/project/database.yml +9 -9
- data/lib/slacker_new/project/lib/helpers/my_helper.rb +1 -1
- data/lib/slacker_new/project/spec/sample_1.rb +66 -66
- data/lib/slacker_new/project/sql/sample_1/my_table_on_power.sql.erb +1 -1
- data/lib/slacker_new/project/sql/sample_1/play_with_numbers.sql.erb +16 -16
- data/lib/slacker_new/project/sql/sample_1/sysobjects_with_params.sql.erb +1 -1
- data/slacker.gemspec +27 -27
- data/spec/application_spec.rb +13 -13
- data/spec/query_result_matcher_spec.rb +268 -268
- data/spec/rspec_ext_spec.rb +87 -87
- data/spec/slacker_spec.rb +59 -59
- data/spec/spec_helper.rb +19 -9
- data/spec/test_files/matcher/test_1.csv +3 -3
- data/spec/test_files/test_slacker_project/data/test_1.csv +3 -3
- data/spec/test_files/test_slacker_project/sql/nest/nested_1.sql.erb +1 -1
- data/spec/test_files/test_slacker_project/sql/no_params.sql.erb +2 -2
- data/spec/test_files/test_slacker_project/sql/params.sql.erb +1 -1
- metadata +24 -24
data/lib/slacker/rspec_ext.rb
CHANGED
@@ -1,57 +1,57 @@
|
|
1
|
-
require 'slacker'
|
2
|
-
require 'slacker/query_result_matcher'
|
3
|
-
require 'csv'
|
4
|
-
|
5
|
-
module Slacker
|
6
|
-
module RSpecExt
|
7
|
-
|
8
|
-
# Execute a SQL query or the contents of a query template query file
|
9
|
-
# In case of a query file, it passes the options to the template
|
10
|
-
def query(query_string, options = {}, log_name = nil)
|
11
|
-
log_name ||= Slacker.construct_log_name('query', query_string, options)
|
12
|
-
sql = Slacker.sql_from_query_string(query_string, options)
|
13
|
-
@results = Slacker.query_script(example, sql, log_name)
|
14
|
-
if block_given?
|
15
|
-
yield @results
|
16
|
-
end
|
17
|
-
@results
|
18
|
-
end
|
19
|
-
|
20
|
-
# Exposes a reference to the sql object, which represents the contents of the SQL folder
|
21
|
-
def sql
|
22
|
-
Slacker.sql(self)
|
23
|
-
end
|
24
|
-
|
25
|
-
# Get a matcher which will compare the query results to a golden master
|
26
|
-
def match(golden_master)
|
27
|
-
QueryResultMatcher.new(Slacker.filter_golden_master(golden_master))
|
28
|
-
end
|
29
|
-
|
30
|
-
# Loads a CSV file and returns a reference to it
|
31
|
-
# The file is relative to the data folder of the Slacker project
|
32
|
-
def csv(csv_file)
|
33
|
-
Slacker.get_csv(csv_file)
|
34
|
-
end
|
35
|
-
|
36
|
-
# Generate CSV::Table objects with an arbitrary number of records populated with data based on a template CSV file
|
37
|
-
def touch_csv(csv_file_or_object, fields, field_generators = {})
|
38
|
-
Slacker.touch_csv(csv_file_or_object, fields, field_generators)
|
39
|
-
end
|
40
|
-
|
41
|
-
# Load a CSV file directly into a table in the target database
|
42
|
-
def load_csv(csv_file_or_object, table_name, log_name = nil)
|
43
|
-
log_name ||= "load_csv '#{csv_file_or_object.kind_of?(CSV::Table) ? 'CSV Object' : csv_file_or_object }', 'table_name'"
|
44
|
-
csv_object = case csv_file_or_object
|
45
|
-
when String then Slacker.get_csv(csv_file_or_object)
|
46
|
-
when CSV::Table then csv_file_or_object
|
47
|
-
when Array then Slacker.hash_array_to_csv(csv_file_or_object)
|
48
|
-
end
|
49
|
-
|
50
|
-
Slacker.load_csv(example, csv_object, table_name, log_name)
|
51
|
-
end
|
52
|
-
|
53
|
-
def yes?(val)
|
54
|
-
val != nil && val.downcase == 'yes'
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
1
|
+
require 'slacker'
|
2
|
+
require 'slacker/query_result_matcher'
|
3
|
+
require 'csv'
|
4
|
+
|
5
|
+
module Slacker
|
6
|
+
module RSpecExt
|
7
|
+
|
8
|
+
# Execute a SQL query or the contents of a query template query file
|
9
|
+
# In case of a query file, it passes the options to the template
|
10
|
+
def query(query_string, options = {}, log_name = nil)
|
11
|
+
log_name ||= Slacker.construct_log_name('query', query_string, options)
|
12
|
+
sql = Slacker.sql_from_query_string(query_string, options)
|
13
|
+
@results = Slacker.query_script(example, sql, log_name)
|
14
|
+
if block_given?
|
15
|
+
yield @results
|
16
|
+
end
|
17
|
+
@results
|
18
|
+
end
|
19
|
+
|
20
|
+
# Exposes a reference to the sql object, which represents the contents of the SQL folder
|
21
|
+
def sql
|
22
|
+
Slacker.sql(self)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Get a matcher which will compare the query results to a golden master
|
26
|
+
def match(golden_master)
|
27
|
+
QueryResultMatcher.new(Slacker.filter_golden_master(golden_master))
|
28
|
+
end
|
29
|
+
|
30
|
+
# Loads a CSV file and returns a reference to it
|
31
|
+
# The file is relative to the data folder of the Slacker project
|
32
|
+
def csv(csv_file)
|
33
|
+
Slacker.get_csv(csv_file)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Generate CSV::Table objects with an arbitrary number of records populated with data based on a template CSV file
|
37
|
+
def touch_csv(csv_file_or_object, fields, field_generators = {})
|
38
|
+
Slacker.touch_csv(csv_file_or_object, fields, field_generators)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Load a CSV file directly into a table in the target database
|
42
|
+
def load_csv(csv_file_or_object, table_name, log_name = nil)
|
43
|
+
log_name ||= "load_csv '#{csv_file_or_object.kind_of?(CSV::Table) ? 'CSV Object' : csv_file_or_object }', 'table_name'"
|
44
|
+
csv_object = case csv_file_or_object
|
45
|
+
when String then Slacker.get_csv(csv_file_or_object)
|
46
|
+
when CSV::Table then csv_file_or_object
|
47
|
+
when Array then Slacker.hash_array_to_csv(csv_file_or_object)
|
48
|
+
end
|
49
|
+
|
50
|
+
Slacker.load_csv(example, csv_object, table_name, log_name)
|
51
|
+
end
|
52
|
+
|
53
|
+
def yes?(val)
|
54
|
+
val != nil && val.downcase == 'yes'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/slacker/rspec_monkey.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
# Monkeypatch a method to reset RSpec
|
2
|
-
module RSpec
|
3
|
-
def self.slacker_reset
|
4
|
-
@world = nil
|
5
|
-
@configuration = nil
|
6
|
-
end
|
7
|
-
end
|
1
|
+
# Monkeypatch a method to reset RSpec
|
2
|
+
module RSpec
|
3
|
+
def self.slacker_reset
|
4
|
+
@world = nil
|
5
|
+
@configuration = nil
|
6
|
+
end
|
7
|
+
end
|
data/lib/slacker/sql.rb
CHANGED
@@ -1,39 +1,39 @@
|
|
1
|
-
require 'slacker'
|
2
|
-
require 'slacker/rspec_ext'
|
3
|
-
|
4
|
-
module Slacker
|
5
|
-
class Sql < BasicObject
|
6
|
-
attr_accessor :base_folder, :rspec_ext
|
7
|
-
|
8
|
-
def initialize(base_folder, rspec_ext)
|
9
|
-
@base_folder = base_folder
|
10
|
-
@rspec_ext = rspec_ext
|
11
|
-
end
|
12
|
-
|
13
|
-
def method_missing(method_name, *params, &block)
|
14
|
-
::Kernel.raise "Slacker::Sql.rspec_ext not initialized" if rspec_ext.nil?
|
15
|
-
::Kernel.raise "Missing folder #{base_folder}" if !::File.directory?(base_folder)
|
16
|
-
|
17
|
-
method_name = method_name.to_s
|
18
|
-
|
19
|
-
if ::File.directory?(::File.join(base_folder, method_name))
|
20
|
-
::Slacker::Sql.new(::File.join(base_folder, method_name), rspec_ext)
|
21
|
-
else
|
22
|
-
sql_file = ::Slacker.sql_file_from_method_name(base_folder, method_name)
|
23
|
-
case sql_file
|
24
|
-
when nil
|
25
|
-
::Kernel.raise "No SQL file found corresponding to method '#{method_name}' in folder #{base_folder}"
|
26
|
-
else
|
27
|
-
rspec_ext.query sql_file, *params, &block
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def respond_to?(method_name)
|
33
|
-
method_name = method_name.to_s
|
34
|
-
::Kernel.raise "Slacker::Sql.rspec_ext not initialized" if rspec_ext.nil?
|
35
|
-
::File.directory?(::File.join(base_folder, method_name)) ||
|
36
|
-
!(::Slacker.sql_file_from_method_name(base_folder, method_name).nil?)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
1
|
+
require 'slacker'
|
2
|
+
require 'slacker/rspec_ext'
|
3
|
+
|
4
|
+
module Slacker
|
5
|
+
class Sql < BasicObject
|
6
|
+
attr_accessor :base_folder, :rspec_ext
|
7
|
+
|
8
|
+
def initialize(base_folder, rspec_ext)
|
9
|
+
@base_folder = base_folder
|
10
|
+
@rspec_ext = rspec_ext
|
11
|
+
end
|
12
|
+
|
13
|
+
def method_missing(method_name, *params, &block)
|
14
|
+
::Kernel.raise "Slacker::Sql.rspec_ext not initialized" if rspec_ext.nil?
|
15
|
+
::Kernel.raise "Missing folder #{base_folder}" if !::File.directory?(base_folder)
|
16
|
+
|
17
|
+
method_name = method_name.to_s
|
18
|
+
|
19
|
+
if ::File.directory?(::File.join(base_folder, method_name))
|
20
|
+
::Slacker::Sql.new(::File.join(base_folder, method_name), rspec_ext)
|
21
|
+
else
|
22
|
+
sql_file = ::Slacker.sql_file_from_method_name(base_folder, method_name)
|
23
|
+
case sql_file
|
24
|
+
when nil
|
25
|
+
::Kernel.raise "No SQL file found corresponding to method '#{method_name}' in folder #{base_folder}"
|
26
|
+
else
|
27
|
+
rspec_ext.query sql_file, *params, &block
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def respond_to?(method_name)
|
33
|
+
method_name = method_name.to_s
|
34
|
+
::Kernel.raise "Slacker::Sql.rspec_ext not initialized" if rspec_ext.nil?
|
35
|
+
::File.directory?(::File.join(base_folder, method_name)) ||
|
36
|
+
!(::Slacker.sql_file_from_method_name(base_folder, method_name).nil?)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -1,24 +1,24 @@
|
|
1
|
-
module Slacker
|
2
|
-
module SqlPreprocessor
|
3
|
-
IVAR_REX = /%{\s*(.*?)\s*}/
|
4
|
-
|
5
|
-
def self.executable_sql(sql, example)
|
6
|
-
# Replace all appearances of %{} with the values of the corresponding example instance variables
|
7
|
-
sql.gsub(IVAR_REX) do
|
8
|
-
ivar = $1.to_sym
|
9
|
-
instance = example.example_group_instance
|
10
|
-
|
11
|
-
if instance.instance_variable_defined?(ivar)
|
12
|
-
instance.instance_variable_get(ivar).to_s
|
13
|
-
else
|
14
|
-
raise "Example is missing instance variable #{ivar}"
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
def self.debuggable_sql(sql)
|
20
|
-
# Replace all appearances of %{} with the names of the sql variables
|
21
|
-
sql.gsub(IVAR_REX) {$1}
|
22
|
-
end
|
23
|
-
end
|
1
|
+
module Slacker
|
2
|
+
module SqlPreprocessor
|
3
|
+
IVAR_REX = /%{\s*(.*?)\s*}/
|
4
|
+
|
5
|
+
def self.executable_sql(sql, example)
|
6
|
+
# Replace all appearances of %{} with the values of the corresponding example instance variables
|
7
|
+
sql.gsub(IVAR_REX) do
|
8
|
+
ivar = $1.to_sym
|
9
|
+
instance = example.example_group_instance
|
10
|
+
|
11
|
+
if instance.instance_variable_defined?(ivar)
|
12
|
+
instance.instance_variable_get(ivar).to_s
|
13
|
+
else
|
14
|
+
raise "Example is missing instance variable #{ivar}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.debuggable_sql(sql)
|
20
|
+
# Replace all appearances of %{} with the names of the sql variables
|
21
|
+
sql.gsub(IVAR_REX) {$1}
|
22
|
+
end
|
23
|
+
end
|
24
24
|
end
|
@@ -1,17 +1,17 @@
|
|
1
|
-
module Slacker
|
2
|
-
module StringHelper
|
3
|
-
class << self
|
4
|
-
def camelize(lower_case_and_underscored_word)
|
5
|
-
lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
|
6
|
-
end
|
7
|
-
|
8
|
-
def constantize(camel_cased_word)
|
9
|
-
unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ camel_cased_word
|
10
|
-
raise NameError, "#{camel_cased_word.inspect} is not a valid constant name!"
|
11
|
-
end
|
12
|
-
|
13
|
-
Object.module_eval("::#{$1}", __FILE__, __LINE__)
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
1
|
+
module Slacker
|
2
|
+
module StringHelper
|
3
|
+
class << self
|
4
|
+
def camelize(lower_case_and_underscored_word)
|
5
|
+
lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
|
6
|
+
end
|
7
|
+
|
8
|
+
def constantize(camel_cased_word)
|
9
|
+
unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ camel_cased_word
|
10
|
+
raise NameError, "#{camel_cased_word.inspect} is not a valid constant name!"
|
11
|
+
end
|
12
|
+
|
13
|
+
Object.module_eval("::#{$1}", __FILE__, __LINE__)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
17
|
end
|
data/lib/slacker/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
module Slacker
|
2
|
-
VERSION = "1.0.
|
3
|
-
end
|
1
|
+
module Slacker
|
2
|
+
VERSION = "1.0.15"
|
3
|
+
end
|
@@ -1,11 +1,11 @@
|
|
1
|
-
"x","y","power"
|
2
|
-
"2","1","2"
|
3
|
-
"3","2","9"
|
4
|
-
"4","3","64"
|
5
|
-
"5","4","625"
|
6
|
-
"4","2","16"
|
7
|
-
"3","3","27"
|
8
|
-
"2","4","16"
|
9
|
-
"1","5","1"
|
10
|
-
"-1","2","1"
|
11
|
-
"-2","5","-32"
|
1
|
+
"x","y","power"
|
2
|
+
"2","1","2"
|
3
|
+
"3","2","9"
|
4
|
+
"4","3","64"
|
5
|
+
"5","4","625"
|
6
|
+
"4","2","16"
|
7
|
+
"3","3","27"
|
8
|
+
"2","4","16"
|
9
|
+
"1","5","1"
|
10
|
+
"-1","2","1"
|
11
|
+
"-2","5","-32"
|
@@ -1,11 +1,11 @@
|
|
1
|
-
"x","y"
|
2
|
-
2,1
|
3
|
-
3,2
|
4
|
-
4,3
|
5
|
-
5,4
|
6
|
-
4,2
|
7
|
-
3,3
|
8
|
-
2,4
|
9
|
-
1,5
|
10
|
-
-1,2
|
11
|
-
-2,5
|
1
|
+
"x","y"
|
2
|
+
2,1
|
3
|
+
3,2
|
4
|
+
4,3
|
5
|
+
5,4
|
6
|
+
4,2
|
7
|
+
3,3
|
8
|
+
2,4
|
9
|
+
1,5
|
10
|
+
-1,2
|
11
|
+
-2,5
|
@@ -1,3 +1,3 @@
|
|
1
|
-
"p","s"
|
2
|
-
2,34
|
3
|
-
12,44
|
1
|
+
"p","s"
|
2
|
+
2,34
|
3
|
+
12,44
|
@@ -1,9 +1,9 @@
|
|
1
|
-
# Database connection.
|
2
|
-
# When Slacker is executed, it will attempt to connect to this database.
|
3
|
-
|
4
|
-
# Replace the following with your connection information.
|
5
|
-
# Note that at this point Slacker only works with SQL Server authentication.
|
6
|
-
server: my_server
|
7
|
-
database: my_database
|
8
|
-
user: user_name
|
9
|
-
password: password
|
1
|
+
# Database connection.
|
2
|
+
# When Slacker is executed, it will attempt to connect to this database.
|
3
|
+
|
4
|
+
# Replace the following with your connection information.
|
5
|
+
# Note that at this point Slacker only works with SQL Server authentication.
|
6
|
+
server: my_server
|
7
|
+
database: my_database
|
8
|
+
user: user_name
|
9
|
+
password: password
|
@@ -1,2 +1,2 @@
|
|
1
|
-
module MyHelper
|
1
|
+
module MyHelper
|
2
2
|
end
|
@@ -1,66 +1,66 @@
|
|
1
|
-
# Method "describe" opens up an example group.
|
2
|
-
describe 'My database' do
|
3
|
-
# Simple inline query example.
|
4
|
-
it 'contains system tables' do
|
5
|
-
# Make sure we have at least one system object in the database.
|
6
|
-
query("select * from sysobjects where xtype = 'S';").count.
|
7
|
-
end
|
8
|
-
|
9
|
-
# The same query, this time using a SQL template stored in file "sql/sample_1/sysobject.sql.erb".
|
10
|
-
it 'contains system tables (take two)' do
|
11
|
-
# Every (*.sql.erb) file in folder "sql" can be called as a method on object "sql".
|
12
|
-
# Subfolders of folder "sql" appear as children of object "sql" with their (*.sql.erb) files automatically available as methods.
|
13
|
-
sql.sample_1.sysobjects.count.
|
14
|
-
end
|
15
|
-
|
16
|
-
# This time we'll use a parameterized template.
|
17
|
-
it 'contains system tables (take three)' do
|
18
|
-
# Every template can accept parameters; see file "sql/sample_1/sysobject_with_params.sql.erb".
|
19
|
-
sql.sample_1.sysobjects_with_params(:xtype => 'S').count.
|
20
|
-
end
|
21
|
-
|
22
|
-
# SQL Templates can contain multiple statements and can return multiple resultsets.
|
23
|
-
it 'can play with numbers' do
|
24
|
-
# Note that this time we're calling the template with a block which receives the results as a block parameter.
|
25
|
-
sql.sample_1.play_with_numbers(:x => 2, :y => 12) do |results|
|
26
|
-
# The results object contains an array of all the resultsets generated by the query script.
|
27
|
-
# A resultset contains an array of records. Each record is a hash of field => value pairs.
|
28
|
-
|
29
|
-
# First resultset; First record; Column "product".
|
30
|
-
results[0][0][:product].
|
31
|
-
|
32
|
-
# A resultset can be matched directly against an array of hashes using method "match".
|
33
|
-
results[1].
|
34
|
-
|
35
|
-
# Or against a CSV file stored in project's "data" folder (see file "data/sample_1/numbers_expected_output.csv").
|
36
|
-
results[2].
|
37
|
-
|
38
|
-
# A resultset's values can be matched one-by-one.
|
39
|
-
results[2][0][:p].
|
40
|
-
results[2][0][:s].
|
41
|
-
results[2][1][:p].
|
42
|
-
results[2][1][:s].
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
# Every "it" (example) is executed in a T-SQL transaction which is rolled back once the example is complete.
|
47
|
-
# No example can ever interfere with the results of another example.
|
48
|
-
#
|
49
|
-
# CSV files can be used to load data directly into a table.
|
50
|
-
# In this example, we will create a table, populate it with data,
|
51
|
-
# calculate the exponentiation of one column based on another column
|
52
|
-
# and verify the results against an expected resultset stored in a CSV file.
|
53
|
-
it 'can play with numbers (take two)' do
|
54
|
-
# Create the table - see file "sql/sample_1/create_my_table.sql.erb".
|
55
|
-
sql.sample_1.create_my_table
|
56
|
-
# We can populate any table with data from the "data" folder by calling method "load_csv".
|
57
|
-
# See file "data/sample_1/my_table_initial_data.csv".
|
58
|
-
load_csv('sample_1/my_table_initial_data.csv', 'MyTable')
|
59
|
-
|
60
|
-
# Now let's test the system scalar function Power.
|
61
|
-
# We will use it in a query expression executed agaings MyTable and we
|
62
|
-
# will compare the results against a CSV file - we should expect them to match.
|
63
|
-
# See files "sql/sample_1/my_table_on_power.sql.erb" and "data/sample_1/my_table_expected_power_results.csv".
|
64
|
-
sql.sample_1.my_table_on_power.
|
65
|
-
end
|
66
|
-
end
|
1
|
+
# Method "describe" opens up an example group.
|
2
|
+
describe 'My database' do
|
3
|
+
# Simple inline query example.
|
4
|
+
it 'contains system tables' do
|
5
|
+
# Make sure we have at least one system object in the database.
|
6
|
+
expect(query("select * from sysobjects where xtype = 'S';").count).to be > 0
|
7
|
+
end
|
8
|
+
|
9
|
+
# The same query, this time using a SQL template stored in file "sql/sample_1/sysobject.sql.erb".
|
10
|
+
it 'contains system tables (take two)' do
|
11
|
+
# Every (*.sql.erb) file in folder "sql" can be called as a method on object "sql".
|
12
|
+
# Subfolders of folder "sql" appear as children of object "sql" with their (*.sql.erb) files automatically available as methods.
|
13
|
+
expect(sql.sample_1.sysobjects.count).to be > 0
|
14
|
+
end
|
15
|
+
|
16
|
+
# This time we'll use a parameterized template.
|
17
|
+
it 'contains system tables (take three)' do
|
18
|
+
# Every template can accept parameters; see file "sql/sample_1/sysobject_with_params.sql.erb".
|
19
|
+
expect(sql.sample_1.sysobjects_with_params(:xtype => 'S').count).to be > 0
|
20
|
+
end
|
21
|
+
|
22
|
+
# SQL Templates can contain multiple statements and can return multiple resultsets.
|
23
|
+
it 'can play with numbers' do
|
24
|
+
# Note that this time we're calling the template with a block which receives the results as a block parameter.
|
25
|
+
sql.sample_1.play_with_numbers(:x => 2, :y => 12) do |results|
|
26
|
+
# The results object contains an array of all the resultsets generated by the query script.
|
27
|
+
# A resultset contains an array of records. Each record is a hash of field => value pairs.
|
28
|
+
|
29
|
+
# First resultset; First record; Column "product".
|
30
|
+
expect(results[0][0][:product]).to be == 24
|
31
|
+
|
32
|
+
# A resultset can be matched directly against an array of hashes using method "match".
|
33
|
+
expect(results[1]).to match([{:x => 2, :y => 12, :sum => 14}])
|
34
|
+
|
35
|
+
# Or against a CSV file stored in project's "data" folder (see file "data/sample_1/numbers_expected_output.csv").
|
36
|
+
expect(results[2]).to match('sample_1/numbers_expected_output.csv')
|
37
|
+
|
38
|
+
# A resultset's values can be matched one-by-one.
|
39
|
+
expect(results[2][0][:p]).to be == 2
|
40
|
+
expect(results[2][0][:s]).to be == 34
|
41
|
+
expect(results[2][1][:p]).to be == 12
|
42
|
+
expect(results[2][1][:s]).to be == 44
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Every "it" (example) is executed in a T-SQL transaction which is rolled back once the example is complete.
|
47
|
+
# No example can ever interfere with the results of another example.
|
48
|
+
#
|
49
|
+
# CSV files can be used to load data directly into a table.
|
50
|
+
# In this example, we will create a table, populate it with data,
|
51
|
+
# calculate the exponentiation of one column based on another column
|
52
|
+
# and verify the results against an expected resultset stored in a CSV file.
|
53
|
+
it 'can play with numbers (take two)' do
|
54
|
+
# Create the table - see file "sql/sample_1/create_my_table.sql.erb".
|
55
|
+
sql.sample_1.create_my_table
|
56
|
+
# We can populate any table with data from the "data" folder by calling method "load_csv".
|
57
|
+
# See file "data/sample_1/my_table_initial_data.csv".
|
58
|
+
load_csv('sample_1/my_table_initial_data.csv', 'MyTable')
|
59
|
+
|
60
|
+
# Now let's test the system scalar function Power.
|
61
|
+
# We will use it in a query expression executed agaings MyTable and we
|
62
|
+
# will compare the results against a CSV file - we should expect them to match.
|
63
|
+
# See files "sql/sample_1/my_table_on_power.sql.erb" and "data/sample_1/my_table_expected_power_results.csv".
|
64
|
+
expect(sql.sample_1.my_table_on_power).to match('sample_1/my_table_expected_power_results.csv')
|
65
|
+
end
|
66
|
+
end
|