Class: Squeel::Nodes::KeyPath

Inherits:
Object
  • Object
show all
Includes:
Operators, PredicateOperators
Defined in:
lib/squeel/nodes/key_path.rb

Overview

A node that stores a path of keys (of Symbol, Stub, or Join values) and an endpoint. Used similarly to a nested hash.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path, endpoint, absolute = false) ⇒ KeyPath

Create a new KeyPath.

Parameters:

  • path (Array, Object)

    The intial path. Will be converted to an array if it isn’t already.

  • endpoint

    the endpoint of the KeyPath

  • absolute (Boolean) (defaults to: false)

    If the KeyPath should start from the base or remain relative to whatever location it’s found.



29
30
31
32
33
34
# File 'lib/squeel/nodes/key_path.rb', line 29

def initialize(path, endpoint, absolute = false)
  @path, @endpoint = path, endpoint
  @path = [@path] unless Array === @path
  @endpoint = Stub.new(@endpoint) if Symbol === @endpoint
  @absolute = absolute
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method_id, *args) ⇒ KeyPath

Appends to the KeyPath or delegates to the endpoint, as appropriate

Returns:

  • (KeyPath)

    The updated KeyPath



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/squeel/nodes/key_path.rb', line 162

def method_missing(method_id, *args)
  super if method_id == :to_ary

  if endpoint.respond_to? method_id
    @endpoint = @endpoint.send(method_id, *args)
    self
  elsif Stub === endpoint || Join === endpoint
    @path << endpoint
    if args.empty?
      @endpoint = Stub.new(method_id)
    elsif (args.size == 1) && (Class === args[0])
      @endpoint = Join.new(method_id, Arel::InnerJoin, args[0])
    else
      @endpoint = Nodes::Function.new method_id, args
    end
    self
  else
    super
  end
end

Instance Attribute Details

#endpointObject (readonly)

Returns The endpoint, either another key as in the path, or a predicate, function, etc.

Returns:

  • The endpoint, either another key as in the path, or a predicate, function, etc.



22
23
24
# File 'lib/squeel/nodes/key_path.rb', line 22

def endpoint
  @endpoint
end

#pathArray<Symbol, Stub, Join> (readonly)

Returns The path.

Returns:



19
20
21
# File 'lib/squeel/nodes/key_path.rb', line 19

def path
  @path
end

Instance Method Details

#%(val) ⇒ KeyPath

Delegate % to the KeyPath’s endpoint, with a bit of special logic for stubs or functions.

Parameters:

  • val

    The value to be supplied to the created/existing predicate

Returns:

  • (KeyPath)

    This KeyPath, with a predicate endpoint containing the given value



138
139
140
141
142
143
144
145
146
147
# File 'lib/squeel/nodes/key_path.rb', line 138

def %(val)
  case endpoint
  when Stub, Function
    Array === val ? self.in(val) : self.eq(val)
    self
  else
    endpoint % val
    self
  end
end

#&(other) ⇒ And

Allow KeyPath to function like its endpoint, in the case where its endpoint responds to &

Parameters:

  • other

    The right hand side of the operation

Returns:

  • (And)

    An And node with the KeyPath on the left side and the other object on the right.



63
64
65
# File 'lib/squeel/nodes/key_path.rb', line 63

def &(other)
  endpoint.respond_to?(:&) ? super : no_method_error(:&)
end

#*(other) ⇒ Operation

Allow KeyPath to function like its endpoint, in the case where its endpoint responds to *

Parameters:

  • other

    The right hand side of the operation

Returns:

  • (Operation)

    An operation (with operator *) with the KeyPath on its left and the other object on the right.



94
95
96
# File 'lib/squeel/nodes/key_path.rb', line 94

def *(other)
  endpoint.respond_to?(:*) ? super : no_method_error(:*)
end

#+(other) ⇒ Operation

Allow KeyPath to function like its endpoint, in the case where its endpoint responds to +

Parameters:

  • other

    The right hand side of the operation

Returns:

  • (Operation)

    An operation (with operator +) with the KeyPath on its left and the other object on the right.



78
79
80
# File 'lib/squeel/nodes/key_path.rb', line 78

def +(other)
  endpoint.respond_to?(:+) ? super : no_method_error(:+)
end

#-(other) ⇒ Operation

Allow KeyPath to function like its endpoint, in the case where its endpoint responds to -

Parameters:

  • other

    The right hand side of the operation

Returns:

  • (Operation)

    An operation (with operator -) with the KeyPath on its left and the other object on the right.



