ar-octopus 0.8.2 → 0.8.3

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.
Files changed (87) hide show
  1. checksums.yaml +9 -9
  2. data/.rspec +1 -1
  3. data/.rubocop.yml +46 -0
  4. data/.rubocop_todo.yml +52 -0
  5. data/.ruby-version +1 -1
  6. data/.travis.yml +3 -9
  7. data/Appraisals +4 -0
  8. data/Rakefile +17 -16
  9. data/ar-octopus.gemspec +22 -16
  10. data/gemfiles/rails41.gemfile +7 -0
  11. data/init.rb +1 -1
  12. data/lib/ar-octopus.rb +1 -1
  13. data/lib/octopus.rb +38 -37
  14. data/lib/octopus/abstract_adapter.rb +0 -2
  15. data/lib/octopus/association.rb +8 -6
  16. data/lib/octopus/association_shard_tracking.rb +80 -81
  17. data/lib/octopus/collection_association.rb +7 -5
  18. data/lib/octopus/collection_proxy.rb +11 -9
  19. data/lib/octopus/has_and_belongs_to_many_association.rb +5 -3
  20. data/lib/octopus/load_balancing.rb +3 -2
  21. data/lib/octopus/load_balancing/round_robin.rb +12 -8
  22. data/lib/octopus/migration.rb +117 -108
  23. data/lib/octopus/model.rb +130 -134
  24. data/lib/octopus/persistence.rb +1 -1
  25. data/lib/octopus/proxy.rb +345 -339
  26. data/lib/octopus/railtie.rb +2 -2
  27. data/lib/octopus/relation_proxy.rb +6 -1
  28. data/lib/octopus/scope_proxy.rb +38 -36
  29. data/lib/octopus/shard_tracking.rb +36 -35
  30. data/lib/octopus/shard_tracking/attribute.rb +12 -14
  31. data/lib/octopus/shard_tracking/dynamic.rb +7 -3
  32. data/lib/octopus/singular_association.rb +5 -3
  33. data/lib/octopus/slave_group.rb +10 -8
  34. data/lib/octopus/version.rb +1 -1
  35. data/rails/init.rb +1 -1
  36. data/sample_app/autotest/discover.rb +2 -2
  37. data/sample_app/config/application.rb +1 -1
  38. data/sample_app/config/boot.rb +1 -1
  39. data/sample_app/config/environments/test.rb +1 -1
  40. data/sample_app/config/initializers/session_store.rb +1 -1
  41. data/sample_app/config/initializers/wrap_parameters.rb +1 -1
  42. data/sample_app/config/routes.rb +1 -1
  43. data/sample_app/db/migrate/20100720210335_create_sample_users.rb +2 -2
  44. data/sample_app/db/schema.rb +10 -10
  45. data/sample_app/db/seeds.rb +3 -3
  46. data/sample_app/features/step_definitions/seeds_steps.rb +4 -4
  47. data/sample_app/features/step_definitions/web_steps.rb +3 -4
  48. data/sample_app/features/support/env.rb +3 -4
  49. data/sample_app/features/support/paths.rb +4 -4
  50. data/sample_app/spec/spec_helper.rb +3 -3
  51. data/spec/migrations/10_create_users_using_replication.rb +3 -3
  52. data/spec/migrations/11_add_field_in_all_slaves.rb +3 -3
  53. data/spec/migrations/12_create_users_using_block.rb +7 -7
  54. data/spec/migrations/13_create_users_using_block_and_using.rb +4 -4
  55. data/spec/migrations/14_create_users_on_shards_of_a_group_with_versions.rb +2 -2
  56. data/spec/migrations/15_create_user_on_shards_of_default_group_with_versions.rb +2 -2
  57. data/spec/migrations/1_create_users_on_master.rb +3 -3
  58. data/spec/migrations/2_create_users_on_canada.rb +3 -3
  59. data/spec/migrations/3_create_users_on_both_shards.rb +3 -3
  60. data/spec/migrations/4_create_users_on_shards_of_a_group.rb +3 -3
  61. data/spec/migrations/5_create_users_on_multiples_groups.rb +2 -2
  62. data/spec/migrations/6_raise_exception_with_invalid_shard_name.rb +3 -3
  63. data/spec/migrations/7_raise_exception_with_invalid_multiple_shard_names.rb +3 -3
  64. data/spec/migrations/8_raise_exception_with_invalid_group_name.rb +3 -3
  65. data/spec/migrations/9_raise_exception_with_multiple_invalid_group_names.rb +4 -4
  66. data/spec/octopus/association_shard_tracking_spec.rb +413 -417
  67. data/spec/octopus/collection_proxy_spec.rb +6 -5
  68. data/spec/octopus/log_subscriber_spec.rb +4 -4
  69. data/spec/octopus/migration_spec.rb +48 -48
  70. data/spec/octopus/model_spec.rb +267 -292
  71. data/spec/octopus/octopus_spec.rb +40 -41
  72. data/spec/octopus/proxy_spec.rb +124 -124
  73. data/spec/octopus/relation_proxy_spec.rb +32 -32
  74. data/spec/octopus/replicated_slave_grouped_spec.rb +23 -23
  75. data/spec/octopus/replication_spec.rb +61 -66
  76. data/spec/octopus/scope_proxy_spec.rb +56 -10
  77. data/spec/octopus/sharded_replicated_slave_grouped_spec.rb +29 -29
  78. data/spec/octopus/sharded_spec.rb +10 -10
  79. data/spec/spec_helper.rb +6 -6
  80. data/spec/support/active_record/connection_adapters/modify_config_adapter.rb +1 -3
  81. data/spec/support/database_connection.rb +2 -2
  82. data/spec/support/database_models.rb +16 -17
  83. data/spec/support/octopus_helper.rb +19 -21
  84. data/spec/support/query_count.rb +1 -3
  85. data/spec/support/shared_contexts.rb +3 -3
  86. data/spec/tasks/octopus.rake_spec.rb +10 -10
  87. metadata +43 -26
