switchman 3.3.6 → 4.2.5

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 (47) 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 +10 -6
  6. data/lib/switchman/active_record/associations.rb +72 -49
  7. data/lib/switchman/active_record/attribute_methods.rb +89 -44
  8. data/lib/switchman/active_record/base.rb +109 -40
  9. data/lib/switchman/active_record/calculations.rb +90 -54
  10. data/lib/switchman/active_record/connection_handler.rb +18 -0
  11. data/lib/switchman/active_record/connection_pool.rb +41 -23
  12. data/lib/switchman/active_record/database_configurations.rb +23 -13
  13. data/lib/switchman/active_record/finder_methods.rb +20 -14
  14. data/lib/switchman/active_record/log_subscriber.rb +3 -6
  15. data/lib/switchman/active_record/migration.rb +35 -12
  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 +26 -17
  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 +87 -17
  24. data/lib/switchman/active_record/spawn_methods.rb +3 -7
  25. data/lib/switchman/active_record/statement_cache.rb +4 -4
  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 +71 -25
  29. data/lib/switchman/active_support/cache.rb +9 -4
  30. data/lib/switchman/arel.rb +16 -25
  31. data/lib/switchman/call_super.rb +2 -8
  32. data/lib/switchman/database_server.rb +67 -48
  33. data/lib/switchman/default_shard.rb +14 -3
  34. data/lib/switchman/engine.rb +35 -23
  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 +168 -68
  41. data/lib/switchman/sharded_instrumenter.rb +9 -3
  42. data/lib/switchman/standard_error.rb +4 -0
  43. data/lib/switchman/test_helper.rb +3 -3
  44. data/lib/switchman/version.rb +1 -1
  45. data/lib/switchman.rb +27 -15
  46. data/lib/tasks/switchman.rake +96 -60
  47. metadata +28 -173
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a0e25e9a338b74ed4340f605d27978bba42bd45ef47ddac2bf7c44047db8a014
4
- data.tar.gz: a6f6cdb8f1686213391d38f1eb172fefa9346012f3ce055b152696f1dcd05975
3
+ metadata.gz: 4676a4e97375d1094b6775fa2a10f907218bbd2cb73f365c801a698822f779a0
4
+ data.tar.gz: 4f88954e9074fcd122ecbb8d965e1b16edb63c63d69fa5311f7e0a97e400c3d5
5
5
  SHA512:
6
- metadata.gz: fe38ed259e6d881d9f60ecd0dd96b65a3dd154b750beb3b5ee5b8af03e7683f056238feb464fc32c5156841b439200c6ff0c9bf5959dc108b5bb91c298bd450a
7
- data.tar.gz: b74decf8b11511749f80ffc792b2a98dbf6cdf36670d1f2db84a80975138aee8f0f4c11dab8883b9be93413324fb7f574589d6788781cd8e664dea34fe964cbd
6
+ metadata.gz: f043f994b2014ce1c980d6fe73a8137a5b497831bbe87592e0c3140e777d10bcbe1460216e3dcf7cc4d1c71cdbda98d3c2b0e5a6a9859f48120cbc2b9425c24f
7
+ data.tar.gz: 943659f21c0c99d8b1db896009a24601a8e698f13d0b2920e3532829a1bf0b1c5a319487d766d24c47b33a975b597ec4dc57e051ee0376e4b4dc6405515e38c8
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
@@ -19,16 +19,20 @@ module Switchman
19
19
 
20
20
  def initialize(*args)
21
21
  super
22
- @instrumenter = Switchman::ShardedInstrumenter.new(@instrumenter, self)
22
+
23
+ @instrumenter = Switchman::ShardedInstrumenter.new(@instrumenter, self) if ::Rails.version < "8.0"
24
+
23
25
  @last_query_at = Time.now
24
26
  end
25
27
 
26
- def quote_local_table_name(name)
27
- quote_table_name(name)
28
+ if ::Rails.version >= "8.0"
29
+ def instrumenter # :nodoc:
30
+ @instrumenter ||= Switchman::ShardedInstrumenter.new(::ActiveSupport::Notifications.instrumenter, self)
31
+ end
28
32
  end
29
33
 
30
- def schema_migration
31
- ::ActiveRecord::SchemaMigration
34
+ def quote_local_table_name(name)
35
+ quote_table_name(name)
32
36
  end
33
37
 
34
38
  protected
@@ -23,12 +23,26 @@ module Switchman
23
23
  end
24
24
 
25
25
  module CollectionAssociation
26
- def find_target
27
- shards = reflection.options[:multishard] && owner.respond_to?(:associated_shards) ? owner.associated_shards : [shard]
26
+ def find_target(async: false)
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
+ elsif ::Rails.version < "8.0"
42
+ super()
43
+ else
44
+ super
45
+ end
32
46
  end
33
47
  end
34
48
 
@@ -50,7 +64,7 @@ module Switchman
50
64
  def shard
51
65
  if @owner.class.sharded_column?(@reflection.foreign_key) &&
52
66
  (foreign_id = @owner[@reflection.foreign_key])
53
- Shard.shard_for(foreign_id, @owner.shard)
67
+ Shard.shard_for(foreign_id, @owner.loaded_from_shard)
54
68
  else
