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.



25
26
27
28
29
30
# File 'lib/squeel/nodes/key_path.rb', line 25

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



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 163

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.



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

def endpoint
  @endpoint
end

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

Returns The path.

Returns:



15
16
17
# File 'lib/squeel/nodes/key_path.rb', line 15

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



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

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.



59
60
61
# File 'lib/squeel/nodes/key_path.rb', line 59

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.



90
91
92
# File 'lib/squeel/nodes/key_path.rb', line 90

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.



74
75
76
# File 'lib/squeel/nodes/key_path.rb', line 74

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.



82
83
84
# File 'lib/squeel/nodes/key_path.rb', line 82

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



66
67
68
# File 'lib/squeel/nodes/key_path.rb', line 66

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.



98
99
100
# File 'lib/squeel/nodes/key_path.rb', line 98

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



35
36
37
# File 'lib/squeel/nodes/key_path.rb', line 35

def absolute?
  @absolute
end

#eql?(other) ⇒ Boolean

Object comparison

Returns:

  • (Boolean)


40
41
42
43
44
45
# File 'lib/squeel/nodes/key_path.rb', line 40

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

#hashObject

For use with equality tests



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

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.



107
108
109
# File 'lib/squeel/nodes/key_path.rb', line 107

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.



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

def path_with_endpoint
  path + [endpoint]
end

#to_sObject Also known as: to_str

Impleneted (and aliased to :to_str) to play nicely with ActiveRecord grouped calculations



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

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



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

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.



51
52
53
# File 'lib/squeel/nodes/key_path.rb', line 51

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



113
114
115
116
# File 'lib/squeel/nodes/key_path.rb', line 113

def ~
  @absolute = true
  self
end