switchman 3.3.1 → 4.0.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 (46) 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/abstract_adapter.rb +4 -2
  6. data/lib/switchman/active_record/associations.rb +89 -49
  7. data/lib/switchman/active_record/attribute_methods.rb +72 -34
  8. data/lib/switchman/active_record/base.rb +145 -27
  9. data/lib/switchman/active_record/calculations.rb +96 -49
  10. data/lib/switchman/active_record/connection_handler.rb +18 -0
  11. data/lib/switchman/active_record/connection_pool.rb +24 -3
  12. data/lib/switchman/active_record/database_configurations.rb +37 -15
  13. data/lib/switchman/active_record/finder_methods.rb +44 -14
  14. data/lib/switchman/active_record/log_subscriber.rb +11 -5
  15. data/lib/switchman/active_record/migration.rb +45 -3
  16. data/lib/switchman/active_record/pending_migration_connection.rb +17 -0
  17. data/lib/switchman/active_record/persistence.rb +30 -0
  18. data/lib/switchman/active_record/postgresql_adapter.rb +11 -10
  19. data/lib/switchman/active_record/predicate_builder.rb +2 -2
  20. data/lib/switchman/active_record/query_cache.rb +49 -20
  21. data/lib/switchman/active_record/query_methods.rb +93 -30
  22. data/lib/switchman/active_record/relation.rb +23 -12
  23. data/lib/switchman/active_record/spawn_methods.rb +2 -2
  24. data/lib/switchman/active_record/statement_cache.rb +2 -2
  25. data/lib/switchman/active_record/tasks/database_tasks.rb +6 -1
  26. data/lib/switchman/active_record/test_fixtures.rb +26 -16
  27. data/lib/switchman/active_support/cache.rb +9 -4
  28. data/lib/switchman/arel.rb +34 -18
  29. data/lib/switchman/call_super.rb +2 -8
  30. data/lib/switchman/database_server.rb +69 -31
  31. data/lib/switchman/default_shard.rb +14 -3
  32. data/lib/switchman/engine.rb +29 -22
  33. data/lib/switchman/environment.rb +2 -2
  34. data/lib/switchman/errors.rb +13 -0
  35. data/lib/switchman/guard_rail/relation.rb +1 -2
  36. data/lib/switchman/parallel.rb +6 -6
  37. data/lib/switchman/r_spec_helper.rb +12 -11
  38. data/lib/switchman/shard.rb +180 -68
  39. data/lib/switchman/sharded_instrumenter.rb +3 -3
  40. data/lib/switchman/shared_schema_cache.rb +11 -0
  41. data/lib/switchman/standard_error.rb +4 -0
  42. data/lib/switchman/test_helper.rb +2 -2
  43. data/lib/switchman/version.rb +1 -1
  44. data/lib/switchman.rb +27 -15
  45. data/lib/tasks/switchman.rake +96 -60
  46. metadata +38 -48
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d0330d210ee4f44c8ceb3b66ba1536f5538c4a1efcd4a939d492ffa65602fca7
4
- data.tar.gz: '0787f10b30acc34e0a5e48423c524d2db27b8b92dabd76bf567fb6631c0587c3'
3
+ metadata.gz: eb5bc75d858d97e55442dfdafcd1826d7040fe1bd4819bc3c2ee6053257a72ab
4
+ data.tar.gz: e73beaf3a5987f26e2402078f66c028831afc6924f21546d0ca1cb35f70e0831
5
5
  SHA512:
6
- metadata.gz: 65e0cbb8c9015940209145aa229648fa763d999399053e9046b0b4a60e1aae1ebfe419983776364e7e2460ca2e0cc0021188d286a8da6a9d9c0a74bd59d1bbc2
7
- data.tar.gz: db938a240af2ffb146453536d42db25d38ac13ca136b757d219fef46cf99476c0916beb65c8e06e2bc48067b94736d8f88b4c13519f85d99ec2b07fc423eccc9
6
+ metadata.gz: 273718d8bc773ded149ac5d81509a6d85c6858f17bc7c1a03f3dbdf15bbb96f101fea5842eddf87ba442aabf1f0bffe1a21421da4b9329548b0f40e51de26645
7
+ data.tar.gz: 9f81d1963fe213d334aed7377057fe29ecbdb637c6b2b8a33a49d52fcc0f726e1a8e87853a6e1030c8c2b0bbaff51e369d54cf8118f1c512ca0d62bf5684267c
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
@@ -27,8 +27,10 @@ module Switchman
27
27
  quote_table_name(name)
