data_objects 0.10.0 → 0.10.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/ChangeLog.markdown +20 -0
  2. data/LICENSE +1 -29
  3. data/README.markdown +16 -2
  4. data/Rakefile +41 -7
  5. data/lib/data_objects.rb +3 -2
  6. data/lib/data_objects/byte_array.rb +6 -0
  7. data/lib/data_objects/connection.rb +21 -9
  8. data/lib/data_objects/logger.rb +15 -15
  9. data/lib/data_objects/pooling.rb +250 -0
  10. data/lib/data_objects/reader.rb +16 -0
  11. data/lib/data_objects/spec/bacon.rb +9 -0
  12. data/lib/data_objects/spec/command_spec.rb +54 -47
  13. data/lib/data_objects/spec/connection_spec.rb +119 -30
  14. data/lib/data_objects/spec/encoding_spec.rb +64 -6
  15. data/lib/data_objects/spec/helpers/immediate_red_green_output.rb +59 -0
  16. data/lib/data_objects/spec/helpers/pending.rb +22 -0
  17. data/lib/data_objects/spec/helpers/ssl.rb +21 -0
  18. data/lib/data_objects/spec/reader_spec.rb +47 -24
  19. data/lib/data_objects/spec/result_spec.rb +10 -19
  20. data/lib/data_objects/spec/typecast/array_spec.rb +16 -20
  21. data/lib/data_objects/spec/typecast/bigdecimal_spec.rb +16 -24
  22. data/lib/data_objects/spec/typecast/boolean_spec.rb +16 -24
  23. data/lib/data_objects/spec/typecast/byte_array_spec.rb +11 -15
  24. data/lib/data_objects/spec/typecast/class_spec.rb +7 -11
  25. data/lib/data_objects/spec/typecast/date_spec.rb +17 -25
  26. data/lib/data_objects/spec/typecast/datetime_spec.rb +18 -26
  27. data/lib/data_objects/spec/typecast/float_spec.rb +19 -27
  28. data/lib/data_objects/spec/typecast/integer_spec.rb +10 -14
  29. data/lib/data_objects/spec/typecast/nil_spec.rb +18 -30
  30. data/lib/data_objects/spec/typecast/other_spec.rb +45 -0
  31. data/lib/data_objects/spec/typecast/range_spec.rb +16 -20
  32. data/lib/data_objects/spec/typecast/string_spec.rb +72 -13
  33. data/lib/data_objects/spec/typecast/time_spec.rb +11 -15
  34. data/lib/data_objects/utilities.rb +18 -0
  35. data/lib/data_objects/version.rb +1 -2
  36. data/spec/command_spec.rb +2 -2
  37. data/spec/connection_spec.rb +7 -5
  38. data/spec/do_mock2.rb +31 -0
  39. data/spec/pooling_spec.rb +162 -0
  40. data/spec/reader_spec.rb +7 -4
  41. data/spec/result_spec.rb +2 -2
  42. data/spec/spec_helper.rb +26 -5
  43. data/spec/transaction_spec.rb +11 -9
  44. data/tasks/metrics.rake +36 -0
  45. data/tasks/release.rake +10 -70
  46. data/tasks/spec.rake +16 -14
  47. data/tasks/yard.rake +9 -0
  48. data/tasks/yardstick.rake +19 -0
  49. metadata +53 -27
  50. data/HISTORY.markdown +0 -7
  51. data/Manifest.txt +0 -44
  52. data/spec/lib/pending_helpers.rb +0 -11
  53. data/spec/lib/rspec_immediate_feedback_formatter.rb +0 -53
  54. data/spec/lib/ssl_helpers.rb +0 -20
  55. data/tasks/gem.rake +0 -8
  56. data/tasks/install.rake +0 -13
@@ -1,36 +1,32 @@
1
- share_examples_for 'supporting Range' do
1
+ shared 'supporting Range' do
2
2
 
3
- include DataObjectsSpecHelpers
3
+ setup_test_environment
4
4
 
5
- before :all do
6
- setup_test_environment
7
- end
8
-
9
- before :each do
5
+ before do
10
6
  @connection = DataObjects::Connection.new(CONFIG.uri)
11
7
  end
12
8
 
13
- after :each do
9
+ after do
14
10
  @connection.close
15
11
  end
16
12
 
17
13
  describe 'passing a Range as a parameter in execute_reader' do
18
14
 
