marjoree 0.0.1 → 0.0.9

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 (174) hide show
  1. data/Marjoree.iml +14 -0
  2. data/README +9 -36
  3. data/bin/create_marj_structure.rb +75 -0
  4. data/bin/create_odbc_connections.rb +39 -0
  5. data/bin/odbc_connection_finder.rb +46 -0
  6. data/examples/contains_example.rb +1 -1
  7. data/examples/count_and_insert_example.rb +1 -1
  8. data/examples/select_example.rb +1 -1
  9. data/html/classes/ExpectedResultSet.html +60 -33
  10. data/html/classes/ExpectedResultSet.src/M000002.html +6 -5
  11. data/html/classes/ExpectedResultSet.src/M000003.html +5 -5
  12. data/html/classes/ExpectedResultSet.src/M000004.html +5 -5
  13. data/html/classes/ExpectedResultSet.src/M000005.html +18 -0
  14. data/html/classes/ExpectedResultSet.src/M000006.html +24 -0
  15. data/html/classes/ExpectedResultSet.src/M000007.html +23 -0
  16. data/html/classes/ExpectedResultSet.src/M000008.html +29 -0
  17. data/html/classes/Fixnum.html +287 -0
  18. data/html/classes/Fixnum.src/M000031.html +18 -0
  19. data/html/classes/Fixnum.src/M000034.html +18 -0
  20. data/html/classes/Fixnum.src/M000037.html +18 -0
  21. data/html/classes/Fixnum.src/M000039.html +18 -0
  22. data/html/classes/Fixnum.src/M000041.html +18 -0
  23. data/html/classes/Hash.html +156 -0
  24. data/html/classes/Hash.src/M000009.html +21 -0
  25. data/html/classes/Hash.src/M000010.html +20 -0
  26. data/html/classes/Marjoree.html +268 -151
  27. data/html/classes/Marjoree.src/M000057.html +19 -0
  28. data/html/classes/Marjoree.src/M000058.html +18 -0
  29. data/html/classes/{ExpectedResultSet.src/M000001.html → Marjoree.src/M000059.html} +4 -4
  30. data/html/classes/Marjoree.src/{M000024.html → M000060.html} +4 -4
  31. data/html/classes/Marjoree.src/{M000031.html → M000061.html} +4 -4
  32. data/html/classes/Marjoree.src/{M000026.html → M000062.html} +3 -3
  33. data/html/classes/Marjoree.src/{M000027.html → M000063.html} +3 -3
  34. data/html/classes/Marjoree.src/{M000028.html → M000064.html} +3 -3
  35. data/html/classes/Marjoree.src/{M000029.html → M000065.html} +5 -6
  36. data/html/classes/Marjoree.src/{M000030.html → M000066.html} +5 -5
  37. data/html/classes/Marjoree.src/M000067.html +19 -0
  38. data/html/classes/Marjoree.src/{M000032.html → M000068.html} +3 -3
  39. data/html/classes/{ResultSetWrapper.src/M000009.html → Marjoree.src/M000069.html} +4 -4
  40. data/html/classes/{ResultSetWrapper.src/M000010.html → Marjoree.src/M000070.html} +4 -4
  41. data/html/classes/{ResultSetWrapper.src/M000011.html → Marjoree.src/M000071.html} +4 -4
  42. data/html/classes/Marjoree.src/M000072.html +20 -0
  43. data/html/classes/Marjoree.src/M000073.html +20 -0
  44. data/html/classes/Marjoree.src/M000074.html +20 -0
  45. data/html/classes/Marjoree.src/{M000033.html → M000075.html} +3 -3
  46. data/html/classes/Marjoree.src/{M000034.html → M000076.html} +4 -4
  47. data/html/classes/Marjoree.src/{M000035.html → M000077.html} +2 -2
  48. data/html/classes/Marjoree.src/{M000036.html → M000078.html} +4 -3
  49. data/html/classes/Marjoree.src/{M000037.html → M000079.html} +1 -1
  50. data/html/classes/Marjoree.src/{M000038.html → M000080.html} +3 -3
  51. data/html/classes/Marjoree.src/{M000039.html → M000081.html} +3 -4
  52. data/html/classes/Marjoree.src/{M000040.html → M000082.html} +3 -3
  53. data/html/classes/Marjoree.src/{M000041.html → M000083.html} +3 -3
  54. data/html/classes/{ResultSetWrapper.src/M000008.html → Marjoree.src/M000084.html} +4 -4
  55. data/html/classes/ODBC.html +4 -0
  56. data/html/classes/ODBC/Error.html +12 -12
  57. data/html/classes/ODBC/Error.src/M000047.html +21 -0
  58. data/html/classes/ODBC/Error.src/{M000021.html → M000048.html} +2 -2
  59. data/html/classes/ODBC/TimeStamp.html +48 -18
  60. data/html/classes/ODBC/TimeStamp.src/{M000017.html → M000042.html} +2 -2
  61. data/html/classes/ODBC/TimeStamp.src/M000043.html +22 -0
  62. data/html/classes/ODBC/TimeStamp.src/{M000019.html → M000044.html} +2 -3
  63. data/html/classes/ODBC/TimeStamp.src/M000045.html +18 -0
  64. data/html/classes/ODBC/TimeStamp.src/M000046.html +18 -0
  65. data/html/classes/OdbcConnectionWrapper.html +244 -0
  66. data/html/classes/OdbcConnectionWrapper.src/M000023.html +18 -0
  67. data/html/classes/OdbcConnectionWrapper.src/M000024.html +18 -0
  68. data/html/classes/OdbcConnectionWrapper.src/M000025.html +22 -0
  69. data/html/classes/OdbcConnectionWrapper.src/M000026.html +25 -0
  70. data/html/classes/OdbcConnectionWrapper.src/M000027.html +20 -0
  71. data/html/classes/OdbcConnectionWrapper.src/M000028.html +28 -0
  72. data/html/classes/OdbcConnectionWrapper.src/M000029.html +24 -0
  73. data/html/classes/OdbcConnectionWrapper.src/M000030.html +30 -0
  74. data/html/classes/{ResultSetWrapper.html → ResultSet.html} +71 -94
  75. data/html/classes/ResultSet.src/M000011.html +27 -0
  76. data/html/classes/ResultSet.src/M000012.html +21 -0
  77. data/html/classes/ResultSet.src/M000013.html +23 -0
  78. data/html/classes/ResultSet.src/M000014.html +18 -0
  79. data/html/classes/ResultSet.src/M000015.html +18 -0
  80. data/html/classes/ResultSet.src/M000016.html +18 -0
  81. data/html/classes/ResultSet.src/M000017.html +18 -0
  82. data/html/classes/ResultSet.src/M000018.html +20 -0
  83. data/html/classes/ResultSet.src/M000019.html +18 -0
  84. data/html/classes/ResultSet.src/M000020.html +22 -0
  85. data/html/classes/ResultSet.src/M000021.html +23 -0
  86. data/html/classes/ResultSet.src/M000022.html +22 -0
  87. data/html/classes/Test.html +107 -0
  88. data/html/classes/Test/Unit.html +107 -0
  89. data/html/classes/Test/Unit/TestCase.html +257 -0
  90. data/html/classes/Test/Unit/TestCase.src/M000049.html +18 -0
  91. data/html/classes/Test/Unit/TestCase.src/M000050.html +18 -0
  92. data/html/classes/Test/Unit/TestCase.src/M000051.html +17 -0
  93. data/html/classes/Test/Unit/TestCase.src/M000052.html +17 -0
  94. data/html/classes/Test/Unit/TestCase.src/M000053.html +25 -0
  95. data/html/classes/Test/Unit/TestCase.src/M000054.html +18 -0
  96. data/html/classes/Test/Unit/TestCase.src/M000055.html +18 -0
  97. data/html/classes/Test/Unit/TestCase.src/M000056.html +35 -0
  98. data/html/created.rid +1 -1
  99. data/html/files/README.html +2 -51
  100. data/html/files/lib/expected_result_set_rb.html +1 -16
  101. data/html/files/lib/marjoree/core_rb.html +111 -0
  102. data/html/files/lib/marjoree/fixtures_rb.html +111 -0
  103. data/html/files/lib/marjoree/odbc_connection_wrapper_rb.html +109 -0
  104. data/html/files/lib/marjoree/odbc_rb.html +108 -0
  105. data/html/files/lib/{odbc_overrides_rb.html → marjoree/result_set_rb.html} +5 -5
  106. data/html/files/lib/{result_set_wrapper_rb.html → marjoree/time_rb.html} +5 -5
  107. data/html/files/lib/marjoree_rb.html +3 -4
  108. data/html/files/lib/run_marjoree_rb.html +145 -0
  109. data/html/files/lib/run_marjoree_rb.src/M000001.html +34 -0
  110. data/html/fr_class_index.html +7 -1
  111. data/html/fr_file_index.html +7 -2
  112. data/html/fr_method_index.html +84 -42
  113. data/lib/Marjoree.iml +12 -0
  114. data/lib/expected_result_set.rb +51 -24
  115. data/lib/marjoree.rb +2 -344
  116. data/lib/marjoree/core.rb +388 -0
  117. data/lib/marjoree/fixtures.rb +156 -0
  118. data/lib/marjoree/odbc.rb +45 -0
  119. data/lib/marjoree/odbc_connection_wrapper.rb +79 -0
  120. data/lib/marjoree/result_set.rb +165 -0
  121. data/lib/marjoree/sybase_definitions.rb +223 -0
  122. data/lib/marjoree/time.rb +31 -0
  123. data/lib/proc_param_info.txt +20 -0
  124. data/lib/run_marjoree.rb +31 -0
  125. data/pkg/marjoree-0.0.9.gem +0 -0
  126. data/rakefile.rb +3 -3
  127. data/tests/all_cruby_tests.rb +24 -0
  128. data/tests/all_jruby_tests.rb +22 -0
  129. data/tests/all_tests.rb +9 -6
  130. data/tests/fixtures/A.yml +3 -0
  131. data/tests/fixtures/B.yml +5 -0
  132. data/tests/fixtures/first_db/FIRST.yml +8 -0
  133. data/tests/fixtures/second_db/SECOND.yml +9 -0
  134. data/tests/marjoree/test_assert_results.rb +82 -0
  135. data/tests/marjoree/test_connection_details.rb +44 -0
  136. data/tests/{test_contains.rb → marjoree/test_contains.rb} +0 -0
  137. data/tests/{test_delete.rb → marjoree/test_delete.rb} +0 -0
  138. data/tests/marjoree/test_drop_user_objects.rb +52 -0
  139. data/tests/marjoree/test_expected_result_set.rb +111 -0
  140. data/tests/marjoree/test_fixtures.rb +112 -0
  141. data/tests/{test_insert.rb → marjoree/test_insert.rb} +0 -0
  142. data/tests/{test_marjoree.rb → marjoree/test_marjoree.rb} +14 -2
  143. data/tests/marjoree/test_multiple_db_fixtures.rb +35 -0
  144. data/tests/{test_num_rows.rb → marjoree/test_num_rows.rb} +0 -0
  145. data/tests/marjoree/test_odbc_connection_wrapper.rb +47 -0
  146. data/tests/{test_result_set_wrapper.rb → marjoree/test_result_set_wrapper.rb} +8 -8
  147. data/tests/marjoree/test_select.rb +55 -0
  148. data/tests/marjoree/test_single_odbc_connection_wrapper.rb +39 -0
  149. data/tests/{test_sproc.rb → marjoree/test_sproc.rb} +3 -4
  150. data/tests/marjoree/test_time.rb +25 -0
  151. data/tests/{test_time_stamp.rb → marjoree/test_time_stamp.rb} +10 -1
  152. data/tests/{test_truncate.rb → marjoree/test_truncate.rb} +0 -0
  153. data/tests/{test_update.rb → marjoree/test_update.rb} +14 -14
  154. data/tests/multiple_odbc_config/connections.yml +13 -0
  155. data/tests/single_odbc_config/connections.yml +3 -0
  156. metadata +214 -112
  157. data/html/classes/Marjoree.src/M000022.html +0 -19
  158. data/html/classes/Marjoree.src/M000023.html +0 -20
  159. data/html/classes/Marjoree.src/M000025.html +0 -18
  160. data/html/classes/Marjoree.src/M000042.html +0 -25
  161. data/html/classes/ODBC/Error.src/M000020.html +0 -21
  162. data/html/classes/ODBC/TimeStamp.src/M000018.html +0 -22
  163. data/html/classes/ResultSetWrapper.src/M000005.html +0 -27
  164. data/html/classes/ResultSetWrapper.src/M000006.html +0 -24
  165. data/html/classes/ResultSetWrapper.src/M000007.html +0 -23
  166. data/html/classes/ResultSetWrapper.src/M000012.html +0 -20
  167. data/html/classes/ResultSetWrapper.src/M000013.html +0 -18
  168. data/html/classes/ResultSetWrapper.src/M000014.html +0 -22
  169. data/html/classes/ResultSetWrapper.src/M000015.html +0 -23
  170. data/html/classes/ResultSetWrapper.src/M000016.html +0 -27
  171. data/lib/odbc_overrides.rb +0 -33
  172. data/lib/result_set_wrapper.rb +0 -193
  173. data/tests/test_expected_result_set.rb +0 -40
  174. data/tests/test_select.rb +0 -30