28
28
  end
29
29
 
30
- def schema_migration
31
- ::ActiveRecord::SchemaMigration
30
+ if ::Rails.version < "7.1"
31
+ def schema_migration
32
+ ::ActiveRecord::SchemaMigration
33
+ end
32
34
  end
33
35
 
34
36
  protected
@@ -24,11 +24,23 @@ 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
31
- super
34
+ Shard.with_each_shard(shards,
35
+ [klass.connection_class_for_self, owner.class.connection_class_for_self].uniq) do
36
+ if reflection.options[:multishard] && owner.respond_to?(:associated_shards) && reflection.has_scope?
37
+ # Prevent duplicate results when reflection has a scope (when it would use the skip_statement_cache? path)
38
+ # otherwise, the super call will set the shard_value to the object, causing it to iterate too many times
39
+ # over the associated shards
40
+ scope.shard(Shard.current(scope.klass.connection_class_for_self), :association).to_a
41
+ else
42
+ super
43
+ end
32
44
  end
33
45
  end
34
46
 
@@ -50,7 +62,7 @@ module Switchman
50
62
  def shard
51
63
  if @owner.class.sharded_column?(@reflection.foreign_key) &&
52
64
  (foreign_id = @owner[@reflection.foreign_key])
53
- Shard.shard_for(foreign_id, @owner.shard)
65
+ Shard.shard_for(foreign_id, @owner.loaded_from_shard)
54
66
  else
55
67
  super
56
68
  end
@@ -128,7 +140,12 @@ module Switchman
128
140
  Shard.lookup(shard).activate do
129
141
  scope_was = loader_query.scope
130
142
  begin
131
- loader_query.instance_variable_set(:@scope, loader_query.scope.shard(Shard.current(loader_query.scope.model.connection_class_for_self)))
143
+ loader_query.instance_variable_set(
144
+ :@scope,
145
+ loader_query.scope.shard(
146
+ Shard.current(loader_query.scope.model.connection_class_for_self)
147
+ )
148
+ )
132
149
  ret += loader_query.load_records_for_keys(keys) do |record|
133
150
  loaders.each { |l| l.set_inverse(record) }
134
151
  end
@@ -186,40 +203,7 @@ module Switchman
186
203
  # #compare_by_identity makes such owners different hash keys
187
204
  @records_by_owner = {}.compare_by_identity
188
205
 
189
- if ::Rails.version >= '7.0'
190
- raw_records ||= loader_query.records_for([self])
191
- elsif owner_keys.empty?
192
- raw_records ||= []
193
- else
194
- # determine the shard to search for each owner
195
- if reflection.macro == :belongs_to
196
- # for belongs_to, it's the shard of the foreign_key
197
- partition_proc = lambda do |owner|
198
- if owner.class.sharded_column?(owner_key_name)
199
- Shard.shard_for(owner[owner_key_name], owner.shard)
200
- else
201
- Shard.current
202
- end
203
- end
204
- elsif !reflection.options[:multishard]
205
- # for non-multishard associations, it's *just* the owner's shard
206
- partition_proc = ->(owner) { owner.shard }
207
- end
208
-
209
- raw_records ||= Shard.partition_by_shard(owners, partition_proc) do |partitioned_owners|
210
- relative_owner_keys = partitioned_owners.map do |owner|
211
- key = owner[owner_key_name]
212
- if key && owner.class.sharded_column?(owner_key_name)
213
- key = Shard.relative_id_for(key, owner.shard,
214
- Shard.current(klass.connection_class_for_self))
215
- end
216
- convert_key(key)
217
- end
218
- relative_owner_keys.compact!
219
- relative_owner_keys.uniq!
220
- records_for(relative_owner_keys)
221
- end
222
- end
206
+ raw_records ||= loader_query.records_for([self])
223
207
 
224
208
  @preloaded_records = raw_records.select do |record|
225
209
  assignments = false
@@ -273,17 +257,73 @@ module Switchman
273
257
  end
274
258
 
275
259
  module AutosaveAssociation
276
- def record_changed?(reflection, record, key)
277
- record.new_record? ||
278
- (record.has_attribute?(reflection.foreign_key) && record.send(reflection.foreign_key) != key) || # have to use send instead of [] because sharding
279
- record.attribute_changed?(reflection.foreign_key)
280
- end
260
+ if ::Rails.version < "7.1"
261
+ def association_foreign_key_changed?(reflection, record, key)
262
+ return false if reflection.through_reflection?
263
+
264
+ # have to use send instead of _read_attribute because sharding
265
+ record.has_attribute?(reflection.foreign_key) && record.send(reflection.foreign_key) != key
266
+ end
281
267
 
