switchman 3.1.0 → 3.5.0

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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +15 -14
  3. data/db/migrate/20180828183945_add_default_shard_index.rb +1 -1
  4. data/db/migrate/20190114212900_add_unique_name_indexes.rb +10 -4
  5. data/lib/switchman/active_record/associations.rb +97 -17
  6. data/lib/switchman/active_record/attribute_methods.rb +72 -43
  7. data/lib/switchman/active_record/base.rb +107 -21
  8. data/lib/switchman/active_record/calculations.rb +37 -33
  9. data/lib/switchman/active_record/connection_pool.rb +21 -2
  10. data/lib/switchman/active_record/database_configurations.rb +12 -7
  11. data/lib/switchman/active_record/finder_methods.rb +1 -1
  12. data/lib/switchman/active_record/log_subscriber.rb +2 -2
  13. data/lib/switchman/active_record/migration.rb +35 -8
  14. data/lib/switchman/active_record/persistence.rb +8 -0
  15. data/lib/switchman/active_record/postgresql_adapter.rb +11 -10
  16. data/lib/switchman/active_record/query_cache.rb +1 -1
  17. data/lib/switchman/active_record/query_methods.rb +172 -132
  18. data/lib/switchman/active_record/relation.rb +21 -11
  19. data/lib/switchman/active_record/spawn_methods.rb +2 -2
  20. data/lib/switchman/active_record/statement_cache.rb +9 -5
  21. data/lib/switchman/active_record/tasks/database_tasks.rb +1 -1
  22. data/lib/switchman/active_record/test_fixtures.rb +19 -16
  23. data/lib/switchman/active_support/cache.rb +4 -1
  24. data/lib/switchman/arel.rb +6 -6
  25. data/lib/switchman/call_super.rb +8 -2
  26. data/lib/switchman/database_server.rb +21 -26
  27. data/lib/switchman/default_shard.rb +3 -3
  28. data/lib/switchman/engine.rb +33 -18
  29. data/lib/switchman/environment.rb +2 -2
  30. data/lib/switchman/errors.rb +13 -0
  31. data/lib/switchman/guard_rail/relation.rb +2 -1
  32. data/lib/switchman/parallel.rb +2 -2
  33. data/lib/switchman/r_spec_helper.rb +10 -10
  34. data/lib/switchman/shard.rb +49 -32
  35. data/lib/switchman/sharded_instrumenter.rb +5 -1
  36. data/lib/switchman/shared_schema_cache.rb +11 -0
  37. data/lib/switchman/test_helper.rb +1 -1
  38. data/lib/switchman/version.rb +1 -1
  39. data/lib/switchman.rb +10 -4
  40. data/lib/tasks/switchman.rake +42 -39
  41. metadata +24 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 48b27124d5233adf585aed105d0b2522c76249bdd1e13fbcfd7e904241d3a624
4
- data.tar.gz: fea1ccc0ad63f00633fb90bee1d7bd4606f6de0e18564250865c1b1dde61ef8d
3
+ metadata.gz: 1f6f878b1a2a9ba1dc0c75ce4fcd5d299178ea7940a048190bd4b181ae306339
4
+ data.tar.gz: 19b31ce1e456fbecf2a9ba9b15644bc6073cd521b6a604da81a0c5f8977f579a
5
5
  SHA512:
6
- metadata.gz: c05b445111dd0e4b16ecbfaea6ab84557fa2cbadc6102fa0af6fb43cff2f6bbb6606e825f76ed20f6b8a4a2a2966e31babadf66c5eb065a6c4d8a7eea6a8b92a
7
- data.tar.gz: 0af9e15706142f90772d24e2187db93ec83331997bccbb25c1b89cec2dde1bd2414d2774fec3785c7bea1a1b06d3fa5ad49e15b716f0aa35794e2e5734c848a8
6
+ metadata.gz: bad542adf6c09d1e1e50ef3054a8ca8d106bd3eedb7cb9dbbe544e44dd88c7f19226e7dd0d805ad5a5685009be7b9c9fe20623182c8c624c88724d00d7bc5656
7
+ data.tar.gz: 43b76369798a3683f838e392466cec0628d0c4a309354db7ccadcafca604f6eeffdcc460e036ca8d0f105c7abed2987baa23d51b830e628951c057adb554f305
data/Rakefile CHANGED
@@ -2,37 +2,38 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  begin
5
- require 'bundler/setup'
5
+ require "bundler/setup"
6
6
  rescue LoadError
