data_objects 0.9.9 → 0.9.10

Sign up to get free protection for your applications and to get access to all the features.
data/Manifest.txt CHANGED
@@ -13,7 +13,6 @@ lib/data_objects/logger.rb
13
13
  lib/data_objects/quoting.rb
14
14
  lib/data_objects/reader.rb
15
15
  lib/data_objects/result.rb
16
- lib/data_objects/support/pooling.rb
17
16
  lib/data_objects/transaction.rb
18
17
  lib/data_objects/uri.rb
19
18
  lib/data_objects/version.rb
@@ -25,5 +24,5 @@ spec/reader_spec.rb
25
24
  spec/result_spec.rb
26
25
  spec/spec.opts
27
26
  spec/spec_helper.rb
28
- spec/support/pooling_spec.rb
29
27
  spec/transaction_spec.rb
28
+ spec/uri_spec.rb
data/Rakefile CHANGED
@@ -1,9 +1,12 @@
1
- require 'rubygems'
2
1
  require 'pathname'
2
+ require 'rubygems'
3
3
  require 'spec/rake/spectask'
4
4
  require 'lib/data_objects/version'
5
5
 
6
- ROOT = Pathname(__FILE__).dirname.expand_path
6
+ ROOT = Pathname(__FILE__).dirname.expand_path
7
+ JRUBY = RUBY_PLATFORM =~ /java/
8
+ WINDOWS = Gem.win_platform?
9
+ SUDO = (WINDOWS || JRUBY) ? '' : ('sudo' unless ENV['SUDOLESS'])
7
10
 
8
11
  AUTHOR = "Yehuda Katz"
9
12
  EMAIL = "wycats@gmail.com"
@@ -17,25 +20,29 @@ PROJECT_NAME = "dorb"
17
20
  PROJECT_URL = "http://rubyforge.org/projects/dorb"
18
21
  PROJECT_DESCRIPTION = PROJECT_SUMMARY = "The Core DataObjects class"
19
22
 
20
- DRIVER = false
23
+ JAVA_DRIVER = false
24
+
25
+ # RCov is run by default, except on the JRuby platform, or if NO_RCOV env is true
26
+ RUN_RCOV = JRUBY ? false : (ENV.has_key?('NO_RCOV') ? ENV['NO_RCOV'] != 'true' : true)
21
27
 
22
28
  if (tasks_dir = ROOT.parent + 'tasks').directory?
23
29
  require tasks_dir + 'hoe'
24
30
  end
25
31
 
26
- # Installation
32
+ def sudo_gem(cmd)
33
+ sh "#{SUDO} #{RUBY} -S gem #{cmd}", :verbose => false
34
+ end
27
35
 
28
- WINDOWS = (RUBY_PLATFORM =~ /mswin|mingw|cygwin/) rescue nil
29
- SUDO = WINDOWS ? '' : ('sudo' unless ENV['SUDOLESS'])
36
+ # Installation
30
37
 
31
- desc "Install #{GEM_NAME} #{GEM_VERSION} (default ruby)"
38
+ desc "Install #{GEM_NAME} #{GEM_VERSION}"
32
39
  task :install => [ :package ] do