@@ -1,35 +1,62 @@
1
- # A class, as the name suggests, to setup and expected result set.
2
- #
3
- # Typical usage
4
- # expected = ExpectedResultSet.new
5
- # expected.columns = ['first_column_name', 'second_column_name']
6
- # expected.rows << [ 'first item', 1 ]
7
- # expected.rows << [ 'second item', 2 ]
8
1
  class ExpectedResultSet
9
- attr_reader :rows, :columns
2
+ attr_reader :rows, :columns
10
3
 
11
- def initialize
12
- @rows = []
13
- end
4
+ def initialize(*names)
5
+ @columns = names.map {|column| column.to_s}
6
+ @rows = []
7
+ end
14
8
 
15
- # columns should be given an array of strings.
16
- def columns=(value)
17
- @columns = value.map {|entry| entry.to_s}
18
- end
9
+ def columns=(value)
10
+ @columns = value.map {|entry| entry.to_s}
11
+ end
12
+
13
+ def row_hash(index)
14
+ return to_hash( @rows[index] )
15
+ end
16
+
17
+ def row_hashes
18
+ return rows.collect { |row| to_hash(row) }
19
+ end
19
20
 
20
- def row_hash(index)
21
- return to_hash( @rows[index] )
21
+ def add_fixture( *fixture_map )
22
+ row = []
23
+
24
+ @columns.each do |column|
25
+ row << get_value_from_maps( column, fixture_map ) #(fixture_map[column.to_sym] or fixture_map[column.to_s])
22
26
  end