7
- puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
7
+ puts "You must `gem install bundler` and `bundle install` to run rake tasks"
8
8
  end
9
9
  begin
10
- require 'rdoc/task'
10
+ require "rdoc/task"
11
11
  rescue LoadError
12
- require 'rdoc/rdoc'
13
- require 'rake/rdoctask'
12
+ require "rdoc/rdoc"
13
+ require "rake/rdoctask"
14
14
  RDoc::Task = Rake::RDocTask
15
15
  end
16
16
 
17
17
  RDoc::Task.new(:rdoc) do |rdoc|
18
- rdoc.rdoc_dir = 'rdoc'
19
- rdoc.title = 'Switchman'
20
- rdoc.options << '--line-numbers'
21
- rdoc.rdoc_files.include('lib/**/*.rb')
18
+ rdoc.rdoc_dir = "rdoc"
19
+ rdoc.title = "Switchman"
20
+ rdoc.options << "--line-numbers"
21
+ rdoc.rdoc_files.include("lib/**/*.rb")
22
22
  end
23
23
 
24
- APP_RAKEFILE = File.expand_path('spec/dummy/Rakefile', __dir__)
25
- load 'rails/tasks/engine.rake'
24
+ load "./spec/tasks/coverage.rake"
25
+ APP_RAKEFILE = File.expand_path("spec/dummy/Rakefile", __dir__)
26
+ load "rails/tasks/engine.rake"
26
27
 
27
28
  Bundler::GemHelper.install_tasks
28
29
 
29
- require 'rspec/core/rake_task'
30
+ require "rspec/core/rake_task"
30
31
  RSpec::Core::RakeTask.new
31
32
 
32
- require 'rubocop/rake_task'
33
+ require "rubocop/rake_task"
33
34
 
34
35
  RuboCop::RakeTask.new do |task|
35
- task.options = ['-S']
36
+ task.options = ["-S"]
36
37
  end
37
38
 
38
39
  task default: %i[spec]
@@ -5,7 +5,7 @@ class AddDefaultShardIndex < ActiveRecord::Migration[4.2]
5
5
  Switchman::Shard.where(default: nil).update_all(default: false) if Switchman::Shard.current.default?
6
6
  change_column_default :switchman_shards, :default, false
7
7
  change_column_null :switchman_shards, :default, false
8
- options = if connection.adapter_name == 'PostgreSQL'
8
+ options = if connection.adapter_name == "PostgreSQL"
9
9
  { unique: true, where: '"default"' }
10
10
  else
11
11
  {}
@@ -3,9 +3,15 @@
3
3
  class AddUniqueNameIndexes < ActiveRecord::Migration[4.2]
4
4
  def change
5
5
  add_index :switchman_shards, %i[database_server_id name], unique: true
6
- add_index :switchman_shards, :database_server_id, unique: true, where: 'name IS NULL',
7
- name: 'index_switchman_shards_unique_primary_shard'
8
- add_index :switchman_shards, '(true)', unique: true, where: 'database_server_id IS NULL AND name IS NULL',
9
- name: 'index_switchman_shards_unique_primary_db_and_shard'
6
+ add_index :switchman_shards,
7
+ :database_server_id,
8
+ unique: true,
9
+ where: "name IS NULL",
10
+ name: "index_switchman_shards_unique_primary_shard"
11
+ add_index :switchman_shards,
12
+ "(true)",
13
+ unique: true,
14
+ where: "database_server_id IS NULL AND name IS NULL",
15
+ name: "index_switchman_shards_unique_primary_db_and_shard"
10
16
  end
11
17
  end
@@ -24,10 +24,15 @@ module Switchman
24
24
 
25
25
  module CollectionAssociation
26
26
  def find_target
