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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +4 -4
  3. data/README.markdown +22 -22
  4. data/Rakefile +11 -11
  5. data/bin/slacker +32 -32
  6. data/bin/slacker_new +34 -34
  7. data/lib/slacker.rb +180 -175
  8. data/lib/slacker/application.rb +224 -214
  9. data/lib/slacker/command_line_formatter.rb +61 -61
  10. data/lib/slacker/configuration.rb +59 -59
  11. data/lib/slacker/formatter.rb +14 -14
  12. data/lib/slacker/query_result_matcher.rb +178 -178
  13. data/lib/slacker/rspec_ext.rb +57 -57
  14. data/lib/slacker/rspec_monkey.rb +7 -7
  15. data/lib/slacker/sql.rb +39 -39
  16. data/lib/slacker/sql_preprocessor.rb +23 -23
  17. data/lib/slacker/string_helper.rb +16 -16
  18. data/lib/slacker/version.rb +3 -3
  19. data/lib/slacker_new/project/data/sample_1/my_table_expected_power_results.csv +11 -11
  20. data/lib/slacker_new/project/data/sample_1/my_table_initial_data.csv +11 -11
  21. data/lib/slacker_new/project/data/sample_1/numbers_expected_output.csv +3 -3
  22. data/lib/slacker_new/project/database.yml +9 -9
  23. data/lib/slacker_new/project/lib/helpers/my_helper.rb +1 -1
  24. data/lib/slacker_new/project/spec/sample_1.rb +66 -66
  25. data/lib/slacker_new/project/sql/sample_1/my_table_on_power.sql.erb +1 -1
  26. data/lib/slacker_new/project/sql/sample_1/play_with_numbers.sql.erb +16 -16
  27. data/lib/slacker_new/project/sql/sample_1/sysobjects_with_params.sql.erb +1 -1
  28. data/slacker.gemspec +27 -27
  29. data/spec/application_spec.rb +13 -13
  30. data/spec/query_result_matcher_spec.rb +268 -268
  31. data/spec/rspec_ext_spec.rb +87 -87
  32. data/spec/slacker_spec.rb +59 -59
  33. data/spec/spec_helper.rb +19 -9
  34. data/spec/test_files/matcher/test_1.csv +3 -3
  35. data/spec/test_files/test_slacker_project/data/test_1.csv +3 -3
  36. data/spec/test_files/test_slacker_project/sql/nest/nested_1.sql.erb +1 -1
  37. data/spec/test_files/test_slacker_project/sql/no_params.sql.erb +2 -2
  38. data/spec/test_files/test_slacker_project/sql/params.sql.erb +1 -1
  39. metadata +24 -24
@@ -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
@@ -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
@@ -1,3 +1,3 @@
1
- module Slacker
2
- VERSION = "1.0.14"
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.should > 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
- sql.sample_1.sysobjects.count.should > 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
- sql.sample_1.sysobjects_with_params(:xtype => 'S').count.should > 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
- results[0][0][:product].should == 24
31
-
32
- # A resultset can be matched directly against an array of hashes using method "match".
33
- results[1].should 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
- results[2].should match('sample_1/numbers_expected_output.csv')
37
-
38
- # A resultset's values can be matched one-by-one.
39
- results[2][0][:p].should == 2
40
- results[2][0][:s].should == 34
41
- results[2][1][:p].should == 12
42
- results[2][1][:s].should == 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
- sql.sample_1.my_table_on_power.should match('sample_1/my_table_expected_power_results.csv')
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