slavery 2.1.1 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 02a75efbc26dd3232de851d07d1903f8808c0afe
4
- data.tar.gz: 1c6bea05c62d6f8806e45b2e853ea5a78e05f70b
3
+ metadata.gz: d8d8a53892c3dc8ba6beea003eeb3e8dda2fcb7f
4
+ data.tar.gz: 884ce3b0bccb928742362447f0a1afdc9f7edc5c
5
5
  SHA512:
6
- metadata.gz: 4e9f079b232c11fc516e14d849a48f8f7de7cd00149fdf8efa951340d76a10f4c0db4b337ce602d5883212f7186d531198a60bb7a33398170dd95231301f19a6
7
- data.tar.gz: e82e262252ab96f041f5e47766e78200e0845ba0af7983910cb6b833d0af743be6525e04bb0c7cfad0c8e0d5ed045c083aa602fd4a392c2a16ea5df5c55f6175
6
+ metadata.gz: 603d8b62b416668e3cec066d49308aa78f194557d14741e551f14280b337dcbc081e52cf48848141c1ab0a0ae8cdc948cc6ba1e85ddb177c2ad61d8a1357ffc3
7
+ data.tar.gz: b0fd5c1246b7ada095fa7a1ef93a88ef1cb6d134a6a4bf0e1b2f9ce9635a1b6a3dd729059729cc5ee10f442b491a7805aeacce26fe7c5b53110e73fc9d5bf243
data/.gitignore CHANGED
@@ -18,4 +18,5 @@ test/version_tmp
18
18
  tmp
19
19
 
20
20
  test_db
21
- test_slave_db
21
+ test_slave_one
22
+ test_slave_two
data/README.md CHANGED
@@ -66,6 +66,7 @@ To start using Slavery, you need to add `Slavery.on_slave` in your code. Queries
66
66
 
67
67
  ```ruby
68
68
  Slavery.on_slave { User.count } # => runs on slave
69
+ Slavery.on_slave(:two) { User.count } # => runs on another slave configured as `development_slave_two`
69
70
  ```
70
71
 
71
72
  You can nest `on_slave` and `on_master` interchangeably. The following code works as expected.
