ar-multidb 0.1.12 → 0.4.2
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 +5 -5
- data/.gitignore +4 -1
- data/.rubocop.yml +62 -0
- data/.travis.yml +15 -3
- data/CHANGELOG.md +16 -0
- data/Gemfile +7 -4
- data/README.markdown +41 -19
- data/Rakefile +10 -9
- data/ar-multidb.gemspec +19 -16
- data/gemfiles/activerecord51.gemfile +7 -0
- data/gemfiles/activerecord52.gemfile +7 -0
- data/lib/ar-multidb.rb +2 -0
- data/lib/multidb.rb +4 -0
- data/lib/multidb/balancer.rb +69 -67
- data/lib/multidb/candidate.rb +33 -0
- data/lib/multidb/configuration.rb +17 -36
- data/lib/multidb/log_subscriber.rb +23 -0
- data/lib/multidb/model_extensions.rb +18 -13
- data/lib/multidb/version.rb +4 -2
- data/spec/balancer_spec.rb +74 -30
- data/spec/helpers.rb +21 -18
- data/spec/spec_helper.rb +6 -3
- metadata +79 -34
- data/Gemfile.lock +0 -54
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 | 
            -
             | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 2 | 
            +
            SHA256:
         | 
| 3 | 
            +
              metadata.gz: 3ecd6501582114326efd8452ffe712c79b5667d1ce54a181eff6efeaf40d1897
         | 
| 4 | 
            +
              data.tar.gz: 85c3e8b3dc155374ef7d7caed121bf5a140cf64030b760bb6cdcdb3123e8cddc
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: e2eefe25c89d914367afecce5ebc07f4be668985097969f5a1c9acb563a22c448778b4128caf64e5dd6029437e28b13188097d6c66c111f4b5911543858f50b1
         | 
| 7 | 
            +
              data.tar.gz: c83c08a921575803b15ebf473e125ad6da3619b321208766e9f8d058bb8226154cd21255d14aa77be260bfd17da29926be8aa3bdeda547fed0c6f46624370283
         | 
    
        data/.gitignore
    CHANGED
    
    
    
        data/.rubocop.yml
    ADDED
    
    | @@ -0,0 +1,62 @@ | |
| 1 | 
            +
            AllCops:
         | 
| 2 | 
            +
              TargetRubyVersion: 2.4
         | 
| 3 | 
            +
              Exclude:
         | 
| 4 | 
            +
                - '**/vendor/**/*'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            Layout/EmptyLinesAroundAttributeAccessor:
         | 
| 7 | 
            +
              Enabled: true
         | 
| 8 | 
            +
            Layout/SpaceAroundMethodCallOperator:
         | 
| 9 | 
            +
              Enabled: true
         | 
| 10 | 
            +
            Layout/LineLength:
         | 
| 11 | 
            +
              Exclude:
         | 
| 12 | 
            +
                - 'ar-multidb.gemspec'
         | 
| 13 | 
            +
            Lint/DeprecatedOpenSSLConstant:
         | 
| 14 | 
            +
              Enabled: true
         | 
| 15 | 
            +
            Lint/DuplicateElsifCondition:
         | 
| 16 | 
            +
              Enabled: true
         | 
| 17 | 
            +
            Lint/MixedRegexpCaptureTypes:
         | 
| 18 | 
            +
              Enabled: true
         | 
| 19 | 
            +
            Lint/RaiseException:
         | 
| 20 | 
            +
              Enabled: true
         | 
| 21 | 
            +
            Lint/StructNewOverride:
         | 
| 22 | 
            +
              Enabled: true
         | 
| 23 | 
            +
            Naming/FileName:
         | 
| 24 | 
            +
              Exclude:
         | 
| 25 | 
            +
                - 'lib/ar-multidb.rb'
         | 
| 26 | 
            +
            Metrics:
         | 
| 27 | 
            +
              Enabled: false
         | 
| 28 | 
            +
            Style/AccessorGrouping:
         | 
| 29 | 
            +
              Enabled: true
         | 
| 30 | 
            +
            Style/ArrayCoercion:
         | 
| 31 | 
            +
              Enabled: true
         | 
| 32 | 
            +
            Style/BisectedAttrAccessor:
         | 
| 33 | 
            +
              Enabled: true
         | 
| 34 | 
            +
            Style/CaseLikeIf:
         | 
| 35 | 
            +
              Enabled: true
         | 
| 36 | 
            +
            Style/Documentation:
         | 
| 37 | 
            +
              Enabled: false
         | 
| 38 | 
            +
            Style/ExponentialNotation:
         | 
| 39 | 
            +
              Enabled: true
         | 
| 40 | 
            +
            Style/HashAsLastArrayItem:
         | 
| 41 | 
            +
              Enabled: true
         | 
| 42 | 
            +
            Style/HashEachMethods:
         | 
| 43 | 
            +
              Enabled: true
         | 
| 44 | 
            +
            Style/HashLikeCase:
         | 
| 45 | 
            +
              Enabled: true
         | 
| 46 | 
            +
            Style/HashTransformKeys:
         | 
| 47 | 
            +
              Enabled: true
         | 
| 48 | 
            +
            Style/HashTransformValues:
         | 
| 49 | 
            +
              Enabled: true
         | 
| 50 | 
            +
            Style/RedundantAssignment:
         | 
| 51 | 
            +
              Enabled: true
         | 
| 52 | 
            +
            Style/RedundantFetchBlock:
         | 
| 53 | 
            +
              Enabled: true
         | 
| 54 | 
            +
            Style/RedundantFileExtensionInRequire:
         | 
| 55 | 
            +
              Enabled: true
         | 
| 56 | 
            +
            Style/RedundantRegexpCharacterClass:
         | 
| 57 | 
            +
              Enabled: true
         | 
| 58 | 
            +
            Style/RedundantRegexpEscape:
         | 
| 59 | 
            +
              Enabled: true
         | 
| 60 | 
            +
            Style/SlicingWithRange:
         | 
| 61 | 
            +
              Enabled: true
         | 
| 62 | 
            +
             | 
    
        data/.travis.yml
    CHANGED
    
    | @@ -1,3 +1,15 @@ | |
| 1 | 
            -
             | 
| 2 | 
            -
             | 
| 3 | 
            -
             | 
| 1 | 
            +
            sudo: false
         | 
| 2 | 
            +
            language: ruby
         | 
| 3 | 
            +
            cache: bundler
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            script:
         | 
| 6 | 
            +
              - bundle exec rubocop
         | 
| 7 | 
            +
              - bundle exec rspec
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            matrix:
         | 
| 10 | 
            +
              fast_finish: true
         | 
| 11 | 
            +
              include:
         | 
| 12 | 
            +
                - rvm: 2.5
         | 
| 13 | 
            +
                  gemfile: gemfiles/activerecord51.gemfile
         | 
| 14 | 
            +
                - rvm: 2.5
         | 
| 15 | 
            +
                  gemfile: gemfiles/activerecord52.gemfile
         | 
    
        data/CHANGELOG.md
    ADDED
    
    | @@ -0,0 +1,16 @@ | |
| 1 | 
            +
            # Changelog
         | 
| 2 | 
            +
            All notable changes to this project will be documented in this file.
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
         | 
| 5 | 
            +
            and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html)
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            ## [0.4.2]
         | 
| 8 | 
            +
            ### Changed
         | 
| 9 | 
            +
            - adjust rails restriction to exclude untested rails 6
         | 
| 10 | 
            +
            - adjust minimum ruby version to 2.4
         | 
| 11 | 
            +
            - add rubocop
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            ## [0.4.1]
         | 
| 14 | 
            +
            ### Added
         | 