27
- shards = reflection.options[:multishard] && owner.respond_to?(:associated_shards) ? owner.associated_shards : [shard]
27
+ shards = if reflection.options[:multishard] && owner.respond_to?(:associated_shards)
28
+ owner.associated_shards
29
+ else
30
+ [shard]
31
+ end
28
32
  # activate both the owner and the target's shard category, so that Reflection#join_id_for,
29
33
  # when called for the owner, will be returned relative to shard the query will execute on
30
- Shard.with_each_shard(shards, [klass.connection_class_for_self, owner.class.connection_class_for_self].uniq) do
34
+ Shard.with_each_shard(shards,
35
+ [klass.connection_class_for_self, owner.class.connection_class_for_self].uniq) do
31
36
  super
32
37
  end
33
38
  end
@@ -50,7 +55,7 @@ module Switchman
50
55
  def shard
51
56
  if @owner.class.sharded_column?(@reflection.foreign_key) &&
52
57
  (foreign_id = @owner[@reflection.foreign_key])
53
- Shard.shard_for(foreign_id, @owner.shard)
58
+ Shard.shard_for(foreign_id, @owner.loaded_from_shard)
54
59
  else
55
60
  super
56
61
  end
@@ -83,16 +88,67 @@ module Switchman
83
88
 
84
89
  module Preloader
85
90
  module Association
86
- module LoaderQuery
87
- def load_records_in_batch(loaders)
88
- # While in theory loading multiple associations that end up being effectively the same would be nice
89
- # it's not very switchman compatible, so just don't bother trying to use that logic
90
- # raw_records = records_for(loaders)
91
+ # significant changes:
92
+ # * associate shards with records
93
+ # * look on all appropriate shards when loading records
94
+ module LoaderRecords
95
+ def populate_keys_to_load_and_already_loaded_records
96
+ @sharded_keys_to_load = {}
91
97
 
92
98
  loaders.each do |loader|
93
- loader.load_records(nil)
94
- loader.run
99
+ multishard = loader.send(:reflection).options[:multishard]
100
+ belongs_to = loader.send(:reflection).macro == :belongs_to
101
+ loader.owners_by_key.each do |key, owners|
102
+ if (loaded_owner = owners.find { |owner| loader.loaded?(owner) })
103
+ already_loaded_records_by_key[key] = loader.target_for(loaded_owner)
104
+ else
105
+ shard_set = @sharded_keys_to_load[key] ||= Set.new
106
+ owner_key_name = loader.send(:owner_key_name)
107
+ owners.each do |owner|
108
+ if multishard && owner.respond_to?(:associated_shards)
109
+ shard_set.merge(owner.associated_shards.map(&:id))
110
+ elsif belongs_to && owner.class.sharded_column?(owner_key_name)
111
+ shard_set.add(Shard.shard_for(owner[owner_key_name], owner.shard).id)
112
+ elsif belongs_to
113
+ shard_set.add(Shard.current.id)
114
+ else
115
+ shard_set.add(owner.shard.id)
116
+ end
117
+ end
118
+ end
119
+ end
95
120
  end
121
+
122
+ @sharded_keys_to_load.delete_if { |key, _shards| already_loaded_records_by_key.include?(key) }
123
+ end
124
+
125
+ def load_records
126
+ ret = []
127
+
128
+ shards_with_keys = @sharded_keys_to_load.each_with_object({}) do |(key, shards), h|
129
+ shards.each { |shard| (h[shard] ||= []) << key }
130
+ end
131
+
132
+ shards_with_keys.each do |shard, keys|
133
+ Shard.lookup(shard).activate do
134
+ scope_was = loader_query.scope
135
+ begin
136
+ loader_query.instance_variable_set(
137
+ :@scope,
138
+ loader_query.scope.shard(
139
+ Shard.current(loader_query.scope.model.connection_class_for_self)
140
+ )
141
+ )
142
+ ret += loader_query.load_records_for_keys(keys) do |record|
143
+ loaders.each { |l| l.set_inverse(record) }
144
+ end
145
+ ensure
146
+ loader_query.instance_variable_set(:@scope, scope_was)
147
+ end
148
+ end
149
+ end
150
+
151
+ ret
96
152
  end
97
153
  end
98
154
 
@@ -110,6 +166,26 @@ module Switchman
110
166
  end
111
167
  end
112
168
 
