switchman 3.3.1 → 4.1.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 (48) 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 +5 -3
  6. data/lib/switchman/active_record/associations.rb +91 -51
  7. data/lib/switchman/active_record/attribute_methods.rb +88 -43
  8. data/lib/switchman/active_record/base.rb +113 -40
  9. data/lib/switchman/active_record/calculations.rb +98 -51
  10. data/lib/switchman/active_record/connection_handler.rb +18 -0
  11. data/lib/switchman/active_record/connection_pool.rb +56 -6
  12. data/lib/switchman/active_record/database_configurations.rb +37 -15
  13. data/lib/switchman/active_record/finder_methods.rb +47 -17
  14. data/lib/switchman/active_record/log_subscriber.rb +11 -5
  15. data/lib/switchman/active_record/migration.rb +51 -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 +37 -22
  19. data/lib/switchman/active_record/predicate_builder.rb +2 -2
  20. data/lib/switchman/active_record/query_cache.rb +57 -20
  21. data/lib/switchman/active_record/query_methods.rb +148 -44
  22. data/lib/switchman/active_record/reflection.rb +9 -2
  23. data/lib/switchman/active_record/relation.rb +79 -15
  24. data/lib/switchman/active_record/spawn_methods.rb +3 -7
  25. data/lib/switchman/active_record/statement_cache.rb +2 -2
  26. data/lib/switchman/active_record/table_definition.rb +1 -1
  27. data/lib/switchman/active_record/tasks/database_tasks.rb +6 -1
  28. data/lib/switchman/active_record/test_fixtures.rb +75 -25
  29. data/lib/switchman/active_support/cache.rb +9 -4
  30. data/lib/switchman/arel.rb +34 -18
  31. data/lib/switchman/call_super.rb +2 -8
  32. data/lib/switchman/database_server.rb +72 -34
  33. data/lib/switchman/default_shard.rb +14 -3
  34. data/lib/switchman/engine.rb +38 -22
  35. data/lib/switchman/environment.rb +2 -2
  36. data/lib/switchman/errors.rb +13 -0
  37. data/lib/switchman/guard_rail/relation.rb +1 -2
  38. data/lib/switchman/parallel.rb +6 -6
  39. data/lib/switchman/r_spec_helper.rb +12 -11
  40. data/lib/switchman/shard.rb +185 -71
  41. data/lib/switchman/sharded_instrumenter.rb +3 -3
  42. data/lib/switchman/shared_schema_cache.rb +11 -0
  43. data/lib/switchman/standard_error.rb +4 -0
  44. data/lib/switchman/test_helper.rb +3 -3
  45. data/lib/switchman/version.rb +1 -1
  46. data/lib/switchman.rb +27 -15
  47. data/lib/tasks/switchman.rake +96 -60
  48. metadata +50 -46
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d0330d210ee4f44c8ceb3b66ba1536f5538c4a1efcd4a939d492ffa65602fca7
4
- data.tar.gz: '0787f10b30acc34e0a5e48423c524d2db27b8b92dabd76bf567fb6631c0587c3'
3
+ metadata.gz: e03940e1a5f3d0e7b933d99b3cbd68e3998b55f14ac0e7eec91ef81a193445e1
4
+ data.tar.gz: 923793689418332ab6ce2ffef2981567bd74b6358916794d958172638b0ec160
5
5
  SHA512:
6
- metadata.gz: 65e0cbb8c9015940209145aa229648fa763d999399053e9046b0b4a60e1aae1ebfe419983776364e7e2460ca2e0cc0021188d286a8da6a9d9c0a74bd59d1bbc2
7
- data.tar.gz: db938a240af2ffb146453536d42db25d38ac13ca136b757d219fef46cf99476c0916beb65c8e06e2bc48067b94736d8f88b4c13519f85d99ec2b07fc423eccc9
6
+ metadata.gz: 6950ac12cf6532f98e3674ded8890e7bb2f9ef50f33657220096de50508715ab047250c163546dc7396c61d15197ee2b517ae342f84cbe007ae22a217ed7751f
7
+ data.tar.gz: c45df067998ec5868bfe2c6a0f52608ffd9de9a19e2ce55ba55fc9986e077833df9964c5783c0f394a9683cb21a610e7f2a781d88e690fbc2bfeef5e8e352826
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
@@ -5,7 +5,7 @@ module Switchman
5
5
  module AbstractAdapter
6
6
  module ForeignKeyCheck
7
7
  def add_column(table, name, type, limit: nil, **)
8
- Switchman.foreign_key_check(name, type, limit: limit)
8
+ Switchman.foreign_key_check(name, type, limit:)
9
9
  super