23
27
 
24
- def row_hashes
25
- return rows.collect { |row| to_hash(row) }
28
+ rows << row
29
+ end
30
+
31
+ def get_value_from_maps( key, fixture_maps)
32
+ fixture_maps.each do |fixture_map|
33
+ value = (fixture_map[key.to_sym] or fixture_map[key.to_s])
34
+ return value unless value.nil?
26
35
  end
27
36
 
28
- private
37
+ return nil
38
+ end
29
39
 
30
- def to_hash(row)
31
- result = {}
32
- columns.each_with_index { |column, i| result[column] = row[i] }
33
- return result
40
+ def with_row(row_hash)
41
+ row = []
42
+ row_hash.each do |column, value|
43
+ column = column.to_s
44
+ if @columns.include? column
45
+ row[@columns.index(column)] = value
46
+ else
47
+ @columns << column.to_s
48
+ row << value
49
+ end
34
50
  end
51
+ @rows << row
52
+ self
53
+ end
54
+
55
+ private
56
+
57
+ def to_hash(row)
58
+ result = {}
59
+ columns.each_with_index { |column, i| result[column] = row[i] }
60
+ return result
61
+ end
35
62
  end
data/lib/marjoree.rb CHANGED
@@ -1,344 +1,2 @@
1
- require 'odbc'
2
- require 'result_set_wrapper'
3
- require 'odbc_overrides'
4
-
5
- #This is the main Marjoree mixin.
6
- # Before you call anything you will need to call 'connect' or no messages will reach your DB :o)
7
- # You might like to stick something like this in your test code to ensure the connection is dropped when you are finished.
8
- # Kernel.at_exit { disconnect }
9
- module Marjoree
10
- # Establish a connection to dataserver.
11
- def connect( odbc_name, username, password )
12
- $db = ODBC.connect(odbc_name, username, password )
13
- puts "Connected"
14
- end
15
-
16
- # Remove connection to dataserver.
17
- def disconnect
18
- $db.drop_all
19
- $db.disconnect
20
- puts "Disconnected"
21
- end
22
-
23
- # Run Store Procedure 'proc_name'
24
- #
25
- # The input_param_map takes the form of
26
- # { :procedure_input_param_name => procedure_input_param_value }
27
- # eg:
28
- # result_set = EXEC 'proc_name' @procedure_input_param_name = procedure_input_param_value
29
- #
30
- # If a proc has output parameters these are automatically bound onto the ResultSetWrapper object.
31
- # eg:
32
- # assert_equal( expected_output_parameter_value, result_set.output_parameter_name )
33
- def run_sproc( proc_name, input_param_map={} )
34
- if has_output_params?( proc_name )
35
- return run_complex_sproc( proc_name, input_param_map )
36
- else
37
- return run_simple_sproc( proc_name, input_param_map )
38
- end
39
- end
40
-
41
- # Performs a select * on table_name
42
- def select( table_name )
43
- return ResultSetWrapper.new( execute( "SELECT * FROM #{table_name}" ) )
44
- end
45
-
46
- # Provides the number of rows in table 'table_name'.
47
- #
48
- # The where_map entries are AND'ed together eg:
49
- # { :table_column_name_one => 1,
50
- # :table_column_name_two => 2 }
51
- # would produce
52
- # SELECT
53
- # *
54
- # FROM
55
- # table_name
56
- # WHERE
57
- # table_column_name_one = 1
58
- # AND table_column_name_two = 2
59
- def num_rows( table_name, where_map = nil )
60
- where_clause = build_where_statement( where_map )
61
- dbCall = execute( "SELECT COUNT(*) FROM #{table_name} #{where_clause}" )
62
- return dbCall.to_a[0][0]
63
- end
64
-
65
- # Does the same as num_rows
66
- def count( table_name, where_map = nil )
67
- return num_rows( table_name, where_map )
68
- end
69
-
70
- # Returns true if table 'table_name' contains the data specified in the where_map.
71
- #
72
- # 'where_map' supplies column_name_as_sym => value
73
- #
74
- # These are AND'ed together.
75
- def contains?( table_name, where_map={} )
76
- return count( table_name, where_map ) > 0
77
- end
78
-
79
- # Performs an INSERT INTO 'table_name'.
80
- # 'value_map' supplies column_name_as_sym => value
81
- def insert( table_name, value_map={} )
82
- columns = build_column_headers(value_map )
83
- values = build_column_values( value_map)
84
-
85
- execute( "INSERT INTO #{table_name} (#{columns}) VALUES (#{values})" )
86
- end
87
-
88
- # Performs an UPDATE 'table_name'
89
- #
90
- # 'where_map' supplies column_name_as_sym => value for the WHERE section of the UPDATE statement.
91
- #
92
- # These are AND'ed together.
93
- #
94
- # 'set_map' supplies column_name_as_sym => value for the SET section of the UPDATE statement.
95
- def update( table_name, where_map={}, set_map={} )
96
- where_clause = build_where_statement( where_map )
97
- assignments = build_set_statement( set_map )
98
- execute( "UPDATE #{table_name} SET #{assignments} #{where_clause}" )
99
- end
100
-
101
- # Performs an DELETE FROM 'table_name'
102
- #
103
- # 'where_map' supplies column_name_as_sym => value for the WHERE section of the UPDATE statement.
104
- #
105
- # These are AND'ed together.
106
- def delete( table_name, where_map={} )
107
- where_clause = build_where_statement( where_map )
108
- execute( "DELETE FROM #{table_name} #{where_clause}" )
109
- end
110
-
111
- # Performs an TRUNCATE TABLE 'table_name'
112
- def truncate( table_name )
113
- execute( "TRUNCATE TABLE #{table_name}" )
114
- end
115
-
116
- # Asserts that the ResultSetWrapper is empty
117
- def assert_empty( result_set )
118
- assert_equal( 0, result_set.hashes.size )
119
- end
120
-
121
- # Asserts that the values in the ExpectedResultSet are contained within the actual ResultSetWrapper.
122
- def assert_results( expected, result_set )
123
- assert_column_headers( expected, result_set )
124
- assert_row_data( expected, result_set )
125
- end
126
-
127
- # Asserts that the values in the ExpectedResultSet are NOT contained within the actual ResultSetWrapper.
128
- def assert_not_equal_results( expected, result_set )
129
- flag1 = !has_column_headers?(expected, result_set)
130
- flag2 = !has_correct_data?(expected, result_set)
131
-
132
- assert( !flag1 || !has_correct_data?(expected, result_set) )
133
- end
134
-
135
- # Asserts that an expected_error_code and error_message are returned when running the block.
136
- def assert_error_thrown( expected_error_code, expected_error_message )
137
- begin
138
- yield
139
- fail
140
- rescue ODBC::Error
141
- exception = $!
142
-
143
- assert_equal( expected_error_code, exception.error_code )
144
- assert_equal( expected_error_message, exception.error_message )
145
- end
146
- end
147
-
148
- # Asserts when the DB throws an error.
149
- #
150
- # ie: Inserting into a table that does not exist.
151
- def assert_db_error
152
- begin
153
- yield
154
- fail
155
- rescue ODBC::Error
156
- end
157
- end
158
-
159
- def has_column_headers?( expected, result_set )
160
- begin
161
- assert_column_headers( expected, result_set )
162
- return true
163
- rescue
164
- return false
165
- end
166
- end
167
-
168
- def has_correct_data?( expected, result_set )
169
-
170
- begin
171
- assert_row_data( expected, result_set )
172
- return true
173
- rescue
174
- return false
175
- end
176
- end
177
-
178
- # Helpful Assert that wraps the contains? method.
179
- def assert_contains( table_name, value_map )
180
- assert( contains?( table_name, value_map ) )
181
- end
182
-
183
- # Helpful Assert that wraps the contains? method.
184
- def assert_does_not_contain( table_name, value_map )
185
- assert( !contains?( table_name, value_map ) )
186
- end
187
-
188
- # Executes raw sql against the db.
189
- def execute( sql )
190
- dbCall = $db.prepare( sql )
191
- begin
192
- dbCall.execute
193
- rescue
194
- raise ODBC::Error.new("Failed executing: #{sql}\nDue to: #{$!}")
195
- end
196
-
197
- return dbCall
198
- end
199
-
200
- private
201
-
202
- def build_set_statement( map )
203
- pairs = []
204
- map.each { |key, value| pairs << "#{key} = #{value}" }
205
- return pairs.join(", ")
206
- end
207
-
208
-
209
- def build_column_values( map )
210
- return map.values.collect {|v| display_value_for v }.join(", ")
211
- end
212
-
213
-
214
- def build_input_parameter_statement( map )
215
- pairs = []
216
- map.each { |key, value| pairs << "@#{key.to_s} = #{display_value_for( value )}" }
217
- return pairs.join(", ")
218
- end
219
-
220
- def build_column_headers( map )
221
- return map.keys.join(", ")
222
- end
223
-
224
- def build_where_statement( map )
225
- if map.nil?
226
- return ''
227
- end
228
-
229
- pairs = []
230
- map.each { |key, value| pairs << "#{key} = #{display_value_for( value ) }" }
231
- expression = pairs.join(" AND ")
232
- if map.size > 0
233
- expression = " WHERE #{expression}"
234
- end
235
-
236
- return expression
237
- end
238
-
239
- def display_value_for( value )
240
- return value.is_a?(Numeric) ? value : "'#{value}'"
241
- end
242
-
243
- def assert_column_headers( expected, result_set )
244
- expected.columns.each do |expected_column_header|
245
- actual_column_headers = result_set.columns.keys
246
- formatted_actual_headers = actual_column_headers.join( " \n\t" )
247
- errorMessage = "\nColumn Header: '#{expected_column_header}' does not exist.\nActual Column Headers available are \n\t#{formatted_actual_headers}\n"
248
- assert( actual_column_headers.include?( expected_column_header ), errorMessage )
249
- end
250
- end
251
-
252
- def assert_row_data( expected, result_set )
253
- result = result_set.has?( expected )
254
-
255
- possibleErrorMessage = "\nExpected: #{expected.row_hashes.inspect}\nActual: [#{actual_values( result_set )}]\n"
256
-
257
- assert( result, possibleErrorMessage )
258
- end
259
-
260
- def actual_values( result_set )
261
- actual_values = []
262
- result_set.hashes.each { |hash| actual_values << hash.inspect }
263
- formatted_actual_values = actual_values.join( ", " )
264
- end
265
-
266
- def has_output_params?( proc_name )
267
- dbCall = $db.procedure_columns proc_name
268
- result_set = ResultSetWrapper.new( dbCall )
269
- return result_set.contains?( { :COLUMN_TYPE => 4 } )
270
- end
271
-
272
-
273
- def build_output_parameter_statement( list )
274
- result = []
275
- list.each { |hash| result << "#{hash[:COLUMN_NAME]} = #{hash[:COLUMN_NAME]} output" }
276
- return result.join(", ")
277
- end
278
-
279
- def run_complex_sproc( proc_name, input_param_map )
280
- output_param_data = get_output_params proc_name
281
- output_params = output_param_names( output_param_data )
282
-
283
- sql = build_sql_for_sproc_with_output_params( proc_name, input_param_map, output_param_data )
284
-
285
- return ResultSetWrapper.new( execute( sql ), output_params )
286
- end
287
-
288
- def build_sql_for_sproc_with_output_params( proc_name, input_param_map, output_params )
289
- sproc_input_param_text = build_input_parameter_statement( input_param_map )
290
- sproc_output_param_text = build_output_parameter_statement( output_params )
291
- sproc_input_param_text << "," unless input_param_map.empty?
292
-
293
- sql = build_output_declarations( output_params )
294
- sql << "EXEC #{proc_name} #{sproc_input_param_text} #{sproc_output_param_text}\n"
295
- output_params.each { |hash| sql << "SELECT #{hash[:COLUMN_NAME]}\n" }
296
-
297
- return sql
298
- end
299
-
300
- def output_param_names( output_params )
301
- result = []
302
- output_params.each{ |hash| result << hash[:COLUMN_NAME] }
303
-
304
- return result
305
- end
306
-
307
- def build_output_declarations output_params
308
- result = ""
309
- output_params.each do |hash|
310
- type_declaration = format_type_declaration( hash )
311
- result << "DECLARE #{hash[:COLUMN_NAME]} #{type_declaration}\n"
312
- end
313
-
314
- return result
315
- end
316
-
317
- def format_type_declaration( hash )
318
- column_type = hash[:TYPE_NAME]
319
- if column_type == 'varchar'
320
- return "#{column_type}(#{hash[:COLUMN_SIZE]})"
321
- end
322
-
323
- return column_type
324
- end
325
-
326
- def get_output_params( proc_name )
327
- dbCall = $db.procedure_columns proc_name
328
- result_set = ResultSetWrapper.new( dbCall )
329
- result = []
330
- result_set.collect do |hash|
331
- if hash[:COLUMN_TYPE] == 4 #varchar
332
- result << hash
333
- end
334
- end
335
-
336
- return result
337
- end
338
-
339
- def run_simple_sproc( proc_name, map={} )
340
- input_param_text = build_input_parameter_statement( map )
341
- sql = "EXEC #{proc_name} #{input_param_text }"
342
- return ResultSetWrapper.new( execute( sql ) )
343
- end
344
- end
1
+ require 'marjoree/core'
2
+ require 'marjoree/time'
@@ -0,0 +1,388 @@
1
+ require 'odbc'
2
+ require 'marjoree/odbc'
3
+ require 'marjoree/result_set'
4
+ require 'marjoree/odbc_connection_wrapper'
5
+
6
+ class Hash
7
+ def symbolize_keys
8
+ self.inject({}) do |result, (key, value)|
9
+ result[key.to_sym] = value
10
+ result
11
+ end
12
+ end
13
+ end
14
+
15
+ # This is the main Marjoree mixin.
16
+ # Before you call anything you will need to call 'connect' or no messages will reach your DB :o)
17
+ # You might like to stick something like this in your test code to ensure the connection is dropped when you are finished.
18
+ # Kernel.at_exit { disconnect }
19
+ module Marjoree
20
+
21
+ # Connect to <odbc_name> server
22
+ def connect_me_to( odbc_name )
23
+ $connection.connect_me_to( odbc_name )
24
+
25
+ end
26
+
27
+ # Establish a connection to dataserver.
28
+ def establish_connection_to( connection )
29
+ $connection = connection
30
+ end
31
+
32
+ # Remove connection to dataserver.
33
+ def disconnect_me
34
+ $connection.disconnect_me
35
+ end
36
+
37
+ # Run Store Procedure 'proc_name'
38
+ #
39
+ # The input_param_map takes the form of
40
+ # { :procedure_input_param_name => procedure_input_param_value }
41
+ # eg:
42
+ # result_set = EXEC 'proc_name' @procedure_input_param_name = procedure_input_param_value
43
+ #
44
+ # If a proc has output parameters these are automatically bound onto the ResultSet object.
45
+ # eg:
46
+ # assert_equal( expected_output_parameter_value, result_set.output_parameter_name )
47
+ def run_sproc( proc_name, map={} )
48
+ if has_output_params?( proc_name )
49
+ return run_complex_sproc( proc_name, map )
50
+ else
51
+ return run_simple_sproc( proc_name, map )
52
+ end
53
+ end
54
+
55
+ # Performs a select * on table_name
56
+ def select(table_name, where_map = nil)
57
+ where_clause = build_where_statement( where_map )
58
+ execute "SELECT * FROM #{table_name} #{where_clause}"
59
+ end
60
+
61
+ # Provides the number of rows in table 'table_name'.
62
+ #
63
+ # The where_map entries are AND'ed together eg:
64
+ # { :table_column_name_one => 1,
65
+ # :table_column_name_two => 2 }
66
+ # would produce
67
+ # SELECT
68
+ # *
69
+ # FROM
70
+ # table_name
71
+ # WHERE
72
+ # table_column_name_one = 1
73
+ # AND table_column_name_two = 2
74
+ def num_rows( table_name, where_map = nil )
75
+ where_clause = build_where_statement( where_map )
76
+ dbCall = execute( "SELECT COUNT(*) AS number_of_rows FROM #{table_name} #{where_clause}" )
77
+ return dbCall.hashes.first[:number_of_rows]
78
+ end
79
+
80
+ # Does the same as num_rows
81
+ def count(table_name, where_map = nil)
82
+ return num_rows(table_name, where_map)
83
+ end
84
+
85
+ # Returns true if table 'table_name' contains the data specified in the where_map.
86
+ #
87
+ # 'where_map' supplies column_name_as_sym => value
88
+ #
89
+ # These are AND'ed together.
90
+ def contains?(table_name, map={})
91
+ return count(table_name, map) > 0
92
+ end
93
+
94
+ # Performs an INSERT INTO 'table_name'.
95
+ # 'value_map' supplies column_name_as_sym => value
96
+ def insert(table_name, map={})
97
+ columns = build_column_headers(map)
98
+ values = build_column_values(map)
99
+ execute "INSERT INTO #{table_name} (#{columns}) VALUES (#{values})"
100
+ end
101
+
102
+ # Performs an UPDATE 'table_name'
103
+ #
104
+ # 'where_map' supplies column_name_as_sym => value for the WHERE section of the UPDATE statement.
105
+ #
106
+ # These are AND'ed together.
107
+ #
108
+ # 'set_map' supplies column_name_as_sym => value for the SET section of the UPDATE statement.
109
+ def update(table_name, where_map={}, map={})
110
+ where_clause = build_where_statement(where_map)
111
+ assignments = build_set_statement(map)
112
+ execute "UPDATE #{table_name} SET #{assignments} #{where_clause}"
113
+ end
114
+
115
+ # Performs an DELETE FROM 'table_name'
116
+ #
117
+ # 'where_map' supplies column_name_as_sym => value for the WHERE section of the UPDATE statement.
118
+ #
119
+ # These are AND'ed together.
120
+ def delete(table_name, map={})
121
+ where_clause = build_where_statement(map)
122
+ execute "DELETE FROM #{table_name} #{where_clause}"
123
+ end
124
+
125
+ # Performs an TRUNCATE TABLE 'table_name'
126
+ def truncate(table_name)
127
+ execute "TRUNCATE TABLE #{table_name}"
128
+ end
129
+
130
+ def get_user_tables
131
+ return get_objects( 'U' )
132
+ end
133
+
134
+ def get_user_views
135
+ return get_objects( 'V' )
136
+ end
137
+
138
+ def get_user_sprocs
139
+ return get_objects( 'P' )
140
+ end
141
+
142
+ # DROPs all user tables
143
+ def drop_user_tables
144
+ drop_objects('TABLE') do
145
+ get_user_tables
146
+ end
147
+ end
148
+
149
+ # DROPs all user views
150
+ def drop_user_views
151
+ drop_objects('VIEW') do
152
+ get_user_views
153
+ end
154
+ end
155
+
156
+ # DROPs all user sprocs
157
+ def drop_user_sprocs
158
+ drop_objects('PROCEDURE') do
159
+ get_user_sprocs
160
+ end
161
+ end
162
+
163
+ # Asserts that the ResultSet is empty
164
+ def assert_empty(result_set)
165
+ assert_equal 0, result_set.hashes.size
166
+ end
167
+
168
+ # Asserts that the values in the ExpectedResultSet are contained within the actual ResultSet.
169
+ def assert_results(expected, result_set)
170
+ assert_column_headers expected, result_set
171
+ assert_row_data expected, result_set
172
+ end
173
+
174
+ # Asserts that the values in the ExpectedResultSet are NOT contained within the actual ResultSet.
175
+ def assert_not_equal_results(expected, result_set)
176
+ flag1 = !has_column_headers?(expected, result_set)
177
+ flag2 = !has_correct_data?(expected, result_set)
178
+
179
+ assert( !flag1 || !has_correct_data?(expected, result_set) )
180
+ end
181
+
182
+ # Asserts that an expected_error_code and error_message are returned when running the block.
183
+ def assert_error_thrown( expected_error_code, expected_error_message )
184
+ begin
185
+ yield
186
+ fail
187
+ rescue ODBC::Error
188
+ exception = $!
189
+ assert_equal( expected_error_code, exception.error_code )
190
+ if expected_error_message != nil
191
+ assert_equal( expected_error_message, exception.error_message )
192
+ end
193
+ end
194
+ end
195
+
196
+ # Asserts when the DB throws an error.
197
+ #
198
+ # ie: Inserting into a table that does not exist.
199
+ def assert_db_error
200
+ begin
201
+ yield
202
+ fail
203
+ rescue ODBC::Error
204
+ end
205
+ end
206
+
207
+ def has_column_headers?(expected, result_set)
208
+ begin
209
+ assert_column_headers expected, result_set
210
+ return true
211
+ rescue
212
+ return false
213
+ end
214
+ end
215
+
216
+ def has_correct_data?(expected, result_set)
217
+ begin
218
+ assert_row_data expected, result_set
219
+ return true
220
+ rescue
221
+ return false
222
+ end
223
+ end
224
+
225
+ # Helpful Assert that wraps the contains? method.
226
+ def assert_contains(table_name, value_map)
227
+ assert contains?(table_name, value_map)
228
+ end
229
+
230
+ # Helpful Assert that wraps the contains? method.
231
+ def assert_does_not_contain(table_name, value_map)
232
+ assert !contains?(table_name, value_map)
233
+ end
234
+
235
+ # Executes raw sql against the db.
236
+ def execute(sql, output_params = [])
237
+ return $connection.execute( sql, output_params )
238
+ end
239
+
240
+ private
241
+
242
+ def build_set_statement(map)
243
+ pairs = []
244
+ map.each { |key, value| pairs << "#{key} = #{display_value_for( value )}" }
245
+ return pairs.join(", ")
246
+ end
247
+
248
+
249
+ def build_column_values(map)
250
+ return map.values.collect {|v| display_value_for v }.join(", ")
251
+ end
252
+
253
+
254
+ def build_input_parameter_statement(map)
255
+ pairs = []
256
+ map.each { |key, value| pairs << "@#{key.to_s} = #{display_value_for( value )}" }
257
+ return pairs.join(", ")
258
+ end
259
+
260
+ def build_column_headers(map)
261
+ return map.keys.join(", ")
262
+ end
263
+
264
+ def build_where_statement(map)
265
+ return '' if map.nil?
266
+
267
+ pairs = []
268
+ map.each { |key, value| pairs << "#{key} = #{display_value_for( value ) }" }
269
+ expression = pairs.join(" AND ")
270
+ if map.size > 0
271
+ expression = " WHERE #{expression}"
272
+ end
273
+
274
+ return expression
275
+ end
276
+
277
+ def display_value_for(value)
278
+ return 'NULL' if value.nil?
279
+ return value.is_a?(Numeric) ? value : "'#{value}'"
280
+ end
281
+
282
+ def assert_column_headers(expected, result_set)
283
+ expected.columns.each do |expected_column_header|
284
+ actual_column_headers = result_set.columns.keys
285
+ formatted_actual_headers = actual_column_headers.join( " \n\t" )
286
+ errorMessage = "\nColumn Header: '#{expected_column_header}' does not exist.\nActual Column Headers available are \n\t#{formatted_actual_headers}\n"
287
+ assert actual_column_headers.include?(expected_column_header), errorMessage
288
+ end
289
+ end
290
+
291
+ def assert_row_data(expected, result_set)
292
+ result = result_set.has?(expected)
293
+
294
+ possibleErrorMessage = "\nExpected: #{expected.row_hashes.map{|row|row.symbolize_keys.rehash}.inspect}\nActual: [#{actual_values( result_set, expected )}]\n"
295
+
296
+ assert result, possibleErrorMessage
297
+ end
298
+
299
+ def actual_values(result_set, expected)
300
+ actual_values = result_set.hashes.collect { |hash| hash.reject{|k, v| !expected.columns.include?(k.to_s)}.rehash.inspect }
301
+ return actual_values.join(", ")
302
+ end
303
+
304
+ def has_output_params?(proc_name)
305
+ return $connection.has_output_params?(proc_name)
306
+ end
307
+
308
+
309
+ def build_output_parameter_statement(list)
310
+ result = list.collect { |hash| "#{hash[:COLUMN_NAME]} = #{hash[:COLUMN_NAME]} output" }
311
+ return result.join(", ")
312
+ end
313
+
314
+ def run_complex_sproc(proc_name, input_param_map)
315
+ output_param_data = get_output_params(proc_name)
316
+ output_params = output_param_names(output_param_data)
317
+
318
+ sql = build_sql_for_sproc_with_output_params(proc_name, input_param_map, output_param_data)
319
+
320
+ execute sql, output_params
321
+ end
322
+
323
+ def build_sql_for_sproc_with_output_params(proc_name, input_param_map, output_params)
324
+ sproc_input_param_text = build_input_parameter_statement(input_param_map)
325
+ sproc_output_param_text = build_output_parameter_statement(output_params)
326
+ sproc_input_param_text << "," unless input_param_map.empty?
327
+
328
+ sql = build_output_declarations(output_params)
329
+ sql << "EXEC #{proc_name} #{sproc_input_param_text} #{sproc_output_param_text}\n"
330
+ output_params.each { |hash| sql << "SELECT #{hash[:COLUMN_NAME]}\n" }
331
+
332
+ return sql
333
+ end
334
+
335
+ def output_param_names(output_params)
336
+ return output_params.collect{ |hash| hash[:COLUMN_NAME] }
337
+ end
338
+
339
+ def build_output_declarations(output_params)
340
+ result = ""
341
+ output_params.each do |hash|
342
+ type_declaration = format_type_declaration( hash )
343
+ result << "DECLARE #{hash[:COLUMN_NAME]} #{type_declaration}\n"
344
+ end
345
+
346
+ return result
347
+ end
348
+
349
+ def format_type_declaration(hash)
350
+ column_type = hash[:TYPE_NAME]
351
+ if column_type == 'varchar'
352
+ return "#{column_type}(#{hash[:COLUMN_SIZE]})"
353
+ end
354
+ return column_type
355
+ end
356
+
357
+ def get_output_params(proc_name)
358
+ return $connection.get_output_params(proc_name)
359
+ end
360
+
361
+ def run_simple_sproc(proc_name, map={})
362
+ input_param_text = build_input_parameter_statement(map)
363
+ execute "EXEC #{proc_name} #{input_param_text }"
364
+ end
365
+
366
+ def get_objects( type )
367
+ result_set = select( "sysobjects", {:type => type} )
368
+ object_names = []
369
+ result_set.hashes.each do |row|
370
+ object_names << row[:name]
371
+ end
372
+ return object_names
373
+
374
+ end
375
+
376
+ def drop_objects( object_type )
377
+ puts "Dropping #{yield.size} #{object_type}(s)."
378
+ while (yield.size != 0)
379
+ yield.each do |object_name|
380
+ begin
381
+ execute "DROP #{object_type} #{object_name}"
382
+ rescue
383
+ end
384
+ end
385
+ end
386
+ puts "All user #{object_type}s dropped."
387
+ end
388
+ end