slavery 2.1.1 → 3.0.0

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