86
87
88
# File 'lib/squeel/nodes/key_path.rb', line 86

def -(other)
  endpoint.respond_to?(:-) ? super : no_method_error(:-)
end

#-@Not

Allow KeyPath to function like its endpoint, in the case where its endpoint responds to -@

Returns:

  • (Not)

    A not node with the KeyPath as its expression



70
71
72
# File 'lib/squeel/nodes/key_path.rb', line 70

def -@
  endpoint.respond_to?(:-@) ? super : no_method_error(:-@)
end

#/(other) ⇒ Operation

Allow KeyPath to function like its endpoint, in the case where its endpoint responds to /

Parameters:

  • other

    The right hand side of the operation

Returns:

  • (Operation)

    An operation (with operator /) with the KeyPath on its left and the other object on the right.



102
103
104
# File 'lib/squeel/nodes/key_path.rb', line 102

def /(other)
  endpoint.respond_to?(:/) ? super : no_method_error(:/)
end

#absolute?Boolean

Whether or not the KeyPath should be interpreted relative to its current location

(if nested in a Hash, for instance) or as though it's at the base.

Returns:

  • (Boolean)

    The flag’s value



39
40
41
# File 'lib/squeel/nodes/key_path.rb', line 39

def absolute?
  @absolute
end

#eql?(other) ⇒ Boolean

Object comparison

Returns:

  • (Boolean)


44
45
46
47
48
49
# File 'lib/squeel/nodes/key_path.rb', line 44

def eql?(other)
  self.class.eql?(other.class) &&
  self.path.eql?(other.path) &&
  self.endpoint.eql?(other.endpoint) &&
  self.absolute?.eql?(other.absolute?)
end

#hashObject

For use with equality tests



123
124
125
# File 'lib/squeel/nodes/key_path.rb', line 123

def hash
  [self.class, endpoint, *path].hash
end

#no_method_error(method_id) ⇒ Object (private)

Raises a NoMethodError manually, bypassing #method_missing. Used by special-case operator overrides.

Raises:

  • (NoMethodError)


187
188
189
# File 'lib/squeel/nodes/key_path.rb', line 187

def no_method_error(method_id)
  raise NoMethodError, "undefined method `#{method_id}' for #{self}:#{self.class}"
end

#op(operator, other) ⇒ Operation

Allow KeyPath to function like its endpoint, in the case where its endpoint responds to #op

Parameters:

  • operator (String, Symbol)

    The custom operator

  • other

    The right hand side of the operation

Returns:

  • (Operation)

    An operation with the given custom operator, the KeyPath on its left and the other object on the right.



111
112
113
# File 'lib/squeel/nodes/key_path.rb', line 111

def op(operator, other)
  endpoint.respond_to?(:op) ? super : no_method_error(:/)
end

#path_with_endpointArray

Returns The KeyPath’s path, including its endpoint, as a single array.

Returns:

  • (Array)

    The KeyPath’s path, including its endpoint, as a single array.



150
151
152
# File 'lib/squeel/nodes/key_path.rb', line 150

def path_with_endpoint
  path + [endpoint]
end

#to_sObject Also known as: to_str

Implement (and alias to :to_str) to play nicely with ActiveRecord grouped calculations



155
156
157
# File 'lib/squeel/nodes/key_path.rb', line 155

def to_s
  path.map(&:to_s).join('.') << ".#{endpoint}"
end

#to_symNilClass

expand_hash_conditions_for_aggregates assumes our hash keys can be converted to symbols, so this has to be implemented, but it doesn’t really have to do anything useful.

Returns:

  • (NilClass)

    Just to avoid bombing out on expand_hash_conditions_for_aggregates



131
132
133
# File 'lib/squeel/nodes/key_path.rb', line 131

def to_sym
  nil
end

#|(other) ⇒ Or

Allow KeyPath to function like its endpoint, in the case where its endpoint responds to |

Parameters:

  • other

    The right hand side of the operation

Returns:

  • (Or)

    An Or node with the KeyPath on the left side and the other object on the right.



55
56
57
# File 'lib/squeel/nodes/key_path.rb', line 55

def |(other)
  endpoint.respond_to?(:|) ? super : no_method_error(:|)
end

#~KeyPath

Set the absolute flag on this KeyPath

Returns:

  • (KeyPath)

    This keypath, with its absolute flag set to true



117
118
119
120
# File 'lib/squeel/nodes/key_path.rb', line 117

def ~
  @absolute = true
  self
end