10
10
  end
11
11
  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
@@ -157,7 +174,7 @@ module Switchman
157
174
  end
158
175
 
159
176
  # Disabling to keep closer to rails original
160
- # rubocop:disable Naming/AccessorMethodName, Style/GuardClause
177
+ # rubocop:disable Naming/AccessorMethodName
161
178
  # significant changes:
162
179
  # * globalize the key to lookup
163
180
  def set_inverse(record)
@@ -174,7 +191,7 @@ module Switchman
174
191
  association.set_inverse_instance(record)
175
192
  end
176
193
  end
177
- # rubocop:enable Naming/AccessorMethodName, Style/GuardClause
194
+ # rubocop:enable Naming/AccessorMethodName
178
195
 
179
196
  # significant changes:
180
197
  # * partition_by_shard the records_for call
@@ -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
@@ -26,13 +26,22 @@ module Switchman
26
26
  end
27
27
 
28
28
  def define_attribute_methods
29
- super
29
+ result = super
30
30
  # ensure that we're using the sharded attribute method
31
31
  # and not the silly one in AR::AttributeMethods::PrimaryKey
32
- return unless sharded_column?(@primary_key)
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__)
32
+ return result unless sharded_column?(@primary_key)
33
+
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__)
44
+ result
36
45
  end
37
46
 
38
47
  protected
@@ -46,20 +55,22 @@ module Switchman
46
55
  raise if connection.open_transactions.positive?
47
56
  end
48
57
 
49
- # rubocop:disable Naming/MethodParameterName
50
58
  def define_cached_method(owner, name, namespace:, as:, &block)
51
- if ::Rails.version < '7.0'
52
- yield owner
53
- owner.rename_method(as, name)
59
+ if ::Rails.version < "7.1.4"
60
+ # https://github.com/rails/rails/commit/a2a12fc2e3f4e6d06f81d4c74c88f8e6b3369ee6#diff-5b59ece6d9396b596f06271cec0ea726e3360911383511c49b1a66f454bfc2b6L30
61
+ # These arguments were effectively swapped in Rails 7.1.4, so previous versions need them reversed
62
+ owner.define_cached_method(as, namespace:, as: name, &block)
54
63
  else
55
- owner.define_cached_method(name, namespace: namespace, as: as, &block)
64
+ owner.define_cached_method(name, namespace:, as:, &block)
56
65
  end
57
66
  end
58
- # rubocop:enable Naming/MethodParameterName
59
67
 
60
- def define_method_global_attribute(attr_name, owner:)
68
+ def define_method_global_attribute(attr_name, owner:, as: attr_name)
61
69
  if sharded_column?(attr_name)
62
- define_cached_method(owner, "global_#{attr_name}", as: "sharded_global_#{attr_name}", namespace: :switchman) do |batch|
70
+ define_cached_method(owner,
71
+ "sharded_global_#{attr_name}",
72
+ as: "global_#{as}",
73
+ namespace: :switchman) do |batch|
63
74
  batch << <<-RUBY
64
75
  def sharded_global_#{attr_name}
65
76
  raw_value = original_#{attr_name}
@@ -71,13 +82,16 @@ module Switchman
71
82
  RUBY
72
83
  end
73
84
  else
74
- define_method_unsharded_column(attr_name, 'global', owner)
85
+ define_method_unsharded_column(attr_name, "global", owner)
75
86
  end
76
87
  end
77
88
 
78
- def define_method_local_attribute(attr_name, owner:)
89
+ def define_method_local_attribute(attr_name, owner:, as: attr_name)
79
90
  if sharded_column?(attr_name)
80
- define_cached_method(owner, "local_#{attr_name}", as: "sharded_local_#{attr_name}", namespace: :switchman) do |batch|
91
+ define_cached_method(owner,
92
+ "sharded_local_#{attr_name}",
93
+ as: "local_#{as}",
94
+ namespace: :switchman) do |batch|
81
95
  batch << <<-RUBY
82
96
  def sharded_local_#{attr_name}
83
97
  raw_value = original_#{attr_name}
@@ -87,7 +101,7 @@ module Switchman
87
101
  RUBY
88
102
  end
89
103
  else
90
- define_method_unsharded_column(attr_name, 'local', owner)
104
+ define_method_unsharded_column(attr_name, "local", owner)
91
105
  end
92
106
  end
93
107
 
@@ -99,7 +113,13 @@ module Switchman
99
113
  if reflection.options[:polymorphic]
100
114
  # a polymorphic association has to be discovered at runtime. This code ends up being something like
101
115
  # context_type.&.constantize&.connection_class_for_self