282
- def save_belongs_to_association(reflection)
283
- # this seems counter-intuitive, but the autosave code will assign to attribute bypassing switchman,
284
- # after reading the id attribute _without_ bypassing switchman. So we need Shard.current for the
285
- # category of the associated record to match Shard.current for the category of self
286
- shard.activate(connection_class_for_self_for_reflection(reflection)) { super }
268
+ def save_belongs_to_association(reflection)
269
+ # this seems counter-intuitive, but the autosave code will assign to attribute bypassing switchman,
270
+ # after reading the id attribute _without_ bypassing switchman. So we need Shard.current for the
271
+ # category of the associated record to match Shard.current for the category of self
272
+ shard.activate(connection_class_for_self_for_reflection(reflection)) { super }
273
+ end
274
+ else
275
+ def association_foreign_key_changed?(reflection, record, key)
276
+ return false if reflection.through_reflection?
277
+
278
+ foreign_key = Array(reflection.foreign_key)
279
+ return false unless foreign_key.all? { |k| record._has_attribute?(k) }
280
+
281
+ # have to use send instead of _read_attribute because sharding
282
+ foreign_key.map { |k| record.send(k) } != Array(key)
283
+ end
284
+
285
+ def save_belongs_to_association(reflection)
286
+ association = association_instance_get(reflection.name)
287
+ return unless association&.loaded? && !association.stale_target?
288
+
289
+ record = association.load_target
290
+ return unless record && !record.destroyed?
291
+
292
+ autosave = reflection.options[:autosave]
293
+
294
+ if autosave && record.marked_for_destruction?
295
+ foreign_key = Array(reflection.foreign_key)
296
+ foreign_key.each { |key| self[key] = nil }
297
+ record.destroy
298
+ elsif autosave != false
299
+ if record.new_record? || (autosave && record.changed_for_autosave?)
300
+ saved = record.save(validate: !autosave)
301
+ end
302
+
303
+ if association.updated?
304
+ primary_key = Array(compute_primary_key(reflection, record)).map(&:to_s)
305
+ foreign_key = Array(reflection.foreign_key)
306
+
307
+ primary_key_foreign_key_pairs = primary_key.zip(foreign_key)
308
+ primary_key_foreign_key_pairs.each do |pk, fk|
309
+ # Notable change: add relative_id_for here
310
+ association_id = if record.class.sharded_column?(pk)
311
+ Shard.relative_id_for(
312
+ record._read_attribute(pk),
313
+ record.shard,
314
+ shard
315
+ )
316
+ else
317
+ record._read_attribute(pk)
318
+ end
319
+ self[fk] = association_id unless self[fk] == association_id
320
+ end
321
+ association.loaded!
322
+ end
323
+
324
+ saved if autosave
325
+ end
326
+ end
287
327
  end
288
328
  end
289
329
  end
@@ -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,16 @@ 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'
52
- yield owner
53
- owner.rename_method(as, name)
54
- else
55
- owner.define_cached_method(name, namespace: namespace, as: as, &block)
56
- end
58
+ owner.define_cached_method(name, namespace: namespace, as: as, &block)
57
59
  end
58
- # rubocop:enable Naming/MethodParameterName
59
60
 
60
61
  def define_method_global_attribute(attr_name, owner:)
61
62
  if sharded_column?(attr_name)
62
- define_cached_method(owner, "global_#{attr_name}", as: "sharded_global_#{attr_name}", namespace: :switchman) do |batch|
63
+ define_cached_method(owner,
64
+ "global_#{attr_name}",
65
+ as: "sharded_global_#{attr_name}",
66
+ namespace: :switchman) do |batch|
63
67
  batch << <<-RUBY
64
68
  def sharded_global_#{attr_name}
65
69
  raw_value = original_#{attr_name}
@@ -71,13 +75,16 @@ module Switchman
71
75
  RUBY
72
76
  end
73
77
  else
74
- define_method_unsharded_column(attr_name, 'global', owner)
78
+ define_method_unsharded_column(attr_name, "global", owner)
75
79
  end
76
80
  end
77
81
 
78
82
  def define_method_local_attribute(attr_name, owner:)
79
83
  if sharded_column?(attr_name)