19
- before do
20
- @reader = @connection.create_command("SELECT * FROM widgets WHERE id between ?").execute_reader(2..5)
21
- end
15
+ before do
16
+ @reader = @connection.create_command("SELECT * FROM widgets WHERE id between ?").execute_reader(2..5)
17
+ end
22
18
 
23
- after do
24
- @reader.close
25
- end
19
+ after do
20
+ @reader.close
21
+ end
26
22
 
27
- it 'should return correct number of rows' do
28
- counter = 0
29
- while(@reader.next!) do
30
- counter += 1
31
- end
32
- counter.should == 4
23
+ it 'should return correct number of rows' do
24
+ counter = 0
25
+ while(@reader.next!) do
26
+ counter += 1
33
27
  end
28
+ counter.should == 4
29
+ end
34
30
 
35
31
  end
36
32
  end
@@ -1,16 +1,14 @@
1
- share_examples_for 'supporting String' do
1
+ # encoding: utf-8
2
2
 
3
- include DataObjectsSpecHelpers
3
+ shared 'supporting String' do
4
4
 
5
- before :all do
6
- setup_test_environment
7
- end
5
+ setup_test_environment
8
6
 
9
- before :each do
7
+ before do
10
8
  @connection = DataObjects::Connection.new(CONFIG.uri)
11
9
  end
12
10
 
13
- after :each do
11
+ after do
14
12
  @connection.close
15
13
  end
16
14
 
@@ -18,7 +16,7 @@ share_examples_for 'supporting String' do
18
16
 
19
17
  describe 'with automatic typecasting' do
20
18
 
21
- before do
19
+ before do
22
20
  @reader = @connection.create_command("SELECT code FROM widgets WHERE ad_description = ?").execute_reader('Buy this product now!')
23
21
  @reader.next!
24
22
  @values = @reader.values
@@ -29,7 +27,7 @@ share_examples_for 'supporting String' do
29
27
  end
30
28
 
31
29
  it 'should return the correctly typed result' do
32
- @values.first.should be_kind_of(String)
30
+ @values.first.should.be.kind_of(String)
33
31
  end
34
32
 
35
33
  it 'should return the correct result' do
@@ -40,7 +38,7 @@ share_examples_for 'supporting String' do
40
38
 
41
39
  describe 'with manual typecasting' do
42
40
 
43
- before do
41
+ before do
44
42
  @command = @connection.create_command("SELECT weight FROM widgets WHERE ad_description = ?")
45
43
  @command.set_types(String)
46
44
  @reader = @command.execute_reader('Buy this product now!')
@@ -53,7 +51,7 @@ share_examples_for 'supporting String' do
53
51
  end
54
52
 
55
53
  it 'should return the correctly typed result' do
56
- @values.first.should be_kind_of(String)
54
+ @values.first.should.be.kind_of(String)
57
55
  end
58
56
 
59
57
  it 'should return the correct result' do
@@ -66,7 +64,7 @@ share_examples_for 'supporting String' do
66
64
 
67
65
  describe 'writing a String' do
68
66
 
69
- before do
67
+ before do
70
68
  @reader = @connection.create_command("SELECT id FROM widgets WHERE id = ?").execute_reader("2")
71
69
  @reader.next!
72
70
  @values = @reader.values
@@ -78,7 +76,68 @@ share_examples_for 'supporting String' do
78
76
 
79
77
  it 'should return the correct entry' do
80
78
  # Some of the drivers starts autoincrementation from 0 not 1
