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.
- data/ChangeLog.markdown +20 -0
- data/LICENSE +1 -29
- data/README.markdown +16 -2
- data/Rakefile +41 -7
- data/lib/data_objects.rb +3 -2
- data/lib/data_objects/byte_array.rb +6 -0
- data/lib/data_objects/connection.rb +21 -9
- data/lib/data_objects/logger.rb +15 -15
- data/lib/data_objects/pooling.rb +250 -0
- data/lib/data_objects/reader.rb +16 -0
- data/lib/data_objects/spec/bacon.rb +9 -0
- data/lib/data_objects/spec/command_spec.rb +54 -47
- data/lib/data_objects/spec/connection_spec.rb +119 -30
- data/lib/data_objects/spec/encoding_spec.rb +64 -6
- data/lib/data_objects/spec/helpers/immediate_red_green_output.rb +59 -0
- data/lib/data_objects/spec/helpers/pending.rb +22 -0
- data/lib/data_objects/spec/helpers/ssl.rb +21 -0
- data/lib/data_objects/spec/reader_spec.rb +47 -24
- data/lib/data_objects/spec/result_spec.rb +10 -19
- data/lib/data_objects/spec/typecast/array_spec.rb +16 -20
- data/lib/data_objects/spec/typecast/bigdecimal_spec.rb +16 -24
- data/lib/data_objects/spec/typecast/boolean_spec.rb +16 -24
- data/lib/data_objects/spec/typecast/byte_array_spec.rb +11 -15
- data/lib/data_objects/spec/typecast/class_spec.rb +7 -11
- data/lib/data_objects/spec/typecast/date_spec.rb +17 -25
- data/lib/data_objects/spec/typecast/datetime_spec.rb +18 -26
- data/lib/data_objects/spec/typecast/float_spec.rb +19 -27
- data/lib/data_objects/spec/typecast/integer_spec.rb +10 -14
- data/lib/data_objects/spec/typecast/nil_spec.rb +18 -30
- data/lib/data_objects/spec/typecast/other_spec.rb +45 -0
- data/lib/data_objects/spec/typecast/range_spec.rb +16 -20
- data/lib/data_objects/spec/typecast/string_spec.rb +72 -13
- data/lib/data_objects/spec/typecast/time_spec.rb +11 -15
- data/lib/data_objects/utilities.rb +18 -0
- data/lib/data_objects/version.rb +1 -2
- data/spec/command_spec.rb +2 -2
- data/spec/connection_spec.rb +7 -5
- data/spec/do_mock2.rb +31 -0
- data/spec/pooling_spec.rb +162 -0
- data/spec/reader_spec.rb +7 -4
- data/spec/result_spec.rb +2 -2
- data/spec/spec_helper.rb +26 -5
- data/spec/transaction_spec.rb +11 -9
- data/tasks/metrics.rake +36 -0
- data/tasks/release.rake +10 -70
- data/tasks/spec.rake +16 -14
- data/tasks/yard.rake +9 -0
- data/tasks/yardstick.rake +19 -0
- metadata +53 -27
- data/HISTORY.markdown +0 -7
- data/Manifest.txt +0 -44
- data/spec/lib/pending_helpers.rb +0 -11
- data/spec/lib/rspec_immediate_feedback_formatter.rb +0 -53
- data/spec/lib/ssl_helpers.rb +0 -20
- data/tasks/gem.rake +0 -8
- data/tasks/install.rake +0 -13
@@ -1,36 +1,32 @@
|
|
1
|
-
|
1
|
+
shared 'supporting Range' do
|
2
2
|
|
3
|
-
|
3
|
+
setup_test_environment
|
4
4
|
|
5
|
-
before
|
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
|
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
|
-
|
20
|
-
|
21
|
-
|
15
|
+
before do
|
16
|
+
@reader = @connection.create_command("SELECT * FROM widgets WHERE id between ?").execute_reader(2..5)
|
17
|
+
end
|
22
18
|
|
23
|
-
|
24
|
-
|
25
|
-
|
19
|
+
after do
|
20
|
+
@reader.close
|
21
|
+
end
|
26
22
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
-
|
1
|
+
# encoding: utf-8
|
2
2
|
|
3
|
-
|
3
|
+
shared 'supporting String' do
|
4
4
|
|
5
|
-
|
6
|
-
setup_test_environment
|
7
|
-
end
|
5
|
+
setup_test_environment
|
8
6
|
|
9
|
-
before
|
7
|
+
before do
|
10
8
|
@connection = DataObjects::Connection.new(CONFIG.uri)
|
11
9
|
end
|
12
10
|
|
13
|
-
after
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
-
|
1
|
+
shared 'supporting Time' do
|
2
2
|
|
3
|
-
|
3
|
+
setup_test_environment
|
4
4
|
|
5
|
-
before
|
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
|
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
|
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
|
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
|
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
|
54
|
+
@values.first.should.be.kind_of(NilClass)
|
59
55
|
end
|
60
56
|
|
61
57
|
it 'should return nil' do
|
62
|
-
@values.first.should
|
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
|
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
|
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
|
data/lib/data_objects/version.rb
CHANGED
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
|
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
|
-
|
21
|
+
should.raise(NotImplementedError) { @command.send(meth.intern, nil) }
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
data/spec/connection_spec.rb
CHANGED
@@ -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
|
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
|
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
|