slavery 2.0.0 → 2.1.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: 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