81
- @values.first.should satisfy { |val| val == 1 or val == 2 }
79
+ @values.first.should.satisfy { |val| val == 1 or val == 2 }
80
+ end
81
+
82
+ end
83
+
84
+ describe 'writing and reading a multibyte String' do
85
+
86
+ ['Aslak Hellesøy',
87
+ 'Пётр Алексе́евич Рома́нов',
88
+ '歐陽龍'].each do |name|
89
+
90
+ before do
91
+ # SQL Server Unicode String Literals
92
+ @n = 'N' if defined?(DataObjects::SqlServer::Connection) && @connection.kind_of?(DataObjects::SqlServer::Connection)
93
+ end
94
+
95
+ it 'should write a multibyte String' do
96
+ @command = @connection.create_command('INSERT INTO users (name) VALUES(?)')
97
+ should.not.raise(DataObjects::DataError) { @command.execute_non_query(name) }
98
+ end
99
+
100
+ it 'should read back the multibyte String' do
101
+ @command = @connection.create_command('SELECT name FROM users WHERE name = ?')
102
+ @reader = @command.execute_reader(name)
103
+ @reader.next!
104
+ @reader.values.first.should == name
105
+ @reader.close
106
+ end
107
+
108
+ it 'should write a multibyte String (without query parameters)' do
109
+ @command = @connection.create_command("INSERT INTO users (name) VALUES(#{@n}\'#{name}\')")
110
+ should.not.raise(DataObjects::DataError) { @command.execute_non_query }
111
+ end
112
+
113
+ it 'should read back the multibyte String (without query parameters)' do
114
+ @command = @connection.create_command("SELECT name FROM users WHERE name = #{@n}\'#{name}\'")
115
+ @reader = @command.execute_reader
116
+ @reader.next!
117
+ @reader.values.first.should == name
118
+ @reader.close
119
+ end
120
+
121
+ end
122
+ end
123
+
124
+ class ::StringWithExtraPowers < String; end
125
+
126
+ describe 'writing a kind of (subclass of) String' do
127
+
128
+ before do
129
+ @reader = @connection.create_command("SELECT id FROM widgets WHERE id = ?").execute_reader(::StringWithExtraPowers.new("2"))
130
+ @reader.next!
131
+ @values = @reader.values
132
+ end
133
+
134
+ after do
135
+ @reader.close
136
+ end
137
+
138
+ it 'should return the correct entry' do
139
+ # Some of the drivers starts autoincrementation from 0 not 1
140
+ @values.first.should.satisfy { |val| val == 1 or val == 2 }
82
141
  end
83
142
 
84
143
  end
@@ -1,16 +1,12 @@
1
- share_examples_for 'supporting Time' do
1
+ shared 'supporting Time' do
2
2
 
3
- include DataObjectsSpecHelpers
3
+ setup_test_environment
4
4
 
5
- before :all do
6
- setup_test_environment
7
- end
8
-
9
- before :each do
5
+ before do
10
6
  @connection = DataObjects::Connection.new(CONFIG.uri)
11
7
  end
12
8
 
13
- after :each do
9
+ after do
14
10
  @connection.close
15
11
  end
16
12
 
@@ -18,7 +14,7 @@ share_examples_for 'supporting Time' do
18
14
 
19
15
  describe 'with manual typecasting' do
20
16
 
21
- before do
17
+ before do
22
18
  @command = @connection.create_command("SELECT release_date FROM widgets WHERE ad_description = ?")
23
19
  @command.set_types(Time)
24
20
  @reader = @command.execute_reader('Buy this product now!')
@@ -31,7 +27,7 @@ share_examples_for 'supporting Time' do
31
27
  end
32
28
 
33
29
  it 'should return the correctly typed result' do
34
- @values.first.should be_kind_of(Time)
30
+ @values.first.should.be.kind_of(Time)
35
31
  end
36
32
 
37
33
  it 'should return the correct result' do
@@ -42,7 +38,7 @@ share_examples_for 'supporting Time' do
42
38
 
43
39
  describe 'with manual typecasting a nil value' do
44
40
 
45
- before do
41
+ before do
46
42
  @command = @connection.create_command("SELECT release_timestamp FROM widgets WHERE id = ?")
47
43
  @command.set_types(Time)
48
44
  @reader = @command.execute_reader(9)
@@ -55,11 +51,11 @@ share_examples_for 'supporting Time' do
55
51
  end
56
52
 
57
53
  it 'should return a nil class' do
58
- @values.first.should be_kind_of(NilClass)
54
+ @values.first.should.be.kind_of(NilClass)
59
55
  end
60
56
 
61
57
  it 'should return nil' do
62
- @values.first.should be_nil
58
+ @values.first.should.be.nil
63
59
  end
64
60
 
65
61
  end
@@ -68,7 +64,7 @@ share_examples_for 'supporting Time' do
68
64
 
69
65
  describe 'writing an Time' do
70
66
 
71
- before do
67
+ before do
72
68
  @reader = @connection.create_command("SELECT id FROM widgets WHERE release_datetime = ? ORDER BY id").execute_reader(Time.local(2008, 2, 14, 00, 31, 12))
73
69
  @reader.next!
74
70
  @values = @reader.values
@@ -80,7 +76,7 @@ share_examples_for 'supporting Time' do
80
76
 
81
77
  it 'should return the correct entry' do
82
78
  #Some of the drivers starts autoincrementation from 0 not 1
83
- @values.first.should satisfy { |val| val == 1 or val == 0 }
79
+ @values.first.should.satisfy { |val| val == 1 or val == 0 }
84
80
  end