33
- sh %{#{SUDO} gem install --local pkg/#{GEM_NAME}-#{GEM_VERSION} --no-update-sources}, :verbose => false
40
+ sudo_gem "install --local pkg/#{GEM_NAME}-#{GEM_VERSION} --no-update-sources"
34
41
  end
35
42
 
36
- desc "Uninstall #{GEM_NAME} #{GEM_VERSION} (default ruby)"
43
+ desc "Uninstall #{GEM_NAME} #{GEM_VERSION}"
37
44
  task :uninstall => [ :clobber ] do
38
- sh "#{SUDO} gem uninstall #{GEM_NAME} -v#{GEM_VERSION} -I -x", :verbose => false
45
+ sudo_gem "uninstall #{GEM_NAME} -v#{GEM_VERSION} -I -x"
39
46
  end
40
47
 
41
48
  # Specs
@@ -46,7 +53,7 @@ Spec::Rake::SpecTask.new(:spec) do |t|
46
53
  t.spec_files = Pathname.glob(ENV['FILES'] || 'spec/**/*_spec.rb')
47
54
 
48
55
  begin
49
- t.rcov = ENV.has_key?('NO_RCOV') ? ENV['NO_RCOV'] != 'true' : true
56
+ t.rcov = RUN_RCOV
50
57
  t.rcov_opts << '--exclude' << 'spec'
51
58
  t.rcov_opts << '--text-summary'
52
59
  t.rcov_opts << '--sort' << 'coverage' << '--sort-reverse'
@@ -55,14 +62,6 @@ Spec::Rake::SpecTask.new(:spec) do |t|
55
62
  end
56
63
  end
57
64
 
58
- # JRuby
59
- namespace :jruby do
60
- desc "Install #{GEM_NAME} #{GEM_VERSION} with JRuby"
61
- task :install => [ :package ] do
62
- sh %{#{SUDO} jruby -S gem install --local pkg/#{GEM_NAME}-#{GEM_VERSION} --no-update-sources}, :verbose => false
63
- end
64
- end
65
-
66
65
  namespace :ci do
67
66
 
68
67
  task :prepare do
@@ -88,4 +87,4 @@ namespace :ci do
88
87
 
89
88
  end
90
89
 
91
- task :ci => ["ci:spec"]
90
+ task :ci => ["ci:spec"]
@@ -8,16 +8,36 @@ end
8
8
  module DataObjects
9
9
  class Connection
10
10
 
11
- def self.new(uri)
12
- uri = DataObjects::URI::parse(uri)
11
+ def self.new(uri_s)
12
+ uri = DataObjects::URI::parse(uri_s)
13
+
14
+ case uri.scheme.to_sym
15
+ when :java
16
+ warn 'JNDI URLs (connection strings) are only for use with JRuby' unless RUBY_PLATFORM =~ /java/
17
+ # TODO: handle jndi connection strings
18
+ when :jdbc
19
+ warn 'JDBC URLs (connection strings) are only for use with JRuby' unless RUBY_PLATFORM =~ /java/
20
+
21
+ driver_name = if uri.path.split(':').first == 'sqlite'
22
+ 'sqlite3'
23
+ elsif uri.path.split(':').first == 'postgresql'
24
+ 'postgres'
25
+ else
26
+ uri.path.split(':').first
27
+ end
13
28
 
14
- if uri.scheme == 'jdbc'
15
- driver_name = uri.path.split(':').first
29
+ conn_uri = uri_s # NOTE: for now, do not reformat this JDBC connection
30
+ # string -- or, in other words, do not let
31
+ # DataObjects::URI#to_s be called -- as it is not
32
+ # correctly handling JDBC URLs, and in doing so, causing
33
+ # java.sql.DriverManager.getConnection to throw a
34
+ # 'No suitable driver found for...' exception.
16
35
  else
17
- driver_name = uri.scheme.capitalize
36
+ driver_name = uri.scheme
37
+ conn_uri = uri
18
38
  end
19
39
 
20
- DataObjects.const_get(driver_name.capitalize)::Connection.new(uri)
40
+ DataObjects.const_get(driver_name.capitalize)::Connection.new(conn_uri)
21
41
  end
22
42
 
23
43
  def self.inherited(target)
@@ -45,6 +45,7 @@ module DataObjects
45
45
  when Array then quote_array(value)
46
46
  when Range then quote_range(value)
47
47
  when Symbol then quote_symbol(value)
48
+ when Regexp then quote_regexp(value)
48
49
  else
49
50
  if value.respond_to?(:to_sql)
50
51
  value.to_sql
@@ -93,6 +94,10 @@ module DataObjects
93
94
  def quote_range(value)
94
95
  "#{quote_value(value.first)} AND #{quote_value(value.last)}"
95
96
  end
97
+
98
+ def quote_regexp(value)
99
+ quote_string(value.source)
100
+ end
96
101
  end
97
102
 
98
103
  end
@@ -8,7 +8,7 @@ module DataObjects
8
8
  def self.parse(uri)
9
9
  return uri if uri.kind_of?(self)
10
10
  uri = Addressable::URI::parse(uri) unless uri.kind_of?(Addressable::URI)
11
- self.new(uri.scheme, uri.user, uri.password, uri.host, uri.port, uri.path, uri.query, uri.fragment)
11
+ self.new(uri.scheme, uri.user, uri.password, uri.host, uri.port, uri.path, uri.query_values, uri.fragment)
12
12
  end
13
13
 
14
14
  def to_s
@@ -22,7 +22,11 @@ module DataObjects
22
22
  string << "#{host}" if host
23
23
  string << ":#{port}" if port
24
24
  string << path.to_s
25
- string << "?#{query}" if query
25
+ if query
26
+ string << "?" << query.map do |key, value|
27
+ "#{key}=#{value}"
28
+ end.join("&")
29
+ end
26
30
  string << "##{fragment}" if fragment
27
31
  string
28
32
  end
@@ -1,3 +1,3 @@
1
1
  module DataObjects
2
- VERSION = "0.9.9" unless defined?(DataObjects::VERSION)
2
+ VERSION = "0.9.10" unless defined?(DataObjects::VERSION)
3
3
  end
data/lib/data_objects.rb CHANGED
@@ -3,7 +3,7 @@ require 'rubygems'
3
3
  gem 'extlib', '~>0.9.8'
4
4
  require 'extlib'
5
5
 
6
- require File.expand_path(File.join(File.dirname(__FILE__), 'data_objects', 'support', 'pooling'))
6
+ require File.expand_path(File.join(File.dirname(__FILE__), 'data_objects', 'version'))
7
7
  require File.expand_path(File.join(File.dirname(__FILE__), 'data_objects', 'logger'))
8
8
  require File.expand_path(File.join(File.dirname(__FILE__), 'data_objects', 'connection'))
9
9
  require File.expand_path(File.join(File.dirname(__FILE__), 'data_objects', 'uri'))
@@ -40,8 +40,22 @@ describe DataObjects::Connection do
40
40
  c = DataObjects::Connection.new(Addressable::URI.parse('mock://localhost/database'))
41
41
  c.should be_kind_of(DataObjects::Mock::Connection)
42
42
 
43
- c = DataObjects::Connection.new(Addressable::URI.parse('jdbc:mock://localhost/database'))
44
- c.should be_kind_of(DataObjects::Mock::Connection)
43
+ c = DataObjects::Connection.new(Addressable::URI.parse('mock:jndi://jdbc/database'))
44
+ #c.should be_kind_of(DataObjects::Mock::Connection)
45
+ end
46
+
47
+ it "should return the Connection using username" do
48
+ c = DataObjects::Connection.new(Addressable::URI.parse('mock://root@localhost/database'))
49
+ c.instance_variable_get(:@uri).user.should == 'root'
50
+ c.instance_variable_get(:@uri).password.should be_nil
51
+
52
+ c = DataObjects::Connection.new(Addressable::URI.parse('mock://root:@localhost/database'))
53
+ c.instance_variable_get(:@uri).user.should == 'root'
54
+ c.instance_variable_get(:@uri).password.should == ''
55
+
56
+ c = DataObjects::Connection.new(Addressable::URI.parse('mock://root:pwd@localhost/database'))
57
+ c.instance_variable_get(:@uri).user.should == 'root'
58
+ c.instance_variable_get(:@uri).password.should == 'pwd'
45
59
  end
46
60
  end
47
61
  end
data/spec/uri_spec.rb ADDED
@@ -0,0 +1,44 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
2
+
3
+ describe DataObjects::URI do
4
+ before do
5
+ @uri = DataObjects::URI.parse('mock://username:password@localhost:12345/path?encoding=utf8#fragment')
6
+ end
7
+
8
+ it "should parse the scheme part" do
9
+ @uri.scheme.should == "mock"
10
+ end
11
+
12
+ it "should parse the user part" do
13
+ @uri.user.should == "username"
14
+ end
15
+
16
+ it "should parse the password part" do
17
+ @uri.password.should == "password"
18
+ end
19
+
20
+ it "should parse the host part" do
21
+ @uri.host.should == "localhost"
22
+ end
23
+
24
+ it "should parse the port part" do
25
+ @uri.port.should == 12345
26
+ end
27
+
28
+ it "should parse the path part" do
29
+ @uri.path.should == "/path"
30
+ end
31
+
32
+ it "should parse the query part" do
33
+ @uri.query.should == { "encoding" => "utf8" }
34
+ end
35
+
36
+ it "should parse the fragment part" do
37
+ @uri.fragment.should == "fragment"
38
+ end
39
+
40
+ it "should provide a correct string representation" do
41
+ @uri.to_s.should == 'mock://username:password@localhost:12345/path?encoding=utf8#fragment'
42
+ end
43
+
44
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: data_objects
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.9
4
+ version: 0.9.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yehuda Katz
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-11-27 00:00:00 -08:00
12
+ date: 2009-01-04 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -69,7 +69,6 @@ files:
69
69
  - lib/data_objects/quoting.rb
70
70
  - lib/data_objects/reader.rb
71
71
  - lib/data_objects/result.rb
72
- - lib/data_objects/support/pooling.rb
73
72
  - lib/data_objects/transaction.rb
74
73
  - lib/data_objects/uri.rb
75
74
  - lib/data_objects/version.rb
@@ -81,8 +80,8 @@ files:
81
80
  - spec/result_spec.rb
82
81
  - spec/spec.opts
83
82
  - spec/spec_helper.rb
84
- - spec/support/pooling_spec.rb
85
83
  - spec/transaction_spec.rb
84
+ - spec/uri_spec.rb
86
85
  has_rdoc: true
87
86
  homepage: http://rubyforge.org/projects/dorb
88
87
  post_install_message:
@@ -1,236 +0,0 @@
1
- require 'set'
2
-
3
- class Object
4
- # ==== Notes
5
- # Provides pooling support to class it got included in.
6
- #
7
- # Pooling of objects is a faster way of aquiring instances
8
- # of objects compared to regular allocation and initialization
9
- # because instances are keeped in memory reused.
10
- #
11
- # Classes that include Pooling module have re-defined new
12
- # method that returns instances acquired from pool.
13
- #
14
- # Term resource is used for any type of poolable objects
15
- # and should NOT be thought as DataMapper Resource or
16
- # ActiveResource resource and such.
17
- #
18
- # In Data Objects connections are pooled so that it is
19
- # unnecessary to allocate and initialize connection object
20
- # each time connection is needed, like per request in a
21
- # web application.
22
- #
23
- # Pool obviously has to be thread safe because state of
24
- # object is reset when it is released.
25
- module Pooling
26
- def self.included(base)
27
- base.send(:extend, ClassMethods)
28
- end
29
-
30
- module ClassMethods
31
- # ==== Notes
32
- # Initializes the pool and returns it.
33
- #
34
- # ==== Parameters
35
- # size_limit<Fixnum>:: maximum size of the pool.
36
- #
37
- # ==== Returns
38
- # <ResourcePool>:: initialized pool
39
- def initialize_pool(size_limit, options = {})
40
- @__pool.flush! if @__pool
41
-
42
- @__pool = ResourcePool.new(size_limit, self, options)
43
- end
44
-
45
- # ==== Notes
46
- # Instances of poolable resource are acquired from
47
- # pool. This quires a new instance from pool and
48
- # returns it.
49
- #
50
- # ==== Returns
51
- # Resource instance acquired from the pool.
52
- #
53
- # ==== Raises
54
- # ArgumentError:: when pool is exhausted and no instance
55
- # can be acquired.
56
- def new
57
- pool.acquire
58
- end
59
-
60
- # ==== Notes
61
- # Returns pool for this resource class.
62
- # Initialization is done when necessary.
63
- # Default size limit of the pool is 10.
64
- #
65
- # ==== Returns
66
- # <Object::Pooling::ResourcePool>:: pool for this resource class.
67
- def pool
68
- @__pool ||= ResourcePool.new(10, self)
69
- end
70
- end
71
-
72
- # ==== Notes
73
- # Pool
74
- #
75
- class ResourcePool
76
- attr_reader :size_limit, :class_of_resources, :expiration_period
77
-
78
- # ==== Notes
79
- # Initializes resource pool.
80
- #
81
- # ==== Parameters
82
- # size_limit<Fixnum>:: maximum number of resources in the pool.
83
- # class_of_resources<Class>:: class of resource.
84
- #
85
- # ==== Raises
86
- # ArgumentError:: when class of resource does not implement
87
- # dispose instance method or is not a Class.
88
- def initialize(size_limit, class_of_resources, options)
89
- raise ArgumentError.new("Expected class of resources to be instance of Class, got: #{class_of_resources.class}") unless class_of_resources.is_a?(Class)
90
- raise ArgumentError.new("Class #{class_of_resources} must implement dispose instance method to be poolable.") unless class_of_resources.instance_methods.include?("dispose")
91
-
92
- @size_limit = size_limit
93
- @class_of_resources = class_of_resources
94
-
95
- @reserved = Set.new
96
- @available = []
97
- @lock = Mutex.new
98
-
99
- initialization_args = options.delete(:initialization_args) || []
100
-
101
- @expiration_period = options.delete(:expiration_period) || 60
102
- @initialization_args = [*initialization_args]
103
-
104
- @pool_expiration_thread = Thread.new do
105
- while true
106
- release_outdated
107
-
108
- sleep (@expiration_period + 1)
109
- end
110
- end
111
- end
112
-
113
- # ==== Notes
114
- # Current size of pool: number of already reserved
115
- # resources.
116
- def size
117
- @reserved.size
118
- end
119
-
120
- # ==== Notes
121
- # Indicates if pool has resources to acquire.
122
- #
123
- # ==== Returns
124
- # <Boolean>:: true if pool has resources can be acquired,
125
- # false otherwise.
126
- def available?
127
- @reserved.size < size_limit
128
- end
129
-
130
- # ==== Notes
131
- # Acquires last used available resource and returns it.
132
- # If no resources available, current implementation
133
- # throws an exception.
134
- def acquire
135
- @lock.synchronize do
136
- if available?
137
- instance = prepair_available_resource
138
- @reserved << instance
139
-
140
- instance
141
- else
142
- raise RuntimeError
143
- end
144
- end
145
- end
146
-
147
- # ==== Notes
148
- # Releases previously acquired instance.
149
- #
150
- # ==== Parameters
151
- # instance <Anything>:: previosly acquired instance.
152
- #
153
- # ==== Raises
154
- # RuntimeError:: when given not pooled instance.
155
- def release(instance)
156
- @lock.synchronize do
157
- if @reserved.include?(instance)
158
- @reserved.delete(instance)
159
- instance.dispose
160
- @available << instance
161
- else
162
- raise RuntimeError
163
- end
164
- end
165
- end
166
-
167
- # ==== Notes
168
- # Releases all objects in the pool.
169
- #
170
- # ==== Returns
171
- # nil
172
- def flush!
173
- @reserved.each do |instance|
174
- self.release(instance)
175
- end
176
-
177
- nil
178
- end
179
-
180
- # ==== Notes
181
- # Check if instance has been acquired from the pool.
182
- #
183
- # ==== Returns
184
- # <Boolean>:: true if given resource instance has been acquired from pool,
185
- # false otherwise.
186
- def acquired?(instance)
187
- @reserved.include?(instance)
188
- end
189
-
190
- # ==== Notes
191
- # Releases instances that haven't been in use and
192
- # hit the expiration period.
193
- #
194
- # ==== Returns
195
- # nil
196
- def release_outdated
197
- @reserved.each do |instance|
198
- release(instance) if time_to_release?(instance)
199
- end
200
-
201
- nil
202
- end
203
-
204
- # ==== Notes
205
- # Checks if pooled resource instance is outdated and
206
- # should be released.
207
- #
208
- # ==== Returns
209
- # <Boolean>:: true if instance should be released, false otherwise.
210
- def time_to_release?(instance)
211
- (Time.now - instance.instance_variable_get("@__pool_acquire_timestamp")) > @expiration_period
212
- end
213
-
214
- protected
215
-
216
- # ==== Notes
217
- # Either allocates new resource,
218
- # or takes last used available resource from
219
- # the pool.
220
- def prepair_available_resource
221
- if @available.size > 0
222
- res = @available.pop
223
- res.instance_variable_set("@__pool_acquire_timestamp", Time.now)
224
-
225
- res
226
- else
227
- res = @class_of_resources.allocate
228
- res.send(:initialize, *@initialization_args)
229
- res.instance_variable_set("@__pool_acquire_timestamp", Time.now)
230
-
231
- res
232
- end
233
- end
234
- end # ResourcePool
235
- end
236
- end
@@ -1,374 +0,0 @@
1
- require File.expand_path(File.join(File.dirname(__FILE__), '..', 'spec_helper'))
2
- require File.join(File.dirname(__FILE__), '..', '..', 'lib', 'data_objects', 'support', 'pooling')
3
- require 'timeout'
4
-
5
- # This implements dispose
6
- # and works perfectly with
7
- # pooling.
8
- class DisposableResource
9
- include Object::Pooling
10
- attr_reader :name
11
-
12
- def initialize(name = "")
13
- @name = name
14
- end
15
-
16
- def dispose
17
- @name = nil
18
- end
19
- end
20
-
21
- # This baby causes exceptions
22
- # to be raised when you use
23
- # it with pooling.
24
- class UndisposableResource
25
- end
26
-
27
- describe Object::Pooling::ResourcePool do
28
- before :each do
29
- @pool = Object::Pooling::ResourcePool.new(7, DisposableResource, :expiration_period => 50)
30
- end
31
-
32
- it "responds to flush!" do
33
- @pool.should respond_to(:flush!)
34
- end
35
-
36
- it "responds to acquire" do
37
- @pool.should respond_to(:acquire)
38
- end
39
-
40
- it "responds to release" do
41
- @pool.should respond_to(:release)
42
- end
43
-
44
- it "responds to :available?" do
45
- @pool.should respond_to(:available?)
46
- end
47
-
48
- it "has a size limit" do
49
- @pool.size_limit.should == 7
50
- end
51
-
52
- it "has initial size of zero" do
53
- @pool.size.should == 0
54
- end
55
-
56
- it "has a set of reserved resources" do
57
- @pool.instance_variable_get("@reserved").should be_empty
58
- end
59
-
60
- it "has a set of available resources" do
61
- @pool.instance_variable_get("@available").should be_empty
62
- end
63
-
64
- it "knows class of resources (objects) it works with" do
65
- @pool.class_of_resources.should == DisposableResource
66
- end
67
-
68
- it "raises exception when given anything but class for resources class" do
69
- lambda {
70
- @pool = Object::Pooling::ResourcePool.new(7, "Hooray!", {})
71
- }.should raise_error(ArgumentError, /class/)
72
- end
73
-
74
- it "requires class of resources (objects) it works with to have a dispose instance method" do
75
- lambda {
76
- @pool = Object::Pooling::ResourcePool.new(3, UndisposableResource, {})
77
- }.should raise_error(ArgumentError, /dispose/)
78
- end
79
-
80
- it "may take initialization arguments" do
81
- @pool = Object::Pooling::ResourcePool.new(7, DisposableResource, { :initialization_args => ["paper"] })
82
- @pool.instance_variable_get("@initialization_args").should == ["paper"]
83
- end
84
-
85
- it "may take expiration period option" do
86
- @pool = Object::Pooling::ResourcePool.new(7, DisposableResource, { :expiration_period => 100 })
87
- @pool.expiration_period.should == 100
88
- end
89
-
90
- it "has default expiration period of one minute" do
91
- @pool = Object::Pooling::ResourcePool.new(7, DisposableResource, {})
92
- @pool.expiration_period.should == 60
93
- end
94
-
95
- it "spawns a thread to dispose objects haven't been used for a while" do
96
- @pool = Object::Pooling::ResourcePool.new(7, DisposableResource, {})
97
- @pool.instance_variable_get("@pool_expiration_thread").should be_an_instance_of(Thread)
98
- end
99
- end
100
-
101
-
102
-
103
- describe "Acquire from constant size pool" do
104
- before :each do
105
- DisposableResource.initialize_pool(2)
106
- end
107
-
108
- after :each do
109
- DisposableResource.instance_variable_set("@__pool", nil)
110
- end
111
-
112
- it "increased size of the pool" do
113
- @time = DisposableResource.pool.acquire
114
- DisposableResource.pool.size.should == 1
115
- end
116
-
117
- it "places initialized instance in the reserved set" do
118
- @time = DisposableResource.pool.acquire
119
- DisposableResource.pool.instance_variable_get("@reserved").size.should == 1
120
- end
121
-
122
- it "raises an exception when pool size limit is hit" do
123
- @t1 = DisposableResource.pool.acquire
124
- @t2 = DisposableResource.pool.acquire
125
-
126
- lambda { DisposableResource.pool.acquire }.should raise_error(RuntimeError)
127
- end
128
-
129
- it "returns last released resource" do
130
- @t1 = DisposableResource.pool.acquire
131
- @t2 = DisposableResource.pool.acquire
132
- DisposableResource.pool.release(@t1)
133
-
134
- DisposableResource.pool.acquire.should == @t1
135
- end
136
-
137
- it "really truly returns last released resource" do
138
- @t1 = DisposableResource.pool.acquire
139
- DisposableResource.pool.release(@t1)
140
-
141
- @t2 = DisposableResource.pool.acquire
142
- DisposableResource.pool.release(@t2)
143
-
144
- @t3 = DisposableResource.pool.acquire
145
- DisposableResource.pool.release(@t3)
146
-
147
- DisposableResource.pool.acquire.should == @t1
148
- @t1.should == @t3
149
- end
150
-
151
- it "sets allocation timestamp on resource instance" do
152
- @t1 = DisposableResource.new
153
- @t1.instance_variable_get("@__pool_acquire_timestamp").should be_close(Time.now, 2)
154
- end
155
- end
156
-
157
-
158
-
159
- describe "Releasing from constant size pool" do
160
- before :each do
161
- DisposableResource.initialize_pool(2)
162
- end
163
-
164
- after :each do
165
- DisposableResource.instance_variable_set("@__pool", nil)
166
- end
167
-
168
- it "decreases size of the pool" do
169
- @t1 = DisposableResource.pool.acquire
170
- @t2 = DisposableResource.pool.acquire
171
- DisposableResource.pool.release(@t1)
172
-
173
- DisposableResource.pool.size.should == 1
174
- end
175
-
176
- it "raises an exception on attempt to releases object not in pool" do
177
- @t1 = DisposableResource.new
178
- @t2 = Set.new
179
-
180
- DisposableResource.pool.release(@t1)
181
- lambda { DisposableResource.pool.release(@t2) }.should raise_error(RuntimeError)
182
- end
183
-
184
- it "disposes released object" do
185
- @t1 = DisposableResource.pool.acquire
186
-
187
- @t1.should_receive(:dispose)
188
- DisposableResource.pool.release(@t1)
189
- end
190
-
191
- it "removes released object from reserved set" do
192
- @t1 = DisposableResource.pool.acquire
193
-
194
- lambda {
195
- DisposableResource.pool.release(@t1)
196
- }.should change(DisposableResource.pool.instance_variable_get("@reserved"), :size).by(-1)
197
- end
198
-
199
- it "returns released object back to available set" do
200
- @t1 = DisposableResource.pool.acquire
201
-
202
- lambda {
203
- DisposableResource.pool.release(@t1)
204
- }.should change(DisposableResource.pool.instance_variable_get("@available"), :size).by(1)
205
- end
206
-
207
- it "updates acquire timestamp on already allocated resource instance" do
208
- # acquire it once
209
- @t1 = DisposableResource.new
210
- # wait a bit
211
- sleep 3
212
-
213
- # check old timestamp
214
- @t1.instance_variable_get("@__pool_acquire_timestamp").should be_close(Time.now, 4)
215
-
216
- # re-acquire
217
- DisposableResource.pool.release(@t1)
218
- @t1 = DisposableResource.new
219
- # see timestamp is updated
220
- @t1.instance_variable_get("@__pool_acquire_timestamp").should be_close(Time.now, 2)
221
- end
222
- end
223
-
224
-
225
-
226
- describe Object::Pooling::ResourcePool, "#available?" do
227
- before :each do
228
- DisposableResource.initialize_pool(2)
229
- DisposableResource.new
230
- end
231
-
232
- after :each do
233
- DisposableResource.instance_variable_set("@__pool", nil)
234
- end
235
-
236
- it "returns true when pool has available instances" do
237
- DisposableResource.pool.should be_available
238
- end
239
-
240
- it "returns false when pool is exhausted" do
241
- # acquires the last available resource
242
- DisposableResource.new
243
- DisposableResource.pool.should_not be_available
244
- end
245
- end
246
-
247
-
248
-
249
- describe "Flushing of constant size pool" do
250
- before :each do
251
- DisposableResource.initialize_pool(2)
252
-
253
- @t1 = DisposableResource.new
254
- @t2 = DisposableResource.new
255
-
256
- # sanity check
257
- DisposableResource.pool.instance_variable_get("@reserved").should_not be_empty
258
- end
259
-
260
- after :each do
261
- DisposableResource.instance_variable_set("@__pool", nil)
262
- end
263
-
264
- it "disposes all pooled objects" do
265
- [@t1, @t2].each { |instance| instance.should_receive(:dispose) }
266
-
267
- DisposableResource.pool.flush!
268
- end
269
-
270
- it "empties reserved set" do
271
- DisposableResource.pool.flush!
272
-
273
- DisposableResource.pool.instance_variable_get("@reserved").should be_empty
274
- end
275
-
276
- it "returns all instances to available set" do
277
- DisposableResource.pool.flush!
278
-
279
- DisposableResource.pool.instance_variable_get("@available").size.should == 2
280
- end
281
- end
282
-
283
-
284
-
285
- describe "Poolable resource class" do
286
- before :each do
287
- DisposableResource.initialize_pool(3, :initialization_args => ["paper"])
288
- end
289
-
290
- after :each do
291
- DisposableResource.instance_variable_set("@__pool", nil)
292
- end
293
-
294
- it "acquires new instances from pool" do
295
- @instance_one = DisposableResource.new
296
-
297
- DisposableResource.pool.acquired?(@instance_one).should be(true)
298
- end
299
-
300
- it "flushed existing pool on re-initialization" do
301
- DisposableResource.pool.should_receive(:flush!)
302
- DisposableResource.initialize_pool(5)
303
- end
304
-
305
- it "replaces pool on re-initialization" do
306
- DisposableResource.initialize_pool(5)
307
- DisposableResource.pool.size_limit.should == 5
308
- end
309
-
310
- it "passes initialization parameters to newly created resource instances" do
311
- DisposableResource.new.name.should == "paper"
312
- end
313
- end
314
-
315
-
316
-
317
- describe "Pooled object", "on initialization" do
318
- after :each do
319
- DisposableResource.instance_variable_set("@__pool", nil)
320
- end
321
-
322
- it "does not flush pool" do
323
- # using pool here initializes the pool first
324
- # so we use instance variable directly
325
- DisposableResource.instance_variable_get("@__pool").should_not_receive(:flush!)
326
- DisposableResource.initialize_pool(23)
327
- end
328
-
329
- it "flushes pool first when re-initialized" do
330
- DisposableResource.initialize_pool(5)
331
- DisposableResource.pool.should_receive(:flush!)
332
- DisposableResource.initialize_pool(23)
333
- end
334
- end
335
-
336
-
337
-
338
- describe Object::Pooling::ResourcePool, "#time_to_dispose?" do
339
- before :each do
340
- DisposableResource.initialize_pool(7, :expiration_period => 2)
341
- end
342
-
343
- after :each do
344
- DisposableResource.instance_variable_set("@__pool", nil)
345
- end
346
-
347
- it "returns true when object's last acquisition time is greater than limit" do
348
- @t1 = DisposableResource.new
349
- DisposableResource.pool.time_to_release?(@t1).should be(false)
350
-
351
- sleep 3
352
- DisposableResource.pool.time_to_release?(@t1).should be(true)
353
- end
354
- end
355
-
356
-
357
-
358
- describe Object::Pooling::ResourcePool, "#dispose_outdated" do
359
- before :each do
360
- DisposableResource.initialize_pool(7, :expiration_period => 2)
361
- end
362
-
363
- after :each do
364
- DisposableResource.instance_variable_set("@__pool", nil)
365
- end
366
-
367
- it "releases and thus disposes outdated instances" do
368
- @t1 = DisposableResource.new
369
- DisposableResource.pool.should_receive(:time_to_release?).with(@t1).and_return(true)
370
- DisposableResource.pool.should_receive(:release).with(@t1)
371
-
372
- DisposableResource.pool.release_outdated
373
- end
374
- end