switchman 3.4.1 → 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 +71 -48
  7. data/lib/switchman/active_record/attribute_methods.rb +84 -37
  8. data/lib/switchman/active_record/base.rb +72 -41
  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 +19 -19
  16. data/lib/switchman/active_record/pending_migration_connection.rb +17 -0
  17. data/lib/switchman/active_record/persistence.rb +22 -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 +86 -16
  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 -24
  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 +4 -1
  37. data/lib/switchman/guard_rail/relation.rb +1 -2
  38. data/lib/switchman/parallel.rb +5 -5
  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 -187
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e080637567925bab42e7d6c87a2963486b16fb5a09eab87ef61fd8056d773ddf
4
- data.tar.gz: 2694d7353b3cb7df6976aa812897540f1a9fc2188c5a6d2cc15bebfbbabb1371
3
+ metadata.gz: 4676a4e97375d1094b6775fa2a10f907218bbd2cb73f365c801a698822f779a0
4
+ data.tar.gz: 4f88954e9074fcd122ecbb8d965e1b16edb63c63d69fa5311f7e0a97e400c3d5
5
5
  SHA512:
6
- metadata.gz: b3318e8323ebda712ad41873279793ddd5b0d5849a536c9f2dc0fcd3addefb35ff4d364882f27eb59d0c8e27e7c5d79e5c5edc6de66a73024746f79fed23170f
7
- data.tar.gz: a2d2a1b1682febaffe2856dbdee8eca77241d5781e60fe037677f39c5c77b30d4d4ec5d6b0ff5e8c6cdd947fa79171fc99f0b19ffc3a5a77185bb400069ab537
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
 
@@ -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,18 +55,22 @@ module Switchman
46
55
  raise if connection.open_transactions.positive?
47
56
  end
48
57
 
49
- def define_cached_method(owner, name, namespace:, as:, &block)
50
- if ::Rails.version < '7.0'
51
- yield owner
52
- 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, &)
53
63
  else
54
- owner.define_cached_method(name, namespace: namespace, as: as, &block)
64
+ owner.define_cached_method(name, namespace:, as:, &)
55
65
  end
56
66
  end
57
67
 
58
- def define_method_global_attribute(attr_name, owner:)
68
+ def define_method_global_attribute(attr_name, owner:, as: attr_name)
59
69
  if sharded_column?(attr_name)
60
- 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|
61
74
  batch << <<-RUBY
62
75
  def sharded_global_#{attr_name}
63
76
  raw_value = original_#{attr_name}
@@ -69,13 +82,16 @@ module Switchman
69
82
  RUBY
70
83
  end
71
84
  else
72
- define_method_unsharded_column(attr_name, 'global', owner)
85
+ define_method_unsharded_column(attr_name, "global", owner)
73
86
  end
74
87
  end
75
88
 
76
- def define_method_local_attribute(attr_name, owner:)
89
+ def define_method_local_attribute(attr_name, owner:, as: attr_name)
77
90
  if sharded_column?(attr_name)
78
- 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|
79
95
  batch << <<-RUBY
80
96
  def sharded_local_#{attr_name}
81
97
  raw_value = original_#{attr_name}
@@ -85,7 +101,7 @@ module Switchman
85
101
  RUBY
86
102
  end
87
103
  else
88
- define_method_unsharded_column(attr_name, 'local', owner)
104
+ define_method_unsharded_column(attr_name, "local", owner)
89
105
  end
90
106
  end
91
107
 
@@ -97,7 +113,13 @@ module Switchman
97
113
  if reflection.options[:polymorphic]
98
114
  # a polymorphic association has to be discovered at runtime. This code ends up being something like
99
115
  # context_type.&.constantize&.connection_class_for_self
100
- "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
101
123
  else
102
124
  # otherwise we can just return a symbol for the statically known type of the association
103
125
  "::#{reflection.klass.connection_class_for_self.name}"
@@ -107,16 +129,21 @@ module Switchman
107
129
  end
108
130
  end
109
131
 
110
- def define_method_attribute(attr_name, owner:)
132
+ def define_method_attribute(attr_name, owner:, as: attr_name)
111
133
  if sharded_column?(attr_name)
112
134
  reflection = reflection_for_integer_attribute(attr_name)
113
135
  class_name = connection_class_for_self_code_for_reflection(reflection)