169
+ # Disabling to keep closer to rails original
170
+ # rubocop:disable Naming/AccessorMethodName, Style/GuardClause
171
+ # significant changes:
172
+ # * globalize the key to lookup
173
+ def set_inverse(record)
174
+ global_key = if model.connection_class_for_self == UnshardedRecord
175
+ convert_key(record[association_key_name])
176
+ else
177
+ Shard.global_id_for(record[association_key_name], record.shard)
178
+ end
179
+
180
+ if (owners = owners_by_key[convert_key(global_key)])
181
+ # Processing only the first owner
182
+ # because the record is modified but not an owner
183
+ association = owners.first.association(reflection.name)
184
+ association.set_inverse_instance(record)
185
+ end
186
+ end
187
+ # rubocop:enable Naming/AccessorMethodName, Style/GuardClause
188
+
113
189
  # significant changes:
114
190
  # * partition_by_shard the records_for call
115
191
  # * re-globalize the fetched owner id before looking up in the map
@@ -120,7 +196,9 @@ module Switchman
120
196
  # #compare_by_identity makes such owners different hash keys
121
197
  @records_by_owner = {}.compare_by_identity
122
198
 
123
- if ::Rails.version < '7.0' && owner_keys.empty?
199
+ if ::Rails.version >= "7.0"
200
+ raw_records ||= loader_query.records_for([self])
201
+ elsif owner_keys.empty?
124
202
  raw_records ||= []
125
203
  else
126
204
  # determine the shard to search for each owner
@@ -142,7 +220,8 @@ module Switchman
142
220
  relative_owner_keys = partitioned_owners.map do |owner|
143
221
  key = owner[owner_key_name]
144
222
  if key && owner.class.sharded_column?(owner_key_name)
145
- key = Shard.relative_id_for(key, owner.shard,
223
+ key = Shard.relative_id_for(key,
224
+ owner.shard,
146
225
  Shard.current(klass.connection_class_for_self))
147
226
  end
148
227
  convert_key(key)
@@ -162,7 +241,7 @@ module Switchman
162
241
  record.shard)
163
242
  end
164
243
 
165
- owners_by_key[convert_key(owner_key)].each do |owner|
244
+ owners_by_key[convert_key(owner_key)]&.each do |owner|
166
245
  entries = (@records_by_owner[owner] ||= [])
167
246
 
168
247
  if reflection.collection? || entries.empty?
@@ -205,10 +284,11 @@ module Switchman
205
284
  end
206
285
 
207
286
  module AutosaveAssociation
208
- def record_changed?(reflection, record, key)
209
- record.new_record? ||
210
- (record.has_attribute?(reflection.foreign_key) && record.send(reflection.foreign_key) != key) || # have to use send instead of [] because sharding
211
- record.attribute_changed?(reflection.foreign_key)
287
+ def association_foreign_key_changed?(reflection, record, key)
288
+ return false if reflection.through_reflection?
289
+
290
+ # have to use send instead of _read_attribute because sharding
291
+ record.has_attribute?(reflection.foreign_key) && record.send(reflection.foreign_key) != key
212
292
  end
213
293
 
214
294
  def save_belongs_to_association(reflection)
@@ -31,8 +31,16 @@ module Switchman
31
31
  # and not the silly one in AR::AttributeMethods::PrimaryKey
32
32
  return unless sharded_column?(@primary_key)
33
33
 
34
- class_eval(build_sharded_getter('id', '_read_attribute(@primary_key)', "::#{connection_class_for_self.name}"), __FILE__, __LINE__)
35
- class_eval(build_sharded_setter('id', @primary_key, "::#{connection_class_for_self.name}"), __FILE__, __LINE__)
34
+ class_eval(
35
+ build_sharded_getter("id",
36
+ "_read_attribute(@primary_key)",
37
+ "::#{connection_class_for_self.name}"),
38
+ __FILE__,
39
+ __LINE__
40
+ )
41
+ class_eval(build_sharded_setter("id", @primary_key, "::#{connection_class_for_self.name}"),
42
+ __FILE__,
43
+ __LINE__)
36
44
  end
37
45
 
38
46
  protected
