data_objects 0.9.11 → 0.9.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. data/Manifest.txt +19 -1
  2. data/Rakefile +6 -80
  3. data/lib/data_objects.rb +1 -6
  4. data/lib/data_objects/command.rb +51 -1
  5. data/lib/data_objects/connection.rb +13 -2
  6. data/lib/data_objects/logger.rb +40 -32
  7. data/lib/data_objects/quoting.rb +28 -32
  8. data/lib/data_objects/reader.rb +6 -5
  9. data/lib/data_objects/result.rb +7 -1
  10. data/lib/data_objects/spec/command_spec.rb +191 -0
  11. data/lib/data_objects/spec/connection_spec.rb +106 -0
  12. data/lib/data_objects/spec/encoding_spec.rb +31 -0
  13. data/lib/data_objects/spec/quoting_spec.rb +0 -0
  14. data/lib/data_objects/spec/reader_spec.rb +156 -0
  15. data/lib/data_objects/spec/result_spec.rb +58 -0
  16. data/lib/data_objects/spec/typecast/array_spec.rb +36 -0
  17. data/lib/data_objects/spec/typecast/bigdecimal_spec.rb +107 -0
  18. data/lib/data_objects/spec/typecast/boolean_spec.rb +107 -0
  19. data/lib/data_objects/spec/typecast/byte_array_spec.rb +86 -0
  20. data/lib/data_objects/spec/typecast/class_spec.rb +63 -0
  21. data/lib/data_objects/spec/typecast/date_spec.rb +108 -0
  22. data/lib/data_objects/spec/typecast/datetime_spec.rb +110 -0
  23. data/lib/data_objects/spec/typecast/float_spec.rb +111 -0
  24. data/lib/data_objects/spec/typecast/integer_spec.rb +86 -0
  25. data/lib/data_objects/spec/typecast/ipaddr_spec.rb +0 -0
  26. data/lib/data_objects/spec/typecast/nil_spec.rb +116 -0
  27. data/lib/data_objects/spec/typecast/range_spec.rb +36 -0
  28. data/lib/data_objects/spec/typecast/string_spec.rb +86 -0
  29. data/lib/data_objects/spec/typecast/time_spec.rb +64 -0
  30. data/lib/data_objects/transaction.rb +20 -13
  31. data/lib/data_objects/uri.rb +24 -2
  32. data/lib/data_objects/version.rb +2 -1
  33. data/spec/command_spec.rb +1 -17
  34. data/spec/connection_spec.rb +1 -23
  35. data/spec/lib/pending_helpers.rb +11 -0
  36. data/spec/lib/rspec_immediate_feedback_formatter.rb +53 -0
  37. data/spec/result_spec.rb +0 -3
  38. data/tasks/gem.rake +49 -0
  39. data/tasks/install.rake +13 -0
  40. data/tasks/release.rake +74 -0
  41. data/tasks/spec.rake +18 -0
  42. metadata +51 -30
  43. data/.gitignore +0 -2
  44. data/spec/dataobjects_spec.rb +0 -1
  45. data/spec/spec.opts +0 -2
@@ -1,30 +1,31 @@
1
1
  module DataObjects
2
+ # Abstract class to read rows from a query result
2
3
  class Reader
3
4
 
5
+ # Return the array of field names
4
6
  def fields
5
7
  raise NotImplementedError.new
6
8
  end
7
9
 
10
+ # Return the array of field values for the current row. Not legal after next! has returned false or before it's been called
8
11
  def values
9
12
  raise NotImplementedError.new
10
13
  end
11
14
 
15
+ # Close the reader discarding any unread results.
12
16
  def close
13
17
  raise NotImplementedError.new
14
18
  end
15
19
 
16
- # Moves the cursor forward.
20
+ # Discard the current row (if any) and read the next one (returning true), or return nil if there is no further row.
17
21
  def next!
18
22
  raise NotImplementedError.new
19
23
  end
20
24
 
25
+ # Return the number of fields in the result set.
21
26
  def field_count
22
27
  raise NotImplementedError.new
23
28
  end
24
29
 