85
81
 
86
82
  end
@@ -0,0 +1,18 @@
1
+ # This is here to remove DataObject's dependency on Extlib.
2
+
3
+ module DataObjects
4
+ # @param name<String> The name of the constant to get, e.g. "Merb::Router".
5
+ #
6
+ # @return <Object> The constant corresponding to the name.
7
+ def self.full_const_get(name)
8
+ list = name.split("::")
9
+ list.shift if list.first.nil? || list.first.strip.empty?
10
+ obj = ::Object
11
+ list.each do |x|
12
+ # This is required because const_get tries to look for constants in the
13
+ # ancestor chain, but we only want constants that are HERE
14
+ obj = obj.const_defined?(x) ? obj.const_get(x) : obj.const_missing(x)
15
+ end
16
+ obj
17
+ end
18
+ end
@@ -1,4 +1,3 @@
1
1
  module DataObjects
2
- # Current DataObjects Gem version
3
- VERSION = "0.10.0" unless defined?(DataObjects::VERSION)
2
+ VERSION = '0.10.1'.freeze
4
3
  end
data/spec/command_spec.rb CHANGED
@@ -12,13 +12,13 @@ describe DataObjects::Command do
12
12
 
13
13
  %w{connection execute_non_query execute_reader set_types}.each do |meth|
14
14
  it "should respond to ##{meth}" do
15
- @command.should respond_to(meth.intern)
15
+ @command.should.respond_to(meth.intern)
16
16
  end
17
17
  end
18
18
 
19
19
  %w{execute_non_query execute_reader set_types}.each do |meth|
20
20
  it "should raise NotImplementedError on ##{meth}" do
21
- lambda { @command.send(meth.intern, nil) }.should raise_error(NotImplementedError)
21
+ should.raise(NotImplementedError) { @command.send(meth.intern, nil) }
22
22
  end
23
23
  end
24
24
 
@@ -11,7 +11,7 @@ describe DataObjects::Connection do
11
11
 
12
12
  %w{dispose create_command}.each do |meth|
13
13
  it "should respond to ##{meth}" do
14
- @connection.should respond_to(meth.intern)
14
+ @connection.should.respond_to(meth.intern)
15
15
  end
16
16
  end
17
17
 
@@ -29,11 +29,13 @@ describe DataObjects::Connection do
29
29
 
30
30
  it "should return the Connection specified by the scheme" do
31
31
  c = DataObjects::Connection.new(Addressable::URI.parse('mock://localhost/database'))
32
- c.should be_kind_of(DataObjects::Mock::Connection)
33
-
34
- c = DataObjects::Connection.new(Addressable::URI.parse('mock:jndi://jdbc/database'))
35
- c.should be_kind_of(DataObjects::Mock::Connection)
32
+ c.should.be.kind_of(DataObjects::Mock::Connection)
33
+ c.should.be.kind_of(DataObjects::Pooling)
36
34
  end
37
35
 
36
+ it "should return the Connection specified by the scheme without pooling" do
37
+ c = DataObjects::Connection.new(Addressable::URI.parse('java://jdbc/database?scheme=mock2'))
38
+ c.should.not.be.kind_of(DataObjects::Pooling)
39
+ end
38
40
  end
39
41
  end