@@ -46,20 +54,21 @@ module Switchman
46
54
  raise if connection.open_transactions.positive?
47
55
  end
48
56
 
49
- # rubocop:disable Naming/MethodParameterName
50
57
  def define_cached_method(owner, name, namespace:, as:, &block)
51
- if ::Rails.version < '7.0'
58
+ if ::Rails.version < "7.0"
52
59
  yield owner
53
60
  owner.rename_method(as, name)
54
61
  else
55
62
  owner.define_cached_method(name, namespace: namespace, as: as, &block)
56
63
  end
57
64
  end
58
- # rubocop:enable Naming/MethodParameterName
59
65
 
60
66
  def define_method_global_attribute(attr_name, owner:)
61
67
  if sharded_column?(attr_name)
62
- define_cached_method(owner, "global_#{attr_name}", as: "sharded_global_#{attr_name}", namespace: :switchman) do |batch|
68
+ define_cached_method(owner,
69
+ "global_#{attr_name}",
70
+ as: "sharded_global_#{attr_name}",
71
+ namespace: :switchman) do |batch|
63
72
  batch << <<-RUBY
64
73
  def sharded_global_#{attr_name}
65
74
  raw_value = original_#{attr_name}
@@ -71,13 +80,16 @@ module Switchman
71
80
  RUBY
72
81
  end
73
82
  else
74
- define_method_unsharded_column(attr_name, 'global', owner)
83
+ define_method_unsharded_column(attr_name, "global", owner)
75
84
  end
76
85
  end
77
86
 
78
87
  def define_method_local_attribute(attr_name, owner:)
79
88
  if sharded_column?(attr_name)
80
- define_cached_method(owner, "local_#{attr_name}", as: "sharded_local_#{attr_name}", namespace: :switchman) do |batch|
89
+ define_cached_method(owner,
90
+ "local_#{attr_name}",
91
+ as: "sharded_local_#{attr_name}",
92
+ namespace: :switchman) do |batch|
81
93
  batch << <<-RUBY
82
94
  def sharded_local_#{attr_name}
83
95
  raw_value = original_#{attr_name}
@@ -87,7 +99,7 @@ module Switchman
87
99
  RUBY
88
100
  end
89
101
  else
90
- define_method_unsharded_column(attr_name, 'local', owner)
102
+ define_method_unsharded_column(attr_name, "local", owner)
91
103
  end
92
104
  end
93
105
 
@@ -99,7 +111,13 @@ module Switchman
99
111
  if reflection.options[:polymorphic]
100
112
  # a polymorphic association has to be discovered at runtime. This code ends up being something like
101
113
  # context_type.&.constantize&.connection_class_for_self
