ar-octopus-master 0.9.2.master → 0.9.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (161) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +12 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +46 -0
  5. data/.rubocop_todo.yml +56 -0
  6. data/.travis.yml +25 -0
  7. data/Appraisals +20 -0
  8. data/Gemfile +4 -0
  9. data/README.mkdn +242 -0
  10. data/Rakefile +172 -0
  11. data/TODO.txt +7 -0
  12. data/ar-octopus.gemspec +39 -0
  13. data/gemfiles/rails4.gemfile +7 -0
  14. data/gemfiles/rails41.gemfile +7 -0
  15. data/gemfiles/rails42.gemfile +7 -0
  16. data/gemfiles/rails5.gemfile +7 -0
  17. data/gemfiles/rails51.gemfile +7 -0
  18. data/lib/ar-octopus.rb +1 -0
  19. data/lib/octopus.rb +205 -0
  20. data/lib/octopus/abstract_adapter.rb +33 -0
  21. data/lib/octopus/association.rb +14 -0
  22. data/lib/octopus/association_shard_tracking.rb +74 -0
  23. data/lib/octopus/collection_association.rb +17 -0
  24. data/lib/octopus/collection_proxy.rb +16 -0
  25. data/lib/octopus/exception.rb +4 -0
  26. data/lib/octopus/finder_methods.rb +8 -0
  27. data/lib/octopus/has_and_belongs_to_many_association.rb +9 -0
  28. data/lib/octopus/load_balancing.rb +4 -0
  29. data/lib/octopus/load_balancing/round_robin.rb +20 -0
  30. data/lib/octopus/log_subscriber.rb +26 -0
  31. data/lib/octopus/migration.rb +195 -0
  32. data/lib/octopus/model.rb +223 -0
  33. data/lib/octopus/persistence.rb +45 -0
  34. data/lib/octopus/proxy.rb +346 -0
  35. data/lib/octopus/proxy_config.rb +252 -0
  36. data/lib/octopus/query_cache_for_shards.rb +25 -0
  37. data/lib/octopus/railtie.rb +11 -0
  38. data/lib/octopus/relation_proxy.rb +58 -0
  39. data/lib/octopus/result_patch.rb +19 -0
  40. data/lib/octopus/scope_proxy.rb +68 -0
  41. data/lib/octopus/shard_tracking.rb +46 -0
  42. data/lib/octopus/shard_tracking/attribute.rb +22 -0
  43. data/lib/octopus/shard_tracking/dynamic.rb +11 -0
  44. data/lib/octopus/singular_association.rb +9 -0
  45. data/lib/octopus/slave_group.rb +13 -0
  46. data/lib/octopus/version.rb +3 -0
  47. data/lib/tasks/octopus.rake +16 -0
  48. data/sample_app/.gitignore +4 -0
  49. data/sample_app/.rspec +1 -0
  50. data/sample_app/Gemfile +20 -0
  51. data/sample_app/README +3 -0
  52. data/sample_app/README.rdoc +261 -0
  53. data/sample_app/Rakefile +7 -0
  54. data/sample_app/app/assets/images/rails.png +0 -0
  55. data/sample_app/app/assets/javascripts/application.js +15 -0
  56. data/sample_app/app/assets/stylesheets/application.css +13 -0
  57. data/sample_app/app/controllers/application_controller.rb +4 -0
  58. data/sample_app/app/helpers/application_helper.rb +2 -0
  59. data/sample_app/app/mailers/.gitkeep +0 -0
  60. data/sample_app/app/models/.gitkeep +0 -0
  61. data/sample_app/app/models/item.rb +3 -0
  62. data/sample_app/app/models/user.rb +3 -0
  63. data/sample_app/app/views/layouts/application.html.erb +14 -0
  64. data/sample_app/autotest/discover.rb +2 -0
  65. data/sample_app/config.ru +4 -0
  66. data/sample_app/config/application.rb +62 -0
  67. data/sample_app/config/boot.rb +6 -0
  68. data/sample_app/config/cucumber.yml +8 -0
  69. data/sample_app/config/database.yml +28 -0
  70. data/sample_app/config/environment.rb +5 -0
  71. data/sample_app/config/environments/development.rb +37 -0
  72. data/sample_app/config/environments/production.rb +67 -0
  73. data/sample_app/config/environments/test.rb +37 -0
  74. data/sample_app/config/initializers/backtrace_silencers.rb +7 -0
  75. data/sample_app/config/initializers/inflections.rb +15 -0
  76. data/sample_app/config/initializers/mime_types.rb +5 -0
  77. data/sample_app/config/initializers/secret_token.rb +7 -0
  78. data/sample_app/config/initializers/session_store.rb +8 -0
  79. data/sample_app/config/initializers/wrap_parameters.rb +14 -0
  80. data/sample_app/config/locales/en.yml +5 -0
  81. data/sample_app/config/routes.rb +58 -0
  82. data/sample_app/config/shards.yml +28 -0
  83. data/sample_app/db/migrate/20100720172715_create_users.rb +15 -0
  84. data/sample_app/db/migrate/20100720172730_create_items.rb +16 -0
  85. data/sample_app/db/migrate/20100720210335_create_sample_users.rb +11 -0
  86. data/sample_app/db/schema.rb +29 -0
  87. data/sample_app/db/seeds.rb +16 -0
  88. data/sample_app/doc/README_FOR_APP +2 -0
  89. data/sample_app/features/migrate.feature +45 -0
  90. data/sample_app/features/seed.feature +15 -0
  91. data/sample_app/features/step_definitions/seeds_steps.rb +13 -0
  92. data/sample_app/features/step_definitions/web_steps.rb +218 -0
  93. data/sample_app/features/support/database.rb +13 -0
  94. data/sample_app/features/support/env.rb +57 -0
  95. data/sample_app/features/support/paths.rb +33 -0
  96. data/sample_app/lib/assets/.gitkeep +0 -0
  97. data/sample_app/lib/tasks/.gitkeep +0 -0
  98. data/sample_app/lib/tasks/cucumber.rake +64 -0
  99. data/sample_app/log/.gitkeep +0 -0
  100. data/sample_app/public/404.html +26 -0
  101. data/sample_app/public/422.html +26 -0
  102. data/sample_app/public/500.html +26 -0
  103. data/sample_app/public/favicon.ico +0 -0
  104. data/sample_app/public/images/rails.png +0 -0
  105. data/sample_app/public/index.html +279 -0
  106. data/sample_app/public/javascripts/application.js +2 -0
  107. data/sample_app/public/javascripts/controls.js +965 -0
  108. data/sample_app/public/javascripts/dragdrop.js +974 -0
  109. data/sample_app/public/javascripts/effects.js +1123 -0
  110. data/sample_app/public/javascripts/prototype.js +4874 -0
  111. data/sample_app/public/javascripts/rails.js +118 -0
  112. data/sample_app/public/robots.txt +5 -0
  113. data/sample_app/public/stylesheets/.gitkeep +0 -0
  114. data/sample_app/script/cucumber +10 -0
  115. data/sample_app/script/rails +6 -0
  116. data/sample_app/spec/models/item_spec.rb +5 -0
  117. data/sample_app/spec/models/user_spec.rb +5 -0
  118. data/sample_app/spec/spec_helper.rb +27 -0
  119. data/sample_app/vendor/assets/javascripts/.gitkeep +0 -0
  120. data/sample_app/vendor/assets/stylesheets/.gitkeep +0 -0
  121. data/sample_app/vendor/plugins/.gitkeep +0 -0
  122. data/spec/config/shards.yml +229 -0
  123. data/spec/migrations/10_create_users_using_replication.rb +9 -0
  124. data/spec/migrations/11_add_field_in_all_slaves.rb +11 -0
  125. data/spec/migrations/12_create_users_using_block.rb +23 -0
  126. data/spec/migrations/13_create_users_using_block_and_using.rb +15 -0
  127. data/spec/migrations/14_create_users_on_shards_of_a_group_with_versions.rb +11 -0
  128. data/spec/migrations/15_create_user_on_shards_of_default_group_with_versions.rb +9 -0
  129. data/spec/migrations/1_create_users_on_master.rb +9 -0
  130. data/spec/migrations/2_create_users_on_canada.rb +11 -0
  131. data/spec/migrations/3_create_users_on_both_shards.rb +11 -0
  132. data/spec/migrations/4_create_users_on_shards_of_a_group.rb +11 -0
  133. data/spec/migrations/5_create_users_on_multiples_groups.rb +11 -0
  134. data/spec/migrations/6_raise_exception_with_invalid_shard_name.rb +11 -0
  135. data/spec/migrations/7_raise_exception_with_invalid_multiple_shard_names.rb +11 -0
  136. data/spec/migrations/8_raise_exception_with_invalid_group_name.rb +11 -0
  137. data/spec/migrations/9_raise_exception_with_multiple_invalid_group_names.rb +11 -0
  138. data/spec/octopus/association_shard_tracking_spec.rb +1036 -0
  139. data/spec/octopus/collection_proxy_spec.rb +16 -0
  140. data/spec/octopus/load_balancing/round_robin_spec.rb +15 -0
  141. data/spec/octopus/log_subscriber_spec.rb +19 -0
  142. data/spec/octopus/migration_spec.rb +134 -0
  143. data/spec/octopus/model_spec.rb +754 -0
  144. data/spec/octopus/octopus_spec.rb +123 -0
  145. data/spec/octopus/proxy_spec.rb +303 -0
  146. data/spec/octopus/query_cache_for_shards_spec.rb +17 -0
  147. data/spec/octopus/relation_proxy_spec.rb +124 -0
  148. data/spec/octopus/replicated_slave_grouped_spec.rb +91 -0
  149. data/spec/octopus/replication_spec.rb +196 -0
  150. data/spec/octopus/scope_proxy_spec.rb +97 -0
  151. data/spec/octopus/sharded_replicated_slave_grouped_spec.rb +55 -0
  152. data/spec/octopus/sharded_spec.rb +33 -0
  153. data/spec/spec_helper.rb +18 -0
  154. data/spec/support/active_record/connection_adapters/modify_config_adapter.rb +15 -0
  155. data/spec/support/database_connection.rb +4 -0
  156. data/spec/support/database_models.rb +118 -0
  157. data/spec/support/octopus_helper.rb +54 -0
  158. data/spec/support/query_count.rb +17 -0
  159. data/spec/support/shared_contexts.rb +18 -0
  160. data/spec/tasks/octopus.rake_spec.rb +32 -0
  161. metadata +203 -5
