seamless_database_pool 1.0.9 → 1.0.10

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -66,6 +66,8 @@ In this configuration, the master connection will be a mysql connection to maste
66
66
 
67
67
  The read pool will use three mysql connections to master-db, read-db-1, and read-db-2. The master connection will use a different port, username, password for the connection. The read connections will use the same values. Further, the connection read-db-1 will get half the traffic as the other two connections, so presumably it's on a more powerful box.
68
68
 
69
+ You must use compatible database adapters for both the master and the read connections. For example, you cannot use an Oracle server as your master and PostgreSQL servers as you read slaves.
70
+
69
71
  = Using the read pool
70
72
 
71
73
  By default, the master connection will be used for everything. This is not terribly useful, so you should really specify a method of using the read pool for the actions that need it. Read connections will only be used for select statements against the database.
data/Rakefile CHANGED
@@ -75,6 +75,9 @@ begin
75
75
  gem.add_dependency('activerecord', '>= 2.2.2')
76
76
  gem.add_development_dependency('rspec', '>= 2.0')
77
77
  gem.add_development_dependency('jeweler')
78
+ gem.add_development_dependency('sqlite3')
79
+ gem.add_development_dependency('mysql')
80
+ gem.add_development_dependency('pg')
78
81
  end
79
82
 
80
83
  Jeweler::GemcutterTasks.new
@@ -16,7 +16,7 @@ module ActiveRecord
16
16
  master_connection.class.send(:include, SeamlessDatabasePool::ConnectTimeout) unless master_connection.class.include?(SeamlessDatabasePool::ConnectTimeout)
17
17
  master_connection.connect_timeout = master_config[:connect_timeout]
18
18
  pool_weights[master_connection] = master_config[:pool_weight].to_i if master_config[:pool_weight].to_i > 0
19
-
19
+
20
20
  read_connections = []
21
21
  config[:read_pool].each do |read_config|
22
22
  read_config = default_config.merge(read_config).with_indifferent_access
@@ -30,6 +30,7 @@ module ActiveRecord
30
30
  read_connections << conn
31
31
  pool_weights[conn] = read_config[:pool_weight]
32
32
  rescue Exception => e
33
+ raise e # TODO remove
33
34
  if logger
34
35
  logger.error("Error connecting to read connection #{read_config.inspect}")
35
36
  logger.error(e)
@@ -48,7 +49,7 @@ module ActiveRecord
48
49
  return klass.new(nil, logger, master_connection, read_connections, pool_weights)
49
50
  end
50
51
 
51
- def establish_adapter (adapter)
52
+ def establish_adapter(adapter)
52
53
  raise AdapterNotSpecified.new("database configuration does not specify adapter") unless adapter
53
54
  raise AdapterNotFound.new("database pool must specify adapters") if adapter == 'seamless_database_pool'
54
55
 
@@ -77,7 +78,7 @@ module ActiveRecord
77
78
  end
78
79
 
79
80
  # Force reload to use the master connection since it's probably being called for a reason.
80
- def reload_with_seamless_database_pool (*args)
81
+ def reload_with_seamless_database_pool(*args)
81
82
  SeamlessDatabasePool.use_master_connection do
82
83
  reload_without_seamless_database_pool(*args)
83
84
  end
@@ -92,50 +93,60 @@ module ActiveRecord
92
93
 
93
94
  attr_reader :read_connections, :master_connection
94
95
 