25
- def row_count
26
- raise NotImplementedError.new
27
- end
28
-
29
30
  end
30
31
  end
@@ -1,11 +1,17 @@
1
1
  module DataObjects
2
+ # The Result class is returned from Connection#execute_non_query.
2
3
  class Result
3
- attr_accessor :insert_id, :affected_rows
4
+ # The ID of a row inserted by the Command
5
+ attr_accessor :insert_id
6
+ # The number of rows affected by the Command
7
+ attr_accessor :affected_rows
4
8
 
9
+ # Create a new Result. Used internally in the adapters.
5
10
  def initialize(command, affected_rows, insert_id = nil)
6
11
  @command, @affected_rows, @insert_id = command, affected_rows, insert_id
7
12
  end
8
13
 
14
+ # Return the number of affected rows
9
15
  def to_i
10
16
  @affected_rows
11
17
  end
@@ -0,0 +1,191 @@
1
+ WINDOWS = Gem.win_platform?
2
+
3
+ share_examples_for 'a Command' do
4
+
5
+ include DataObjectsSpecHelpers
6
+
7
+ before :all do
8
+ setup_test_environment
9
+ end
10
+
11
+ before :each do
12
+ @connection = DataObjects::Connection.new(CONFIG.uri)
13
+ @command = @connection.create_command("INSERT INTO users (name) VALUES (?)")
14
+ @reader = @connection.create_command("SELECT code, name FROM widgets WHERE ad_description = ?")
15
+ end
16
+
17
+ after :each do
18
+ @connection.close
19
+ end
20
+
21
+ it { @command.should be_kind_of(DataObjects::Command) }
22
+
23
+ it { @command.should respond_to(:execute_non_query) }
24
+
25
+ describe 'execute_non_query' do
26
+
27
+ describe 'with an invalid statement' do
28
+
29
+ before :each do
30
+ @invalid_command = @connection.create_command("INSERT INTO non_existent_table (tester) VALUES (1)")
31
+ end
32
+
33
+ it 'should raise an error on an invalid query' do
34
+ lambda { @invalid_command.execute_non_query }.should raise_error
35
+ end
36
+
37
+ it 'should raise an error with too few binding parameters' do
38
+ lambda { @command.execute_non_query("Too", "Many") }.should raise_error(ArgumentError, "Binding mismatch: 2 for 1")
39
+ end
40
+
41
+ it 'should raise an error with too many binding parameters' do
42
+ lambda { @command.execute_non_query }.should raise_error(ArgumentError, "Binding mismatch: 0 for 1")
43
+ end
44
+
45
+ end
46
+
47
+ describe 'with a valid statement' do
48
+
49
+ it 'should not raise an error with an explicit nil as parameter' do
50
+ lambda { @command.execute_non_query(nil) }.should_not raise_error
51
+ end
52
+
53
+ end
54
+
55
+ end
56
+
57
+ it { @command.should respond_to(:execute_reader) }
58
+
59
+ describe 'execute_reader' do
60
+
61
+ describe 'with an invalid reader' do
62
+
63
+ before :each do
64
+ @invalid_reader = @connection.create_command("SELECT * FROM non_existent_widgets WHERE ad_description = ?")
65
+ end
66
+
67
+ it 'should raise an error on an invalid query' do
68
+ lambda { @invalid_reader.execute_reader }.should raise_error
69
+ end
70
+
71
+ it 'should raise an error with too few binding parameters' do
72
+ lambda { @reader.execute_reader("Too", "Many") }.should raise_error(ArgumentError, "Binding mismatch: 2 for 1")
73
+ end
74
+
75
+ it 'should raise an error with too many binding parameters' do
76
+ lambda { @reader.execute_reader }.should raise_error(ArgumentError, "Binding mismatch: 0 for 1")
77
+ end
78
+
79
+ end
80
+
81
+ describe 'with a valid reader' do
82
+
83
+ it 'should not raise an error with an explicit nil as parameter' do
84
+ lambda { @reader.execute_reader(nil) }.should_not raise_error
85
+ end
86
+
87
+ end
88
+
89
+ end
90
+
91
+ it { @command.should respond_to(:set_types) }
92
+
93
+ describe 'set_types' do
94
+
95
+ describe 'is invalid when used with a statement' do
96
+
97
+ before :each do
98
+ @command.set_types(String)
99
+ end
100
+
101
+ it 'should raise an error when types are set' do
102
+ lambda { @command.execute_non_query }.should raise_error
103
+ end
104
+
105
+ end
106
+
107
+ describe 'with an invalid reader' do
108
+
109
+ it 'should raise an error with too few types' do
110
+ @reader.set_types(String)
111
+ lambda { @reader.execute_reader("One parameter") }.should raise_error(ArgumentError, "Field-count mismatch. Expected 1 fields, but the query yielded 2")
112
+ end
113
+
114
+ it 'should raise an error with too many types' do
115
+ @reader.set_types(String, String, BigDecimal)
116
+ lambda { @reader.execute_reader("One parameter") }.should raise_error(ArgumentError, "Field-count mismatch. Expected 3 fields, but the query yielded 2")
117
+ end
118
+
119
+ end
120
+
121
+ describe 'with a valid reader' do
122
+
123
+ it 'should not raise an error with correct number of types' do
124
+ @reader.set_types(String, String)
125
+ lambda { @result = @reader.execute_reader('Buy this product now!') }.should_not raise_error
126
+ lambda { @result.next! }.should_not raise_error
127
+ lambda { @result.values }.should_not raise_error
128
+ @result.close
129
+ end
130
+
131
+ it 'should also support old style array argument types' do
132
+ @reader.set_types([String, String])
133
+ lambda { @result = @reader.execute_reader('Buy this product now!') }.should_not raise_error
134
+ lambda { @result.next! }.should_not raise_error
135
+ lambda { @result.values }.should_not raise_error
136
+ @result.close
137
+ end
138
+
139
+ end
140
+
141
+ end
142
+
143
+ it { @command.should respond_to(:to_s) }
144
+
145
+ describe 'to_s' do
146
+
147
+ end
148
+
149
+
150
+ end
151
+
152
+ share_examples_for 'a Command with async' do
153
+
154
+ include DataObjectsSpecHelpers
155
+
156
+ before :all do
157
+ setup_test_environment
158
+ end
159
+
160
+ describe 'running queries in parallel' do
161
+
162
+ before :each do
163
+
164
+ threads = []
165
+
166
+ @start = Time.now
167
+ 4.times do |i|
168
+ threads << Thread.new do
169
+ connection = DataObjects::Connection.new(CONFIG.uri)
170
+ command = connection.create_command(CONFIG.sleep)
171
+ result = command.execute_non_query
172
+ connection.close
173
+ end
174
+ end
175
+
176
+ threads.each{|t| t.join }
177
+ @finish = Time.now
178
+ end
179
+
180
+ after :each do
181
+ @connection.close
182
+ end
183
+
184
+ it "should finish within 2 seconds" do
185
+ pending_if("Ruby on Windows doesn't support asynchronious operations", WINDOWS) do
186
+ (@finish - @start).should < 2
187
+ end
188
+ end
189
+
190
+ end
191
+ end
@@ -0,0 +1,106 @@
1
+ share_examples_for 'a Connection' do
2
+
3
+ include DataObjectsSpecHelpers
4
+
5
+ before :all do
6
+ setup_test_environment
7
+ end
8
+
9
+ before :each do
10
+ @connection = DataObjects::Connection.new(CONFIG.uri)
11
+ end
12
+
13
+ after :each do
14
+ @connection.close
15
+ end
16
+
17
+ it { @connection.should be_kind_of(DataObjects::Connection) }
18
+ it { @connection.should be_kind_of(Extlib::Pooling) }
19
+
20
+ it { @connection.should respond_to(:dispose) }
21
+
22
+ describe 'dispose' do
23
+
24
+ describe 'on open connection' do
25
+ before do
26
+ @open_connection = DataObjects::Connection.new("#{@driver}://#{@user}:#{@password}@#{@host}:#{@port}#{@database}")
27
+ @open_connection.detach
28
+ end
29
+
30
+ after do
31
+ @open_connection.close
32
+ end
33
+
34
+ it { @open_connection.dispose.should be_true }
35
+ end
36
+
37
+ describe 'on closed connection' do
38
+ before do
39
+ @closed_connection = DataObjects::Connection.new("#{@driver}://#{@user}:#{@password}@#{@host}:#{@port}#{@database}")
40
+ @closed_connection.detach
41
+ @closed_connection.dispose
42
+ end
43
+
44
+ after do
45
+ @closed_connection.close
46
+ end
47
+
48
+ it { @closed_connection.dispose.should be_false }
49
+
50
+ it 'should raise an error on creating a command' do
51
+ lambda { @closed_connection.create_command("INSERT INTO non_existant_table (tester) VALUES (1)").execute_non_query }.should raise_error
52
+ end
53
+ end
54
+
55
+ end
56
+
57
+ it { @connection.should respond_to(:create_command) }
58
+
59
+ describe 'create_command' do
60
+ it { @connection.create_command('This is a dummy command').should be_kind_of(DataObjects::Command) }
61
+ end
62
+
63
+ end
64
+
65
+ share_examples_for 'a Connection with authentication support' do
66
+
67
+ before :all do
68
+ %w[ @driver @user @password @host @port @database ].each do |ivar|
69
+ raise "+#{ivar}+ should be defined in before block" unless instance_variable_get(ivar)
70
+ end
71
+
72
+ end
73
+
74
+ describe 'with an invalid URI' do
75
+
76
+ def connecting_with(uri)
77
+ lambda { DataObjects::Connection.new(uri) }
78
+ end
79
+
80
+ it 'should raise an error if no database specified' do
81
+ connecting_with("#{@driver}://#{@user}:#{@password}@#{@host}:#{@port}").should raise_error
82
+ end
83
+
84
+ it 'should raise an error if bad username is given' do
85
+ connecting_with("#{@driver}://thisreallyshouldntexist:#{@password}@#{@host}:#{@port}#{@database}").should raise_error
86
+ end
87
+
88
+ it 'should raise an error if bad password is given' do
89
+ connecting_with("#{@driver}://#{@user}:completelyincorrectpassword:#{@host}:#{@port}#{@database}").should raise_error
90
+ end
91
+
92
+ it 'should raise an error if an invalid port is given' do
93
+ connecting_with("#{@driver}://#{@user}:#{@password}:#{@host}:648646543#{@database}").should raise_error
94
+ end
95
+
96
+ it 'should raise an error if an invalid database is given' do
97
+ connecting_with("#{@driver}://#{@user}:#{@password}:#{@host}:#{@port}/someweirddatabase").should raise_error
98
+ end
99
+
100
+ it 'should raise an error with a meaningless URI' do
101
+ connecting_with("#{@driver}://peekaboo$2!@#4543").should raise_error
102
+ end
103
+
104
+ end
105
+
106
+ end
@@ -0,0 +1,31 @@
1
+ share_examples_for 'a driver supporting encodings' do
2
+
3
+ before :each do
4
+ @connection = DataObjects::Connection.new(CONFIG.uri)
5
+ end
6
+
7
+ after :each do
8
+ @connection.close
9
+ end
10
+
11
+
12
+ it { @connection.should respond_to(:character_set) }
13
+
14
+ describe 'character_set' do
15
+
16
+ it 'uses utf8 by default' do
17
+ @connection.character_set.should == 'utf8'
18
+ end
19
+
20
+ describe 'sets the character set through the URI' do
21
+ before do
22
+ @latin1_connection = DataObjects::Connection.new("#{CONFIG.scheme}://#{CONFIG.user}:#{CONFIG.pass}@#{CONFIG.host}:#{CONFIG.port}#{CONFIG.database}?encoding=latin1")
23
+ end
24
+
25
+ after { @latin1_connection.close }
26
+
27
+ it { @latin1_connection.character_set.should == 'latin1' }
28
+ end
29
+
30
+ end
31
+ end
File without changes
@@ -0,0 +1,156 @@
1
+ share_examples_for 'a Reader' do
2
+
3
+ include DataObjectsSpecHelpers
4
+
5
+ before :all do
6
+ setup_test_environment
7
+ end
8
+
9
+ before :each do
10
+ @connection = DataObjects::Connection.new(CONFIG.uri)
11
+ @reader = @connection.create_command("SELECT code, name FROM widgets WHERE ad_description = ? order by id").execute_reader('Buy this product now!')
12
+ end
13
+
14
+ after :each do
15
+ @reader.close
16
+ @connection.close
17
+ end
18
+
19
+ it { @reader.should respond_to(:fields) }
20
+
21
+ describe 'fields' do
22
+
23
+ it 'should return the correct fields in the reader' do
24
+ # we downcase the field names as some drivers such as do_derby, do_h2,
25
+ # do_hsqldb return the field names as uppercase
26
+ @reader.fields.map{ |f| f.downcase }.should == ['code', 'name']
27
+ end
28
+
29
+ end
30
+
31
+ it { @reader.should respond_to(:values) }
32
+
33
+ describe 'values' do
34
+
35
+ describe 'when the reader is uninitialized' do
36
+
37
+ it 'should raise an error' do
38
+ lambda { @reader.values }.should raise_error
39
+ end
40
+
41
+ end
42
+
43
+ describe 'when the reader is moved to the first result' do
44
+
45
+ before do
46
+ @reader.next!
47
+ end
48
+
49
+ it 'should return the correct first set of in the reader' do
50
+ @reader.values.should == ["W0000001", "Widget 1"]
51
+ end
52
+
53
+ end
54
+
55
+ describe 'when the reader is moved to the second result' do
56
+
57
+ before do
58
+ @reader.next!; @reader.next!
59
+ end
60
+
61
+ it 'should return the correct first set of in the reader' do
62
+ @reader.values.should == ["W0000002", "Widget 2"]
63
+ end
64
+
65
+ end
66
+
67
+ describe 'when the reader is moved to the end' do
68
+
69
+ before do
70
+ while @reader.next! ; end
71
+ end
72
+
73
+ it 'should raise an error again' do
74
+ lambda { @reader.values }.should raise_error
75
+ end
76
+ end
77
+
78
+ end
79
+
80
+ it { @reader.should respond_to(:close) }
81
+
82
+ describe 'close' do
83
+
84
+ describe 'on an open reader' do
85
+
86
+ it 'should return true' do
87
+ @reader.close.should be_true
88
+ end
89
+
90
+ end
91
+
92
+ describe 'on an already closed reader' do
93
+
94
+ before do
95
+ @reader.close
96
+ end
97
+
98
+ it 'should return false' do
99
+ @reader.close.should be_false
100
+ end
101
+
102
+ end
103
+
104
+ end
105
+
106
+ it { @reader.should respond_to(:next!) }
107
+
108
+ describe 'next!' do
109
+
110
+ describe 'successfully moving the cursor initially' do
111
+
112
+ it 'should return true' do
113
+ @reader.next!.should be_true
114
+ end
115
+
116
+ end
117
+
118
+ describe 'moving the cursor' do
119
+
120
+ before do
121
+ @reader.next!
122
+ end
123
+
124
+ it 'should move the cursor to the next value' do
125
+ lambda { @reader.next! }.should change(@reader, :values).
126
+ from(["W0000001", "Widget 1"]).
127
+ to(["W0000002", "Widget 2"])
128
+ end
129
+
130
+ end
131
+
132
+ describe 'arriving at the end of the reader' do
133
+
134
+ before do
135
+ while @reader.next!; end
136
+ end
137
+
138
+ it 'should return false when the end is reached' do
139
+ @reader.next!.should be_false
140
+ end
141
+
142
+ end
143
+
144
+ end
145
+
146
+ it { @reader.should respond_to(:field_count) }
147
+
148
+ describe 'field_count' do
149
+
150
+ it 'should count the number of fields' do
151
+ @reader.field_count.should == 2
152
+ end
153
+
154
+ end
155
+
156
+ end