@@ -0,0 +1,7 @@
1
+ - Find the best way to move data along shards.
2
+ - Improve documentation.
3
+ - Automatic configuration file for shards.
4
+ - Support specifics cases of sharding/replication.
5
+ - Basic algorithm to do load balancing between slaves server (handling servers down)
6
+ - Support replication + sharding
7
+ - Wiki about validation
@@ -0,0 +1,39 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $LOAD_PATH.push File.expand_path('../lib', __FILE__)
3
+ require 'octopus/version'
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'ar-octopus-master'
7
+ s.version = Octopus::VERSION
8
+ s.authors = ['Thiago Pradi', 'Mike Perham', 'Gabriel Sobrinho']
9
+ s.email = ['tchandy@gmail.com', 'mperham@gmail.com', 'gabriel.sobrinho@gmail.com']
10
+ s.homepage = 'https://github.com/tchandy/octopus'
11
+ s.summary = 'Easy Database Sharding for ActiveRecord'
12
+ s.description = 'This gem allows you to use sharded databases with ActiveRecord. This also provides a interface for replication and for running migrations with multiples shards.'
13
+
14
+ s.files = `git ls-files`.split("\n")
15
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
17
+ s.require_paths = ['lib']
18
+
19
+ s.post_install_message = "Important: If you are upgrading from < Octopus 0.5.0 you need to run:\n" \
20
+ "$ rake octopus:copy_schema_versions\n\n" \
21
+ 'Octopus now stores schema version information in each shard and migrations will not ' \
22
+ 'work properly unless this task is invoked.'
23
+
24
+ s.required_ruby_version = '>= 2.1.0'
25
+
26
+ s.add_dependency 'activerecord', '>= 4.0.0', "< 5.2.0"
27
+ s.add_dependency 'activesupport', '>= 4.0.0', "< 5.2.0"
28
+
29
+ s.add_development_dependency 'appraisal', '>= 0.3.8'
30
+ s.add_development_dependency 'mysql2', '>= 0.4', "< 0.5"
31
+ s.add_development_dependency 'pg', '~> 0.18'
32
+ s.add_development_dependency 'rake'
33
+ s.add_development_dependency 'rspec', '>= 3'
34
+ s.add_development_dependency 'rubocop'
35
+ s.add_development_dependency 'sqlite3', '>= 1.3.4'
36
+ s.add_development_dependency 'pry-byebug'
37
+
38
+ s.license = 'MIT'
39
+ end
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 4.0.0"
6
+
7
+ gemspec :path => "../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 4.1.0"
6
+
7
+ gemspec :path => "../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 4.2.0"
6
+
7
+ gemspec :path => "../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 5.0.0"
6
+
7
+ gemspec :path => "../"
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "activerecord", "~> 5.1.0"
6
+
7
+ gemspec :path => "../"
@@ -0,0 +1 @@
1
+ require 'octopus'
@@ -0,0 +1,205 @@
1
+ require 'active_record'
2
+ require 'active_support/version'
3
+ require 'active_support/core_ext/class'
4
+
5
+ require 'yaml'
6
+ require 'erb'
7
+
8
+ module Octopus
9
+ def self.env
10
+ @env ||= 'octopus'
11
+ end
12
+
13
+ def self.rails_env
14
+ @rails_env ||= defined?(::Rails.env) ? Rails.env.to_s : 'shards'
15
+ end
16
+
17
+ def self.config
18
+ @config ||= begin
19
+ file_name = File.join(Octopus.directory, 'config/shards.yml').to_s
20
+
21
+ if File.exist?(file_name) || File.symlink?(file_name)
22
+ config ||= HashWithIndifferentAccess.new(YAML.load(ERB.new(File.read(file_name)).result))[Octopus.env]
23
+ else
24
+ config ||= HashWithIndifferentAccess.new
25
+ end
26
+
27
+ config
28
+ end
29
+ end
30
+
31
+ def self.load_balancer=(balancer)
32
+ @load_balancer = balancer
33
+ end
34
+
35
+ def self.load_balancer
36
+ @load_balancer ||= Octopus::LoadBalancing::RoundRobin
37
+ end
38
+
39
+ def self.master_shard
40
+ ((config && config[:master_shard]) || :master).to_sym
41
+ end
42
+
43
+ # Public: Whether or not Octopus is configured and should hook into the
44
+ # current environment. Checks the environments config option for the Rails
45
+ # environment by default.
46
+ #
47
+ # Returns a boolean
48
+ def self.enabled?
49
+ if defined?(::Rails.env)
50
+ Octopus.environments.include?(Rails.env.to_s)
51
+ else
52
+ # TODO: This doens't feel right but !Octopus.config.blank? is breaking a
53
+ # test. Also, Octopus.config is always returning a hash.
54
+ Octopus.config
55
+ end
56
+ end
57
+
58
+ # Returns the Rails.root_to_s when you are using rails
59
+ # Running the current directory in a generic Ruby process
60
+ def self.directory
61
+ @directory ||= defined?(::Rails.root) ? Rails.root.to_s : Dir.pwd
62
+ end
63
+
64
+ # This is the default way to do Octopus Setup
65
+ # Available variables:
66
+ # :enviroments => the enviroments that octopus will run. default: 'production'
67
+ def self.setup
68
+ yield self
69
+ end
70
+
71
+ def self.environments=(environments)
72
+ @environments = environments.map(&:to_s)
73
+ end
74
+
75
+ def self.environments
76
+ @environments ||= config['environments'] || ['production']
77
+ end
78
+
79
+ def self.robust_environments=(environments)
80
+ @robust_environments = environments.map(&:to_s)
81
+ end
82
+
83
+ # Environments in which to swallow failures from a single shard
84
+ # when iterating through all.
85
+ def self.robust_environments
86
+ @robust_environments ||= config['robust_environments'] || ['production']
87
+ end
88
+
89
+ def self.robust_environment?
90
+ robust_environments.include? rails_env
91
+ end
92
+
93
+ def self.rails4?
94
+ ActiveRecord::VERSION::MAJOR == 4
95
+ end
96
+
97
+ def self.rails40?
98
+ rails4? && ActiveRecord::VERSION::MINOR == 0
99
+ end
100
+
101
+ def self.rails41_only?
102
+ rails4? && ActiveRecord::VERSION::MINOR == 1
103
+ end
104
+
105
+ def self.rails41?
106
+ rails4? && ActiveRecord::VERSION::MINOR >= 1
107
+ end
108
+
109
+ def self.rails42?
110
+ rails4? && ActiveRecord::VERSION::MINOR == 2
111
+ end
112
+
113
+ def self.rails50?
114
+ ActiveRecord::VERSION::MAJOR == 5 && ActiveRecord::VERSION::MINOR == 0
115
+ end
116
+
117
+ def self.rails51?
118
+ ActiveRecord::VERSION::MAJOR == 5 && ActiveRecord::VERSION::MINOR == 1
119
+ end
120
+
121
+ def self.atleast_rails51?
122
+ ActiveRecord::VERSION::MAJOR > 5 || (ActiveRecord::VERSION::MAJOR == 5 && ActiveRecord::VERSION::MINOR >= 1)
123
+ end
124
+
125
+ attr_writer :logger
126
+
127
+ def self.logger
128
+ if defined?(Rails.logger)
129
+ @logger ||= Rails.logger
130
+ else
131
+ @logger ||= Logger.new($stderr)
132
+ end
133
+ end
134
+
135
+ def self.shards=(shards)
136
+ config[rails_env] = HashWithIndifferentAccess.new(shards)
137
+ ActiveRecord::Base.connection.initialize_shards(@config)
138
+ end
139
+
140
+ def self.using(shard, &block)
141
+ conn = ActiveRecord::Base.connection
142
+
143
+ if conn.is_a?(Octopus::Proxy)
144
+ conn.run_queries_on_shard(shard, &block)
145
+ else
146
+ yield
147
+ end
148
+ end
149
+
150
+ def self.using_group(group, &block)
151
+ conn = ActiveRecord::Base.connection
152
+
153
+ if conn.is_a?(Octopus::Proxy)
154
+ conn.send_queries_to_group(group, &block)
155
+ else
156
+ yield
157
+ end
158
+ end
159
+
160
+ def self.using_all(&block)
161
+ conn = ActiveRecord::Base.connection
162
+
163
+ if conn.is_a?(Octopus::Proxy)
164
+ conn.send_queries_to_all_shards(&block)
165
+ else
166
+ yield
167
+ end
168
+ end
169
+
170
+ def self.fully_replicated(&_block)
171
+ old_fully_replicated = Thread.current[Octopus::ProxyConfig::FULLY_REPLICATED_KEY]
172
+ Thread.current[Octopus::ProxyConfig::FULLY_REPLICATED_KEY] = true
173
+ yield
174
+ ensure
175
+ Thread.current[Octopus::ProxyConfig::FULLY_REPLICATED_KEY] = old_fully_replicated
176
+ end
177
+ end
178
+
179
+ require 'octopus/exception'
180
+
181
+ require 'octopus/shard_tracking'
182
+ require 'octopus/shard_tracking/attribute'
183
+ require 'octopus/shard_tracking/dynamic'
184
+
185
+ require 'octopus/model'
186
+ require 'octopus/result_patch'
187
+ require 'octopus/migration'
188
+ require 'octopus/association'
189
+ require 'octopus/collection_association'
190
+ require 'octopus/has_and_belongs_to_many_association' unless Octopus.rails41? || Octopus.rails50? || Octopus.rails51?
191
+ require 'octopus/association_shard_tracking'
192
+ require 'octopus/persistence'
193
+ require 'octopus/log_subscriber'
194
+ require 'octopus/abstract_adapter'
195
+ require 'octopus/singular_association'
196
+ require 'octopus/finder_methods'
197
+ require 'octopus/query_cache_for_shards' unless Octopus.rails4?
198
+
199
+ require 'octopus/railtie' if defined?(::Rails::Railtie)
200
+
201
+ require 'octopus/proxy_config'
202
+ require 'octopus/proxy'
203
+ require 'octopus/collection_proxy'
204
+ require 'octopus/relation_proxy'
205
+ require 'octopus/scope_proxy'
@@ -0,0 +1,33 @@
1
+ # Implementation courtesy of db-charmer.
2
+ module Octopus
3
+ module AbstractAdapter
4
+ module OctopusShard
5
+ class InstrumenterDecorator < ActiveSupport::ProxyObject
6
+ def initialize(adapter, instrumenter)
7
+ @adapter = adapter
8
+ @instrumenter = instrumenter
9
+ end
10
+
11
+ def instrument(name, payload = {}, &block)
12
+ payload[:octopus_shard] ||= @adapter.octopus_shard
13
+ @instrumenter.instrument(name, payload, &block)
14
+ end
15
+
16
+ def method_missing(meth, *args, &block)
17
+ @instrumenter.send(meth, *args, &block)
18
+ end
19
+ end
20
+
21
+ def octopus_shard
22
+ @config[:octopus_shard]
23
+ end
24
+
25
+ def initialize(*args)
26
+ super
27
+ @instrumenter = InstrumenterDecorator.new(self, @instrumenter)
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ ActiveRecord::ConnectionAdapters::AbstractAdapter.send(:prepend, Octopus::AbstractAdapter::OctopusShard)
@@ -0,0 +1,14 @@
1
+ module Octopus
2
+ module Association
3
+ def self.included(base)
4
+ base.send(:include, Octopus::ShardTracking::Dynamic)
5
+ base.sharded_methods :target_scope
6
+ end
7
+
8
+ def current_shard
9
+ owner.current_shard
10
+ end
11
+ end
12
+ end
13
+
14
+ ActiveRecord::Associations::Association.send(:include, Octopus::Association)
@@ -0,0 +1,74 @@
1
+ module Octopus
2
+ module AssociationShardTracking
3
+ class MismatchedShards < StandardError
4
+ attr_reader :record, :current_shard
5
+
6
+ def initialize(record, current_shard)
7
+ @record = record
8
+ @current_shard = current_shard
9
+ end
10
+
11
+ def message
12
+ [
13
+ "Association Error: Records are from different shards",
14
+ "Record: #{record.inspect}",
15
+ "Current Shard: #{current_shard.inspect}",
16
+ "Current Record Shard: #{record.current_shard.inspect}",
17
+ ].join(" ")
18
+ end
19
+ end
20
+
21
+ def self.extended(base)
22
+ base.send(:include, InstanceMethods)
23
+ end
24
+
25
+ module InstanceMethods
26
+ def connection_on_association=(record)
27
+ return unless ::Octopus.enabled?
28
+ return if !self.class.connection.respond_to?(:current_shard) || !self.respond_to?(:current_shard)
29
+
30
+ if !record.current_shard.nil? && !current_shard.nil? && record.current_shard.to_s != current_shard.to_s
31
+ raise MismatchedShards.new(record, current_shard)
32
+ end
33
+
34
+ record.current_shard = self.class.connection.current_shard = current_shard if should_set_current_shard?
35
+ end
36
+ end
37
+
38
+ def has_many(association_id, scope = nil, options = {}, &extension)
39
+ if options == {} && scope.is_a?(Hash)
40
+ default_octopus_opts(scope)
41
+ else
42
+ default_octopus_opts(options)
43
+ end
44
+ super
45
+ end
46
+
47
+ def has_and_belongs_to_many(association_id, scope = nil, options = {}, &extension)
48
+ assign_octopus_opts(scope, options)
49
+ super
50
+ end
51
+
52
+ def default_octopus_opts(options)
53
+ options[:before_add] = [ :connection_on_association=, options[:before_add] ].compact.flatten
54
+ options[:before_remove] = [ :connection_on_association=, options[:before_remove] ].compact.flatten
55
+ end
56
+
57
+ def assign_octopus_opts(scope, options)
58
+ if options == {} && scope.is_a?(Hash)
59
+ default_octopus_opts(scope)
60
+ else
61
+ default_octopus_opts(options)
62
+ end
63
+ end
64
+
65
+ if Octopus.atleast_rails51?
66
+ def has_and_belongs_to_many(association_id, scope = nil, **options, &extension)
67
+ assign_octopus_opts(scope, options)
68
+ super
69
+ end
70
+ end
71
+ end
72
+ end
73
+
74
+ ActiveRecord::Base.extend(Octopus::AssociationShardTracking)
@@ -0,0 +1,17 @@
1
+ module Octopus
2
+ module CollectionAssociation
3
+ def self.included(base)
4
+ if Octopus.rails51?
5
+ base.sharded_methods :reader, :writer, :ids_reader, :ids_writer, :create, :create!,
6
+ :build, :include?,
7
+ :load_target, :reload, :size, :select
8
+ else
9
+ base.sharded_methods :reader, :writer, :ids_reader, :ids_writer, :create, :create!,
10
+ :build, :any?, :count, :empty?, :first, :include?, :last, :length,
11
+ :load_target, :many?, :reload, :size, :select, :uniq
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+ ActiveRecord::Associations::CollectionAssociation.send(:include, Octopus::CollectionAssociation)
@@ -0,0 +1,16 @@
1
+ module Octopus
2
+ module CollectionProxy
3
+ def self.included(base)
4
+ base.send(:include, Octopus::ShardTracking::Dynamic)
5
+ base.sharded_methods :any?, :build, :count, :create, :create!, :concat, :delete, :delete_all,
6
+ :destroy, :destroy_all, :empty?, :find, :first, :include?, :last, :length,
7
+ :many?, :pluck, :replace, :select, :size, :sum, :to_a, :uniq
8
+ end
9
+
10
+ def current_shard
11
+ @association.owner.current_shard
12
+ end
13
+ end
14
+ end
15
+
16
+ ActiveRecord::Associations::CollectionProxy.send(:include, Octopus::CollectionProxy)