Class: Octoball::RelationProxy

Inherits:
BasicObject
Defined in:
lib/octoball/relation_proxy.rb

Constant Summary collapse

ENUM_METHODS =
(::Enumerable.instance_methods - ::Object.instance_methods).reject do |m|
  ::ActiveRecord::Relation.instance_method(m).source_location rescue nil
end + [:each, :map, :index_by]
ENUM_WITH_BLOCK_METHODS =
[:find, :select, :none?, :any?, :one?, :many?, :sum]

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(rel, shard) ⇒ RelationProxy

Returns a new instance of RelationProxy.



7
8
9
10
# File 'lib/octoball/relation_proxy.rb', line 7

def initialize(rel, shard)
  @rel = rel
  self.current_shard = shard
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args, &block) ⇒ Object



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/octoball/relation_proxy.rb', line 36

def method_missing(method, *args, &block)
  # raise NoMethodError unless the method is defined in @rel
  return @rel.public_send(method, *args, &block) unless @rel.respond_to?(method)

  preamble = <<-EOS
    def #{method}(*margs, &mblock)
      return @rel.#{method}(*margs, &mblock) unless @current_shard
  EOS
  postamble = <<-EOS
      return ret unless ret.is_a?(::ActiveRecord::Relation) || ret.is_a?(::ActiveRecord::QueryMethods::WhereChain) || ret.is_a?(::Enumerator)
      ::Octoball::RelationProxy.new(ret, @current_shard)
    end
    ruby2_keywords(:#{method}) if respond_to?(:ruby2_keywords, true)
  EOS
  connected_to = '::ActiveRecord::Base.connected_to(role: ::Octoball.current_role, shard: @current_shard)'

  if ENUM_METHODS.include?(method)
    ::Octoball::RelationProxy.class_eval <<-EOS, __FILE__, __LINE__ - 1
      #{preamble}
      ret = #{connected_to} { @rel.to_a }.#{method}(*margs, &mblock)
      #{postamble}
    EOS
  elsif ENUM_WITH_BLOCK_METHODS.include?(method)
    ::Octoball::RelationProxy.class_eval <<-EOS, __FILE__, __LINE__ - 1
      #{preamble}
      ret = nil
      if mblock
        ret = #{connected_to} { @rel.to_a }.#{method}(*margs, &mblock)
      else
        #{connected_to} { ret = @rel.#{method}(*margs, &mblock); nil } # return nil to avoid loading relation
      end
      #{postamble}
    EOS
  else
    ::Octoball::RelationProxy.class_eval <<-EOS, __FILE__, __LINE__ - 1
      #{preamble}
      ret = nil
      #{connected_to} { ret = @rel.#{method}(*margs, &mblock); nil } # return nil to avoid loading relation
      #{postamble}
    EOS
  end

  public_send(method, *args, &block)
end

Instance Attribute Details

#current_shardObject

Returns the value of attribute current_shard.



5
6
7
# File 'lib/octoball/relation_proxy.rb', line 5

def current_shard
  @current_shard
end

Instance Method Details

#==(obj) ⇒ Object



87
88
89
90
91
# File 'lib/octoball/relation_proxy.rb', line 87

def ==(obj)
  return false if obj.respond_to?(:current_shard) && obj.current_shard != @current_shard
  return @rel == obj unless @current_shard
  ::ActiveRecord::Base.connected_to(shard: @current_shard, role: ::Octoball.current_role) { @rel == obj }
end

#===(obj) ⇒ Object



93
94
95
96
97
# File 'lib/octoball/relation_proxy.rb', line 93

def ===(obj)
  return false if obj.respond_to?(:current_shard) && obj.current_shard != @current_shard
  return @rel === obj unless @current_shard
  ::ActiveRecord::Base.connected_to(shard: @current_shard, role: ::Octoball.current_role) { @rel === obj }
end

#ar_relationObject



22
23
24
# File 'lib/octoball/relation_proxy.rb', line 22

def ar_relation
  @rel
end

#inspectObject



82
83
84
85
# File 'lib/octoball/relation_proxy.rb', line 82

def inspect
  return @rel.inspect unless @current_shard
  ::ActiveRecord::Base.connected_to(shard: @current_shard, role: ::Octoball.current_role) { @rel.inspect }
end

#respond_to?(method, include_all = false) ⇒ Boolean

Returns:

  • (Boolean)


26
27
28
29
# File 'lib/octoball/relation_proxy.rb', line 26

def respond_to?(method, include_all = false)
  return true if [:ar_relation, :current_shard, :current_shard=, :using].include?(method)
  @rel.respond_to?(method, include_all)
end

#using(shard) ⇒ Object



17
18
19
20
# File 'lib/octoball/relation_proxy.rb', line 17

def using(shard)
  self.current_shard = shard
  self
end