95
- # Create an anonymous class that extends this one and proxies methods to the pool connections.
96
- def self.adapter_class(master_connection)
97
- # Define methods to proxy to the appropriate pool
98
- read_only_methods = [:select_one, :select_all, :select_value, :select_values, :select, :select_rows, :execute, :tables, :columns]
99
- master_methods = []
100
- master_connection_classes = [AbstractAdapter, Quoting, DatabaseStatements, SchemaStatements]
101
- master_connection_classes << DatabaseLimits if const_defined?(:DatabaseLimits)
102
- master_connection_class = master_connection.class
103
- while ![Object, AbstractAdapter].include?(master_connection_class) do
104
- master_connection_classes << master_connection_class
105
- master_connection_class = master_connection_class.superclass
106
- end
107
- master_connection_classes.each do |connection_class|
108
- master_methods.concat(connection_class.public_instance_methods(false))
109
- master_methods.concat(connection_class.protected_instance_methods(false))
110
- #master_methods.concat(connection_class.private_instance_methods(false))
111
- end
112
- master_methods.uniq!
113
- master_methods -= public_instance_methods(false) + protected_instance_methods(false) + private_instance_methods(false)
114
- master_methods = master_methods.collect{|m| m.to_sym}
115
- master_methods -= read_only_methods
96
+ class << self
97
+ # Create an anonymous class that extends this one and proxies methods to the pool connections.
98
+ def adapter_class(master_connection)
99
+ # Define methods to proxy to the appropriate pool
100
+ read_only_methods = [:select_one, :select_all, :select_value, :select_values, :select, :select_rows, :execute, :tables, :columns]
101
+ master_methods = []
102
+ master_connection_classes = [AbstractAdapter, Quoting, DatabaseStatements, SchemaStatements]
103
+ master_connection_classes << DatabaseLimits if const_defined?(:DatabaseLimits)
104
+ master_connection_class = master_connection.class
105
+ while ![Object, AbstractAdapter].include?(master_connection_class) do
106
+ master_connection_classes << master_connection_class
107
+ master_connection_class = master_connection_class.superclass
108
+ end
109
+ master_connection_classes.each do |connection_class|
110
+ master_methods.concat(connection_class.public_instance_methods(false))
111
+ master_methods.concat(connection_class.protected_instance_methods(false))
112
+ end
113
+ master_methods.uniq!
114
+ master_methods -= public_instance_methods(false) + protected_instance_methods(false) + private_instance_methods(false)
115
+ master_methods = master_methods.collect{|m| m.to_sym}
116
+ master_methods -= read_only_methods
116
117
 