@@ -1,10 +1,10 @@
1
1
  begin
2
- require "rails/railtie"
2
+ require 'rails/railtie'
3
3
 
4
4
  module Octopus
5
5
  class Railtie < Rails::Railtie
6
6
  rake_tasks do
7
- Dir[File.join(File.dirname(__FILE__), "../tasks/*.rake")].each { |ext| load ext }
7
+ Dir[File.join(File.dirname(__FILE__), '../tasks/*.rake')].each { |ext| load ext }
8
8
  end
9
9
  end
10
10
  end
@@ -18,6 +18,11 @@ module Octopus
18
18
  end
19
19
 
20
20
  # these methods are not normally sent to method_missing
21
+
22
+ def select(*args, &block)
23
+ method_missing(:select, *args, &block)
24
+ end
25
+
21
26
  def inspect
22
27
  method_missing(:inspect)
23
28
  end
@@ -34,6 +39,6 @@ module Octopus
34
39
  method_missing(:==, other)
35
40
  end
36
41
  end
37
- alias :eql? :==
42
+ alias_method :eql?, :==
38
43
  end
39
44
  end
@@ -1,47 +1,49 @@
1
- class Octopus::ScopeProxy
2
- include Octopus::ShardTracking::Attribute
3
- attr_accessor :klass
1
+ module Octopus
2
+ class ScopeProxy
3
+ include Octopus::ShardTracking::Attribute
4
+ attr_accessor :klass
5
+
6
+ def initialize(shard, klass)
7
+ @current_shard = shard
8
+ @klass = klass
9
+ end
4
10
 
5
- def initialize(shard, klass)
6
- @current_shard = shard
7
- @klass = klass
8
- end
11
+ def using(shard)
12
+ fail "Nonexistent Shard Name: #{shard}" if @klass.connection.instance_variable_get(:@shards)[shard].nil?
13
+ @current_shard = shard
14
+ self
15
+ end
9
16
 
