data_objects 0.10.0 → 0.10.1

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 (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