@@ -158,15 +159,8 @@ ActiveRecord::Base.configurations = {
158
159
  ActiveRecord::Base.establish_connection(:development)
159
160
  ```
160
161
 
161
- ## Custom slave key in database.yml
162
-
163
- This is useful for deploying on EngineYard where the configuration key in database.yml is simple "slave". Put the following line in `config/initializers/slavery.rb`.
164
-
165
- ```ruby
166
- Slavery.spec_key = "slave" #instead of production_slave
167
- ```
168
-
169
162
  ## Changelog
170
163
 
164
+ * v3.0.0: Support for multiple slave targets ([@punchh](https://github.com/punchh))
171
165
  * v2.1.0: Debug log support / Database URL support / Rails 3.2 & 4.0 compatibility (Thanks to [@citrus](https://github.com/citrus))
172
166
  * v2.0.0: Rails 5 support
data/lib/slavery.rb CHANGED
@@ -12,14 +12,14 @@ require 'slavery/active_record/log_subscriber'
12
12
  module Slavery
13
13
  class << self
14
14
  attr_accessor :disabled
15
- attr_writer :spec_key
16
15
 
17
- def spec_key
18
- @spec_key ||= "#{ActiveRecord::ConnectionHandling::RAILS_ENV.call}_slave"
16
+ def slave_connections
17
+ @slave_connections ||= {}
19
18
  end
20
19
 
21
- def on_slave(&block)
22
- Base.new(:slave).run &block
20
+ def on_slave(name = :null_state, &block)
21
+ raise Slavery::Error.new('invalid slave target') unless name.is_a?(Symbol)
22
+ Base.new(name).run &block
23
23
  end
24
24
 
25
25
  def on_master(&block)
@@ -5,21 +5,19 @@ module ActiveRecord
5
5
 
6
6
  def connection
7
7
  case Thread.current[:slavery]
8
- when :slave
9
- Slavery.connection_holder.connection_without_slavery
10
8
  when :master, NilClass
11
9
  connection_without_slavery
12
10
  else
13
- raise Slavery::Error.new("invalid target: #{Thread.current[:slavery]}")
11
+ Slavery.connection_holder(Thread.current[:slavery]).connection_without_slavery
14
12
  end
15
13
  end
16
14
 
17
15
  # Generate scope at top level e.g. User.on_slave
18
- def on_slave
16
+ def on_slave(name = :null_state)
19
17
  # Why where(nil)?
20
18
  # http://stackoverflow.com/questions/18198963/with-rails-4-model-scoped-is-deprecated-but-model-all-cant-replace-it
21
19
  context = where(nil)
22
- context.slavery_target = :slave
20
+ context.slavery_target = name || :null_state
23
21
  context
24
22
  end
25
23
  end
@@ -6,8 +6,8 @@ module ActiveRecord
6
6
  alias_method :exec_queries_without_slavery, :exec_queries
7
7
 
8
8
  def exec_queries
9
- if slavery_target == :slave
10
- Slavery.on_slave { exec_queries_without_slavery }
9
+ if slavery_target
10
+ Slavery.on_slave(slavery_target) { exec_queries_without_slavery }
11
11
  else
12
12
  exec_queries_without_slavery
13
13
  end
@@ -18,8 +18,8 @@ module ActiveRecord
18
18
  alias_method :calculate_without_slavery, :calculate
19
19
 
20
20
  def calculate(*args)
21
- if slavery_target == :slave
22
- Slavery.on_slave { calculate_without_slavery(*args) }
21
+ if slavery_target
22
+ Slavery.on_slave(slavery_target) { calculate_without_slavery(*args) }
23
23
  else
24
24
  calculate_without_slavery(*args)
25
25
  end
data/lib/slavery/base.rb CHANGED
@@ -11,12 +11,16 @@ module Slavery
11
11
  private
12
12
 
13
13
  def decide_with(target)
14
- if Slavery.disabled
14
+ if Slavery.disabled || target == :master
15
15
  :master
16
+ elsif inside_transaction?
17
+ raise Slavery::Error.new('on_slave cannot be used inside transaction block!')
18
+ elsif target == :null_state
19
+ :slave
20
+ elsif target.present?
21
+ "slave_#{target}".to_sym
16
22
  else
17
- raise Slavery::Error.new('on_slave cannot be used inside transaction block!') if inside_transaction?
18
-
19
- target
23
+ raise Slavery::Error.new('on_slave cannot be used with a nil target!')
20
24
  end
21
25
  end
22
26
 
@@ -4,19 +4,24 @@ module Slavery
4
4
 
5
5
  class << self
6
6
  # for delayed activation
7
- def activate
8
- spec = ActiveRecord::Base.configurations[Slavery.spec_key]
9
- raise Error.new('Slavery.spec_key invalid!') if spec.nil?
7
+ def activate(target)
8
+ spec = ActiveRecord::Base.configurations["#{ActiveRecord::ConnectionHandling::RAILS_ENV.call}_#{target}"]
9
+ raise Error.new("Slave target '#{target}' is invalid!") if spec.nil?
10
10
  establish_connection spec
11
11
  end
12
12
  end
13
13
  end
14
14
 
15
15
  class << self
16
- def connection_holder
17
- @connection_holder ||= begin
18
- ConnectionHolder.activate
19
- ConnectionHolder
16
+ def connection_holder(target)
17
+ klass_name = "Slavery#{target.to_s.camelize}ConnectionHolder"
18
+ slave_connections[klass_name] ||= begin
19
+ klass = Class.new(Slavery::ConnectionHolder) do
20
+ self.abstract_class = true
21
+ end
22
+ Object.const_set(klass_name, klass)
23
+ klass.activate(target)
24
+ klass
20
25
  end
21
26
  end
22
27
  end
@@ -1,3 +1,3 @@
1
1
  module Slavery
2
- VERSION = '2.1.1'
2
+ VERSION = '3.0.0'
3
3
  end
@@ -3,15 +3,18 @@ require 'spec_helper'
3
3
  describe 'configuration' do
4
4
  before do
5
5
  # Backup connection and configs
6
- @backup_conn = Slavery.instance_variable_get :@connection_holder
6
+ @backup_conn = Slavery.instance_variable_get :@slave_connections
7
7
  @backup_config = ActiveRecord::Base.configurations.dup
8
8
  @backup_disabled = Slavery.disabled
9
- Slavery.instance_variable_set :@connection_holder, nil
9
+ @backup_conn.each_key do |klass_name|
10
+ Object.send(:remove_const, klass_name) if Object.const_defined?(klass_name)
11
+ end
12
+ Slavery.instance_variable_set :@slave_connections, {}
10
13
  end
11
14
 
12
15
  after do
13
16
  # Restore connection and configs
14
- Slavery.instance_variable_set :@connection_holder, @backup_conn
17
+ Slavery.instance_variable_set :@slave_connections, @backup_conn
15
18
  ActiveRecord::Base.configurations = @backup_config
16
19
  Slavery.disabled = @backup_disabled
17
20
  end
@@ -22,34 +25,10 @@ describe 'configuration' do
22
25
  expect { Slavery.on_slave { User.count } }.to raise_error(Slavery::Error)
23
26
  end
24
27
 
25
- it 'connects to master if slave configuration not specified' do
28
+ it 'connects to master if slave configuration is disabled' do
26
29
  ActiveRecord::Base.configurations['test_slave'] = nil
27
30
  Slavery.disabled = true
28
31
 
29
32
  expect(Slavery.on_slave { User.count }).to be 2
30
33
  end
31
-
32
- it 'connects to slave when specified as a hash' do
33
- Slavery.spec_key = 'test_slave'
34
- hash = ActiveRecord::Base.configurations['test_slave']
35
- expect(Slavery::ConnectionHolder).to receive(:establish_connection).with(hash)
36
- Slavery::ConnectionHolder.activate
37
- end
38
-
39
- it 'connects to slave when specified as a url' do
40
- expected = if Gem::Version.new(ActiveRecord::VERSION::STRING) < Gem::Version.new('4.1.0')
41
- 'postgres://root:@localhost:5432/test_slave'
42
- else
43
- {
44
- 'adapter' => 'postgresql',
45
- 'username' => 'root',
46
- 'host' => 'localhost',
47
- 'port' => 5432,
48
- 'database' => 'test_slave'
49
- }
50
- end
51
- Slavery.spec_key = 'test_slave_url'
52
- expect(Slavery::ConnectionHolder).to receive(:establish_connection).with(expected)
53
- Slavery::ConnectionHolder.activate
54
- end
55
34
  end
data/spec/slavery_spec.rb CHANGED
@@ -12,11 +12,13 @@ describe Slavery do
12
12
  it 'sets thread local' do
13
13
  Slavery.on_master { expect(slavery_value).to be :master }
14
14
  Slavery.on_slave { expect(slavery_value).to be :slave }
15
+ Slavery.on_slave(:two) { expect(slavery_value).to be :slave_two}
15
16
  end
16
17
 
17
18
  it 'returns value from block' do
18
19
  expect(Slavery.on_master { User.count }).to be 2
19
20
  expect(Slavery.on_slave { User.count }).to be 1
21
+ expect(Slavery.on_slave(:two) { User.count }).to be 0
20
22
  end
21
23
 
22
24
  it 'handles nested calls' do
@@ -61,11 +63,6 @@ describe Slavery do
61
63
  Slavery.disabled = backup
62
64
  end
63
65
 
64
- it 'sets the Slavery database spec name by configuration' do
65
- Slavery.spec_key = 'custom_slave'
66
- expect(Slavery.spec_key).to eq 'custom_slave'
67
- end
68
-
69
66
  it 'avoids stack overflow with 3rdparty gem that defines alias_method. namely newrelic...' do
70
67
  class ActiveRecord::Relation
71
68
  alias_method :calculate_without_thirdparty, :calculate
@@ -82,13 +79,29 @@ describe Slavery do
82
79
  end
83
80
  end
84
81
 
82
+ it 'works with nils like slave' do
83
+ expect(User.on_slave(nil).count).to be User.on_slave.count
84
+ end
85
+
86
+ it 'raises on blanks and strings' do
87
+ expect { User.on_slave("").count }.to raise_error(Slavery::Error)
88
+ expect { User.on_slave("two").count }.to raise_error(Slavery::Error)
89
+ expect { User.on_slave("slave").count }.to raise_error(Slavery::Error)
90
+ end
91
+
92
+ it 'raises with non existent extension' do
93
+ expect { Slavery.on_slave(:non_existent) { User.first } }.to raise_error(Slavery::Error)
94
+ end
95
+
85
96
  it 'works with any scopes' do
86
97
  expect(User.count).to be 2
98
+ expect(User.on_slave(:two).count).to be 0
87
99
  expect(User.on_slave.count).to be 1
88
100
 
89
101
  # Why where(nil)?
90
102
  # http://stackoverflow.com/questions/18198963/with-rails-4-model-scoped-is-deprecated-but-model-all-cant-replace-it
91
103
  expect(User.where(nil).to_a.size).to be 2
104
+ expect(User.on_slave(:two).where(nil).to_a.size).to be 0
92
105
  expect(User.on_slave.where(nil).to_a.size).to be 1
93
106
  end
94
107
  end
data/spec/spec_helper.rb CHANGED
@@ -7,7 +7,8 @@ require 'slavery'
7
7
 
8
8
  ActiveRecord::Base.configurations = {
9
9
  'test' => { 'adapter' => 'sqlite3', 'database' => 'test_db' },
10
- 'test_slave' => { 'adapter' => 'sqlite3', 'database' => 'test_slave_db' },
10
+ 'test_slave' => { 'adapter' => 'sqlite3', 'database' => 'test_slave_one' },
11
+ 'test_slave_two' => { 'adapter' => 'sqlite3', 'database' => 'test_slave_two'},
11
12
  'test_slave_url' => 'postgres://root:@localhost:5432/test_slave'
12
13
  }
13
14
 
@@ -34,6 +35,10 @@ class Seeder
34
35
  create_tables
35
36
  User.create
36
37
 
38
+ # Populate on slave two
39
+ connect(:test_slave_two)
40
+ create_tables
41
+
37
42
  # Reconnect to master
38
43
  connect(:test)
39
44
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: slavery
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.1
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kenn Ejima
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-08-23 00:00:00.000000000 Z
11
+ date: 2017-10-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -105,7 +105,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
105
105
  version: '0'
106
106
  requirements: []
107
107
  rubyforge_project:
108
- rubygems_version: 2.6.10
108
+ rubygems_version: 2.6.13
109
109
  signing_key:
110
110
  specification_version: 4
111
111
  summary: Simple, conservative slave reads for ActiveRecord