10
- def using(shard)
11
- raise "Nonexistent Shard Name: #{shard}" if @klass.connection.instance_variable_get(:@shards)[shard].nil?
12
- @current_shard = shard
13
- return self
14
- end
17
+ # Transaction Method send all queries to a specified shard.
18
+ def transaction(options = {}, &block)
19
+ run_on_shard { @klass = klass.transaction(options, &block) }
20
+ end
15
21
 
16
- # Transaction Method send all queries to a specified shard.
17
- def transaction(options = {}, &block)
18
- run_on_shard { @klass = klass.transaction(options, &block) }
19
- end
22
+ def connection
23
+ @klass.connection.current_shard = @current_shard
24
+ @klass.connection
25
+ end
20
26
 
21
- def connection
22
- @klass.connection().current_shard = @current_shard
23
- @klass.connection()
24
- end
27
+ def method_missing(method, *args, &block)
28
+ result = run_on_shard { @klass.send(method, *args, &block) }
25
29
 
26
- def method_missing(method, *args, &block)
27
- result = run_on_shard { @klass.send(method, *args, &block) }
30
+ if result.respond_to?(:all)
31
+ @klass = result
32
+ return self
33
+ end
28
34
 
29
- if result.respond_to?(:scoped)
30
- @klass = result
31
- return self
35
+ result
32
36
  end
33
37
 
34
- result
35
- end
36
-
37
- def as_json(options = nil)
38
- method_missing(:as_json, options)
39
- end
38
+ def as_json(options = nil)
39
+ method_missing(:as_json, options)
40
+ end
40
41
 
41
- # Delegates to method_missing (instead of @klass) so that User.using(:blah).where(:name => "Mike")
42
- # gets run in the correct shard context when #== is evaluated.
43
- def ==(*args)
44
- method_missing(:==, *args)
42
+ # Delegates to method_missing (instead of @klass) so that User.using(:blah).where(:name => "Mike")
43
+ # gets run in the correct shard context when #== is evaluated.
44
+ def ==(other)
45
+ method_missing(:==, other)
46
+ end
47
+ alias_method :eql?, :==
45
48
  end
46
- alias :eql? :==
47
49
  end
@@ -1,45 +1,46 @@
1
- module Octopus::ShardTracking
2
- def self.included(base)
3
- base.extend(ClassMethods)
4
- end
5
-
6
- module ClassMethods
7
- # If the class which includes this module responds to the class
8
- # method sharded_methods, then automagically alias_method_chain
9
- # a sharding-friendly version of each of those methods into existence
10
- def sharded_methods(*methods)
11
- methods.each { |m| create_sharded_method(m) }
1
+ module Octopus
2
+ module ShardTracking
3
+ def self.included(base)
4
+ base.extend(ClassMethods)
12
5
  end
13
6
 
14
- def create_sharded_method(name)
15
- name.to_s =~ /([^!?]+)([!?])?/
16
- method, punctuation = [ $1, $2 ]
17
- with = :"#{method}_with_octopus#{punctuation}"
18
- without = :"#{method}_without_octopus#{punctuation}"
19
- define_method with do |*args, &block|
20
- run_on_shard { send(without, *args, &block) }
7
+ module ClassMethods
8
+ # If the class which includes this module responds to the class
9
+ # method sharded_methods, then automagically alias_method_chain
10
+ # a sharding-friendly version of each of those methods into existence
11
+ def sharded_methods(*methods)
12
+ methods.each { |m| create_sharded_method(m) }
13
+ end
14
+
15
+ def create_sharded_method(name)
16
+ name.to_s =~ /([^!?]+)([!?])?/
17
+ method, punctuation = [Regexp.last_match[1], Regexp.last_match[2]]
18
+ with = :"#{method}_with_octopus#{punctuation}"
19
+ without = :"#{method}_without_octopus#{punctuation}"
20
+ define_method with do |*args, &block|
21
+ run_on_shard { send(without, *args, &block) }
22
+ end
23
+ alias_method_chain name.to_sym, :octopus
21
24
  end