102
- "begin;read_attribute(:#{reflection.foreign_type})&.constantize&.connection_class_for_self;rescue NameError;::ActiveRecord::Base;end"
116
+ <<~RUBY
117
+ begin
118
+ read_attribute(:#{reflection.foreign_type})&.constantize&.connection_class_for_self
119
+ rescue NameError
120
+ ::ActiveRecord::Base
121
+ end
122
+ RUBY
103
123
  else
104
124
  # otherwise we can just return a symbol for the statically known type of the association
105
125
  "::#{reflection.klass.connection_class_for_self.name}"
@@ -109,16 +129,21 @@ module Switchman
109
129
  end
110
130
  end
111
131
 
112
- def define_method_attribute(attr_name, owner:)
132
+ def define_method_attribute(attr_name, owner:, as: attr_name)
113
133
  if sharded_column?(attr_name)
114
134
  reflection = reflection_for_integer_attribute(attr_name)
115
135
  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)
136
+ safe_class_name = class_name.unpack1("h*")
137
+ define_cached_method(owner,
138
+ "sharded_#{safe_class_name}_#{attr_name}",
139
+ as:,
140
+ namespace: :switchman) do |batch|
141
+ batch << build_sharded_getter("sharded_#{safe_class_name}_#{attr_name}",
142
+ "original_#{as}",
143
+ class_name)
119
144
  end
120
145
  else
121
- define_cached_method(owner, attr_name, as: "plain_#{attr_name}", namespace: :switchman) do |batch|
146
+ define_cached_method(owner, "plain_#{attr_name}", as:, namespace: :switchman) do |batch|
122
147
  batch << <<-RUBY
123
148
  def plain_#{attr_name}
124
149
  _read_attribute("#{attr_name}") { |n| missing_attribute(n, caller) }
@@ -136,7 +161,7 @@ module Switchman
136
161
 
137
162
  abs_raw_value = raw_value.abs
