Module: Switchman::ActiveRecord::QueryMethods

Defined in:
lib/switchman/active_record/query_methods.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#shard_source_valueObject

Returns the value of attribute shard_source_value.



16
17
18
# File 'lib/switchman/active_record/query_methods.rb', line 16

def shard_source_value
  @shard_source_value
end

#shard_valueObject

Returns the value of attribute shard_value.



16
17
18
# File 'lib/switchman/active_record/query_methods.rb', line 16

def shard_value
  @shard_value
end

Instance Method Details

#all_shardsObject

the shard value as an array or a relation



101
102
103
104
105
106
107
108
109
110
# File 'lib/switchman/active_record/query_methods.rb', line 101

def all_shards
  case shard_value
  when Shard, DefaultShard
    [shard_value]
  when ::ActiveRecord::Base
    shard_value.respond_to?(:associated_shards) ? shard_value.associated_shards : [shard_value.shard]
  else
    shard_value
  end
end

#build_where(opts, other = []) ⇒ Object



70
71
72
73
74
75
76
77
78
79
80
# File 'lib/switchman/active_record/query_methods.rb', line 70

def build_where(opts, other = [])
  case opts
  when Hash, Arel::Nodes::Node
    predicates = super
    infer_shards_from_primary_key(predicates) if shard_source_value == :implicit && shard_value.is_a?(Shard)
    predicates = transpose_predicates(predicates, nil, primary_shard) if shard_source_value != :explicit
    predicates
  else
    super
  end
end

#having(opts, *rest) ⇒ Object



61
62
63
64
65
66
67
# File 'lib/switchman/active_record/query_methods.rb', line 61

def having(opts, *rest)
  return self if opts.blank?

  relation = clone
  relation.having_values += relation.build_where(opts, rest)
  relation
end

#primary_shardObject

the shard that where_values are relative to. if it’s multiple shards, they’re stored relative to the first shard



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/switchman/active_record/query_methods.rb', line 84

def primary_shard
  case shard_value
  when Shard, DefaultShard
    shard_value
  # associated_shards
  when ::ActiveRecord::Base
    shard_value.shard
  when Array
    shard_value.first
  when ::ActiveRecord::Relation
    Shard.default
  else
    raise ArgumentError, "invalid shard value #{shard_value}"
  end
end

#shard(value, source = :explicit) ⇒ Object



34
35
36
# File 'lib/switchman/active_record/query_methods.rb', line 34

def shard(value, source = :explicit)
  (::Rails.version < '4' ? clone : spawn).shard!(value, source)
end

#shard!(value, source = :explicit) ⇒ Object



38
39
40
41
42
43
44
45
46
47
# File 'lib/switchman/active_record/query_methods.rb', line 38

def shard!(value, source = :explicit)
  primary_shard = self.primary_shard
  self.shard_value = value
  self.shard_source_value = source
  if (primary_shard != self.primary_shard || source == :to_a)
    self.where_values = transpose_predicates(where_values, primary_shard, self.primary_shard, source == :to_a) if !where_values.empty?
    self.having_values = transpose_predicates(having_values, primary_shard, self.primary_shard, source == :to_a) if !having_values.empty?
  end
  self
end

#transpose_predicates(predicates, source_shard, target_shard, remove_nonlocal_primary_keys = false) ⇒ Object



195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/switchman/active_record/query_methods.rb', line 195

def transpose_predicates(predicates, source_shard, target_shard, remove_nonlocal_primary_keys = false)
  predicates.map do |predicate|
    next predicate unless predicate.is_a?(Arel::Nodes::Binary)
    next predicate unless predicate.left.is_a?(Arel::Attributes::Attribute)
    relation, column = relation_and_column(predicate.left)
    next predicate unless (type = transposable_attribute_type(relation, column))

    remove = true if type == :primary && remove_nonlocal_primary_keys && predicate.left.relation.engine == klass
    current_source_shard =
        if source_shard
          source_shard
        elsif type == :primary
          Shard.current(klass.shard_category)
        elsif type == :foreign
          source_shard_for_foreign_key(relation, column)
        end

    new_right_value = case predicate.right
    when Array
      local_ids = []
      predicate.right.each do |value|
        local_id = Shard.relative_id_for(value, current_source_shard, target_shard)
        local_ids << local_id unless remove && local_id > Shard::IDS_PER_SHARD
      end
      local_ids
    when Arel::Nodes::BindParam
      # look for a bind param with a matching column name
      if bind_values && idx = bind_values.find_index{|b| b.is_a?(Array) && b.first.try(:name) == predicate.left.name}
        column, value = bind_values[idx]
        local_id = Shard.relative_id_for(value, current_source_shard, target_shard)
        local_id = [] if remove && local_id > Shard::IDS_PER_SHARD
        bind_values[idx] = [column, local_id]
      end
      predicate.right
    else
      local_id = Shard.relative_id_for(predicate.right, current_source_shard, target_shard)
      local_id = [] if remove && local_id > Shard::IDS_PER_SHARD
      local_id
    end

    if new_right_value == predicate.right
      predicate
    else
      predicate.class.new(predicate.left, new_right_value)
    end
  end
end

#where(opts, *rest) ⇒ Object

replace these with versions that call build_where on the result relation, not the source relation (so build_where is able to implicitly change the shard_value)



53
54
55
56
57
58
59
# File 'lib/switchman/active_record/query_methods.rb', line 53

def where(opts, *rest)
  return self if opts.blank?

  relation = clone
  relation.where_values += relation.build_where(opts, rest)
  relation
end