114
- safe_class_name = class_name.unpack1('h*')
115
- define_cached_method(owner, attr_name, as: "sharded_#{safe_class_name}_#{attr_name}", namespace: :switchman) do |batch|
116
- 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)
117
144
  end
118
145
  else
119
- 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|
120
147
  batch << <<-RUBY
121
148
  def plain_#{attr_name}
122
149
  _read_attribute("#{attr_name}") { |n| missing_attribute(n, caller) }
@@ -149,16 +176,19 @@ module Switchman
149
176
  RUBY
150
177
  end
151
178
 
152
- def define_method_attribute=(attr_name, owner:)
179
+ def define_method_attribute=(attr_name, owner:, as: attr_name)
153
180
  if sharded_column?(attr_name)
154
181
  reflection = reflection_for_integer_attribute(attr_name)
155
182
  class_name = connection_class_for_self_code_for_reflection(reflection)
156
- safe_class_name = class_name.unpack1('h*')
157
- 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|
158
188
  batch << build_sharded_setter("sharded_#{safe_class_name}_#{attr_name}", attr_name, class_name)
159
189
  end
160
190
  else
161
- 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|
162
192
  batch << <<-RUBY
163
193
  def plain_#{attr_name}=(new_value)
164
194
  _write_attribute('#{attr_name}', new_value)
@@ -176,9 +206,12 @@ module Switchman
176
206
  RUBY
177
207
  end
178
208
 
179
- def define_method_original_attribute(attr_name, owner:)
209
+ def define_method_original_attribute(attr_name, owner:, as: attr_name)
180
210
  if sharded_column?(attr_name)
181
- 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|
182
215
  batch << <<-RUBY
183
216
  def sharded_original_#{attr_name}
184
217
  _read_attribute("#{attr_name}") { |n| missing_attribute(n, caller) }
@@ -186,14 +219,17 @@ module Switchman
186
219
  RUBY
187
220
  end
188
221
  else
189
- define_method_unsharded_column(attr_name, 'global', owner)
222
+ define_method_unsharded_column(attr_name, "global", owner)
190
223
  end
191
224
  end
192
225
 
193
- def define_method_original_attribute=(attr_name, owner:)
226
+ def define_method_original_attribute=(attr_name, owner:, as: attr_name)
194
227
  return unless sharded_column?(attr_name)
195
228
 
196
- 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|
197
233
  batch << <<-RUBY
198
234
  def sharded_original_#{attr_name}=(new_value)
199
235
  _write_attribute('#{attr_name}', new_value)
@@ -203,9 +239,12 @@ module Switchman
203
239
  end
204
240
 
205
241
  def define_method_unsharded_column(attr_name, prefix, owner)
206
- return if columns_hash["#{prefix}_#{attr_name}"] || attr_name == 'id'
242
+ return if columns_hash["#{prefix}_#{attr_name}"] || attr_name == "id"
207
243
 
208
- 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|
209
248
  batch << <<-RUBY
210
249
  def unsharded_#{prefix}_#{attr_name}
211
250
  raise NoMethodError, "undefined method `#{prefix}_#{attr_name}'; are you missing an association?"
@@ -217,8 +256,8 @@ module Switchman
217
256
 
218
257
  def self.prepended(klass)
219
258
  klass.singleton_class.prepend(ClassMethods)
220
- klass.attribute_method_prefix 'global_', 'local_', 'original_'
221
- klass.attribute_method_affix prefix: 'original_', suffix: '='
259
+ klass.attribute_method_prefix "global_", "local_", "original_"
260
+ klass.attribute_method_affix prefix: "original_", suffix: "="
222
261
  end
223
262
 
224
263
  # these are called if the specific methods haven't been defined yet
@@ -226,7 +265,11 @@ module Switchman
226
265
  return super unless self.class.sharded_column?(attr_name)
227
266
 
228
267
  reflection = self.class.send(:reflection_for_integer_attribute, attr_name)
229
- ::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
+ )
230
273
  end
231
274
 
232
275
  def attribute=(attr_name, new_value)
@@ -236,7 +279,11 @@ module Switchman
236
279
  end
237
280
 
238
281
  reflection = self.class.send(:reflection_for_integer_attribute, attr_name)
239
- 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
+ ))
240
287
  end
241
288
 
242
289
  def global_attribute(attr_name)