diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml new file mode 100644 index 00000000..0495d44d --- /dev/null +++ b/.github/workflows/CI.yml @@ -0,0 +1,74 @@ +name: CI + +on: + push: + branches: + - master + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + ruby: + - 2.5 + - 2.6 + - 2.7 + - 3.0 + - ruby-head + - jruby + gemfile: + - ar52 + - ar60 + - ar61 + - ar-head + exclude: + - gemfile: ar52 + ruby: 3.0 + - gemfile: ar52 + ruby: ruby-head + continue-on-error: ${{ matrix.ruby == 'jruby' || matrix.ruby == 'ruby-head' || matrix.gemfile == 'ar-head' }} + services: + postgres: + image: postgis/postgis:12-3.1 + ports: + - 5432:5432 + env: + POSTGRES_HOST_AUTH_METHOD: trust + POSTGRES_DB: makara_test + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + mysql: + image: mysql:5.7 + env: + MYSQL_ALLOW_EMPTY_PASSWORD: yes + MYSQL_DATABASE: makara_test + ports: + - 3306:3306 + options: >- + --health-cmd "mysqladmin ping" + --health-interval 10s + --health-timeout 5s + --health-retries 5 + env: + BUNDLE_GEMFILE: gemfiles/${{ matrix.gemfile }}.gemfile + JRUBY_OPTS: --dev -J-Xmx1024M + steps: + - uses: actions/checkout@v2 + - uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true + - run: | + bundle exec rake + env: + PGHOST: localhost + PGUSER: postgres + MYSQL_HOST: 127.0.0.1 + RAILS_ENV: test + diff --git a/lib/makara/connection_wrapper.rb b/lib/makara/connection_wrapper.rb index e63b2eb6..8904fcf4 100644 --- a/lib/makara/connection_wrapper.rb +++ b/lib/makara/connection_wrapper.rb @@ -95,20 +95,14 @@ def execute(*args) # we want to forward all private methods, since we could have kicked out from a private scenario def method_missing(m, *args, &block) - if _makara_connection.respond_to?(m) - _makara_connection.public_send(m, *args, &block) - else # probably private method - _makara_connection.__send__(m, *args, &block) - end + _makara_connection.send(m, *args, &block) end + ruby2_keywords :method_missing if Module.private_method_defined?(:ruby2_keywords) - class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 - def respond_to#{RUBY_VERSION.to_s =~ /^1.8/ ? nil : '_missing'}?(m, include_private = false) - _makara_connection.respond_to?(m, true) - end - RUBY_EVAL - + def respond_to_missing?(m, include_private = false) + _makara_connection.respond_to?(m, true) + end protected @@ -116,7 +110,7 @@ def respond_to#{RUBY_VERSION.to_s =~ /^1.8/ ? nil : '_missing'}?(m, include_priv # all extra functionality is in the format of _makara* def _makara_decorate_connection(con) - extension = %Q{ + extension = <<~RUBY # the proxy object controlling this connection def _makara @_makara @@ -140,22 +134,26 @@ def _makara_hijack def _makara_name #{@config[:name].inspect} end - } + RUBY + + args = RUBY_VERSION >= "3.0.0" ? "..." : "*args, &block" # Each method the Makara::Proxy needs to hijack should be redefined in the underlying connection. # The new definition should allow for the proxy to intercept the invocation if required. @proxy.class.hijack_methods.each do |meth| - extension << %Q{ - def #{meth}(*args, &block) + method_call = RUBY_VERSION >= "3.0.0" ? "public_send(#{meth.inspect}, ...)" : "#{meth}(*args, &block)" + + extension << <<~RUBY + def #{meth}(#{args}) _makara_hijack do |proxy| if proxy - proxy.#{meth}(*args, &block) + proxy.#{method_call} else super end end end - } + RUBY end # extend the instance diff --git a/lib/makara/proxy.rb b/lib/makara/proxy.rb index e3e661fb..61fe80b0 100644 --- a/lib/makara/proxy.rb +++ b/lib/makara/proxy.rb @@ -23,19 +23,23 @@ def hijack_method(*method_names) self.hijack_methods |= method_names method_names.each do |method_name| - define_method method_name do |*args, &block| + define_method(method_name) do |*args, &block| appropriate_connection(method_name, args) do |con| con.send(method_name, *args, &block) end end + + ruby2_keywords method_name if Module.private_method_defined?(:ruby2_keywords) end end def send_to_all(*method_names) method_names.each do |method_name| - define_method method_name do |*args| - send_to_all method_name, *args + define_method(method_name) do |*args| + send_to_all(method_name, *args) end + + ruby2_keywords method_name if Module.private_method_defined?(:ruby2_keywords) end end end @@ -97,27 +101,25 @@ def strategy_class_for(strategy_name) def method_missing(m, *args, &block) if METHOD_MISSING_SKIP.include?(m) - return super(m, *args, &block) + return super end any_connection do |con| - if con.respond_to?(m) - con.public_send(m, *args, &block) - elsif con.respond_to?(m, true) - con.__send__(m, *args, &block) + if con.respond_to?(m, true) + con.send(m, *args, &block) else - super(m, *args, &block) + super end end end - class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 - def respond_to#{RUBY_VERSION.to_s =~ /^1.8/ ? nil : '_missing'}?(m, include_private = false) - any_connection do |con| - con._makara_connection.respond_to?(m, true) - end + ruby2_keywords :method_missing if Module.private_method_defined?(:ruby2_keywords) + + def respond_to_missing?(m, include_private = false) + any_connection do |con| + con._makara_connection.respond_to?(m, true) end - RUBY_EVAL + end def graceful_connection_for(config) fake_wrapper = Makara::ConnectionWrapper.new(self, nil, config) @@ -138,7 +140,6 @@ def disconnect! protected - def send_to_all(method_name, *args) # slave pool must run first to allow for slave-->master failover without running operations on master twice. handling_an_all_execution(method_name) do @@ -147,6 +148,8 @@ def send_to_all(method_name, *args) end end + ruby2_keywords :send_to_all if Module.private_method_defined?(:ruby2_keywords) + def any_connection @master_pool.provide do |con| yield con diff --git a/makara.gemspec b/makara.gemspec index e18fbe14..07af76af 100644 --- a/makara.gemspec +++ b/makara.gemspec @@ -9,7 +9,7 @@ Gem::Specification.new do |gem| gem.homepage = "https://github.com/taskrabbit/makara" gem.licenses = ['MIT'] gem.metadata = { - source_code_uri: 'https://github.com/taskrabbit/makara' + 'source_code_uri' => 'https://github.com/taskrabbit/makara' } gem.files = `git ls-files`.split($\) diff --git a/spec/active_record/connection_adapters/makara_mysql2_adapter_spec.rb b/spec/active_record/connection_adapters/makara_mysql2_adapter_spec.rb index 445f5f5e..d305e4d6 100644 --- a/spec/active_record/connection_adapters/makara_mysql2_adapter_spec.rb +++ b/spec/active_record/connection_adapters/makara_mysql2_adapter_spec.rb @@ -166,9 +166,13 @@ con = connection.slave_pool.connections.first if (ActiveRecord::VERSION::MAJOR == 5 && ActiveRecord::VERSION::MINOR <= 0) - expect(con).to receive(:execute).with(/SELECT\s+1\s*(AS one)?\s+FROM .?users.?\s+LIMIT\s+.?1/, any_args).once.and_call_original + expect(con).to receive(:execute) do |query| + expect(query).to match(/SELECT\s+1\s*(AS one)?\s+FROM .?users.?\s+LIMIT\s+.?1/) + end.once.and_call_original else - expect(con).to receive(:exec_query).with(/SELECT\s+1\s*(AS one)?\s+FROM .?users.?\s+LIMIT\s+.?1/, any_args).once.and_call_original + expect(con).to receive(:exec_query) do |query| + expect(query).to match(/SELECT\s+1\s*(AS one)?\s+FROM .?users.?\s+LIMIT\s+.?1/) + end.once.and_call_original end Test::User.exists? end diff --git a/spec/active_record/connection_adapters/makara_postgresql_adapter_spec.rb b/spec/active_record/connection_adapters/makara_postgresql_adapter_spec.rb index 6702c465..288c33b8 100644 --- a/spec/active_record/connection_adapters/makara_postgresql_adapter_spec.rb +++ b/spec/active_record/connection_adapters/makara_postgresql_adapter_spec.rb @@ -81,9 +81,13 @@ con = connection.slave_pool.connections.first if (ActiveRecord::VERSION::MAJOR == 4 && ActiveRecord::VERSION::MINOR >= 2) || (ActiveRecord::VERSION::MAJOR == 5 && ActiveRecord::VERSION::MINOR <= 0) - expect(con).to receive(:exec_no_cache).with(/SELECT\s+1\s*(AS one)?\s+FROM .?users.?\s+LIMIT\s+.?1/, any_args).once.and_call_original + expect(con).to receive(:exec_no_cache) do |query| + expect(query).to match(/SELECT\s+1\s*(AS one)?\s+FROM .?users.?\s+LIMIT\s+.?1/) + end.once.and_call_original else - expect(con).to receive(:exec_query).with(/SELECT\s+1\s*(AS one)?\s+FROM .?users.?\s+LIMIT\s+.?1/, any_args).once.and_call_original + expect(con).to receive(:exec_query) do |query| + expect(query).to match(/SELECT\s+1\s*(AS one)?\s+FROM .?users.?\s+LIMIT\s+.?1/) + end.once.and_call_original end Test::User.exists? end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 2d8cd236..d4864c8c 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -14,6 +14,10 @@ rescue LoadError end +if RUBY_VERSION >= "2.7.0" + Warning[:deprecated] = true +end + RSpec.configure do |config| config.run_all_when_everything_filtered = true config.filter_run :focus