102
- "read_attribute(:#{reflection.foreign_type})&.constantize&.connection_class_for_self"
114
+ <<~RUBY
115
+ begin
116
+ read_attribute(:#{reflection.foreign_type})&.constantize&.connection_class_for_self
117
+ rescue NameError
118
+ ::ActiveRecord::Base
119
+ end
120
+ RUBY
103
121
  else
104
122
  # otherwise we can just return a symbol for the statically known type of the association
105
123
  "::#{reflection.klass.connection_class_for_self.name}"
@@ -113,9 +131,14 @@ module Switchman
113
131
  if sharded_column?(attr_name)
114
132
  reflection = reflection_for_integer_attribute(attr_name)
115
133
  class_name = connection_class_for_self_code_for_reflection(reflection)
116
- safe_class_name = class_name.unpack1('h*')
117
- define_cached_method(owner, attr_name, as: "sharded_#{safe_class_name}_#{attr_name}", namespace: :switchman) do |batch|
118
- batch << build_sharded_getter("sharded_#{safe_class_name}_#{attr_name}", "original_#{attr_name}", class_name)
134
+ safe_class_name = class_name.unpack1("h*")
135
+ define_cached_method(owner,
136
+ attr_name,
137
+ as: "sharded_#{safe_class_name}_#{attr_name}",
138
+ namespace: :switchman) do |batch|
139
+ batch << build_sharded_getter("sharded_#{safe_class_name}_#{attr_name}",
140
+ "original_#{attr_name}",
141
+ class_name)
119
142
  end
120
143
  else
121
144
  define_cached_method(owner, attr_name, as: "plain_#{attr_name}", namespace: :switchman) do |batch|
@@ -136,7 +159,7 @@ module Switchman
136
159
 
137
160
  abs_raw_value = raw_value.abs
138
161
  current_shard = ::Switchman::Shard.current(#{attr_connection_class})
139
- same_shard = shard == current_shard
162
+ same_shard = loaded_from_shard == current_shard
140
163
  return raw_value if same_shard && abs_raw_value < ::Switchman::Shard::IDS_PER_SHARD
141
164
 
142
165
  value_shard_id = abs_raw_value / ::Switchman::Shard::IDS_PER_SHARD
@@ -144,9 +167,9 @@ module Switchman
144
167
  # of a local id
145
168
  return raw_value % ::Switchman::Shard::IDS_PER_SHARD if value_shard_id == current_shard.id
146
169
  return raw_value if !same_shard && abs_raw_value > ::Switchman::Shard::IDS_PER_SHARD
147
- return shard.global_id_for(raw_value) if !same_shard && abs_raw_value < ::Switchman::Shard::IDS_PER_SHARD
170
+ return loaded_from_shard.global_id_for(raw_value) if !same_shard && abs_raw_value < ::Switchman::Shard::IDS_PER_SHARD
148
171
 
149
- ::Switchman::Shard.relative_id_for(raw_value, shard, current_shard)
172
+ ::Switchman::Shard.relative_id_for(raw_value, loaded_from_shard, current_shard)
150
173
  end
151
174
  RUBY
152
175
  end
@@ -155,8 +178,11 @@ module Switchman
155
178
  if sharded_column?(attr_name)
156
179
  reflection = reflection_for_integer_attribute(attr_name)
157
180
  class_name = connection_class_for_self_code_for_reflection(reflection)
158
- safe_class_name = class_name.unpack1('h*')
159
- define_cached_method(owner, "#{attr_name}=", as: "sharded_#{safe_class_name}_#{attr_name}=", namespace: :switchman) do |batch|
181
+ safe_class_name = class_name.unpack1("h*")
182
+ define_cached_method(owner,
183
+ "#{attr_name}=",
184
+ as: "sharded_#{safe_class_name}_#{attr_name}=",
185
+ namespace: :switchman) do |batch|
160
186
  batch << build_sharded_setter("sharded_#{safe_class_name}_#{attr_name}", attr_name, class_name)
161
187
  end
162
188
  else
@@ -173,14 +199,17 @@ module Switchman
173
199
  def build_sharded_setter(attr_name, attr_field, attr_connection_class)
174
200
  <<-RUBY
175
201
  def #{attr_name}=(new_value)
176
- self.original_#{attr_field} = ::Switchman::Shard.relative_id_for(new_value, ::Switchman::Shard.current(#{attr_connection_class}), shard)
202
+ self.original_#{attr_field} = ::Switchman::Shard.relative_id_for(new_value, ::Switchman::Shard.current(#{attr_connection_class}), loaded_from_shard)
177
203
  end
178
204
  RUBY
179
205
  end
180
206
 
181
207
  def define_method_original_attribute(attr_name, owner:)
182
208
  if sharded_column?(attr_name)
183
- define_cached_method(owner, "original_#{attr_name}", as: "sharded_original_#{attr_name}", namespace: :switchman) do |batch|
209
+ define_cached_method(owner,
210
+ "original_#{attr_name}",
211
+ as: "sharded_original_#{attr_name}",
212
+ namespace: :switchman) do |batch|
184
213
  batch << <<-RUBY
185
214
  def sharded_original_#{attr_name}
186
215
  _read_attribute("#{attr_name}") { |n| missing_attribute(n, caller) }
@@ -188,14 +217,17 @@ module Switchman
188
217
  RUBY
189
218
  end
190
219
  else
191
- define_method_unsharded_column(attr_name, 'global', owner)
220
+ define_method_unsharded_column(attr_name, "global", owner)
192
221
  end
193
222
  end
194
223
 
195
224
  def define_method_original_attribute=(attr_name, owner:)
196
225
  return unless sharded_column?(attr_name)
197
226
 
198
- define_cached_method(owner, "original_#{attr_name}=", as: "sharded_original_#{attr_name}=", namespace: :switchman) do |batch|
227
+ define_cached_method(owner,
228
+ "original_#{attr_name}=",
229
+ as: "sharded_original_#{attr_name}=",
230
+ namespace: :switchman) do |batch|
199
231
  batch << <<-RUBY
200
232
  def sharded_original_#{attr_name}=(new_value)
201
233
  _write_attribute('#{attr_name}', new_value)
@@ -205,9 +237,12 @@ module Switchman
205
237
  end
206
238
 
207
239
  def define_method_unsharded_column(attr_name, prefix, owner)
208
- return if columns_hash["#{prefix}_#{attr_name}"] || attr_name == 'id'
240
+ return if columns_hash["#{prefix}_#{attr_name}"] || attr_name == "id"
209
241
 
210
- define_cached_method(owner, "#{prefix}_#{attr_name}", as: "unsharded_#{prefix}_#{attr_name}", namespace: :switchman) do |batch|
242
+ define_cached_method(owner,
243
+ "#{prefix}_#{attr_name}",
244
+ as: "unsharded_#{prefix}_#{attr_name}",
245
+ namespace: :switchman) do |batch|
211
246
  batch << <<-RUBY
212
247
  def unsharded_#{prefix}_#{attr_name}
213
248
  raise NoMethodError, "undefined method `#{prefix}_#{attr_name}'; are you missing an association?"
@@ -219,8 +254,8 @@ module Switchman
219
254
 
220
255
  def self.prepended(klass)
221
256
  klass.singleton_class.prepend(ClassMethods)
222
- klass.attribute_method_prefix 'global_', 'local_', 'original_'
223
- klass.attribute_method_affix prefix: 'original_', suffix: '='
257
+ klass.attribute_method_prefix "global_", "local_", "original_"
258
+ klass.attribute_method_affix prefix: "original_", suffix: "="
224
259
  end
225
260
 
226
261
  # these are called if the specific methods haven't been defined yet
@@ -228,7 +263,11 @@ module Switchman
228
263
  return super unless self.class.sharded_column?(attr_name)
229
264
 
230
265
  reflection = self.class.send(:reflection_for_integer_attribute, attr_name)
231
- ::Switchman::Shard.relative_id_for(super, shard, ::Switchman::Shard.current(connection_class_for_self_for_reflection(reflection)))
266
+ ::Switchman::Shard.relative_id_for(
267
+ super,
268
+ shard,
269
+ ::Switchman::Shard.current(connection_class_for_self_for_reflection(reflection))
270
+ )
232
271
  end
233
272
 
234
273
  def attribute=(attr_name, new_value)
@@ -238,7 +277,11 @@ module Switchman
238
277
  end
239
278
 
240
279
  reflection = self.class.send(:reflection_for_integer_attribute, attr_name)
241
- super(::Switchman::Shard.relative_id_for(new_value, ::Switchman::Shard.current(connection_class_for_self_for_reflection(reflection)), shard))
280
+ super(::Switchman::Shard.relative_id_for(
281
+ new_value,
282
+ ::Switchman::Shard.current(connection_class_for_self_for_reflection(reflection)),
283
+ shard
284
+ ))
242
285
  end
243
286
 
244
287
  def global_attribute(attr_name)
@@ -251,25 +294,11 @@ module Switchman
251
294
 
252
295
  def local_attribute(attr_name)
253
296
  if self.class.sharded_column?(attr_name)
254
- ::Switchman::Shard.local_id_for(attribute(attr_name), shard).first
297
+ ::Switchman::Shard.local_id_for(attribute(attr_name)).first
255
298
  else
256
299
  attribute(attr_name)
257
300
  end
258
301
  end
259
-
260
- private
261
-
262
- def connection_class_for_self_for_reflection(reflection)
263
- if reflection
264
- if reflection.options[:polymorphic]
265
- read_attribute(reflection.foreign_type)&.constantize&.connection_class_for_self
266
- else
267
- reflection.klass.connection_class_for_self
268
- end
269
- else
270
- self.class.connection_class_for_self
271
- end
272
- end
273
302
  end
274
303
  end
275
304
  end