55
69
  super
56
70
  end
@@ -128,7 +142,12 @@ module Switchman
128
142
  Shard.lookup(shard).activate do
129
143
  scope_was = loader_query.scope
130
144
  begin
131
- loader_query.instance_variable_set(:@scope, loader_query.scope.shard(Shard.current(loader_query.scope.model.connection_class_for_self)))
145
+ loader_query.instance_variable_set(
146
+ :@scope,
147
+ loader_query.scope.shard(
148
+ Shard.current(loader_query.scope.model.connection_class_for_self)
149
+ )
150
+ )
132
151
  ret += loader_query.load_records_for_keys(keys) do |record|
133
152
  loaders.each { |l| l.set_inverse(record) }
134
153
  end
@@ -157,7 +176,7 @@ module Switchman
157
176
  end
158
177
 
159
178
  # Disabling to keep closer to rails original
160
- # rubocop:disable Naming/AccessorMethodName, Style/GuardClause
179
+ # rubocop:disable Naming/AccessorMethodName
161
180
  # significant changes:
162
181
  # * globalize the key to lookup
163
182
  def set_inverse(record)
@@ -174,7 +193,7 @@ module Switchman
174
193
  association.set_inverse_instance(record)
175
194
  end
176
195
  end
177
- # rubocop:enable Naming/AccessorMethodName, Style/GuardClause
196
+ # rubocop:enable Naming/AccessorMethodName
178
197
 
179
198
  # significant changes:
180
199
  # * partition_by_shard the records_for call
@@ -186,40 +205,7 @@ module Switchman
186
205
  # #compare_by_identity makes such owners different hash keys
187
206
  @records_by_owner = {}.compare_by_identity
188
207
 
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
208
+ raw_records ||= loader_query.records_for([self])
223
209
 
224
210
  @preloaded_records = raw_records.select do |record|
225
211
  assignments = false
@@ -267,8 +253,8 @@ module Switchman
267
253
  self.shard_source_value = :association
268
254
  end
269
255
 
270
- def shard(*args)
271
- scope.shard(*args)
256
+ def shard(*)
257
+ scope.shard(*)
272
258
  end
273
259
  end
274
260
 
@@ -276,15 +262,52 @@ module Switchman
276
262
  def association_foreign_key_changed?(reflection, record, key)
277
263
  return false if reflection.through_reflection?
278
264
 
265
+ foreign_key = Array(reflection.foreign_key)
266
+ return false unless foreign_key.all? { |k| record._has_attribute?(k) }
267
+
279
268
  # have to use send instead of _read_attribute because sharding
280
- record.has_attribute?(reflection.foreign_key) && record.send(reflection.foreign_key) != key
269
+ foreign_key.map { |k| record.send(k) } != Array(key)
281
270
  end
282
271
 
283
272
  def save_belongs_to_association(reflection)
284
- # this seems counter-intuitive, but the autosave code will assign to attribute bypassing switchman,
285
- # after reading the id attribute _without_ bypassing switchman. So we need Shard.current for the
286
- # category of the associated record to match Shard.current for the category of self
287
- shard.activate(connection_class_for_self_for_reflection(reflection)) { super }
273
+ association = association_instance_get(reflection.name)
274
+ return unless association&.loaded? && !association.stale_target?
275
+
276
+ record = association.load_target
277
+ return unless record && !record.destroyed?
278
+
279
+ autosave = reflection.options[:autosave]
280
+
281
+ if autosave && record.marked_for_destruction?
282
+ foreign_key = Array(reflection.foreign_key)
283
+ foreign_key.each { |key| self[key] = nil }
284
+ record.destroy
285
+ elsif autosave != false
286
+ saved = record.save(validate: !autosave) if record.new_record? || (autosave && record.changed_for_autosave?)
287
+
288
+ if association.updated?
289
+ primary_key = Array(compute_primary_key(reflection, record)).map(&:to_s)
290
+ foreign_key = Array(reflection.foreign_key)
291
+
292
+ primary_key_foreign_key_pairs = primary_key.zip(foreign_key)
293
+ primary_key_foreign_key_pairs.each do |pk, fk|
294
+ # Notable change: add relative_id_for here
295
+ association_id = if record.class.sharded_column?(pk)
296
+ Shard.relative_id_for(
297
+ record._read_attribute(pk),
298
+ record.shard,
299
+ shard
300
+ )
301
+ else
302
+ record._read_attribute(pk)
303
+ end
304
+ self[fk] = association_id unless self[fk] == association_id
305
+ end
306
+ association.loaded!
307
+ end
308
+
309
+ saved if autosave
310
+ end
288
311
  end
289
312
  end
290
313
  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
- def define_cached_method(owner, name, namespace:, as:, &block)
51
- if ::Rails.version < '7.0'
52
- yield owner
53
- owner.rename_method(as, name)
58
+ def define_cached_method(owner, name, namespace:, as:, &)
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, &)
54
63
  else
55
- owner.define_cached_method(name, namespace: namespace, as: as, &block)
64
+ owner.define_cached_method(name, namespace:, as:, &)
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