138
163
  current_shard = ::Switchman::Shard.current(#{attr_connection_class})
139
- same_shard = shard == current_shard
164
+ same_shard = loaded_from_shard == current_shard
140
165
  return raw_value if same_shard && abs_raw_value < ::Switchman::Shard::IDS_PER_SHARD
141
166
 
142
167
  value_shard_id = abs_raw_value / ::Switchman::Shard::IDS_PER_SHARD
@@ -144,23 +169,26 @@ module Switchman
144
169
  # of a local id
145
170
  return raw_value % ::Switchman::Shard::IDS_PER_SHARD if value_shard_id == current_shard.id
146
171
  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
172
+ return loaded_from_shard.global_id_for(raw_value) if !same_shard && abs_raw_value < ::Switchman::Shard::IDS_PER_SHARD
148
173
 
149
- ::Switchman::Shard.relative_id_for(raw_value, shard, current_shard)
174
+ ::Switchman::Shard.relative_id_for(raw_value, loaded_from_shard, current_shard)
150
175
  end
151
176
  RUBY
152
177
  end
153
178
 
154
- def define_method_attribute=(attr_name, owner:)
179
+ def define_method_attribute=(attr_name, owner:, as: attr_name)
155
180
  if sharded_column?(attr_name)
156
181
  reflection = reflection_for_integer_attribute(attr_name)
157
182
  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|
183
+ safe_class_name = class_name.unpack1("h*")
184
+ define_cached_method(owner,
185
+ "sharded_#{safe_class_name}_#{attr_name}=",
186
+ as: "#{as}=",
187
+ namespace: :switchman) do |batch|
160
188
  batch << build_sharded_setter("sharded_#{safe_class_name}_#{attr_name}", attr_name, class_name)
161
189
  end
162
190
  else
163
- define_cached_method(owner, "#{attr_name}=", as: "plain_#{attr_name}=", namespace: :switchman) do |batch|
191
+ define_cached_method(owner, "plain_#{attr_name}=", as: "#{as}=", namespace: :switchman) do |batch|
164
192
  batch << <<-RUBY
165
193
  def plain_#{attr_name}=(new_value)
166
194
  _write_attribute('#{attr_name}', new_value)
@@ -173,14 +201,17 @@ module Switchman
173
201
  def build_sharded_setter(attr_name, attr_field, attr_connection_class)
174
202
  <<-RUBY
175
203
  def #{attr_name}=(new_value)
176
- self.original_#{attr_field} = ::Switchman::Shard.relative_id_for(new_value, ::Switchman::Shard.current(#{attr_connection_class}), shard)
204
+ self.original_#{attr_field} = ::Switchman::Shard.relative_id_for(new_value, ::Switchman::Shard.current(#{attr_connection_class}), loaded_from_shard)
177
205
  end
178
206
  RUBY
179
207
  end
180
208
 
181
- def define_method_original_attribute(attr_name, owner:)
209
+ def define_method_original_attribute(attr_name, owner:, as: attr_name)
182
210
  if sharded_column?(attr_name)
183
- define_cached_method(owner, "original_#{attr_name}", as: "sharded_original_#{attr_name}", namespace: :switchman) do |batch|
211
+ define_cached_method(owner,
212
+ "sharded_original_#{attr_name}",
213
+ as: "original_#{as}",
214
+ namespace: :switchman) do |batch|
184
215
  batch << <<-RUBY
185
216
  def sharded_original_#{attr_name}
186
217
  _read_attribute("#{attr_name}") { |n| missing_attribute(n, caller) }
@@ -188,14 +219,17 @@ module Switchman
188
219
  RUBY
189
220
  end
190
221
  else
191
- define_method_unsharded_column(attr_name, 'global', owner)
222
+ define_method_unsharded_column(attr_name, "global", owner)
192
223
  end
193
224
  end
194
225
 
195
- def define_method_original_attribute=(attr_name, owner:)
226
+ def define_method_original_attribute=(attr_name, owner:, as: attr_name)
196
227
  return unless sharded_column?(attr_name)
197
228
 
198
- define_cached_method(owner, "original_#{attr_name}=", as: "sharded_original_#{attr_name}=", namespace: :switchman) do |batch|
229
+ define_cached_method(owner,
230
+ "sharded_original_#{attr_name}=",
231
+ as: "original_#{as}=",
232
+ namespace: :switchman) do |batch|
199
233
  batch << <<-RUBY
200
234
  def sharded_original_#{attr_name}=(new_value)
201
235
  _write_attribute('#{attr_name}', new_value)
@@ -205,9 +239,12 @@ module Switchman
205
239
  end
206
240
 
207
241
  def define_method_unsharded_column(attr_name, prefix, owner)
208
- return if columns_hash["#{prefix}_#{attr_name}"] || attr_name == 'id'
242
+ return if columns_hash["#{prefix}_#{attr_name}"] || attr_name == "id"
209
243
 
210
- define_cached_method(owner, "#{prefix}_#{attr_name}", as: "unsharded_#{prefix}_#{attr_name}", namespace: :switchman) do |batch|
244
+ define_cached_method(owner,
245
+ "unsharded_#{prefix}_#{attr_name}",
246
+ as: "#{prefix}_#{attr_name}",
247
+ namespace: :switchman) do |batch|
211
248
  batch << <<-RUBY
212
249
  def unsharded_#{prefix}_#{attr_name}
213
250
  raise NoMethodError, "undefined method `#{prefix}_#{attr_name}'; are you missing an association?"
@@ -219,8 +256,8 @@ module Switchman
219
256
 
220
257
  def self.prepended(klass)
221
258
  klass.singleton_class.prepend(ClassMethods)
222
- klass.attribute_method_prefix 'global_', 'local_', 'original_'
223
- klass.attribute_method_affix prefix: 'original_', suffix: '='
259
+ klass.attribute_method_prefix "global_", "local_", "original_"
260
+ klass.attribute_method_affix prefix: "original_", suffix: "="
224
261
  end
225
262
 
226
263
  # these are called if the specific methods haven't been defined yet
@@ -228,7 +265,11 @@ module Switchman
228
265
  return super unless self.class.sharded_column?(attr_name)
229
266
 
230
267
  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)))
268
+ ::Switchman::Shard.relative_id_for(
269
+ super,
270
+ shard,
271
+ ::Switchman::Shard.current(connection_class_for_self_for_reflection(reflection))
272
+ )
232
273
  end
233
274
 
234
275
  def attribute=(attr_name, new_value)
@@ -238,7 +279,11 @@ module Switchman
238
279
  end
239
280
 
240
281
  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))
282
+ super(attr_name, ::Switchman::Shard.relative_id_for(
283
+ new_value,
284
+ ::Switchman::Shard.current(connection_class_for_self_for_reflection(reflection)),
285
+ shard
286
+ ))
242
287
  end
243
288
 
244
289
  def global_attribute(attr_name)
@@ -251,7 +296,7 @@ module Switchman
251
296
 
252
297
  def local_attribute(attr_name)
253
298
  if self.class.sharded_column?(attr_name)
254
- ::Switchman::Shard.local_id_for(attribute(attr_name), shard).first
299
+ ::Switchman::Shard.local_id_for(attribute(attr_name)).first
255
300
  else
256
301
  attribute(attr_name)
257
302
  end