80
- define_cached_method(owner, "local_#{attr_name}", as: "sharded_local_#{attr_name}", namespace: :switchman) do |batch|
84
+ define_cached_method(owner,
85
+ "local_#{attr_name}",
86
+ as: "sharded_local_#{attr_name}",
87
+ namespace: :switchman) do |batch|
81
88
  batch << <<-RUBY
82
89
  def sharded_local_#{attr_name}
83
90
  raw_value = original_#{attr_name}
@@ -87,7 +94,7 @@ module Switchman
87
94
  RUBY
88
95
  end
89
96
  else
90
- define_method_unsharded_column(attr_name, 'local', owner)
97
+ define_method_unsharded_column(attr_name, "local", owner)
91
98
  end
92
99
  end
93
100
 
@@ -99,7 +106,13 @@ module Switchman
99
106
  if reflection.options[:polymorphic]
100
107
  # a polymorphic association has to be discovered at runtime. This code ends up being something like
101
108
  # context_type.&.constantize&.connection_class_for_self
102
- "begin;read_attribute(:#{reflection.foreign_type})&.constantize&.connection_class_for_self;rescue NameError;::ActiveRecord::Base;end"
109
+ <<~RUBY
110
+ begin
111
+ read_attribute(:#{reflection.foreign_type})&.constantize&.connection_class_for_self
112
+ rescue NameError
113
+ ::ActiveRecord::Base
114
+ end
115
+ RUBY
103
116
  else
104
117
  # otherwise we can just return a symbol for the statically known type of the association
105
118
  "::#{reflection.klass.connection_class_for_self.name}"
@@ -113,9 +126,14 @@ module Switchman
113
126
  if sharded_column?(attr_name)
114
127
  reflection = reflection_for_integer_attribute(attr_name)
115
128
  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)
129
+ safe_class_name = class_name.unpack1("h*")
130
+ define_cached_method(owner,
131
+ attr_name,
132
+ as: "sharded_#{safe_class_name}_#{attr_name}",
133
+ namespace: :switchman) do |batch|
134
+ batch << build_sharded_getter("sharded_#{safe_class_name}_#{attr_name}",
135
+ "original_#{attr_name}",
136
+ class_name)
119
137
  end
120
138
  else
121
139
  define_cached_method(owner, attr_name, as: "plain_#{attr_name}", namespace: :switchman) do |batch|
@@ -136,7 +154,7 @@ module Switchman
136
154
 
137
155
  abs_raw_value = raw_value.abs