| 15 | 
            +
            - Added support for database aliases ( PR #26 )
         | 
| 16 | 
            +
             | 
    
        data/Gemfile
    CHANGED
    
    | @@ -1,9 +1,12 @@ | |
| 1 | 
            -
             | 
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            source 'http://rubygems.org'
         | 
| 2 4 |  | 
| 3 5 | 
             
            # Specify your gem's dependencies in ar-multidb.gemspec
         | 
| 4 6 | 
             
            gemspec
         | 
| 5 7 |  | 
| 6 | 
            -
             | 
| 7 | 
            -
             | 
| 8 | 
            -
             | 
| 8 | 
            +
            local_gemfile = 'Gemfile.local'
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            if File.exist?(local_gemfile)
         | 
| 11 | 
            +
              eval(File.read(local_gemfile)) # rubocop:disable Security/Eval
         | 
| 9 12 | 
             
            end
         | 
    
        data/README.markdown
    CHANGED
    
    | @@ -2,9 +2,9 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            # Multidb
         | 
| 4 4 |  | 
| 5 | 
            -
            A simple, no-nonsense ActiveRecord extension which allows the application to switch between multiple database connections, such as in a  | 
| 5 | 
            +
            A simple, no-nonsense ActiveRecord extension which allows the application to switch between multiple database connections, such as in a primary/replica environment. For example:
         | 
| 6 6 |  | 
| 7 | 
            -
                Multidb.use(: | 
| 7 | 
            +
                Multidb.use(:replica) do
         | 
| 8 8 | 
             
                  @posts = Post.all
         | 
| 9 9 | 
             
                end
         | 
| 10 10 |  | 
| @@ -14,8 +14,13 @@ Randomized balancing of multiple connections within a group is supported. In the | |
| 14 14 |  | 
| 15 15 | 
             
            ## Requirements
         | 
| 16 16 |  | 
| 17 | 
            -
            * Ruby  | 
| 18 | 
            -
            * ActiveRecord  | 
| 17 | 
            +
            * Ruby 2.4 or later.
         | 
| 18 | 
            +
            * ActiveRecord 5.1 or later.
         | 
| 19 | 
            +
             | 
| 20 | 
            +
            ## Older releases
         | 
| 21 | 
            +
            For ActiveRecord 4. through 5.0 use version 0.3
         | 
| 22 | 
            +
            For ActiveRecord older than 4.0 use the gem version 0.1.13
         | 
| 23 | 
            +
            For ActiveRecord older than 3.0 use 0.1.10
         | 
| 19 24 |  | 
| 20 25 | 
             
            ## Comparison to other ActiveRecord extensions
         | 
| 21 26 |  | 
| @@ -27,10 +32,10 @@ Compared to other, more full-featured extensions such as Octopus and Seamless Da | |
| 27 32 |  | 
| 28 33 | 
             
            **Orthogonal**. Unlike Octopus, for example, connections follow context:
         | 
| 29 34 |  | 
| 30 | 
            -
                Multidb.use(: | 
| 35 | 
            +
                Multidb.use(:primary) do
         | 
| 31 36 | 
             
                  @post = Post.find(1)
         | 
| 32 | 
            -
                  Multidb.use(: | 
| 33 | 
            -
                    @post.authors  # This will use the  | 
| 37 | 
            +
                  Multidb.use(:replica) do
         | 
| 38 | 
            +
                    @post.authors  # This will use the replica
         | 
| 34 39 | 
             
                  end
         | 
| 35 40 | 
             
                end
         | 
| 36 41 |  | 
| @@ -55,8 +60,8 @@ All that is needed is to set up your `database.yml` file: | |
| 55 60 | 
             
                  host: db1
         | 
| 56 61 | 
             
                  multidb:
         | 
| 57 62 | 
             
                    databases:
         | 
| 58 | 
            -
                       | 
| 59 | 
            -
                        host: db- | 
| 63 | 
            +
                      replica:
         | 
| 64 | 
            +
                        host: db-replica
         | 
| 60 65 |  | 
| 61 66 | 
             
            Each database entry may be a hash or an array. So this also works:
         | 
| 62 67 |  | 
| @@ -68,24 +73,41 @@ Each database entry may be a hash or an array. So this also works: | |
| 68 73 | 
             
                  host: db1
         | 
| 69 74 | 
             
                  multidb:
         | 
| 70 75 | 
             
                    databases:
         | 
| 71 | 
            -
                       | 
| 72 | 
            -
                        - host: db- | 
| 73 | 
            -
                        - host: db- | 
| 76 | 
            +
                      replica:
         | 
| 77 | 
            +
                        - host: db-replica1
         | 
| 78 | 
            +
                        - host: db-replica2
         | 
| 74 79 |  | 
| 75 80 | 
             
            If multiple elements are specified, Multidb will use the list to pick a random candidate connection.
         | 
| 76 81 |  | 
| 77 82 | 
             
            The database hashes follow the same format as the top-level adapter configuration. In other words, each database connection may override the adapter, database name, username and so on.
         | 
| 78 83 |  | 
| 84 | 
            +
            You may also add an "alias" record to the configuration to support more than one name for a given database configuration.
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                production:
         | 
| 87 | 
            +
                  adapter: postgresql
         | 
| 88 | 
            +
                  database: myapp_production
         | 
| 89 | 
            +
                  username: ohoh
         | 
| 90 | 
            +
                  password: mymy
         | 
| 91 | 
            +
                  host: db1
         | 
| 92 | 
            +
                  multidb:
         | 
| 93 | 
            +
                    databases:
         | 
| 94 | 
            +
                      main_db:
         | 
| 95 | 
            +
                        host: db1-a
         | 
| 96 | 
            +
                      secondary_db:
         | 
| 97 | 
            +
                        alias: main_db
         | 
| 98 | 
            +
             | 
| 99 | 
            +
            With the above, `Multidb.use(:main_db)` and `Multidb.use(:secondary_db)` will work identically. This can be useful to support naming scheme migrations transparently: once your application is updated to use `secondary_db` where necessary, you can swap out the configuration.
         | 
| 100 | 
            +
             | 
| 79 101 | 
             
            To use the connection, modify your code by wrapping database access logic in blocks:
         | 
| 80 102 |  | 
| 81 | 
            -
                Multidb.use(: | 
| 103 | 
            +
                Multidb.use(:replica) do
         | 
| 82 104 | 
             
                  @posts = Post.all
         | 
| 83 105 | 
             
                end
         | 
| 84 106 |  | 
| 85 107 | 
             
            To wrap entire controller requests, for example:
         | 
| 86 108 |  | 
| 87 109 | 
             
                class PostsController < ApplicationController
         | 
| 88 | 
            -
                  around_filter : | 
| 110 | 
            +
                  around_filter :run_using_replica, only: [:index]
         | 
| 89 111 |  | 
| 90 112 | 
             
                  def index
         | 
| 91 113 | 
             
                    @posts = Post.all
         | 
| @@ -95,22 +117,22 @@ To wrap entire controller requests, for example: | |
| 95 117 | 
             
                    # Won't be wrapped
         | 
| 96 118 | 
             
                  end
         | 
| 97 119 |  | 
| 98 | 
            -
                  def  | 
| 99 | 
            -
                    Multidb.use(: | 
| 120 | 
            +
                  def run_using_replica(&block)
         | 
| 121 | 
            +
                    Multidb.use(:replica, &block)
         | 
| 100 122 | 
             
                  end
         | 
| 101 123 | 
             
                end
         | 
| 102 124 |  | 
| 103 125 | 
             
            You can also set the current connection for the remainder of the thread's execution:
         | 
| 104 126 |  | 
| 105 | 
            -
                Multidb.use(: | 
| 127 | 
            +
                Multidb.use(:replica)
         | 
| 106 128 | 
             
                # Do work
         | 
| 107 | 
            -
                Multidb.use(: | 
| 129 | 
            +
                Multidb.use(:primary)
         | 
| 108 130 |  | 
| 109 131 | 
             
            Note that the symbol `:default` will (unless you override it) refer to the default top-level ActiveRecord configuration.
         | 
| 110 132 |  | 
| 111 133 | 
             
            ## Development mode
         | 
| 112 134 |  | 
| 113 | 
            -
            In development you will typically want `Multidb.use(: | 
| 135 | 
            +
            In development you will typically want `Multidb.use(:replica)` to still work, but you probably don't want to run multiple databases on your development box. To make `use` silently fall back to using the default connection, Multidb can run in fallback mode.
         | 
| 114 136 |  | 
| 115 137 | 
             
            If you are using Rails, this will be automatically enabled in `development` and `test` environments. Otherwise, simply set `fallback: true` in `database.yml`:
         | 
| 116 138 |  | 
    
        data/Rakefile
    CHANGED
    
    | @@ -1,26 +1,27 @@ | |
| 1 | 
            -
             | 
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'bundler/gem_tasks'
         | 
| 2 4 | 
             
            require 'rspec/core/rake_task'
         | 
| 3 5 |  | 
| 4 6 | 
             
            RSpec::Core::RakeTask.new
         | 
| 5 7 |  | 
| 6 | 
            -
            task : | 
| 8 | 
            +
            task default: :spec
         | 
| 7 9 |  | 
| 8 10 | 
             
            desc 'Bump version'
         | 
| 9 11 | 
             
            task :bump do
         | 
| 10 | 
            -
              if `git status -uno -s --porcelain | wc -l`.to_i | 
| 11 | 
            -
             | 
| 12 | 
            -
              end
         | 
| 12 | 
            +
              abort 'You have uncommitted changed.' if `git status -uno -s --porcelain | wc -l`.to_i.positive?
         | 
| 13 | 
            +
             | 
| 13 14 | 
             
              text = File.read('lib/multidb/version.rb')
         | 
| 14 15 | 
             
              if text =~ /VERSION = '(.*)'/
         | 
| 15 | 
            -
                old_version =  | 
| 16 | 
            +
                old_version = Regexp.last_match(1)
         | 
| 16 17 | 
             
                version_parts = old_version.split('.')
         | 
| 17 18 | 
             
                version_parts[-1] = version_parts[-1].to_i + 1
         | 
| 18 19 | 
             
                new_version = version_parts.join('.')
         | 
| 19 20 | 
             
                text.gsub!(/VERSION = '(.*)'/, "VERSION = '#{new_version}'")
         | 
| 20 21 | 
             
                File.open('lib/multidb/version.rb', 'w') { |f| f << text }
         | 
| 21 | 
            -
                (system( | 
| 22 | 
            -
                  system("git commit -m 'Bump to #{new_version}.'"))  | 
| 22 | 
            +
                (system('git add lib/multidb/version.rb') &&
         | 
| 23 | 
            +
                  system("git commit -m 'Bump to #{new_version}.'")) || abort('Failed to commit.')
         | 
| 23 24 | 
             
              else
         | 
| 24 | 
            -
                abort  | 
| 25 | 
            +
                abort 'Could not find version number'
         | 
| 25 26 | 
             
              end
         | 
| 26 27 | 
             
            end
         | 
    
        data/ar-multidb.gemspec
    CHANGED
    
    | @@ -1,25 +1,28 @@ | |
| 1 | 
            -
            #  | 
| 2 | 
            -
             | 
| 3 | 
            -
             | 
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            $LOAD_PATH.push File.expand_path('lib', __dir__)
         | 
| 4 | 
            +
            require 'multidb/version'
         | 
| 4 5 |  | 
| 5 6 | 
             
            Gem::Specification.new do |s|
         | 
| 6 | 
            -
              s.name        =  | 
| 7 | 
            +
              s.name        = 'ar-multidb'
         | 
| 7 8 | 
             
              s.version     = Multidb::VERSION
         | 
| 8 | 
            -
              s.authors     = [ | 
| 9 | 
            -
              s.email       = [ | 
| 10 | 
            -
              s.homepage    =  | 
| 11 | 
            -
              s.summary     = s.description =  | 
| 12 | 
            -
             | 
| 13 | 
            -
              s.rubyforge_project = "ar-multidb"
         | 
| 9 | 
            +
              s.authors     = ['Alexander Staubo', 'Edward Rudd']
         | 
| 10 | 
            +
              s.email       = ['alex@bengler.no', 'urkle@outoforder.cc']
         | 
| 11 | 
            +
              s.homepage    = ''
         | 
| 12 | 
            +
              s.summary     = s.description = 'Multidb is an ActiveRecord extension for switching between multiple database connections, such as primary/replica setups.'
         | 
| 14 13 |  | 
| 15 14 | 
             
              s.files         = `git ls-files`.split("\n")
         | 
| 16 15 | 
             
              s.test_files    = `git ls-files -- {test,spec,features}/*`.split("\n")
         | 
| 17 | 
            -
              s.executables   = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
         | 
| 18 | 
            -
              s.require_paths = [ | 
| 16 | 
            +
              s.executables   = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
         | 
| 17 | 
            +
              s.require_paths = ['lib']
         | 
| 18 | 
            +
             | 
| 19 | 
            +
              s.required_ruby_version = '>= 2.4.0'
         | 
| 19 20 |  | 
| 20 | 
            -
              s.add_runtime_dependency ' | 
| 21 | 
            -
              s.add_runtime_dependency ' | 
| 21 | 
            +
              s.add_runtime_dependency 'activerecord', '>= 5.1', '< 6.0'
         | 
| 22 | 
            +
              s.add_runtime_dependency 'activesupport', '>= 5.1', '< 6.0'
         | 
| 22 23 |  | 
| 23 | 
            -
              s.add_development_dependency ' | 
| 24 | 
            -
              s.add_development_dependency ' | 
| 24 | 
            +
              s.add_development_dependency 'rake', '~> 12.0'
         | 
| 25 | 
            +
              s.add_development_dependency 'rspec', '~> 3.8'
         | 
| 26 | 
            +
              s.add_development_dependency 'rubocop', '~> 0.88.0'
         | 
| 27 | 
            +
              s.add_development_dependency 'sqlite3', '~> 1.3'
         | 
| 25 28 | 
             
            end
         | 
    
        data/lib/ar-multidb.rb
    CHANGED
    
    
    
        data/lib/multidb.rb
    CHANGED
    
    | @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            require 'active_record'
         | 
| 2 4 |  | 
| 3 5 | 
             
            require 'active_support/core_ext/module/delegation'
         | 
| @@ -6,5 +8,7 @@ require 'active_support/core_ext/module/aliasing' | |
| 6 8 |  | 
| 7 9 | 
             
            require_relative 'multidb/configuration'
         | 
| 8 10 | 
             
            require_relative 'multidb/model_extensions'
         | 
| 11 | 
            +
            require_relative 'multidb/log_subscriber'
         | 
| 12 | 
            +
            require_relative 'multidb/candidate'
         | 
| 9 13 | 
             
            require_relative 'multidb/balancer'
         | 
| 10 14 | 
             
            require_relative 'multidb/version'
         | 
    
        data/lib/multidb/balancer.rb
    CHANGED
    
    | @@ -1,107 +1,107 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            module Multidb
         | 
| 4 | 
            +
              class Balancer
         | 
| 5 | 
            +
                attr_accessor :fallback
         | 
| 2 6 |  | 
| 3 | 
            -
             | 
| 4 | 
            -
             | 
| 5 | 
            -
                   | 
| 6 | 
            -
                    adapter = target[:adapter]
         | 
| 7 | 
            -
                    begin
         | 
| 8 | 
            -
                      require "active_record/connection_adapters/#{adapter}_adapter"
         | 
| 9 | 
            -
                    rescue LoadError
         | 
| 10 | 
            -
                      raise "Please install the #{adapter} adapter: `gem install activerecord-#{adapter}-adapter` (#{$!})"
         | 
| 11 | 
            -
                    end
         | 
| 12 | 
            -
                    if defined?(ActiveRecord::ConnectionAdapters::ConnectionSpecification)
         | 
| 13 | 
            -
                      spec_class = ActiveRecord::ConnectionAdapters::ConnectionSpecification
         | 
| 14 | 
            -
                    else
         | 
| 15 | 
            -
                      spec_class = ActiveRecord::Base::ConnectionSpecification
         | 
| 16 | 
            -
                    end
         | 
| 17 | 
            -
                    @connection_pool = ActiveRecord::ConnectionAdapters::ConnectionPool.new(
         | 
| 18 | 
            -
                      spec_class.new(target, "#{adapter}_connection"))
         | 
| 19 | 
            -
                  else
         | 
| 20 | 
            -
                    @connection_pool = target
         | 
| 21 | 
            -
                  end
         | 
| 22 | 
            -
                end
         | 
| 7 | 
            +
                def initialize(configuration)
         | 
| 8 | 
            +
                  @candidates = {}.with_indifferent_access
         | 
| 9 | 
            +
                  @default_configuration = configuration
         | 
| 23 10 |  | 
| 24 | 
            -
             | 
| 25 | 
            -
                  if block_given?
         | 
| 26 | 
            -
                    @connection_pool.with_connection(&block)
         | 
| 27 | 
            -
                  else
         | 
| 28 | 
            -
                    @connection_pool.connection
         | 
| 29 | 
            -
                  end
         | 
| 30 | 
            -
                end
         | 
| 11 | 
            +
                  return unless @default_configuration
         | 
| 31 12 |  | 
| 32 | 
            -
             | 
| 33 | 
            -
              end
         | 
| 13 | 
            +
                  append(@default_configuration.raw_configuration[:databases] || {})
         | 
| 34 14 |  | 
| 35 | 
            -
             | 
| 15 | 
            +
                  @fallback = if @default_configuration.raw_configuration.include?(:fallback)
         | 
| 16 | 
            +
                                @default_configuration.raw_configuration[:fallback]
         | 
| 17 | 
            +
                              elsif defined?(Rails)
         | 
| 18 | 
            +
                                %w[development test].include?(Rails.env)
         | 
| 19 | 
            +
                              else
         | 
| 20 | 
            +
                                false
         | 
| 21 | 
            +
                              end
         | 
| 36 22 |  | 
| 37 | 
            -
             | 
| 38 | 
            -
             | 
| 39 | 
            -
                  @ | 
| 40 | 
            -
             | 
| 41 | 
            -
             | 
| 42 | 
            -
             | 
| 43 | 
            -
             | 
| 44 | 
            -
             | 
| 45 | 
            -
             | 
| 46 | 
            -
             | 
| 23 | 
            +
                  @default_candidate = Candidate.new('default', @default_configuration.default_handler)
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  @candidates[:default] = [@default_candidate] unless @candidates.include?(:default)
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                def append(databases)
         | 
| 29 | 
            +
                  databases.each_pair do |name, config|
         | 
| 30 | 
            +
                    configs = config.is_a?(Array) ? config : [config]
         | 
| 31 | 
            +
                    configs.each do |cfg|
         | 
| 32 | 
            +
                      if cfg['alias']
         | 
| 33 | 
            +
                        @candidates[name] = @candidates[cfg['alias']]
         | 
| 34 | 
            +
                        next
         | 
| 47 35 | 
             
                      end
         | 
| 48 | 
            -
             | 
| 49 | 
            -
             | 
| 50 | 
            -
                      @ | 
| 51 | 
            -
             | 
| 52 | 
            -
                      @fallback = %w(development test).include?(Rails.env)
         | 
| 53 | 
            -
                    else
         | 
| 54 | 
            -
                      @fallback = false
         | 
| 55 | 
            -
                    end
         | 
| 56 | 
            -
                    @default_candidate = Candidate.new(@configuration.default_pool)
         | 
| 57 | 
            -
                    unless @candidates.include?(:default)
         | 
| 58 | 
            -
                      @candidates[:default] = [@default_candidate]
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                      candidate = Candidate.new(name, @default_configuration.default_adapter.merge(cfg))
         | 
| 38 | 
            +
                      @candidates[name] ||= []
         | 
| 39 | 
            +
                      @candidates[name].push(candidate)
         | 
| 59 40 | 
             
                    end
         | 
| 60 41 | 
             
                  end
         | 
| 61 42 | 
             
                end
         | 
| 62 43 |  | 
| 63 44 | 
             
                def disconnect!
         | 
| 64 | 
            -
                  @candidates.values.flatten.each | 
| 65 | 
            -
                    candidate.connection_pool.disconnect!
         | 
| 66 | 
            -
                  end
         | 
| 45 | 
            +
                  @candidates.values.flatten.each(&:disconnect!)
         | 
| 67 46 | 
             
                end
         | 
| 68 47 |  | 
| 69 | 
            -
                def get(name, & | 
| 48 | 
            +
                def get(name, &_block)
         | 
| 70 49 | 
             
                  candidates = @candidates[name]
         | 
| 71 50 | 
             
                  candidates ||= @fallback ? @candidates[:default] : []
         | 
| 51 | 
            +
             | 
| 72 52 | 
             
                  raise ArgumentError, "No such database connection '#{name}'" if candidates.empty?
         | 
| 73 | 
            -
             | 
| 74 | 
            -
             | 
| 53 | 
            +
             | 
| 54 | 
            +
                  candidate = candidates.respond_to?(:sample) ? candidates.sample : candidates[rand(candidates.length)]
         | 
| 55 | 
            +
             | 
| 75 56 | 
             
                  block_given? ? yield(candidate) : candidate
         | 
| 76 57 | 
             
                end
         | 
| 77 58 |  | 
| 78 | 
            -
                def use(name, & | 
| 59 | 
            +
                def use(name, &_block)
         | 
| 79 60 | 
             
                  result = nil
         | 
| 80 61 | 
             
                  get(name) do |candidate|
         | 
| 81 62 | 
             
                    if block_given?
         | 
| 82 63 | 
             
                      candidate.connection do |connection|
         | 
| 83 | 
            -
                         | 
| 84 | 
            -
             | 
| 64 | 
            +
                        previous_configuration = Thread.current[:multidb]
         | 
| 65 | 
            +
                        Thread.current[:multidb] = {
         | 
| 66 | 
            +
                          connection: connection,
         | 
| 67 | 
            +
                          connection_name: name
         | 
| 68 | 
            +
                        }
         | 
| 85 69 | 
             
                        begin
         | 
| 86 70 | 
             
                          result = yield
         | 
| 71 | 
            +
                          result = result.to_a if result.is_a?(ActiveRecord::Relation)
         | 
| 87 72 | 
             
                        ensure
         | 
| 88 | 
            -
                          Thread.current[: | 
| 73 | 
            +
                          Thread.current[:multidb] = previous_configuration
         | 
| 89 74 | 
             
                        end
         | 
| 90 75 | 
             
                        result
         | 
| 91 76 | 
             
                      end
         | 
| 92 77 | 
             
                    else
         | 
| 93 | 
            -
                       | 
| 78 | 
            +
                      Thread.current[:multidb] = {
         | 
| 79 | 
            +
                        connection: candidate.connection,
         | 
| 80 | 
            +
                        connection_name: name
         | 
| 81 | 
            +
                      }
         | 
| 82 | 
            +
                      result = candidate.connection
         | 
| 94 83 | 
             
                    end
         | 
| 95 84 | 
             
                  end
         | 
| 96 85 | 
             
                  result
         | 
| 97 86 | 
             
                end
         | 
| 98 87 |  | 
| 99 88 | 
             
                def current_connection
         | 
| 100 | 
            -
                  Thread.current[: | 
| 89 | 
            +
                  if Thread.current[:multidb]
         | 
| 90 | 
            +
                    Thread.current[:multidb][:connection]
         | 
| 91 | 
            +
                  else
         | 
| 92 | 
            +
                    @default_candidate.connection
         | 
| 93 | 
            +
                  end
         | 
| 94 | 
            +
                end
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                def current_connection_name
         | 
| 97 | 
            +
                  if Thread.current[:multidb]
         | 
| 98 | 
            +
                    Thread.current[:multidb][:connection_name]
         | 
| 99 | 
            +
                  else
         | 
| 100 | 
            +
                    :default
         | 
| 101 | 
            +
                  end
         | 
| 101 102 | 
             
                end
         | 
| 102 103 |  | 
| 103 104 | 
             
                class << self
         | 
| 104 | 
            -
                  delegate :use, :current_connection, :disconnect!, to: :balancer
         | 
| 105 105 | 
             
                  def use(name, &block)
         | 
| 106 106 | 
             
                    Multidb.balancer.use(name, &block)
         | 
| 107 107 | 
             
                  end
         | 
| @@ -110,11 +110,13 @@ module Multidb | |
| 110 110 | 
             
                    Multidb.balancer.current_connection
         | 
| 111 111 | 
             
                  end
         | 
| 112 112 |  | 
| 113 | 
            +
                  def current_connection_name
         | 
| 114 | 
            +
                    Multidb.balancer.current_connection_name
         | 
| 115 | 
            +
                  end
         | 
| 116 | 
            +
             | 
| 113 117 | 
             
                  def disconnect!
         | 
| 114 118 | 
             
                    Multidb.balancer.disconnect!
         | 
| 115 119 | 
             
                  end
         | 
| 116 120 | 
             
                end
         | 
| 117 | 
            -
             | 
| 118 121 | 
             
              end
         | 
| 119 | 
            -
             | 
| 120 | 
            -
            end
         | 
| 122 | 
            +
            end
         | 
| @@ -0,0 +1,33 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Multidb
         | 
| 4 | 
            +
              class Candidate
         | 
| 5 | 
            +
                def initialize(name, target)
         | 
| 6 | 
            +
                  @name = name
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  case target
         | 
| 9 | 
            +
                  when Hash
         | 
| 10 | 
            +
                    @connection_handler = ActiveRecord::ConnectionAdapters::ConnectionHandler.new
         | 
| 11 | 
            +
                    @connection_handler.establish_connection(target.merge(name: 'primary'))
         | 
| 12 | 
            +
                  when ActiveRecord::ConnectionAdapters::ConnectionHandler
         | 
| 13 | 
            +
                    @connection_handler = target
         | 
| 14 | 
            +
                  else
         | 
| 15 | 
            +
                    raise ArgumentError, 'Connection handler not passed to target'
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                def connection(&block)
         | 
| 20 | 
            +
                  if block_given?
         | 
| 21 | 
            +
                    @connection_handler.retrieve_connection_pool('primary').with_connection(&block)
         | 
| 22 | 
            +
                  else
         | 
| 23 | 
            +
                    @connection_handler.retrieve_connection('primary')
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                def disconnect!
         | 
| 28 | 
            +
                  @connection_handler.clear_all_connections!
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                attr_reader :name
         | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
            end
         | 
| @@ -1,54 +1,35 @@ | |
| 1 | 
            -
             | 
| 2 | 
            -
             | 
| 3 | 
            -
              mattr_reader :configuration
         | 
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 4 2 |  | 
| 3 | 
            +
            module Multidb
         | 
| 5 4 | 
             
              class << self
         | 
| 6 5 | 
             
                delegate :use, :get, :disconnect!, to: :balancer
         | 
| 7 6 | 
             
              end
         | 
| 8 7 |  | 
| 8 | 
            +
              def self.init(config)
         | 
| 9 | 
            +
                activerecord_config = config.dup.with_indifferent_access
         | 
| 10 | 
            +
                default_adapter = activerecord_config
         | 
| 11 | 
            +
                configuration_hash = activerecord_config.delete(:multidb)
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                @balancer = Balancer.new(Configuration.new(default_adapter, configuration_hash || {}))
         | 
| 14 | 
            +
              end
         | 
| 15 | 
            +
             | 
| 9 16 | 
             
              def self.balancer
         | 
| 10 | 
            -
                @balancer  | 
| 17 | 
            +
                @balancer || raise(NotInitializedError, 'Balancer not initialized. You need to run Multidb.init first')
         | 
| 11 18 | 
             
              end
         | 
| 12 19 |  | 
| 13 20 | 
             
              def self.reset!
         | 
| 14 | 
            -
                @balancer | 
| 21 | 
            +
                @balancer = nil
         | 
| 15 22 | 
             
              end
         | 
| 16 23 |  | 
| 24 | 
            +
              class NotInitializedError < StandardError; end
         | 
| 25 | 
            +
             | 
| 17 26 | 
             
              class Configuration
         | 
| 18 27 | 
             
                def initialize(default_adapter, configuration_hash)
         | 
| 19 | 
            -
                  @ | 
| 28 | 
            +
                  @default_handler = ActiveRecord::Base.connection_handler
         | 
| 20 29 | 
             
                  @default_adapter = default_adapter
         | 
| 21 30 | 
             
                  @raw_configuration = configuration_hash
         | 
| 22 31 | 
             
                end
         | 
| 23 32 |  | 
| 24 | 
            -
                attr_reader : | 
| 25 | 
            -
                attr_reader :default_adapter
         | 
| 26 | 
            -
                attr_reader :raw_configuration
         | 
| 33 | 
            +
                attr_reader :default_handler, :default_adapter, :raw_configuration
         | 
| 27 34 | 
             
              end
         | 
| 28 | 
            -
             | 
| 29 | 
            -
              private
         | 
| 30 | 
            -
             | 
| 31 | 
            -
                def self.create_balancer
         | 
| 32 | 
            -
                  unless @configuration
         | 
| 33 | 
            -
                    begin
         | 
| 34 | 
            -
                      connection_pool = ActiveRecord::Base.connection_pool
         | 
| 35 | 
            -
                    rescue ActiveRecord::ConnectionNotEstablished
         | 
| 36 | 
            -
                      # Ignore
         | 
| 37 | 
            -
                    else
         | 
| 38 | 
            -
                      connection = connection_pool.connection
         | 
| 39 | 
            -
             | 
| 40 | 
            -
                      # FIXME: This is hacky, but apparently the only way to get at
         | 
| 41 | 
            -
                      #   the internal configuration hash.
         | 
| 42 | 
            -
                      activerecord_config = connection.instance_variable_get(:@config).dup.with_indifferent_access
         | 
| 43 | 
            -
             | 
| 44 | 
            -
                      default_adapter, configuration_hash = activerecord_config, activerecord_config.delete(:multidb)
         | 
| 45 | 
            -
             | 
| 46 | 
            -
                      @configuration = Configuration.new(default_adapter, configuration_hash || {})
         | 
| 47 | 
            -
                    end
         | 
| 48 | 
            -
                  end
         | 
| 49 | 
            -
                  if @configuration
         | 
| 50 | 
            -
                    Balancer.new(@configuration)
         | 
| 51 | 
            -
                  end
         | 
| 52 | 
            -
                end
         | 
| 53 | 
            -
             | 
| 54 | 
            -
            end
         | 
| 35 | 
            +
            end
         | 
| @@ -0,0 +1,23 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Multidb
         | 
| 4 | 
            +
              module LogSubscriberExtension
         | 
| 5 | 
            +
                def sql(event)
         | 
| 6 | 
            +
                  name = Multidb.balancer.current_connection_name
         | 
| 7 | 
            +
                  event.payload[:db_name] = name if name
         | 
| 8 | 
            +
                  super
         | 
| 9 | 
            +
                end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                def debug(msg)
         | 
| 12 | 
            +
                  name = Multidb.balancer.current_connection_name
         | 
| 13 | 
            +
                  if name
         | 
| 14 | 
            +
                    db = color("[DB: #{name}]", ActiveSupport::LogSubscriber::GREEN, true)
         | 
| 15 | 
            +
                    super(db + msg)
         | 
| 16 | 
            +
                  else
         | 
| 17 | 
            +
                    super
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
              end
         | 
| 21 | 
            +
            end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
            ActiveRecord::LogSubscriber.prepend(Multidb::LogSubscriberExtension)
         | 
| @@ -1,24 +1,29 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'active_record/base'
         | 
| 4 | 
            +
             | 
| 1 5 | 
             
            module Multidb
         | 
| 2 | 
            -
              module  | 
| 6 | 
            +
              module Connection
         | 
| 7 | 
            +
                def establish_connection(spec = nil)
         | 
| 8 | 
            +
                  super(spec)
         | 
| 9 | 
            +
                  Multidb.init(connection_pool.spec.config)
         | 
| 10 | 
            +
                end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                def connection
         | 
| 13 | 
            +
                  Multidb.balancer.current_connection
         | 
| 14 | 
            +
                rescue Multidb::NotInitializedError
         | 
| 15 | 
            +
                  super
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
              end
         | 
| 3 18 |  | 
| 19 | 
            +
              module ModelExtensions
         | 
| 4 20 | 
             
                extend ActiveSupport::Concern
         | 
| 5 21 |  | 
| 6 22 | 
             
                included do
         | 
| 7 23 | 
             
                  class << self
         | 
| 8 | 
            -
                     | 
| 9 | 
            -
                  end
         | 
| 10 | 
            -
                end
         | 
| 11 | 
            -
             | 
| 12 | 
            -
                module ClassMethods
         | 
| 13 | 
            -
                  def connection_with_multidb
         | 
| 14 | 
            -
                    if (balancer = Multidb.balancer)
         | 
| 15 | 
            -
                      balancer.current_connection
         | 
| 16 | 
            -
                    else
         | 
| 17 | 
            -
                      connection_without_multidb
         | 
| 18 | 
            -
                    end
         | 
| 24 | 
            +
                    prepend Multidb::Connection
         | 
| 19 25 | 
             
                  end
         | 
| 20 26 | 
             
                end
         | 
| 21 | 
            -
             | 
| 22 27 | 
             
              end
         | 
| 23 28 | 
             
            end
         | 
| 24 29 |  | 
    
        data/lib/multidb/version.rb
    CHANGED
    
    
    
        data/spec/balancer_spec.rb
    CHANGED
    
    | @@ -1,16 +1,17 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            require_relative 'spec_helper'
         | 
| 2 4 |  | 
| 3 5 | 
             
            describe 'Multidb.balancer' do
         | 
| 4 | 
            -
             | 
| 5 6 | 
             
              context 'with no configuration' do
         | 
| 6 | 
            -
                it ' | 
| 7 | 
            -
                  Multidb.balancer.should  | 
| 7 | 
            +
                it 'raises exception' do
         | 
| 8 | 
            +
                  -> { Multidb.balancer }.should raise_error(Multidb::NotInitializedError)
         | 
| 8 9 | 
             
                end
         | 
| 9 10 | 
             
              end
         | 
| 10 11 |  | 
| 11 12 | 
             
              context 'with configuration' do
         | 
| 12 13 | 
             
                before do
         | 
| 13 | 
            -
                  ActiveRecord::Base.establish_connection( | 
| 14 | 
            +
                  ActiveRecord::Base.establish_connection(configuration_with_replicas)
         | 
| 14 15 | 
             
                end
         | 
| 15 16 |  | 
| 16 17 | 
             
                it 'returns balancer' do
         | 
| @@ -26,25 +27,54 @@ describe 'Multidb.balancer' do | |
| 26 27 |  | 
| 27 28 | 
             
                  Multidb.balancer.current_connection.should eq conn
         | 
| 28 29 | 
             
                end
         | 
| 29 | 
            -
              end
         | 
| 30 30 |  | 
| 31 | 
            -
             | 
| 32 | 
            -
             | 
| 33 | 
            -
             | 
| 34 | 
            -
             | 
| 35 | 
            -
             | 
| 36 | 
            -
             | 
| 37 | 
            -
             | 
| 31 | 
            +
                it 'returns default connection name for default connection' do
         | 
| 32 | 
            +
                  ActiveRecord::Base.connection
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                  Multidb.balancer.current_connection_name.should eq :default
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                context 'with additional configurations' do
         | 
| 38 | 
            +
                  before do
         | 
| 39 | 
            +
                    additional_configuration = { replica4: { database: 'spec/test-replica4.sqlite' } }
         | 
| 40 | 
            +
                    Multidb.balancer.append(additional_configuration)
         | 
| 41 | 
            +
                  end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                  it 'makes the new database available' do
         | 
| 44 | 
            +
                    Multidb.use(:replica4) do
         | 
| 45 | 
            +
                      conn = ActiveRecord::Base.connection
         | 
| 46 | 
            +
                      conn.should eq Multidb.balancer.current_connection
         | 
| 47 | 
            +
                      list = conn.execute('pragma database_list')
         | 
| 48 | 
            +
                      list.length.should eq 1
         | 
| 49 | 
            +
                      File.basename(list[0]['file']).should eq 'test-replica4.sqlite'
         | 
| 50 | 
            +
                    end
         | 
| 51 | 
            +
                  end
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                  it 'returns the connection name' do
         | 
| 54 | 
            +
                    Multidb.use(:replica4) do
         | 
| 55 | 
            +
                      Multidb.balancer.current_connection_name.should eq :replica4
         | 
| 56 | 
            +
                    end
         | 
| 38 57 | 
             
                  end
         | 
| 39 58 | 
             
                end
         | 
| 59 | 
            +
              end
         | 
| 40 60 |  | 
| 61 | 
            +
              describe '#use' do
         | 
| 41 62 | 
             
                context 'with configuration' do
         | 
| 42 63 | 
             
                  before do
         | 
| 43 | 
            -
                    ActiveRecord::Base.establish_connection( | 
| 64 | 
            +
                    ActiveRecord::Base.establish_connection(configuration_with_replicas)
         | 
| 65 | 
            +
                  end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                  context 'undefined connection' do
         | 
| 68 | 
            +
                    it 'raises exception' do
         | 
| 69 | 
            +
                      lambda {
         | 
| 70 | 
            +
                        Multidb.use(:something) do
         | 
| 71 | 
            +
                        end
         | 
| 72 | 
            +
                      }.should raise_error(ArgumentError)
         | 
| 73 | 
            +
                    end
         | 
| 44 74 | 
             
                  end
         | 
| 45 75 |  | 
| 46 76 | 
             
                  it 'returns default connection on :default' do
         | 
| 47 | 
            -
                     | 
| 77 | 
            +
                    ActiveRecord::Base.connection
         | 
| 48 78 | 
             
                    Multidb.use(:default) do
         | 
| 49 79 | 
             
                      conn2 = ActiveRecord::Base.connection
         | 
| 50 80 | 
             
                      conn2.should eq Multidb.balancer.current_connection
         | 
| @@ -55,61 +85,75 @@ describe 'Multidb.balancer' do | |
| 55 85 | 
             
                    end
         | 
| 56 86 | 
             
                  end
         | 
| 57 87 |  | 
| 58 | 
            -
                  it 'returns  | 
| 59 | 
            -
                    Multidb.use(: | 
| 88 | 
            +
                  it 'returns replica connection' do
         | 
| 89 | 
            +
                    Multidb.use(:replica1) do
         | 
| 60 90 | 
             
                      conn = ActiveRecord::Base.connection
         | 
| 61 91 | 
             
                      conn.should eq Multidb.balancer.current_connection
         | 
| 62 92 | 
             
                      list = conn.execute('pragma database_list')
         | 
| 63 93 | 
             
                      list.length.should eq 1
         | 
| 64 | 
            -
                      File.basename(list[0]['file']).should eq 'test- | 
| 94 | 
            +
                      File.basename(list[0]['file']).should eq 'test-replica1.sqlite'
         | 
| 95 | 
            +
                    end
         | 
| 96 | 
            +
                  end
         | 
| 97 | 
            +
             | 
| 98 | 
            +
                  it 'returns results instead of relation' do
         | 
| 99 | 
            +
                    class FooBar < ActiveRecord::Base; end
         | 
| 100 | 
            +
                    res = Multidb.use(:replica1) do
         | 
| 101 | 
            +
                      ActiveRecord::Migration.verbose = false
         | 
| 102 | 
            +
                      ActiveRecord::Schema.define(version: 1) { create_table :foo_bars }
         | 
| 103 | 
            +
                      FooBar.where(id: 42)
         | 
| 65 104 | 
             
                    end
         | 
| 105 | 
            +
                    res.should eq []
         | 
| 66 106 | 
             
                  end
         | 
| 67 107 |  | 
| 68 | 
            -
                  it 'returns supports nested  | 
| 69 | 
            -
                    Multidb.use(: | 
| 70 | 
            -
                      Multidb.use(: | 
| 108 | 
            +
                  it 'returns supports nested replica connection' do
         | 
| 109 | 
            +
                    Multidb.use(:replica1) do
         | 
| 110 | 
            +
                      Multidb.use(:replica2) do
         | 
| 71 111 | 
             
                        conn = ActiveRecord::Base.connection
         | 
| 72 112 | 
             
                        conn.should eq Multidb.balancer.current_connection
         | 
| 73 113 | 
             
                        list = conn.execute('pragma database_list')
         | 
| 74 114 | 
             
                        list.length.should eq 1
         | 
| 75 | 
            -
                        File.basename(list[0]['file']).should eq 'test- | 
| 115 | 
            +
                        File.basename(list[0]['file']).should eq 'test-replica2.sqlite'
         | 
| 76 116 | 
             
                      end
         | 
| 77 117 | 
             
                    end
         | 
| 78 118 | 
             
                  end
         | 
| 79 119 |  | 
| 80 120 | 
             
                  it 'returns preserves state when nesting' do
         | 
| 81 | 
            -
                    Multidb.use(: | 
| 82 | 
            -
                      Multidb.use(: | 
| 121 | 
            +
                    Multidb.use(:replica1) do
         | 
| 122 | 
            +
                      Multidb.use(:replica2) do
         | 
| 83 123 | 
             
                        conn = ActiveRecord::Base.connection
         | 
| 84 124 | 
             
                        conn.should eq Multidb.balancer.current_connection
         | 
| 85 125 | 
             
                        list = conn.execute('pragma database_list')
         | 
| 86 126 | 
             
                        list.length.should eq 1
         | 
| 87 | 
            -
                        File.basename(list[0]['file']).should eq 'test- | 
| 127 | 
            +
                        File.basename(list[0]['file']).should eq 'test-replica2.sqlite'
         | 
| 88 128 | 
             
                      end
         | 
| 89 129 |  | 
| 90 130 | 
             
                      conn = ActiveRecord::Base.connection
         | 
| 91 131 | 
             
                      conn.should eq Multidb.balancer.current_connection
         | 
| 92 132 | 
             
                      list = conn.execute('pragma database_list')
         | 
| 93 133 | 
             
                      list.length.should eq 1
         | 
| 94 | 
            -
                      File.basename(list[0]['file']).should eq 'test- | 
| 134 | 
            +
                      File.basename(list[0]['file']).should eq 'test-replica1.sqlite'
         | 
| 95 135 | 
             
                    end
         | 
| 96 136 | 
             
                  end
         | 
| 97 137 |  | 
| 138 | 
            +
                  it 'returns the parent connection for aliases' do
         | 
| 139 | 
            +
                    Multidb.use(:replica1).should_not eq Multidb.use(:replica_alias)
         | 
| 140 | 
            +
                    Multidb.use(:replica2).should eq Multidb.use(:replica_alias)
         | 
| 141 | 
            +
                  end
         | 
| 142 | 
            +
             | 
| 98 143 | 
             
                  it 'returns random candidate' do
         | 
| 99 144 | 
             
                    names = []
         | 
| 100 145 | 
             
                    100.times do
         | 
| 101 | 
            -
                      Multidb.use(: | 
| 146 | 
            +
                      Multidb.use(:replica3) do
         | 
| 102 147 | 
             
                        list = ActiveRecord::Base.connection.execute('pragma database_list')
         | 
| 103 148 | 
             
                        list.length.should eq 1
         | 
| 104 149 | 
             
                        names.push(File.basename(list[0]['file']))
         | 
| 105 150 | 
             
                      end
         | 
| 106 151 | 
             
                    end
         | 
| 107 152 | 
             
                    names.sort.uniq.should eq [
         | 
| 108 | 
            -
                      'test- | 
| 109 | 
            -
                      'test- | 
| 153 | 
            +
                      'test-replica3-1.sqlite',
         | 
| 154 | 
            +
                      'test-replica3-2.sqlite'
         | 
| 110 155 | 
             
                    ]
         | 
| 111 156 | 
             
                  end
         | 
| 112 157 | 
             
                end
         | 
| 113 158 | 
             
              end
         | 
| 114 | 
            -
             | 
| 115 | 
            -
            end
         | 
| 159 | 
            +
            end
         | 
    
        data/spec/helpers.rb
    CHANGED
    
    | @@ -1,20 +1,23 @@ | |
| 1 | 
            -
             | 
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
             | 
| 4 | 
            -
             | 
| 5 | 
            -
             | 
| 6 | 
            -
             | 
| 7 | 
            -
             | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 13 | 
            -
             | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 17 | 
            -
             | 
| 3 | 
            +
            module Helpers
         | 
| 4 | 
            +
              def configuration_with_replicas
         | 
| 5 | 
            +
                YAML.safe_load(<<~YAML)
         | 
| 6 | 
            +
                  adapter: sqlite3
         | 
| 7 | 
            +
                  database: spec/test.sqlite
         | 
| 8 | 
            +
                  encoding: utf-8
         | 
| 9 | 
            +
                  multidb:
         | 
| 10 | 
            +
                    databases:
         | 
| 11 | 
            +
                      replica1:
         | 
| 12 | 
            +
                        database: spec/test-replica1.sqlite
         | 
| 13 | 
            +
                      replica2:
         | 
| 14 | 
            +
                        database: spec/test-replica2.sqlite
         | 
| 15 | 
            +
                      replica3:
         | 
| 16 | 
            +
                        - database: spec/test-replica3-1.sqlite
         | 
| 17 | 
            +
                        - database: spec/test-replica3-2.sqlite
         | 
| 18 | 
            +
                      replica_alias:
         | 
| 19 | 
            +
                        database: spec/test-replica2.sqlite
         | 
| 20 | 
            +
                        alias: replica2
         | 
| 21 | 
            +
                YAML
         | 
| 18 22 | 
             
              end
         | 
| 19 | 
            -
             | 
| 20 | 
            -
            end
         | 
| 23 | 
            +
            end
         | 
    
        data/spec/spec_helper.rb
    CHANGED
    
    | @@ -1,23 +1,26 @@ | |
| 1 | 
            -
             | 
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            ENV['RACK_ENV'] ||= 'test'
         | 
| 2 4 |  | 
| 3 5 | 
             
            require 'rspec'
         | 
| 4 6 | 
             
            require 'yaml'
         | 
| 5 7 | 
             
            require 'active_record'
         | 
| 6 8 | 
             
            require 'fileutils'
         | 
| 7 9 |  | 
| 8 | 
            -
            $LOAD_PATH.unshift(File.expand_path(' | 
| 10 | 
            +
            $LOAD_PATH.unshift(File.expand_path('lib', __dir__))
         | 
| 9 11 | 
             
            require 'multidb'
         | 
| 10 12 |  | 
| 11 13 | 
             
            require_relative 'helpers'
         | 
| 12 14 |  | 
| 13 15 | 
             
            RSpec.configure do |config|
         | 
| 14 16 | 
             
              config.include Helpers
         | 
| 17 | 
            +
              config.expect_with(:rspec) { |c| c.syntax = :should }
         | 
| 15 18 | 
             
              config.before :each do
         | 
| 16 19 | 
             
                ActiveRecord::Base.clear_all_connections!
         | 
| 17 20 | 
             
                Multidb.reset!
         | 
| 18 21 | 
             
              end
         | 
| 19 22 | 
             
              config.after :each do
         | 
| 20 | 
            -
                Dir.glob(File.expand_path(' | 
| 23 | 
            +
                Dir.glob(File.expand_path('test*.sqlite', __dir__)).each do |f|
         | 
| 21 24 | 
             
                  FileUtils.rm(f)
         | 
| 22 25 | 
             
                end
         | 
| 23 26 | 
             
              end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,91 +1,138 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: ar-multidb
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 0.4.2
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Alexander Staubo
         | 
| 8 | 
            -
             | 
| 8 | 
            +
            - Edward Rudd
         | 
| 9 | 
            +
            autorequire:
         | 
| 9 10 | 
             
            bindir: bin
         | 
| 10 11 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date:  | 
| 12 | 
            +
            date: 2020-07-20 00:00:00.000000000 Z
         | 
| 12 13 | 
             
            dependencies:
         | 
| 13 14 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 | 
            -
              name:  | 
| 15 | 
            +
              name: activerecord
         | 
| 15 16 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 16 17 | 
             
                requirements:
         | 
| 17 | 
            -
                - -  | 
| 18 | 
            +
                - - ">="
         | 
| 18 19 | 
             
                  - !ruby/object:Gem::Version
         | 
| 19 | 
            -
                    version: ' | 
| 20 | 
            +
                    version: '5.1'
         | 
| 21 | 
            +
                - - "<"
         | 
| 22 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 23 | 
            +
                    version: '6.0'
         | 
| 20 24 | 
             
              type: :runtime
         | 
| 21 25 | 
             
              prerelease: false
         | 
| 22 26 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 23 27 | 
             
                requirements:
         | 
| 24 | 
            -
                - -  | 
| 28 | 
            +
                - - ">="
         | 
| 29 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 30 | 
            +
                    version: '5.1'
         | 
| 31 | 
            +
                - - "<"
         | 
| 25 32 | 
             
                  - !ruby/object:Gem::Version
         | 
| 26 | 
            -
                    version: ' | 
| 33 | 
            +
                    version: '6.0'
         | 
| 27 34 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 28 | 
            -
              name:  | 
| 35 | 
            +
              name: activesupport
         | 
| 29 36 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 30 37 | 
             
                requirements:
         | 
| 31 | 
            -
                - -  | 
| 38 | 
            +
                - - ">="
         | 
| 39 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 40 | 
            +
                    version: '5.1'
         | 
| 41 | 
            +
                - - "<"
         | 
| 32 42 | 
             
                  - !ruby/object:Gem::Version
         | 
| 33 | 
            -
                    version: ' | 
| 43 | 
            +
                    version: '6.0'
         | 
| 34 44 | 
             
              type: :runtime
         | 
| 35 45 | 
             
              prerelease: false
         | 
| 36 46 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 37 47 | 
             
                requirements:
         | 
| 38 | 
            -
                - -  | 
| 48 | 
            +
                - - ">="
         | 
| 39 49 | 
             
                  - !ruby/object:Gem::Version
         | 
| 40 | 
            -
                    version: ' | 
| 50 | 
            +
                    version: '5.1'
         | 
| 51 | 
            +
                - - "<"
         | 
| 52 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 53 | 
            +
                    version: '6.0'
         | 
| 54 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 55 | 
            +
              name: rake
         | 
| 56 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 57 | 
            +
                requirements:
         | 
| 58 | 
            +
                - - "~>"
         | 
| 59 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 60 | 
            +
                    version: '12.0'
         | 
| 61 | 
            +
              type: :development
         | 
| 62 | 
            +
              prerelease: false
         | 
| 63 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 64 | 
            +
                requirements:
         | 
| 65 | 
            +
                - - "~>"
         | 
| 66 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 67 | 
            +
                    version: '12.0'
         | 
| 41 68 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 42 69 | 
             
              name: rspec
         | 
| 43 70 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 44 71 | 
             
                requirements:
         | 
| 45 | 
            -
                - -  | 
| 72 | 
            +
                - - "~>"
         | 
| 46 73 | 
             
                  - !ruby/object:Gem::Version
         | 
| 47 | 
            -
                    version: ' | 
| 74 | 
            +
                    version: '3.8'
         | 
| 48 75 | 
             
              type: :development
         | 
| 49 76 | 
             
              prerelease: false
         | 
| 50 77 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 51 78 | 
             
                requirements:
         | 
| 52 | 
            -
                - -  | 
| 79 | 
            +
                - - "~>"
         | 
| 53 80 | 
             
                  - !ruby/object:Gem::Version
         | 
| 54 | 
            -
                    version: ' | 
| 81 | 
            +
                    version: '3.8'
         | 
| 82 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 83 | 
            +
              name: rubocop
         | 
| 84 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 85 | 
            +
                requirements:
         | 
| 86 | 
            +
                - - "~>"
         | 
| 87 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 88 | 
            +
                    version: 0.88.0
         | 
| 89 | 
            +
              type: :development
         | 
| 90 | 
            +
              prerelease: false
         | 
| 91 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 92 | 
            +
                requirements:
         | 
| 93 | 
            +
                - - "~>"
         | 
| 94 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 95 | 
            +
                    version: 0.88.0
         | 
| 55 96 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 56 97 | 
             
              name: sqlite3
         | 
| 57 98 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 58 99 | 
             
                requirements:
         | 
| 59 | 
            -
                - -  | 
| 100 | 
            +
                - - "~>"
         | 
| 60 101 | 
             
                  - !ruby/object:Gem::Version
         | 
| 61 | 
            -
                    version: ' | 
| 102 | 
            +
                    version: '1.3'
         | 
| 62 103 | 
             
              type: :development
         | 
| 63 104 | 
             
              prerelease: false
         | 
| 64 105 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 65 106 | 
             
                requirements:
         | 
| 66 | 
            -
                - -  | 
| 107 | 
            +
                - - "~>"
         | 
| 67 108 | 
             
                  - !ruby/object:Gem::Version
         | 
| 68 | 
            -
                    version: ' | 
| 109 | 
            +
                    version: '1.3'
         | 
| 69 110 | 
             
            description: Multidb is an ActiveRecord extension for switching between multiple database
         | 
| 70 | 
            -
              connections, such as  | 
| 111 | 
            +
              connections, such as primary/replica setups.
         | 
| 71 112 | 
             
            email:
         | 
| 72 113 | 
             
            - alex@bengler.no
         | 
| 114 | 
            +
            - urkle@outoforder.cc
         | 
| 73 115 | 
             
            executables: []
         | 
| 74 116 | 
             
            extensions: []
         | 
| 75 117 | 
             
            extra_rdoc_files: []
         | 
| 76 118 | 
             
            files:
         | 
| 77 | 
            -
            - .gitignore
         | 
| 78 | 
            -
            - . | 
| 119 | 
            +
            - ".gitignore"
         | 
| 120 | 
            +
            - ".rubocop.yml"
         | 
| 121 | 
            +
            - ".travis.yml"
         | 
| 122 | 
            +
            - CHANGELOG.md
         | 
| 79 123 | 
             
            - Gemfile
         | 
| 80 | 
            -
            - Gemfile.lock
         | 
| 81 124 | 
             
            - LICENSE
         | 
| 82 125 | 
             
            - README.markdown
         | 
| 83 126 | 
             
            - Rakefile
         | 
| 84 127 | 
             
            - ar-multidb.gemspec
         | 
| 128 | 
            +
            - gemfiles/activerecord51.gemfile
         | 
| 129 | 
            +
            - gemfiles/activerecord52.gemfile
         | 
| 85 130 | 
             
            - lib/ar-multidb.rb
         | 
| 86 131 | 
             
            - lib/multidb.rb
         | 
| 87 132 | 
             
            - lib/multidb/balancer.rb
         | 
| 133 | 
            +
            - lib/multidb/candidate.rb
         | 
| 88 134 | 
             
            - lib/multidb/configuration.rb
         | 
| 135 | 
            +
            - lib/multidb/log_subscriber.rb
         | 
| 89 136 | 
             
            - lib/multidb/model_extensions.rb
         | 
| 90 137 | 
             
            - lib/multidb/version.rb
         | 
| 91 138 | 
             
            - spec/balancer_spec.rb
         | 
| @@ -94,29 +141,27 @@ files: | |
| 94 141 | 
             
            homepage: ''
         | 
| 95 142 | 
             
            licenses: []
         | 
| 96 143 | 
             
            metadata: {}
         | 
| 97 | 
            -
            post_install_message: | 
| 144 | 
            +
            post_install_message:
         | 
| 98 145 | 
             
            rdoc_options: []
         | 
| 99 146 | 
             
            require_paths:
         | 
| 100 147 | 
             
            - lib
         | 
| 101 148 | 
             
            required_ruby_version: !ruby/object:Gem::Requirement
         | 
| 102 149 | 
             
              requirements:
         | 
| 103 | 
            -
              - -  | 
| 150 | 
            +
              - - ">="
         | 
| 104 151 | 
             
                - !ruby/object:Gem::Version
         | 
| 105 | 
            -
                  version:  | 
| 152 | 
            +
                  version: 2.4.0
         | 
| 106 153 | 
             
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 107 154 | 
             
              requirements:
         | 
| 108 | 
            -
              - -  | 
| 155 | 
            +
              - - ">="
         | 
| 109 156 | 
             
                - !ruby/object:Gem::Version
         | 
| 110 157 | 
             
                  version: '0'
         | 
| 111 158 | 
             
            requirements: []
         | 
| 112 | 
            -
             | 
| 113 | 
            -
             | 
| 114 | 
            -
            signing_key: 
         | 
| 159 | 
            +
            rubygems_version: 3.0.6
         | 
| 160 | 
            +
            signing_key:
         | 
| 115 161 | 
             
            specification_version: 4
         | 
| 116 162 | 
             
            summary: Multidb is an ActiveRecord extension for switching between multiple database
         | 
| 117 | 
            -
              connections, such as  | 
| 163 | 
            +
              connections, such as primary/replica setups.
         | 
| 118 164 | 
             
            test_files:
         | 
| 119 165 | 
             
            - spec/balancer_spec.rb
         | 
| 120 166 | 
             
            - spec/helpers.rb
         | 
| 121 167 | 
             
            - spec/spec_helper.rb
         | 
| 122 | 
            -
            has_rdoc: 
         | 
    
        data/Gemfile.lock
    DELETED
    
    | @@ -1,54 +0,0 @@ | |
| 1 | 
            -
            PATH
         | 
| 2 | 
            -
              remote: .
         | 
| 3 | 
            -
              specs:
         | 
| 4 | 
            -
                ar-multidb (0.1.11)
         | 
| 5 | 
            -
                  activerecord (>= 3.0)
         | 
| 6 | 
            -
                  activesupport (>= 3.0)
         | 
| 7 | 
            -
             | 
| 8 | 
            -
            GEM
         | 
| 9 | 
            -
              remote: http://rubygems.org/
         | 
| 10 | 
            -
              specs:
         | 
| 11 | 
            -
                activemodel (4.0.2)
         | 
| 12 | 
            -
                  activesupport (= 4.0.2)
         | 
| 13 | 
            -
                  builder (~> 3.1.0)
         | 
| 14 | 
            -
                activerecord (4.0.2)
         | 
| 15 | 
            -
                  activemodel (= 4.0.2)
         | 
| 16 | 
            -
                  activerecord-deprecated_finders (~> 1.0.2)
         | 
| 17 | 
            -
                  activesupport (= 4.0.2)
         | 
| 18 | 
            -
                  arel (~> 4.0.0)
         | 
| 19 | 
            -
                activerecord-deprecated_finders (1.0.3)
         | 
| 20 | 
            -
                activesupport (4.0.2)
         | 
| 21 | 
            -
                  i18n (~> 0.6, >= 0.6.4)
         | 
| 22 | 
            -
                  minitest (~> 4.2)
         | 
| 23 | 
            -
                  multi_json (~> 1.3)
         | 
| 24 | 
            -
                  thread_safe (~> 0.1)
         | 
| 25 | 
            -
                  tzinfo (~> 0.3.37)
         | 
| 26 | 
            -
                arel (4.0.1)
         | 
| 27 | 
            -
                atomic (1.1.14)
         | 
| 28 | 
            -
                builder (3.1.4)
         | 
| 29 | 
            -
                diff-lcs (1.2.5)
         | 
| 30 | 
            -
                i18n (0.6.9)
         | 
| 31 | 
            -
                minitest (4.7.5)
         | 
| 32 | 
            -
                multi_json (1.8.4)
         | 
| 33 | 
            -
                rake (10.1.1)
         | 
| 34 | 
            -
                rspec (2.14.1)
         | 
| 35 | 
            -
                  rspec-core (~> 2.14.0)
         | 
| 36 | 
            -
                  rspec-expectations (~> 2.14.0)
         | 
| 37 | 
            -
                  rspec-mocks (~> 2.14.0)
         | 
| 38 | 
            -
                rspec-core (2.14.7)
         | 
| 39 | 
            -
                rspec-expectations (2.14.5)
         | 
| 40 | 
            -
                  diff-lcs (>= 1.1.3, < 2.0)
         | 
| 41 | 
            -
                rspec-mocks (2.14.5)
         | 
| 42 | 
            -
                sqlite3 (1.3.8)
         | 
| 43 | 
            -
                thread_safe (0.1.3)
         | 
| 44 | 
            -
                  atomic
         | 
| 45 | 
            -
                tzinfo (0.3.38)
         | 
| 46 | 
            -
             | 
| 47 | 
            -
            PLATFORMS
         | 
| 48 | 
            -
              ruby
         | 
| 49 | 
            -
             | 
| 50 | 
            -
            DEPENDENCIES
         | 
| 51 | 
            -
              ar-multidb!
         | 
| 52 | 
            -
              rake
         | 
| 53 | 
            -
              rspec
         | 
| 54 | 
            -
              sqlite3
         |