diff --git a/Appraisals b/Appraisals index 223fb4dc..abc86452 100644 --- a/Appraisals +++ b/Appraisals @@ -13,4 +13,8 @@ end appraise "rails52" do gem "activerecord", "~> 5.2.0" end + +appraise "rails60" do + gem "activerecord", "~> 6.0.6.1" +end # vim: ft=ruby diff --git a/ar-octopus.gemspec b/ar-octopus.gemspec index 6e8c1017..53662c9c 100644 --- a/ar-octopus.gemspec +++ b/ar-octopus.gemspec @@ -23,16 +23,16 @@ Gem::Specification.new do |s| s.required_ruby_version = '>= 2.2.0' - s.add_dependency 'activerecord', '>= 4.2.0' - s.add_dependency 'activesupport', '>= 4.2.0' + s.add_dependency 'activerecord', "~> 6.0.6.1" + s.add_dependency 'activesupport', "~> 6.0.6.1" s.add_development_dependency 'appraisal', '>= 0.3.8' - s.add_development_dependency 'mysql2', '>= 0.3.18', "< 0.5" + s.add_development_dependency 'mysql2', '~> 0.5' s.add_development_dependency 'pg', '~> 0.18' s.add_development_dependency 'rake' s.add_development_dependency 'rspec', '>= 3' s.add_development_dependency 'rubocop' - s.add_development_dependency 'sqlite3', '~> 1.3.6' + s.add_development_dependency 'sqlite3', '~> 1.4' s.add_development_dependency 'pry-byebug' s.license = 'MIT' diff --git a/gemfiles/rails42.gemfile b/gemfiles/rails42.gemfile index 24e5e488..9be86420 100644 --- a/gemfiles/rails42.gemfile +++ b/gemfiles/rails42.gemfile @@ -3,5 +3,5 @@ source "https://rubygems.org" gem "activerecord", "~> 4.2.0" -gem "mysql2", "0.4.10" + gemspec path: "../" diff --git a/gemfiles/rails60.gemfile b/gemfiles/rails60.gemfile new file mode 100644 index 00000000..12482216 --- /dev/null +++ b/gemfiles/rails60.gemfile @@ -0,0 +1,7 @@ +# This file was generated by Appraisal + +source "https://rubygems.org" + +gem "activerecord", "~> 6.0.6.1" + +gemspec path: "../" diff --git a/lib/octopus.rb b/lib/octopus.rb index ffea3f78..1a408f47 100644 --- a/lib/octopus.rb +++ b/lib/octopus.rb @@ -19,7 +19,7 @@ def self.config file_name = File.join(Octopus.directory, 'config/shards.yml').to_s if File.exist?(file_name) || File.symlink?(file_name) - config ||= HashWithIndifferentAccess.new(YAML.load(ERB.new(File.read(file_name)).result))[Octopus.env] + config ||= HashWithIndifferentAccess.new(load_yaml(file_name))[Octopus.env] else config ||= HashWithIndifferentAccess.new end @@ -28,6 +28,18 @@ def self.config end end + # To support psych-4 + # Copied from rails patch + # https://github.com/rails/rails/commit/179d0a1f474ada02e0030ac3bd062fc653765dbe + def self.load_yaml(file_name) + source = ERB.new(File.read(file_name)).result + begin + YAML.load(source, aliases: true) || {} + rescue ArgumentError + YAML.load(source) || {} + end + end + def self.load_balancer=(balancer) @load_balancer = balancer end @@ -114,6 +126,10 @@ def self.rails52? ActiveRecord::VERSION::MAJOR == 5 && ActiveRecord::VERSION::MINOR == 2 end + def self.rails60? + ActiveRecord::VERSION::MAJOR > 6 || ActiveRecord::VERSION::MAJOR == 6 + end + def self.atleast_rails51? ActiveRecord::VERSION::MAJOR > 5 || (ActiveRecord::VERSION::MAJOR == 5 && ActiveRecord::VERSION::MINOR >= 1) end diff --git a/lib/octopus/collection_association.rb b/lib/octopus/collection_association.rb index 6713271a..e688f0c4 100644 --- a/lib/octopus/collection_association.rb +++ b/lib/octopus/collection_association.rb @@ -1,7 +1,7 @@ module Octopus module CollectionAssociation def self.included(base) - if Octopus.rails51? || Octopus.rails52? + if Octopus.atleast_rails51? base.sharded_methods :reader, :writer, :ids_reader, :ids_writer, :create, :create!, :build, :include?, :load_target, :reload, :size, :select diff --git a/lib/octopus/persistence.rb b/lib/octopus/persistence.rb index 9e07da17..48ba0cda 100644 --- a/lib/octopus/persistence.rb +++ b/lib/octopus/persistence.rb @@ -12,6 +12,14 @@ def update_attributes!(*args) run_on_shard { super } end + def update(*args) + run_on_shard { super } + end + + def update!(*args) + run_on_shard { super } + end + def reload(*args) run_on_shard { super } end @@ -32,8 +40,8 @@ def update_column(*args) run_on_shard { super } end - def increment!(*args) - run_on_shard { super } + def increment!(...) + run_on_shard { super(...) } end def decrement!(*args) diff --git a/lib/octopus/proxy.rb b/lib/octopus/proxy.rb index b18f5ab1..9faee889 100644 --- a/lib/octopus/proxy.rb +++ b/lib/octopus/proxy.rb @@ -120,10 +120,10 @@ def check_schema_migrations(shard) def transaction(options = {}, &block) if !sharded && current_model_replicated? run_queries_on_shard(Octopus.master_shard) do - select_connection.transaction(options, &block) + select_connection.transaction(**options, &block) end else - select_connection.transaction(options, &block) + select_connection.transaction(**options, &block) end end @@ -212,9 +212,13 @@ def initialize_metadata_table # We are planning to migrate to a much stable logic for the Proxy that doesn't require method missing. def legacy_method_missing_logic(method, *args, &block) if should_clean_connection_proxy?(method) - conn = select_connection clean_connection_proxy - conn.send(method, *args, &block) + args, preparable = handle_args(args) + if preparable.nil? + select_connection.send(method, *args, &block) + else + select_connection.send(method, *args, **preparable, &block) + end elsif should_send_queries_to_shard_slave_group?(method) send_queries_to_shard_slave_group(method, *args, &block) elsif should_send_queries_to_slave_group?(method) @@ -222,7 +226,12 @@ def legacy_method_missing_logic(method, *args, &block) elsif should_send_queries_to_replicated_databases?(method) send_queries_to_selected_slave(method, *args, &block) else - val = select_connection.send(method, *args, &block) + args, preparable = handle_args(args) + val = if preparable.nil? + select_connection.send(method, *args, &block) + else + select_connection.send(method, *args, **preparable, &block) + end if val.instance_of? ActiveRecord::Result val.current_shard = shard_name @@ -309,7 +318,12 @@ def send_queries_to_balancer(balancer, method, *args, &block) # while preserving `current_shard` def send_queries_to_slave(slave, method, *args, &block) using_shard(slave) do - val = select_connection.send(method, *args, &block) + args, preparable = handle_args(args) + val = if preparable.nil? + select_connection.send(method, *args, &block) + else + select_connection.send(method, *args, **preparable, &block) + end if val.instance_of? ActiveRecord::Result val.current_shard = slave end @@ -361,5 +375,17 @@ def using_group(group, &_block) self.current_group = older_group end end + + private + + def handle_args(args) + return [[], nil] if args.empty? + + preparable_args = args.select { |arg| arg.is_a?(Hash) && arg.key?(:preparable) } + return [args, nil] if preparable_args.empty? + + args -= preparable_args + [args, preparable_args.first] + end end end diff --git a/lib/octopus/relation_proxy.rb b/lib/octopus/relation_proxy.rb index aaf36d69..92fc672d 100644 --- a/lib/octopus/relation_proxy.rb +++ b/lib/octopus/relation_proxy.rb @@ -33,7 +33,8 @@ def method_missing(method, *args, &block) if !block && BATCH_METHODS.include?(method) ::Enumerator.new do |yielder| run_on_shard do - @ar_relation.public_send(method, *args) do |batch_item| + parsed_args = args.empty? ? {} : args.first + @ar_relation.public_send(method, **parsed_args) do |batch_item| yielder << batch_item end end @@ -43,7 +44,8 @@ def method_missing(method, *args, &block) elsif WHERE_CHAIN_METHODS.include?(method) ::Octopus::ScopeProxy.new(@current_shard, run_on_shard { @ar_relation.public_send(method, *args) } ) elsif block - @ar_relation.public_send(method, *args, &block) + parsed_args = args.empty? ? {} : args.first + @ar_relation.public_send(method, **parsed_args, &block) else run_on_shard do if method == :load_records diff --git a/lib/octopus/scope_proxy.rb b/lib/octopus/scope_proxy.rb index 0cf3dd5b..12723a77 100644 --- a/lib/octopus/scope_proxy.rb +++ b/lib/octopus/scope_proxy.rb @@ -28,7 +28,7 @@ def using(shard) # Transaction Method send all queries to a specified shard. def transaction(options = {}, &block) - run_on_shard { klass.transaction(options, &block) } + run_on_shard { klass.transaction(**options, &block) } end def connection diff --git a/lib/octopus/version.rb b/lib/octopus/version.rb index 47839e61..b4579539 100644 --- a/lib/octopus/version.rb +++ b/lib/octopus/version.rb @@ -1,3 +1,3 @@ module Octopus - VERSION = '0.10.2' + VERSION = '0.11.0' end diff --git a/spec/octopus/migration_spec.rb b/spec/octopus/migration_spec.rb index 999db255..ffd7bbeb 100644 --- a/spec/octopus/migration_spec.rb +++ b/spec/octopus/migration_spec.rb @@ -2,8 +2,9 @@ def get_all_versions if Octopus.atleast_rails52? + schema = ActiveRecord::SchemaMigration migrations_root = File.expand_path(File.join(File.dirname(__FILE__), '..', 'migrations')) - ActiveRecord::MigrationContext.new(migrations_root).get_all_versions + ActiveRecord::MigrationContext.new(migrations_root, schema).get_all_versions else ActiveRecord::Migrator.get_all_versions end diff --git a/spec/octopus/model_spec.rb b/spec/octopus/model_spec.rb index 5f827e52..bd51b564 100644 --- a/spec/octopus/model_spec.rb +++ b/spec/octopus/model_spec.rb @@ -497,7 +497,7 @@ end user = User.using(:brazil).where(:name => 'User1').first - expect(user.as_json(:except => [:created_at, :updated_at, :id])).to eq('admin' => nil, 'name' => 'User1', 'number' => nil) + expect(user.as_json(:except => [:created_at, :updated_at, :id, :current_shard])).to eq('admin' => nil, 'name' => 'User1', 'number' => nil) end describe 'transaction' do diff --git a/spec/octopus/proxy_spec.rb b/spec/octopus/proxy_spec.rb index 6dc15277..b46bed2f 100644 --- a/spec/octopus/proxy_spec.rb +++ b/spec/octopus/proxy_spec.rb @@ -33,7 +33,7 @@ expect(config[:username]).to eq("#{ENV['MYSQL_USER'] || 'root'}") end - unless Octopus.rails50? || Octopus.rails51?|| Octopus.rails52? + unless Octopus.rails50? || Octopus.rails51?|| Octopus.rails52? || Octopus.rails60? it 'should respond correctly to respond_to?(:pk_and_sequence_for)' do expect(proxy.respond_to?(:pk_and_sequence_for)).to be true end diff --git a/spec/support/octopus_helper.rb b/spec/support/octopus_helper.rb index 01db6431..b2f3279c 100644 --- a/spec/support/octopus_helper.rb +++ b/spec/support/octopus_helper.rb @@ -40,8 +40,9 @@ def self.migrating_to_version(version, &_block) def self.migrate_to_version(direction, root, version) if Octopus.atleast_rails52? - migrations = ActiveRecord::MigrationContext.new(root).migrations.select {|mig| version == mig.version } - ActiveRecord::Migrator.new(direction, migrations, version).run + schema = ActiveRecord::SchemaMigration + migrations = ActiveRecord::MigrationContext.new(root, schema).migrations.select {|mig| version == mig.version } + ActiveRecord::Migrator.new(direction, migrations, schema, version).run else ActiveRecord::Migrator.run(direction, root, version) end