22
- alias_method_chain name.to_sym, :octopus
23
25
  end
24
- end
25
26
 
26
- # Adds run_on_shard method, but does not implement current_shard method
27
- def run_on_shard(&block)
28
- cs = current_shard
29
- if !!cs
30
- r = ActiveRecord::Base.connection_proxy.run_queries_on_shard(current_shard, &block)
31
- # Use a case statement to avoid any path through ActiveRecord::Delegation's
32
- # respond_to? code. We want to avoid the respond_to? code because it can have
33
- # the side effect of causing a call to load_target
34
- # return r
35
- case r
36
- when ActiveRecord::Relation
37
- Octopus::RelationProxy.new(cs, r)
27
+ # Adds run_on_shard method, but does not implement current_shard method
28
+ def run_on_shard(&block)
29
+ if (cs = current_shard)
30
+ r = ActiveRecord::Base.connection_proxy.run_queries_on_shard(cs, &block)
31
+ # Use a case statement to avoid any path through ActiveRecord::Delegation's
32
+ # respond_to? code. We want to avoid the respond_to? code because it can have
33
+ # the side effect of causing a call to load_target
34
+ # return r
35
+ case r
36
+ when ActiveRecord::Relation
37
+ Octopus::RelationProxy.new(cs, r)
38
+ else
39
+ r
40
+ end
38
41
  else
39
- r
42
+ yield
40
43
  end
41
- else
42
- yield
43
44
  end
44
45
  end
45
46
  end
@@ -1,24 +1,22 @@
1
1
  # Adds current_shard as an attribute; provide a default
2
2
  # implementation of set_current_shard which considers
3
3
  # only the current ActiveRecord::Base.connection_proxy
4
- module Octopus::ShardTracking::Attribute
5
- def self.included(base)
6
- base.send(:include, Octopus::ShardTracking)
7
- base.extend(ClassMethods)
8
- base.track_current_shard_as_attribute
9
- end
4
+ module Octopus
5
+ module ShardTracking
6
+ module Attribute
7
+ def self.included(base)
8
+ base.send(:include, Octopus::ShardTracking)
9
+ end
10
10
 
11
- module ClassMethods
12
- def track_current_shard_as_attribute
13
11
  attr_accessor :current_shard
14
- end
15
- end
16
12
 
17
- def set_current_shard
18
- return unless Octopus.enabled?
13
+ def set_current_shard
14
+ return unless Octopus.enabled?
19
15
 
20
- if ActiveRecord::Base.connection_proxy.block
21
- self.current_shard = ActiveRecord::Base.connection_proxy.current_shard
16
+ if ActiveRecord::Base.connection_proxy.block
17
+ self.current_shard = ActiveRecord::Base.connection_proxy.current_shard
18
+ end
19
+ end
22
20
  end
23
21
  end
24
22
  end
@@ -1,7 +1,11 @@
1
1
  require 'octopus/shard_tracking'
2
2
 
3
- module Octopus::ShardTracking::Dynamic
4
- def self.included(base)
5
- base.send(:include, Octopus::ShardTracking)
3
+ module Octopus
4
+ module ShardTracking
5
+ module Dynamic
6
+ def self.included(base)
7
+ base.send(:include, Octopus::ShardTracking)
8
+ end
9
+ end
6
10
  end
7
11
  end
@@ -1,6 +1,8 @@
1
- module Octopus::SingularAssociation
2
- def self.included(base)
3
- base.sharded_methods :reader, :writer, :create, :create!, :build
1
+ module Octopus
2
+ module SingularAssociation
3
+ def self.included(base)
4
+ base.sharded_methods :reader, :writer, :create, :create!, :build
5
+ end
4
6
  end
5
7
  end
6
8
 