138
156
  current_shard = ::Switchman::Shard.current(#{attr_connection_class})
139
- same_shard = shard == current_shard
157
+ same_shard = loaded_from_shard == current_shard
140
158
  return raw_value if same_shard && abs_raw_value < ::Switchman::Shard::IDS_PER_SHARD
141
159
 
142
160
  value_shard_id = abs_raw_value / ::Switchman::Shard::IDS_PER_SHARD
@@ -144,9 +162,9 @@ module Switchman
144
162
  # of a local id
145
163
  return raw_value % ::Switchman::Shard::IDS_PER_SHARD if value_shard_id == current_shard.id
146
164
  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
165
+ return loaded_from_shard.global_id_for(raw_value) if !same_shard && abs_raw_value < ::Switchman::Shard::IDS_PER_SHARD
148
166
 
149
- ::Switchman::Shard.relative_id_for(raw_value, shard, current_shard)
167
+ ::Switchman::Shard.relative_id_for(raw_value, loaded_from_shard, current_shard)
150
168
  end
151
169
  RUBY
152
170
  end
@@ -155,8 +173,11 @@ module Switchman
155
173
  if sharded_column?(attr_name)
156
174
  reflection = reflection_for_integer_attribute(attr_name)
157
175
  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|
176
+ safe_class_name = class_name.unpack1("h*")
177
+ define_cached_method(owner,
178
+ "#{attr_name}=",
179
+ as: "sharded_#{safe_class_name}_#{attr_name}=",
180
+ namespace: :switchman) do |batch|
160
181
  batch << build_sharded_setter("sharded_#{safe_class_name}_#{attr_name}", attr_name, class_name)
161
182
  end
162
183
  else
@@ -173,14 +194,17 @@ module Switchman
173
194
  def build_sharded_setter(attr_name, attr_field, attr_connection_class)
174
195
  <<-RUBY
175
196
  def #{attr_name}=(new_value)
176
- self.original_#{attr_field} = ::Switchman::Shard.relative_id_for(new_value, ::Switchman::Shard.current(#{attr_connection_class}), shard)
197
+ self.original_#{attr_field} = ::Switchman::Shard.relative_id_for(new_value, ::Switchman::Shard.current(#{attr_connection_class}), loaded_from_shard)
177
198
  end
178
199
  RUBY
179
200
  end
180
201
 
181
202
  def define_method_original_attribute(attr_name, owner:)
182
203
  if sharded_column?(attr_name)
183
- define_cached_method(owner, "original_#{attr_name}", as: "sharded_original_#{attr_name}", namespace: :switchman) do |batch|
204
+ define_cached_method(owner,
205
+ "original_#{attr_name}",
206
+ as: "sharded_original_#{attr_name}",
207
+ namespace: :switchman) do |batch|
184
208
  batch << <<-RUBY
185
209
  def sharded_original_#{attr_name}
186
210
  _read_attribute("#{attr_name}") { |n| missing_attribute(n, caller) }
@@ -188,14 +212,17 @@ module Switchman
188
212
  RUBY
189
213
  end
190
214
  else
191
- define_method_unsharded_column(attr_name, 'global', owner)
215
+ define_method_unsharded_column(attr_name, "global", owner)
192
216
  end
193
217
  end
194
218
 
195
219
  def define_method_original_attribute=(attr_name, owner:)
196
220
  return unless sharded_column?(attr_name)
197
221
 
198
- define_cached_method(owner, "original_#{attr_name}=", as: "sharded_original_#{attr_name}=", namespace: :switchman) do |batch|
222
+ define_cached_method(owner,
223
+ "original_#{attr_name}=",
224
+ as: "sharded_original_#{attr_name}=",
225
+ namespace: :switchman) do |batch|
199
226
  batch << <<-RUBY
200
227
  def sharded_original_#{attr_name}=(new_value)
201
228
  _write_attribute('#{attr_name}', new_value)
@@ -205,9 +232,12 @@ module Switchman
205
232
  end
206
233
 
207
234
  def define_method_unsharded_column(attr_name, prefix, owner)
208
- return if columns_hash["#{prefix}_#{attr_name}"] || attr_name == 'id'
235
+ return if columns_hash["#{prefix}_#{attr_name}"] || attr_name == "id"
209
236
 
210
- define_cached_method(owner, "#{prefix}_#{attr_name}", as: "unsharded_#{prefix}_#{attr_name}", namespace: :switchman) do |batch|
237
+ define_cached_method(owner,
238
+ "#{prefix}_#{attr_name}",
239
+ as: "unsharded_#{prefix}_#{attr_name}",
240
+ namespace: :switchman) do |batch|
211
241
  batch << <<-RUBY
212
242
  def unsharded_#{prefix}_#{attr_name}
213
243
  raise NoMethodError, "undefined method `#{prefix}_#{attr_name}'; are you missing an association?"
@@ -219,8 +249,8 @@ module Switchman
219
249
 
220
250
  def self.prepended(klass)
221
251
  klass.singleton_class.prepend(ClassMethods)
222
- klass.attribute_method_prefix 'global_', 'local_', 'original_'
223
- klass.attribute_method_affix prefix: 'original_', suffix: '='
252
+ klass.attribute_method_prefix "global_", "local_", "original_"
253
+ klass.attribute_method_affix prefix: "original_", suffix: "="
224
254
  end
225
255
 
226
256
  # these are called if the specific methods haven't been defined yet
@@ -228,7 +258,11 @@ module Switchman
228
258
  return super unless self.class.sharded_column?(attr_name)
229
259
 
230
260
  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)))
261
+ ::Switchman::Shard.relative_id_for(
262
+ super,
263
+ shard,
264
+ ::Switchman::Shard.current(connection_class_for_self_for_reflection(reflection))
265
+ )
232
266
  end
233
267
 
234
268
  def attribute=(attr_name, new_value)
@@ -238,7 +272,11 @@ module Switchman
238
272
  end
239
273
 
240
274
  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))
275
+ super(attr_name, ::Switchman::Shard.relative_id_for(
276
+ new_value,
277
+ ::Switchman::Shard.current(connection_class_for_self_for_reflection(reflection)),
278
+ shard
279
+ ))
242
280
  end
243
281
 
244
282
  def global_attribute(attr_name)
@@ -251,7 +289,7 @@ module Switchman
251
289
 
252
290
  def local_attribute(attr_name)
253
291
  if self.class.sharded_column?(attr_name)
254
- ::Switchman::Shard.local_id_for(attribute(attr_name), shard).first
292
+ ::Switchman::Shard.local_id_for(attribute(attr_name)).first
255
293
  else
256
294
  attribute(attr_name)
257
295
  end