117
- klass = Class.new(self)
118
- master_methods.each do |method_name|
119
- klass.class_eval %Q(
120
- def #{method_name}(*args, &block)
121
- use_master_connection do
122
- return proxy_connection_method(master_connection, :#{method_name}, :master, *args, &block)
118
+ klass = Class.new(self)
119
+ master_methods.each do |method_name|
120
+ klass.class_eval <<-EOS, __FILE__, __LINE__ + 1
121
+ def #{method_name}(*args, &block)
122
+ use_master_connection do
123
+ return proxy_connection_method(master_connection, :#{method_name}, :master, *args, &block)
124
+ end
123
125
  end
124
- end
125
- )
126
- end
126
+ EOS
127
+ end
127
128
 
128
- read_only_methods.each do |method_name|
129
- klass.class_eval %Q(
130
- def #{method_name}(*args, &block)
131
- connection = @use_master ? master_connection : current_read_connection
132
- proxy_connection_method(connection, :#{method_name}, :read, *args, &block)
133
- end
134
- )
135
- end
136
- klass.send :protected, :select
129
+ read_only_methods.each do |method_name|
130
+ klass.class_eval <<-EOS, __FILE__, __LINE__ + 1
131
+ def #{method_name}(*args, &block)
132
+ connection = @use_master ? master_connection : current_read_connection
133
+ proxy_connection_method(connection, :#{method_name}, :read, *args, &block)
134
+ end
135
+ EOS
136
+ end
137
+ klass.send :protected, :select
137
138
 
138
- return klass
139
+ return klass
140
+ end
141
+
142
+ # Set the arel visitor on the connections.
143
+ def visitor_for(pool)
144
+ # This is ugly, but then again, so is the code in ActiveRecord for setting the arel
145
+ # visitor. There is a note in the code indicating the method signatures should be updated.
146
+ config = pool.spec.config.with_indifferent_access
147
+ adapter = config[:master][:adapter] || config[:pool_adapter]
148
+ SeamlessDatabasePool.adapter_class_for(adapter).visitor_for(pool)
149
+ end
139
150
  end
140
151
 
141
152
  def initialize(connection, logger, master_connection, read_connections, pool_weights)
@@ -161,7 +172,7 @@ module ActiveRecord
161
172
  end
162
173
 
163
174
  # Get the pool weight of a connection
164
- def pool_weight (connection)
175
+ def pool_weight(connection)
165
176
  return @weighted_read_connections.select{|conn| conn == connection}.size
166
177
  end
167
178
 
@@ -169,6 +180,14 @@ module ActiveRecord
169
180
  false
170
181
  end
171
182
 
183
+ def visitor=(visitor)
184
+ all_connections.each{|conn| conn.visitor = visitor}
185
+ end
186
+
187
+ def visitor
188
+ master_connection.visitor
189
+ end
190
+
172
191
  def active?
173
192
  active = true
174
193
  do_to_connections {|conn| active &= conn.active?}
@@ -237,7 +256,7 @@ module ActiveRecord
237
256
  attr_reader :connections, :failed_connection
238
257
  attr_writer :expires
239
258
 
240
- def initialize (connections, failed_connection = nil, expires = nil)
259
+ def initialize(connections, failed_connection = nil, expires = nil)
241
260
  @connections = connections
242
261
  @failed_connection = failed_connection
243
262
  @expires = expires
@@ -1,7 +1,7 @@
1
- require File.join(File.dirname(__FILE__), 'seamless_database_pool', 'connect_timeout')
2
- require File.join(File.dirname(__FILE__), 'seamless_database_pool', 'connection_statistics')
3
- require File.join(File.dirname(__FILE__), 'seamless_database_pool', 'controller_filter')
4
- require File.join(File.dirname(__FILE__), 'active_record', 'connection_adapters', 'seamless_database_pool_adapter')
1
+ require File.join(File.dirname(__FILE__), 'seamless_database_pool', 'connect_timeout.rb')
2
+ require File.join(File.dirname(__FILE__), 'seamless_database_pool', 'connection_statistics.rb')
3
+ require File.join(File.dirname(__FILE__), 'seamless_database_pool', 'controller_filter.rb')
4
+ require File.join(File.dirname(__FILE__), 'active_record', 'connection_adapters', 'seamless_database_pool_adapter.rb')
5
5
  $LOAD_PATH << File.dirname(__FILE__) unless $LOAD_PATH.include?(File.dirname(__FILE__))
6
6
 
7
7
  # This module allows setting the read pool connection type. Generally you will use one of
@@ -15,7 +15,12 @@ $LOAD_PATH << File.dirname(__FILE__) unless $LOAD_PATH.include?(File.dirname(__F
15
15
  # read connection type. If none is ever called, the read connection type will be :master.
16
16
 
17
17
  module SeamlessDatabasePool
18
-
18
+
19
+ # Adapter name to class name map. This exists because there isn't an obvious way to translate things like
20
+ # sqlite3 to SQLite3. The adapters that ship with ActiveRecord are defined here. If you use
21
+ # an adapter that doesn't translate directly to camel case, then add the mapping here in an initializer.
22
+ ADAPTER_TO_CLASS_NAME_MAP = {"sqlite" => "SQLite", "sqlite3" => "SQLite3", "postgresql" => "PostgreSQL"}
23
+
19
24
  READ_CONNECTION_METHODS = [:master, :persistent, :random]
20
25
 
21
26
  class << self
@@ -111,6 +116,16 @@ module SeamlessDatabasePool
111
116
  def clear_read_only_connection
112
117
  Thread.current[:read_only_connection] = nil
113
118
  end
119
+
120
+ # Get the connection adapter class for an adapter name. The class will be loaded from
121
+ # ActiveRecord::ConnectionAdapters::NameAdapter where Name is the camelized version of the name.
122
+ # If the adapter class does not fit this pattern (i.e. sqlite3 => SQLite3Adapter), then add
123
+ # the mapping to the +ADAPTER_TO_CLASS_NAME_MAP+ Hash.
124
+ def adapter_class_for(name)
125
+ name = name.to_s
126
+ class_name = ADAPTER_TO_CLASS_NAME_MAP[name] || name.camelize
127
+ "ActiveRecord::ConnectionAdapters::#{class_name}Adapter".constantize
128
+ end
114
129
  end
115
130
 
116
131
  end
@@ -1,4 +1,5 @@
1
1
  require 'spec_helper'
2
+ require 'active_record/connection_adapters/read_only_adapter'
2
3
 
3
4
  describe "Test connection adapters" do
4
5
  if SeamlessDatabasePool::TestModel.database_configs.empty?
@@ -41,32 +42,32 @@ describe "Test connection adapters" do
41
42
  it "should quote table names properly" do
42
43
  connection.quote_table_name("foo").should == master_connection.quote_table_name("foo")
43
44
  end
44
-
45
+
45
46
  it "should quote column names properly" do
46
47
  connection.quote_column_name("foo").should == master_connection.quote_column_name("foo")
47
48
  end
48
-
49
+
49
50
  it "should quote string properly" do
50
51
  connection.quote_string("foo").should == master_connection.quote_string("foo")
51
52
  end
52
-
53
+
53
54
  it "should quote booleans properly" do
54
55
  connection.quoted_true.should == master_connection.quoted_true
55
56
  connection.quoted_false.should == master_connection.quoted_false
56
57
  end
57
-
58
+
58
59
  it "should quote dates properly" do
59
60
  date = Date.today
60
61
  time = Time.now
61
62
  connection.quoted_date(date).should == master_connection.quoted_date(date)
62
63
  connection.quoted_date(time).should == master_connection.quoted_date(time)
63
64
  end
64
-
65
+
65
66
  it "should query for records" do
66
67
  record = model.find_by_name("test")
67
68
  record.name.should == "test"
68
69
  end
69
-
70
+
70
71
  it "should work with query caching" do
71
72
  record_id = model.first.id
72
73
  model.cache do
@@ -76,62 +77,62 @@ describe "Test connection adapters" do
76
77
  model.find(record_id).name.should == "new value"
77
78
  end
78
79
  end
79
-
80
+
80
81
  context "read connection" do
81
82
  let(:sample_sql){"SELECT #{connection.quote_column_name('name')} FROM #{connection.quote_table_name(model.table_name)}"}
82
-
83
+
83
84
  it "should not include the master connection in the read pool for these tests" do
84
85
  connection.available_read_connections.should_not include(master_connection)
85
86
  connection.current_read_connection.should_not == master_connection
86
87
  end
87
-
88
+
88
89
  it "should send select to the read connection" do
89
90
  results = connection.send(:select, sample_sql)
90
91
  results.should == [{"name" => "test"}]
91
92
  results.should == master_connection.send(:select, sample_sql)
92
93
  results.should be_read_only
93
94
  end
94
-
95
+
95
96
  it "should send select_all to the read connection" do
96
97
  results = connection.select_all(sample_sql)
97
98
  results.should == [{"name" => "test"}]
98
99
  results.should == master_connection.select_all(sample_sql)
99
100
  results.should be_read_only
100
101
  end
101
-
102
+
102
103
  it "should send select_one to the read connection" do
103
104
  results = connection.select_one(sample_sql)
104
105
  results.should == {"name" => "test"}
105
106
  results.should == master_connection.select_one(sample_sql)
106
107
  results.should be_read_only
107
108
  end
108
-
109
+
109
110
  it "should send select_values to the read connection" do
110
111
  results = connection.select_values(sample_sql)
111
112
  results.should == ["test"]
112
113
  results.should == master_connection.select_values(sample_sql)
113
114
  results.should be_read_only
114
115
  end
115
-
116
+
116
117
  it "should send select_value to the read connection" do
117
118
  results = connection.select_value(sample_sql)
118
119
  results.should == "test"
119
120
  results.should == master_connection.select_value(sample_sql)
120
121
  results.should be_read_only
121
122
  end
122
-
123
+
123
124
  it "should send select_rows to the read connection" do
124
125
  results = connection.select_all(sample_sql)
125
126
  results.should == [{"name" => "test"}]
126
127
  results.should == master_connection.select_all(sample_sql)
127
128
  results.should be_read_only
128
129
  end
129
-
130
+
130
131
  it "should send execute to the read connection" do
131
132
  results = connection.execute(sample_sql)
132
133
  results.should be_read_only
133
134
  end
134
-
135
+
135
136
  it "should send columns to the read connection" do
136
137
  results = connection.columns(model.table_name)
137
138
  columns = results.collect{|c| c.name}.sort.should
@@ -139,14 +140,14 @@ describe "Test connection adapters" do
139
140
  columns.should == master_connection.columns(model.table_name).collect{|c| c.name}.sort
140
141
  results.should be_read_only
141
142
  end
142
-
143
+
143
144
  it "should send tables to the read connection" do
144
145
  results = connection.tables
145
146
  results.should == ["test_models"]
146
147
  results.should == master_connection.tables
147
148
  results.should be_read_only
148
149
  end
149
-
150
+
150
151
  it "should reconnect dead connections in the read pool" do
151
152
  read_connection.disconnect!
152
153
  read_connection.should_not be_active
@@ -155,12 +156,12 @@ describe "Test connection adapters" do
155
156
  read_connection.should be_active
156
157
  end
157
158
  end
158
-
159
+
159
160
  context "master connection" do
160
161
  let(:insert_sql){ "INSERT INTO #{connection.quote_table_name(model.table_name)} (#{connection.quote_column_name('name')}) VALUES ('new')" }
161
162
  let(:update_sql){ "UPDATE #{connection.quote_table_name(model.table_name)} SET #{connection.quote_column_name('value')} = 2" }
162
163
  let(:delete_sql){ "DELETE FROM #{connection.quote_table_name(model.table_name)}" }
163
-
164
+
164
165
  it "should blow up if a master connection method is sent to the read only connection" do
165
166
  lambda{read_connection.update(update_sql)}.should raise_error(NotImplementedError)
166
167
  lambda{read_connection.update(insert_sql)}.should raise_error(NotImplementedError)
@@ -168,29 +169,29 @@ describe "Test connection adapters" do
168
169
  lambda{read_connection.transaction{}}.should raise_error(NotImplementedError)
169
170
  lambda{read_connection.create_table(:test)}.should raise_error(NotImplementedError)
170
171
  end
171
-
172
+
172
173
  it "should send update to the master connection" do
173
174
  connection.update(update_sql)
174
175
  model.first.value.should == 2
175
176
  end
176
-
177
+
177
178
  it "should send insert to the master connection" do
178
179
  connection.update(insert_sql)
179
180
  model.find_by_name("new").should_not == nil
180
181
  end
181
-
182
+
182
183
  it "should send delete to the master connection" do
183
184
  connection.update(delete_sql)
184
185
  model.first.should == nil
185
186
  end
186
-
187
+
187
188
  it "should send transaction to the master connection" do
188
189
  connection.transaction do
189
190
  connection.update(update_sql)
190
191
  end
191
192
  model.first.value.should == 2
192
193
  end
193
-
194
+
194
195
  it "should send schema altering statements to the master connection" do
195
196
  SeamlessDatabasePool.use_master_connection do
196
197
  begin
@@ -1,6 +1,6 @@
1
1
  module ActiveRecord
2
2
  class Base
3
- def self.read_only_connection (config)
3
+ def self.read_only_connection(config)
4
4
  real_adapter = config.delete("real_adapter")
5
5
  connection = send("#{real_adapter}_connection", config.merge("adapter" => real_adapter))
6
6
  ConnectionAdapters::ReadOnlyAdapter.new(connection)
@@ -10,7 +10,7 @@ module ActiveRecord
10
10
  module ConnectionAdapters
11
11
  class ReadOnlyAdapter < AbstractAdapter
12
12
  %w(select_one select_all select_value select_values select select_rows execute tables columns).each do |read_method|
13
- class_eval <<-EOS
13
+ class_eval <<-EOS, __FILE__, __LINE__ + 1
14
14
  def #{read_method} (*args, &block)
15
15
  raise "Not Connected" unless @connected
16
16
  result = @connection.send(:#{read_method}, *args, &block)
@@ -22,7 +22,7 @@ module ActiveRecord
22
22
  EOS
23
23
 
24
24
  %w(update insert delete reload create_table drop_table add_index remove_index transaction).each do |write_method|
25
- class_eval <<-EOS
25
+ class_eval <<-EOS, __FILE__, __LINE__ + 1
26
26
  def #{write_method} (*args, &block)
27
27
  raise NotImplementedError.new("Master method '#{write_method}' called on read only connection")
28
28
  end
@@ -30,11 +30,24 @@ module ActiveRecord
30
30
  end
31
31
  end
32
32
 
33
- def initialize (connection)
33
+ def initialize(connection)
34
34
  @connection = connection
35
35
  @connected = true
36
+ super
36
37
  end
37
-
38
+
39
+ def test_select
40
+ @connection.select_all('SELECT "test_models".* FROM "test_models" LIMIT 1')
41
+ end
42
+
43
+ def visitor
44
+ @connection.visitor
45
+ end
46
+
47
+ def visitor=(v)
48
+ @connection.visitor = v
49
+ end
50
+
38
51
  def reconnect!
39
52
  @connected = true
40
53
  end
data/spec/test_model.rb CHANGED
@@ -5,16 +5,16 @@ module SeamlessDatabasePool
5
5
  adapters = ENV['TEST_ADAPTERS'].blank? ? [] : ENV['TEST_ADAPTERS'].split(/\s+/)
6
6
  configs = {}
7
7
  YAML.load_file(File.expand_path("../database.yml", __FILE__)).each do |adapter_name, adapter_config|
8
- configs[adapter_name] = adapter_config if adapters.include?(adapter_name)
8
+ configs[adapter_name] = adapter_config if adapters.include?(adapter_name.downcase)
9
9
  end
10
10
  configs
11
11
  end
12
12
 
13
- def use_database_connection (db_name)
13
+ def use_database_connection(db_name)
14
14
  establish_connection(database_configs[db_name.to_s])
15
15
  end
16
16
 
17
- def db_model (db_name)
17
+ def db_model(db_name)
18
18
  model_class_name = db_name.classify
19
19
  unless const_defined?(model_class_name)
20
20
  klass = Class.new(self)
metadata CHANGED
@@ -1,13 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: seamless_database_pool
3
3
  version: !ruby/object:Gem::Version
4
- hash: 5
5
4
  prerelease:
6
- segments:
7
- - 1
8
- - 0
9
- - 9
10
- version: 1.0.9
5
+ version: 1.0.10
11
6
  platform: ruby
12
7
  authors:
13
8
  - Brian Durand
@@ -15,7 +10,7 @@ autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
12
 
18
- date: 2011-07-20 00:00:00 -05:00
13
+ date: 2011-10-06 00:00:00 -05:00
19
14
  default_executable:
20
15
  dependencies:
21
16
  - !ruby/object:Gem::Dependency
@@ -26,11 +21,6 @@ dependencies:
26
21
  requirements:
27
22
  - - ">="
28
23
  - !ruby/object:Gem::Version
29
- hash: 3
30
- segments:
31
- - 2
32
- - 2
33
- - 2
34
24
  version: 2.2.2
35
25
  type: :runtime
36
26
  version_requirements: *id001
@@ -42,10 +32,6 @@ dependencies:
42
32
  requirements:
43
33
  - - ">="
44
34
  - !ruby/object:Gem::Version
45
- hash: 3
46
- segments:
47
- - 2
48
- - 0
49
35
  version: "2.0"
50
36
  type: :development
51
37
  version_requirements: *id002
@@ -57,12 +43,42 @@ dependencies:
57
43
  requirements:
58
44
  - - ">="
59
45
  - !ruby/object:Gem::Version
60
- hash: 3
61
- segments:
62
- - 0
63
46
  version: "0"
64
47
  type: :development
65
48
  version_requirements: *id003
49
+ - !ruby/object:Gem::Dependency
50
+ name: sqlite3
51
+ prerelease: false
52
+ requirement: &id004 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: "0"
58
+ type: :development
59
+ version_requirements: *id004
60
+ - !ruby/object:Gem::Dependency
61
+ name: mysql
62
+ prerelease: false
63
+ requirement: &id005 !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: "0"
69
+ type: :development
70
+ version_requirements: *id005
71
+ - !ruby/object:Gem::Dependency
72
+ name: pg
73
+ prerelease: false
74
+ requirement: &id006 !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: "0"
80
+ type: :development
81
+ version_requirements: *id006
66
82
  description:
67
83
  email: brian@embellishedvisions.com
68
84
  executables: []
@@ -105,25 +121,26 @@ required_ruby_version: !ruby/object:Gem::Requirement
105
121
  requirements:
106
122
  - - ">="
107
123
  - !ruby/object:Gem::Version
108
- hash: 3
109
- segments:
110
- - 0
111
124
  version: "0"
112
125
  required_rubygems_version: !ruby/object:Gem::Requirement
113
126
  none: false
114
127
  requirements:
115
128
  - - ">="
116
129
  - !ruby/object:Gem::Version
117
- hash: 3
118
- segments:
119
- - 0
120
130
  version: "0"
121
131
  requirements: []
122
132
 
123
133
  rubyforge_project:
124
- rubygems_version: 1.5.2
134
+ rubygems_version: 1.6.2
125
135
  signing_key:
126
136
  specification_version: 3
127
137
  summary: Add support for master/slave database clusters in ActiveRecord to improve performance.
128
- test_files: []
129
-
138
+ test_files:
139
+ - spec/connection_adapters_spec.rb
140
+ - spec/connection_statistics_spec.rb
141
+ - spec/controller_filter_spec.rb
142
+ - spec/seamless_database_pool_adapter_spec.rb
143
+ - spec/seamless_database_pool_spec.rb
144
+ - spec/spec_helper.rb
145
+ - spec/test_adapter/active_record/connection_adapters/read_only_adapter.rb
146
+ - spec/test_model.rb