@@ -1,11 +1,13 @@
1
- class Octopus::SlaveGroup
2
- def initialize(slaves)
3
- slaves = HashWithIndifferentAccess.new(slaves)
4
- slaves_list = slaves.values
5
- @load_balancer = Octopus::LoadBalancing::RoundRobin.new(slaves_list)
6
- end
1
+ module Octopus
2
+ class SlaveGroup
3
+ def initialize(slaves)
4
+ slaves = HashWithIndifferentAccess.new(slaves)
5
+ slaves_list = slaves.values
6
+ @load_balancer = Octopus::LoadBalancing::RoundRobin.new(slaves_list)
7
+ end
7
8
 
8
- def next
9
- @load_balancer.next
9
+ def next
10
+ @load_balancer.next
11
+ end
10
12
  end
11
13
  end
@@ -1,3 +1,3 @@
1
1
  module Octopus
2
- VERSION = '0.8.2'
2
+ VERSION = '0.8.3'
3
3
  end
data/rails/init.rb CHANGED
@@ -1 +1 @@
1
- require "octopus"
1
+ require 'octopus'
@@ -1,2 +1,2 @@
1
- Autotest.add_discovery { "rails" }
2
- Autotest.add_discovery { "rspec2" }
1
+ Autotest.add_discovery { 'rails' }
2
+ Autotest.add_discovery { 'rspec2' }
@@ -34,7 +34,7 @@ module SampleApp
34
34
  # config.i18n.default_locale = :de
35
35
 
36
36
  # Configure the default encoding used in templates for Ruby 1.9.
37
- config.encoding = "utf-8"
37
+ config.encoding = 'utf-8'
38
38
 
39
39
  # Configure sensitive parameters which will be filtered from the log file.
40
40
  config.filter_parameters += [:password]
@@ -3,4 +3,4 @@ require 'rubygems'
3
3
  # Set up gems listed in the Gemfile.
4
4
  ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
5
5
 
6
- require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
6
+ require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
@@ -9,7 +9,7 @@ SampleApp::Application.configure do
9
9
 
10
10
  # Configure static asset server for tests with Cache-Control for performance
11
11
  config.serve_static_assets = true
12
- config.static_cache_control = "public, max-age=3600"
12
+ config.static_cache_control = 'public, max-age=3600'
13
13
 
14
14
  # Log error messages when you accidentally call methods on nil
15
15
  config.whiny_nils = true
@@ -1,6 +1,6 @@
1
1
  # Be sure to restart your server when you modify this file.
2
2
 
3
- SampleApp::Application.config.session_store :cookie_store, key: '_sample_app_session'
3
+ SampleApp::Application.config.session_store :cookie_store, :key => '_sample_app_session'
4
4
 
5
5
  # Use the database for sessions instead of the cookie-based default,
6
6
  # which shouldn't be used to store highly confidential information
@@ -5,7 +5,7 @@
5
5
 
6
6
  # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
7
7
  ActiveSupport.on_load(:action_controller) do
8
- wrap_parameters format: [:json]
8
+ wrap_parameters :format => [:json]
9
9
  end
10
10
 
11
11
  # Disable root element in JSON by default.
@@ -1,4 +1,4 @@
1
- SampleApp::Application.routes.draw do
1
+ SampleApp::Application.routes.draw do
2
2
  # The priority is based upon order of creation:
3
3
  # first created -> highest priority.
4
4
 
@@ -2,10 +2,10 @@ class CreateSampleUsers < ActiveRecord::Migration
2
2
  using(:master, :asia, :europe, :america)
3
3
 
4
4
  def self.up
5
- User.create!(:name => "Exception")
5
+ User.create!(:name => 'Exception')
6
6
  end
7
7
 
8
8
  def self.down
9
- User.find_by_name("Exception").delete()
9
+ User.find_by_name('Exception').delete
10
10
  end
11
11
  end
@@ -11,19 +11,19 @@
11
11
  #
12
12
  # It's strongly recommended to check this file into your version control system.
13
13
 
14
- ActiveRecord::Schema.define(:version => 20100720210335) do
14
+ ActiveRecord::Schema.define(:version => 20_100_720_210_335) do
15
15
 
