slavery 2.0.0 → 2.1.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: 844d663a8d247cdb698ca1e17d781749372d980c
4
- data.tar.gz: 7fb134582dfa4eb45971cd1afb35f08286aef10f
3
+ metadata.gz: 5346c12e4fcf3eaad7b55ce70ca0ffea79f33fe0
4
+ data.tar.gz: 74b684b645a28770ecb4d8ae08f6bd5b8daaf7b3
5
5
  SHA512:
6
- metadata.gz: a227e3ba41594385d60c96692969dbbd59d16c5e952ccfa54daf423526025b25dcc99464f9f7eee4458e8f941db1873d3f57d66d9548f9d80625e7b6c600f241
7
- data.tar.gz: 1321abb5bd5394ac032bd5531117a707873251c4fd9026456ad08e3c27fb9383e45f1cdfdc4a6aad4f9080c8bf4de3270432854cfef96d5bb2395c70eb724595
6
+ metadata.gz: 7b9d823e65b28450161595daaef467bc96dc819ed38c4f2d8e9ea663958fca628dd59942700ad829a36903b415088c3ee07ac0df80f3b81e52fd463f588862b2
7
+ data.tar.gz: ff44e84b3580ce6bccafe3d42ca0b00a07dfb6d83bce83be21bce320c3c2df1b3ab2b4de430612332a0136d072f64ffea0b1ca52cac0d0ac7e41a631efcdffcb
data/.gitignore CHANGED
@@ -4,6 +4,7 @@
4
4
  .config
5
5
  .yardoc
6
6
  Gemfile.lock
