Class: Squeel::Visitors::Base

Inherits:
Object
  • Object
show all
Defined in:
lib/squeel/visitors/base.rb

Overview

The Base visitor class, containing the default behavior common to subclasses.

Direct Known Subclasses

AttributeVisitor, PredicateVisitor, SymbolVisitor

Constant Summary collapse

DISPATCH =

A hash that caches the method name to use for a visitor for a given class

Hash.new do |hash, klass|
  hash[klass] = "visit_#{(klass.name || '').gsub('::', '_')}"
end

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(context = nil) ⇒ Base

Create a new Visitor that uses the supplied context object to contextualize visited nodes.

Parameters:

  • context (Context) (defaults to: nil)

    The context to use for node visitation.



15
16
17
# File 'lib/squeel/visitors/base.rb', line 15

def initialize(context = nil)
  @context = context
end

Instance Attribute Details

#contextObject

Returns the value of attribute context.



8
9
10
# File 'lib/squeel/visitors/base.rb', line 8

def context
  @context
end

Class Method Details

.can_accept?(object) ⇒ Boolean

Returns Whether or not visitors of this class can accept the given object.

Parameters:

  • object

    The object to check

Returns:

  • (Boolean)

    Whether or not visitors of this class can accept the given object



37
38
39
40
41
42
43
44
# File 'lib/squeel/visitors/base.rb', line 37

def self.can_accept?(object)
  @can_accept ||= Hash.new do |hash, klass|
    hash[klass] = klass.ancestors.detect { |ancestor|
      private_method_defined? DISPATCH[ancestor]
    } ? true : false
  end
  @can_accept[object.class]
end

Instance Method Details

#accept(object, parent = context.base) ⇒ Object

Accept an object.

Parameters:

  • object

    The object to visit

  • parent (defaults to: context.base)

    The parent of this object, to track the object’s place in any association hierarchy.

Returns:

  • The results of the node visitation, typically an ARel object of some kind.



25
26
27
# File 'lib/squeel/visitors/base.rb', line 25

def accept(object, parent = context.base)
  visit(object, parent)
end

#can_accept?(object) ⇒ Boolean

Returns Whether or not the visitor can accept the given object.

Parameters:

  • object

    The object to check

Returns:

  • (Boolean)

    Whether or not the visitor can accept the given object



31
32
33
# File 'lib/squeel/visitors/base.rb', line 31

def can_accept?(object)
  self.class.can_accept? object
end

#quote(value) ⇒ Arel::Nodes::SqlLiteral (private)

Quote a value based on its type, not on the last column used by the ARel visitor. This is occasionally necessary to avoid having ARel quote a value according to an integer column, converting ‘My String’ to 0.

Parameters:

  • value

    The value to quote

Returns:

  • (Arel::Nodes::SqlLiteral)

    if the value needs to be pre-quoted

  • the unquoted value, if default quoting won’t hurt.



77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/squeel/visitors/base.rb', line 77

def quote(value)
  if quoted? value
    case value
    when Array
      value.map {|v| quote(v)}
    when Range
      Range.new(quote(value.begin), quote(value.end), value.exclude_end?)
    else
      Arel.sql(arel_visitor.accept value)
    end
  else
    value
  end
end

#quoted?(object) ⇒ Boolean (private)

Important to avoid accidentally allowing the default ARel visitor’s last_column quoting behavior (where a value is quoted as though it is of the type of the last visited column). This can wreak havoc with Functions and Operations.

Parameters:

  • object

    The object to check

Returns:

  • (Boolean)

    Whether or not the ARel visitor will try to quote the object if not passed as an SqlLiteral.



61
62
63
64
65
66
67
68
# File 'lib/squeel/visitors/base.rb', line 61

def quoted?(object)
  case object
  when Arel::Nodes::SqlLiteral, Bignum, Fixnum, Arel::SelectManager
    false
  else
    true
  end
end

#visit(object, parent) ⇒ Object (private)

Visit the object. This is not called directly, but instead via the public #accept method.

Parameters:

  • object

    The object to visit

  • parent

    The object’s parent within the context



97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/squeel/visitors/base.rb', line 97

def visit(object, parent)
  send(DISPATCH[object.class], object, parent)
rescue NoMethodError => e
  raise e if respond_to?(DISPATCH[object.class], true)

  superklass = object.class.ancestors.find { |klass|
    respond_to?(DISPATCH[klass], true)
  }
  raise(TypeError, "Cannot visit #{object.class}") unless superklass
  DISPATCH[object.class] = DISPATCH[superklass]
  retry
end