16
- create_table "items", :force => true do |t|
17
- t.string "name"
18
- t.integer "user_id"
19
- t.datetime "created_at", :null => false
20
- t.datetime "updated_at", :null => false
16
+ create_table 'items', :force => true do |t|
17
+ t.string 'name'
18
+ t.integer 'user_id'
19
+ t.datetime 'created_at', :null => false
20
+ t.datetime 'updated_at', :null => false
21
21
  end
22
22
 
23
- create_table "users", :force => true do |t|
24
- t.string "name"
25
- t.datetime "created_at", :null => false
26
- t.datetime "updated_at", :null => false
23
+ create_table 'users', :force => true do |t|
24
+ t.string 'name'
25
+ t.datetime 'created_at', :null => false
26
+ t.datetime 'updated_at', :null => false
27
27
  end
28
28
 
29
29
  end
@@ -6,11 +6,11 @@
6
6
  # cities = City.create([{ :name => 'Chicago' }, { :name => 'Copenhagen' }])
7
7
  # Mayor.create(:name => 'Daley', :city => cities.first)
8
8
  Octopus.using(:asia) do
9
- User.create!(:name => "Asia User")
9
+ User.create!(:name => 'Asia User')
10
10
  end
11
11
 
12
12
  Octopus.using(:america) do
13
- users_america = User.create([{ :name => 'America User 1' }, { :name => 'America User 2' }])
13
+ User.create([{ :name => 'America User 1' }, { :name => 'America User 2' }])
14
14
  end
15
15
 
16
- User.create!(:name => "Teste")
16
+ User.create!(:name => 'Teste')
@@ -3,11 +3,11 @@ Then /^the "([^"]*)" shard should have one user named "([^"]*)"$/ do |shard_name
3
3
  end
4
4
 
5
5
  Then /^the version of "([^"]*)" shard should be "([^"]*)"$/ do |shard_name, version|
6
- ab = ActiveRecord::Base.using(shard_name.to_sym).connection.select_value("select * from schema_migrations order by version desc limit 1;")
7
- version = "" if version == "nil"
6
+ ab = ActiveRecord::Base.using(shard_name.to_sym).connection.select_value('select * from schema_migrations order by version desc limit 1;')
7
+ version = '' if version == 'nil'
8
8
  ab.to_s.should == version
9
9
  end
10
10
 
11
11
  When /^I run inside my Rails project "([^"]*)" with enviroment "([^"]*)"$/ do |command, enviroment|
12
- run("cd #{Rails.root.to_s} && RAILS_ENV=#{enviroment} #{command}")
13
- end
12
+ run("cd #{Rails.root} && RAILS_ENV=#{enviroment} #{command}")
13
+ end
@@ -4,10 +4,9 @@
4
4
  # instead of editing this one. Cucumber will automatically load all features/**/*.rb
5
5
  # files.
6
6
 
7
-
8
7
  require 'uri'
9
8
  require 'cgi'
10
- require File.expand_path(File.join(File.dirname(__FILE__), "..", "support", "paths"))
9
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'support', 'paths'))
11
10
 
12
11
  module WithinHelpers
13
12
  def with_scope(locator)
@@ -62,7 +61,7 @@ end
62
61
  When /^(?:|I )fill in the following(?: within "([^"]*)")?:$/ do |selector, fields|
63
62
  with_scope(selector) do
64
63
  fields.rows_hash.each do |name, value|
65
- When %{I fill in "#{name}" with "#{value}"}
64
+ When %(I fill in "#{name}" with "#{value}")
66
65
  end
67
66
  end
68
67
  end
@@ -205,7 +204,7 @@ Then /^(?:|I )should have the following query string:$/ do |expected_pairs|
205
204
  query = URI.parse(current_url).query
206
205
  actual_params = query ? CGI.parse(query) : {}
207
206
  expected_params = {}
208
- expected_pairs.rows_hash.each_pair{|k,v| expected_params[k] = v.split(',')}
207
+ expected_pairs.rows_hash.each_pair { |k, v| expected_params[k] = v.split(',') }
209
208
 
210
209
  if actual_params.respond_to? :should
211
210
  actual_params.should == expected_params