data/spec/do_mock2.rb ADDED
@@ -0,0 +1,31 @@
1
+ module DataObjects
2
+
3
+ module Mock2
4
+ class Connection < DataObjects::Connection
5
+ def initialize(uri)
6
+ @uri = uri
7
+ end
8
+
9
+ def dispose
10
+ nil
11
+ end
12
+ end
13
+
14
+ class Command < DataObjects::Command
15
+ def execute_non_query(*args)
16
+ Result.new(self, 0, nil)
17
+ end
18
+
19
+ def execute_reader(*args)
20
+ Reader.new
21
+ end
22
+ end
23
+
24
+ class Result < DataObjects::Result
25
+ end
26
+
27
+ class Reader < DataObjects::Reader
28
+ end
29
+ end
30
+
31
+ end
@@ -0,0 +1,162 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
2
+ require 'timeout'
3
+
4
+ describe "DataObjects::Pooling" do
5
+ before do
6
+
7
+ Object.send(:remove_const, :Person) if defined?(Person)
8
+ class ::Person
9
+ include DataObjects::Pooling
10
+
11
+ attr_accessor :name
12
+
13
+ def initialize(name)
14
+ @name = name
15
+ end
16
+
17
+ def dispose
18
+ @name = nil
19
+ end
20
+ end
21
+
22
+ Object.send(:remove_const, :Overwriter) if defined?(Overwriter)
23
+ class ::Overwriter
24
+
25
+ def self.new(*args)
26
+ instance = allocate
27
+ instance.send(:initialize, *args)
28
+ instance.overwritten = true
29
+ instance
30
+ end
31
+
32
+ include DataObjects::Pooling
33
+
34
+ attr_accessor :name
35
+
36
+ def initialize(name)
37
+ @name = name
38
+ @overwritten = false
39
+ end
40
+
41
+ def overwritten?
42
+ @overwritten
43
+ end
44
+
45
+ def overwritten=(value)
46
+ @overwritten = value
47
+ end
48
+
49
+ class << self
50
+ remove_method :pool_size if instance_methods(false).any? { |m| m.to_sym == :pool_size }
51
+ def pool_size
52
+ pool_size = if RUBY_PLATFORM =~ /java/
53
+ 20
54
+ else
55
+ 2
56
+ end
57
+ pool_size
58
+ end
59
+ end
60
+
61
+ def dispose
62
+ @name = nil
63
+ end
64
+ end
65
+ end
66
+
67
+ after do
68
+ DataObjects::Pooling.lock.synchronize do
69
+ DataObjects::Pooling.pools.each do |pool|
70
+ pool.lock.synchronize do
71
+ pool.dispose
72
+ end
73
+ end
74
+ end
75
+ end
76
+
77
+ it "should maintain a size of 1" do
78
+ bob = Person.new('Bob')
79
+ fred = Person.new('Fred')
80
+ ted = Person.new('Ted')
81
+
82
+ Person.__pools.each do |args, pool|
83
+ pool.size.should == 1
84
+ end
85
+
86
+ bob.release
87
+ fred.release
88
+ ted.release
89
+
90
+ Person.__pools.each do |args, pool|
91
+ pool.size.should == 1
92
+ end
93
+ end
94
+
95
+ it "should track the initialized pools" do
96
+ bob = Person.new('Bob') # Ensure the pool is "primed"
97
+ bob.name.should == 'Bob'
98
+ bob.instance_variable_get(:@__pool).should.not.be.nil
99
+ Person.__pools.size.should == 1
100
+ bob.release
101
+ Person.__pools.size.should == 1
102
+
103
+ DataObjects::Pooling::pools.should.not.be.empty
104
+
105
+ sleep(1.2)
106
+
107
+ # NOTE: This assertion is commented out, as our MockConnection objects are
108
+ # currently in the pool.
109
+ DataObjects::Pooling::pools.should.be.empty
110
+ bob.name.should == nil
111
+ end
112
+
113
+ it "should allow you to overwrite Class#new" do
114
+ bob = Overwriter.new('Bob')
115
+ bob.should.be.overwritten
116
+ bob.release
117
+ end
118
+
119
+ it "should allow multiple threads to access the pool" do
120
+ t1 = Thread.new do
121
+ bob = Person.new('Bob')
122
+ sleep(1)
123
+ bob.release
124
+ end
125
+
126
+ lambda do
127
+ bob = Person.new('Bob')
128
+ t1.join
129
+ bob.release
130
+ end.should.not.raise(DataObjects::Pooling::InvalidResourceError)
131
+ end
132
+
133
+ it "should allow you to flush a pool" do
134
+ bob = Overwriter.new('Bob')
135
+ Overwriter.new('Bob').release
136
+ bob.release
137
+
138
+ bob.name.should == 'Bob'
139
+
140
+ Overwriter.__pools[['Bob']].size.should == 2
141
+ Overwriter.__pools[['Bob']].flush!
142
+ Overwriter.__pools[['Bob']].size.should == 0
143
+
144
+ bob.name.should.be.nil
145
+ end
146
+
147
+ it "should wake up the scavenger thread when exiting" do
148
+ bob = Person.new('Bob')
149
+ bob.release
150
+ DataObjects.exiting = true
151
+ sleep(0.1)
152
+ DataObjects::Pooling.scavenger?.should.be.false
153
+ end
154
+
155
+ it "should be able to detach an instance from the pool" do
156
+ bob = Person.new('Bob')
157
+ Person.__pools[['Bob']].size.should == 1
158
+ bob.detach
159
+ Person.__pools[['Bob']].size.should == 0
160
+ end
161
+
162
+ end