ar-octopus 0.8.2 → 0.8.3

Sign up to get free protection for your applications and to get access to all the features.
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