7
+ gemfiles/*.lock
7
8
  InstalledFiles
8
9
  _yardoc
9
10
  coverage
@@ -0,0 +1,13 @@
1
+ language: ruby
2
+
3
+ rvm:
4
+ - 2.2.2
5
+ - 2.3.3
6
+
7
+ gemfile:
8
+ - Gemfile
9
+ - gemfiles/rails3.2.gemfile
10
+ - gemfiles/rails4.gemfile
11
+ - gemfiles/rails4.2.gemfile
12
+
13
+ script: bundle exec rspec spec
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Slavery - Simple, conservative slave reads for ActiveRecord
2
2
 
3
+ [![Build Status](https://travis-ci.org/kenn/slavery.svg)](https://travis-ci.org/kenn/slavery)
4
+
3
5
  Slavery is a simple, easy to use gem for ActiveRecord that enables conservative slave reads, which means it doesn't automatically redirect all SELECTs to slaves.
4
6
 
5
7
  Instead, you can do `Slavery.on_slave { User.count }` to send a particular query to a slave.
@@ -49,6 +51,13 @@ development_slave:
49
51
  <<: *common
50
52
  ```
51
53
 
54
+ Optionally, you can use a database url for your connections:
55
+
56
+ ```yaml
57
+ development: postgres://root:@localhost:5432/myapp_development
58
+ development_slave: postgres://root:@localhost:5432/myapp_development_slave
59
+ ```
60
+
52
61
  At this point, Slavery does nothing. Run tests and confirm that nothing is broken.
53
62
 
54
63
  ## Usage
@@ -101,6 +110,12 @@ With this user, writes on slave should raise an exception.
101
110
  Slavery.on_slave { User.create } # => ActiveRecord::StatementInvalid: Mysql2::Error: INSERT command denied...
102
111
  ```
103
112
 
113
+ With Postgres you can set the entire database to be readonly:
114
+
115
+ ```SQL
116
+ ALTER DATABASE myapp_development_slave SET default_transaction_read_only = true;
117
+ ```
118
+
104
119
  It is a good idea to confirm this behavior in your test code as well.
105
120
 
106
121
  ## Disable temporarily
@@ -139,4 +154,5 @@ Slavery.spec_key = "slave" #instead of production_slave
139
154
 
140
155
  ## Changelog
141
156
 
157
+ * v2.1.0: Debug log support / Database URL support / Rails 3.2 & 4.0 compatibility (Thanks to [@citrus](https://github.com/citrus))
142
158
  * v2.0.0: Rails 5 support
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative "../spec/spec_helper"
4
+ require "irb"
5
+ IRB.start
@@ -0,0 +1,9 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem 'activerecord', '~> 3.2'
4
+
5
+ group :development, :test do
6
+ gem 'test-unit', '~> 3.0'
7
+ end
8
+
9
+ gemspec :path => '../'
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem 'activerecord', '~> 4.2'
4
+
5
+ gemspec :path => '../'
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem 'activerecord', '~> 4.0.0'
4
+
5
+ gemspec :path => '../'
@@ -1,10 +1,13 @@
1
1
  require 'active_record'
2
+ require 'slavery/version'
2
3
  require 'slavery/base'
3
4
  require 'slavery/error'
4
- require 'slavery/slave_connection_holder'
5
- require 'slavery/version'
5
+ require 'slavery/connection_holder'
6
+ require 'slavery/transaction'
6
7
  require 'slavery/active_record/base'
8
+ require 'slavery/active_record/connection_handling'
7
9
  require 'slavery/active_record/relation'
10
+ require 'slavery/active_record/log_subscriber'
8
11
 
9
12
  module Slavery
10
13
  class << self
@@ -12,10 +15,7 @@ module Slavery
12
15
  attr_writer :spec_key
13
16
 
14
17
  def spec_key
15
- case @spec_key
16
- when String then @spec_key
17
- when NilClass then @spec_key = "#{ActiveRecord::ConnectionHandling::RAILS_ENV.call}_slave"
18
- end
18
+ @spec_key ||= "#{ActiveRecord::ConnectionHandling::RAILS_ENV.call}_slave"
19
19
  end
20
20
 
21
21
  def on_slave(&block)
@@ -25,25 +25,5 @@ module Slavery
25
25
  def on_master(&block)
26
26
  Base.new(:master).run &block
27
27
  end
28
-
29
- def slave_connection_holder
30
- @slave_connection_holder ||= begin
31
- SlaveConnectionHolder.activate
32
- SlaveConnectionHolder
33
- end
34
- end
35
-
36
- def base_transaction_depth
37
- @base_transaction_depth ||= begin
38
- testcase = ActiveSupport::TestCase
39
- if defined?(testcase) &&
40
- testcase.respond_to?(:use_transactional_fixtures) &&
41
- testcase.try(:use_transactional_fixtures)
42
- 1
43
- else
44
- 0
45
- end
46
- end
47
- end
48
28
  end
49
29
  end
@@ -6,7 +6,7 @@ module ActiveRecord
6
6
  def connection
7
7
  case Thread.current[:slavery]
8
8
  when :slave
9
- Slavery.slave_connection_holder.connection_without_slavery
9
+ Slavery.connection_holder.connection_without_slavery
10
10
  when :master, NilClass
11
11
  connection_without_slavery
12
12
  else
@@ -0,0 +1,6 @@
1
+ module ActiveRecord
2
+ module ConnectionHandling
3
+ # Already defined in Rails 4.1+
4
+ RAILS_ENV ||= -> { (Rails.env if defined?(Rails.env)) || ENV["RAILS_ENV"] || ENV["RACK_ENV"] }
5
+ end
6
+ end
@@ -0,0 +1,12 @@
1
+ module ActiveRecord
2
+ class LogSubscriber
3
+
4
+ alias_method :debug_without_slavery, :debug
5
+
6
+ def debug(msg)
7
+ db = Slavery.disabled ? "" : color("[#{Thread.current[:slavery] || "master"}]", ActiveSupport::LogSubscriber::GREEN, true)
8
+ debug_without_slavery(db + msg)
9
+ end
10
+
11
+ end
12
+ end
@@ -22,7 +22,7 @@ module Slavery
22
22
 
23
23
  def inside_transaction?
24
24
  open_transactions = run_on(:master) { ActiveRecord::Base.connection.open_transactions }
25
- open_transactions > Slavery.base_transaction_depth
25
+ open_transactions > Slavery::Transaction.base_depth
26
26
  end
27
27
 
28
28
  def run_on(target)
@@ -0,0 +1,23 @@
1
+ module Slavery
2
+ class ConnectionHolder < ActiveRecord::Base
3
+ self.abstract_class = true
4
+
5
+ class << self
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?
10
+ establish_connection spec
11
+ end
12
+ end
13
+ end
14
+
15
+ class << self
16
+ def connection_holder
17
+ @connection_holder ||= begin
18
+ ConnectionHolder.activate
19
+ ConnectionHolder
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,18 @@
1
+ module Slavery
2
+ class Transaction
3
+ class << self
4
+ def base_depth
5
+ @base_depth ||= begin
6
+ testcase = ActiveSupport::TestCase
7
+ if defined?(testcase) &&
8
+ testcase.respond_to?(:use_transactional_fixtures) &&
9
+ testcase.try(:use_transactional_fixtures)
10
+ 1
11
+ else
12
+ 0
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -1,3 +1,3 @@
1
1
  module Slavery
2
- VERSION = '2.0.0'
2
+ VERSION = '2.1.0'
3
3
  end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+ require 'logger'
3
+
4
+ describe ActiveRecord::LogSubscriber do
5
+
6
+ describe 'logging' do
7
+
8
+ let(:log) { StringIO.new }
9
+ let(:logger) { Logger.new(log) }
10
+
11
+ before do
12
+ ActiveRecord::Base.logger = logger
13
+ end
14
+
15
+ after do
16
+ Slavery.disabled = false
17
+ end
18
+
19
+ it 'it prefixes log messages with master' do
20
+ User.count
21
+ log.rewind
22
+ expect(log.read).to include('[master]')
23
+ end
24
+
25
+ it 'it prefixes log messages with the slave connection' do
26
+ User.on_slave.count
27
+ log.rewind
28
+ expect(log.read).to include('[slave]')
29
+ end
30
+
31
+ it 'it does nothing when slavery is disabled' do
32
+ Slavery.disabled = true
33
+ User.count
34
+ log.rewind
35
+ expect(log.read).to_not include('[master]')
36
+ end
37
+
38
+ end
39
+
40
+ end
@@ -95,15 +95,15 @@ describe Slavery do
95
95
  describe 'configuration' do
96
96
  before do
97
97
  # Backup connection and configs
98
- @backup_conn = Slavery.instance_variable_get :@slave_connection_holder
98
+ @backup_conn = Slavery.instance_variable_get :@connection_holder
99
99
  @backup_config = ActiveRecord::Base.configurations.dup
100
100
  @backup_disabled = Slavery.disabled
101
- Slavery.instance_variable_set :@slave_connection_holder, nil
101
+ Slavery.instance_variable_set :@connection_holder, nil
102
102
  end
103
103
 
104
104
  after do
105
105
  # Restore connection and configs
106
- Slavery.instance_variable_set :@slave_connection_holder, @backup_conn
106
+ Slavery.instance_variable_set :@connection_holder, @backup_conn
107
107
  ActiveRecord::Base.configurations = @backup_config
108
108
  Slavery.disabled = @backup_disabled
109
109
  end
@@ -120,5 +120,29 @@ describe Slavery do
120
120
 
121
121
  expect(Slavery.on_slave { User.count }).to be 2
122
122
  end
123
+
124
+ it 'connects to slave when specified as a hash' do
125
+ Slavery.spec_key = 'test_slave'
126
+ hash = ActiveRecord::Base.configurations['test_slave']
127
+ expect(Slavery::ConnectionHolder).to receive(:establish_connection).with(hash)
128
+ Slavery::ConnectionHolder.activate
129
+ end
130
+
131
+ it 'connects to slave when specified as a url' do
132
+ expected = if Gem::Version.new(ActiveRecord::VERSION::STRING) < Gem::Version.new('4.1.0')
133
+ 'postgres://root:@localhost:5432/test_slave'
134
+ else
135
+ {
136
+ 'adapter' => 'postgresql',
137
+ 'username' => 'root',
138
+ 'port' => 5432,
139
+ 'database' => 'test_slave',
140
+ 'host' => 'localhost'
141
+ }
142
+ end
143
+ Slavery.spec_key = 'test_slave_url'
144
+ expect(Slavery::ConnectionHolder).to receive(:establish_connection).with(expected)
145
+ Slavery::ConnectionHolder.activate
146
+ end
123
147
  end
124
148
  end
@@ -6,8 +6,9 @@ ENV['RACK_ENV'] = 'test'
6
6
  require 'slavery'
7
7
 
8
8
  ActiveRecord::Base.configurations = {
9
- 'test' => { adapter: 'sqlite3', database: 'test_db' },
10
- 'test_slave' => { adapter: 'sqlite3', database: 'test_slave_db' }
9
+ 'test' => { adapter: 'sqlite3', database: 'test_db' },
10
+ 'test_slave' => { adapter: 'sqlite3', database: 'test_slave_db' },
11
+ 'test_slave_url' => "postgres://root:@localhost:5432/test_slave"
11
12
  }
12
13
 
13
14
  # Prepare databases
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.0.0
4
+ version: 2.1.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: 2016-11-04 00:00:00.000000000 Z
11
+ date: 2016-12-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -55,24 +55,34 @@ dependencies:
55
55
  description: Simple, conservative slave reads for ActiveRecord
56
56
  email:
57
57
  - kenn.ejima@gmail.com
58
- executables: []
58
+ executables:
59
+ - console
59
60
  extensions: []
60
61
  extra_rdoc_files: []
61
62
  files:
62
63
  - ".gitignore"
63
64
  - ".rspec"
65
+ - ".travis.yml"
64
66
  - Gemfile
65
67
  - LICENSE.txt
66
68
  - README.md
67
69
  - Rakefile
70
+ - bin/console
71
+ - gemfiles/rails3.2.gemfile
72
+ - gemfiles/rails4.2.gemfile
73
+ - gemfiles/rails4.gemfile
68
74
  - lib/slavery.rb
69
75
  - lib/slavery/active_record/base.rb
76
+ - lib/slavery/active_record/connection_handling.rb
77
+ - lib/slavery/active_record/log_subscriber.rb
70
78
  - lib/slavery/active_record/relation.rb
71
79
  - lib/slavery/base.rb
80
+ - lib/slavery/connection_holder.rb
72
81
  - lib/slavery/error.rb
73
- - lib/slavery/slave_connection_holder.rb
82
+ - lib/slavery/transaction.rb
74
83
  - lib/slavery/version.rb
75
84
  - slavery.gemspec
85
+ - spec/active_record/log_subscriber_spec.rb
76
86
  - spec/slavery_spec.rb
77
87
  - spec/spec_helper.rb
78
88
  homepage: https://github.com/kenn/slavery
@@ -94,10 +104,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
94
104
  version: '0'
95
105
  requirements: []
96
106
  rubyforge_project:
97
- rubygems_version: 2.5.1
107
+ rubygems_version: 2.5.2
98
108
  signing_key:
99
109
  specification_version: 4
100
110
  summary: Simple, conservative slave reads for ActiveRecord
101
111
  test_files:
112
+ - spec/active_record/log_subscriber_spec.rb
102
113
  - spec/slavery_spec.rb
103
114
  - spec/spec_helper.rb
@@ -1,13 +0,0 @@
1
- module Slavery
2
- class SlaveConnectionHolder < ActiveRecord::Base
3
- self.abstract_class = true
4
-
5
- class << self
6
- # for delayed activation
7
- def activate
8
- raise Error.new('Slavery.spec_key invalid!') unless ActiveRecord::Base.configurations[Slavery.spec_key]
9
- establish_connection Slavery.spec_key.to_sym
10
- end
11
- end
12
- end
13
- end