squeel 1.1.1 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +3 -0
  3. data/.rspec +3 -0
  4. data/.travis.yml +36 -4
  5. data/CHANGELOG.md +15 -0
  6. data/Gemfile +1 -1
  7. data/README.md +47 -6
  8. data/Rakefile +14 -2
  9. data/lib/squeel.rb +9 -1
  10. data/lib/squeel/adapters/active_record.rb +0 -1
  11. data/lib/squeel/adapters/active_record/3.0/join_dependency_extensions.rb +1 -0
  12. data/lib/squeel/adapters/active_record/3.0/relation_extensions.rb +12 -1
  13. data/lib/squeel/adapters/active_record/3.1/join_dependency_extensions.rb +1 -0
  14. data/lib/squeel/adapters/active_record/3.2/join_dependency_extensions.rb +1 -0
  15. data/lib/squeel/adapters/active_record/4.0/join_dependency_extensions.rb +1 -0
  16. data/lib/squeel/adapters/active_record/4.0/relation_extensions.rb +92 -0
  17. data/lib/squeel/adapters/active_record/4.1/compat.rb +15 -0
  18. data/lib/squeel/adapters/active_record/4.1/context.rb +88 -0
  19. data/lib/squeel/adapters/active_record/4.1/preloader_extensions.rb +31 -0
  20. data/lib/squeel/adapters/active_record/4.1/reflection_extensions.rb +37 -0
  21. data/lib/squeel/adapters/active_record/4.1/relation_extensions.rb +307 -0
  22. data/lib/squeel/adapters/active_record/4.2/compat.rb +1 -0
  23. data/lib/squeel/adapters/active_record/4.2/context.rb +1 -0
  24. data/lib/squeel/adapters/active_record/4.2/preloader_extensions.rb +1 -0
  25. data/lib/squeel/adapters/active_record/4.2/relation_extensions.rb +108 -0
  26. data/lib/squeel/adapters/active_record/context.rb +7 -7
  27. data/lib/squeel/adapters/active_record/join_dependency_extensions.rb +9 -13
  28. data/lib/squeel/adapters/active_record/relation_extensions.rb +38 -8
  29. data/lib/squeel/core_ext/symbol.rb +3 -3
  30. data/lib/squeel/dsl.rb +1 -1
  31. data/lib/squeel/nodes.rb +1 -0
  32. data/lib/squeel/nodes/as.rb +12 -0
  33. data/lib/squeel/nodes/join.rb +8 -4
  34. data/lib/squeel/nodes/key_path.rb +10 -1
  35. data/lib/squeel/nodes/node.rb +21 -0
  36. data/lib/squeel/nodes/stub.rb +8 -4
  37. data/lib/squeel/nodes/subquery_join.rb +44 -0
  38. data/lib/squeel/version.rb +1 -1
  39. data/lib/squeel/visitors.rb +2 -0
  40. data/lib/squeel/visitors/enumeration_visitor.rb +101 -0
  41. data/lib/squeel/visitors/order_visitor.rb +9 -2
  42. data/lib/squeel/visitors/predicate_visitor.rb +11 -0
  43. data/lib/squeel/visitors/preload_visitor.rb +12 -0
  44. data/lib/squeel/visitors/visitor.rb +89 -13
  45. data/spec/config.travis.yml +13 -0
  46. data/spec/config.yml +12 -0
  47. data/spec/console.rb +3 -12
  48. data/spec/core_ext/symbol_spec.rb +3 -3
  49. data/spec/helpers/squeel_helper.rb +8 -5
  50. data/spec/spec_helper.rb +4 -16
  51. data/spec/squeel/adapters/active_record/context_spec.rb +8 -4
  52. data/spec/squeel/adapters/active_record/join_dependency_extensions_spec.rb +123 -38
  53. data/spec/squeel/adapters/active_record/relation_extensions_spec.rb +350 -124
  54. data/spec/squeel/core_ext/symbol_spec.rb +3 -3
  55. data/spec/squeel/nodes/join_spec.rb +4 -4
  56. data/spec/squeel/nodes/stub_spec.rb +3 -3
  57. data/spec/squeel/nodes/subquery_join_spec.rb +46 -0
  58. data/spec/squeel/visitors/order_visitor_spec.rb +3 -3
  59. data/spec/squeel/visitors/predicate_visitor_spec.rb +69 -36
  60. data/spec/squeel/visitors/select_visitor_spec.rb +1 -1
  61. data/spec/squeel/visitors/visitor_spec.rb +7 -6
  62. data/spec/support/models.rb +99 -15
  63. data/spec/support/schema.rb +109 -4
  64. data/squeel.gemspec +8 -6
  65. metadata +89 -107
  66. data/.ruby-gemset +0 -1
  67. data/.ruby-version +0 -1
  68. data/spec/blueprints/articles.rb +0 -5
  69. data/spec/blueprints/comments.rb +0 -5
  70. data/spec/blueprints/notes.rb +0 -3
  71. data/spec/blueprints/people.rb +0 -4
  72. data/spec/blueprints/tags.rb +0 -3
@@ -3,6 +3,12 @@ require 'squeel/context'
3
3
  module Squeel
4
4
  module Adapters
5
5
  module ActiveRecord
6
+ JoinPart = if defined?(::ActiveRecord::Associations::JoinDependency::JoinPart)
7
+ ::ActiveRecord::Associations::JoinDependency::JoinPart
8
+ elsif defined?(::ActiveRecord::Associations::ClassMethods::JoinDependency::JoinBase)
9
+ ::ActiveRecord::Associations::ClassMethods::JoinDependency::JoinBase
10
+ end
11
+
6
12
  class Context < ::Squeel::Context
7
13
 
8
14
  def initialize(object)
@@ -23,7 +29,7 @@ module Squeel
23
29
  }
24
30
  when Nodes::Join
25
31
  @object.join_associations.detect { |j|
26
- j.reflection.name == object.name && j.parent == parent &&
32
+ j.reflection.name == object._name && j.parent == parent &&
27
33
  (object.polymorphic? ? j.reflection.klass == object._klass : true)
28
34
  }
29
35
  else
@@ -73,12 +79,6 @@ module Squeel
73
79
  end
74
80
 
75
81
  end
76
-
77
- if defined?(::ActiveRecord::Associations::JoinDependency::JoinPart)
78
- JoinPart = ::ActiveRecord::Associations::JoinDependency::JoinPart
79
- elsif defined?(::ActiveRecord::Associations::ClassMethods::JoinDependency::JoinBase)
80
- JoinPart = ::ActiveRecord::Associations::ClassMethods::JoinDependency::JoinBase
81
- end
82
82
  end
83
83
  end
84
84
  end
@@ -1,17 +1,22 @@
1
- require 'polyamorous'
2
-
3
1
  module Squeel
4
2
  module Adapters
5
3
  module ActiveRecord
6
- module JoinDependencyExtensions
4
+ if defined?(::ActiveRecord::Associations::JoinDependency)
5
+ JoinAssociation = ::ActiveRecord::Associations::JoinDependency::JoinAssociation
6
+ JoinDependency = ::ActiveRecord::Associations::JoinDependency
7
+ elsif defined?(::ActiveRecord::Associations::ClassMethods::JoinDependency)
8
+ JoinAssociation = ::ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation
9
+ JoinDependency = ::ActiveRecord::Associations::ClassMethods::JoinDependency
10
+ end
7
11
 
12
+ module JoinDependencyExtensions
8
13
  def self.included(base)
9
14
  base.class_eval do
10
15
  alias_method_chain :build, :squeel
11
16
  end
12
17
  end
13
18
 
14
- def build_with_squeel(associations, parent = nil, join_type = Arel::InnerJoin)
19
+ def build_with_squeel(associations, parent = nil, join_type = InnerJoin)
15
20
  case associations
16
21
  when Nodes::Stub
17
22
  associations = associations.symbol
@@ -29,15 +34,6 @@ module Squeel
29
34
  build_without_squeel(associations, parent, join_type)
30
35
  end
31
36
  end
32
-
33
- end
34
-
35
- if defined?(::ActiveRecord::Associations::JoinDependency)
36
- JoinAssociation = ::ActiveRecord::Associations::JoinDependency::JoinAssociation
37
- JoinDependency = ::ActiveRecord::Associations::JoinDependency
38
- elsif defined?(::ActiveRecord::Associations::ClassMethods::JoinDependency)
39
- JoinAssociation = ::ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation
40
- JoinDependency = ::ActiveRecord::Associations::ClassMethods::JoinDependency
41
37
  end
42
38
 
43
39
  JoinDependency.send :include, Adapters::ActiveRecord::JoinDependencyExtensions
@@ -45,7 +45,6 @@ module Squeel
45
45
  merge_resolving_duplicate_squeel_equalities(r)
46
46
  end
47
47
  else
48
- puts r.inspect if r.is_a?(Proc)
49
48
  super(r)
50
49
  end
51
50
  end
@@ -115,6 +114,8 @@ module Squeel
115
114
  :stashed_join
116
115
  when Arel::Nodes::Join
117
116
  :join_node
117
+ when Nodes::SubqueryJoin
118
+ :subquery_join
118
119
  else
119
120
  raise 'unknown class: %s' % join.class.name
120
121
  end
@@ -123,6 +124,7 @@ module Squeel
123
124
  association_joins = buckets[:association_join] || []
124
125
  stashed_association_joins = buckets[:stashed_join] || []
125
126
  join_nodes = (buckets[:join_node] || []).uniq
127
+ subquery_joins = (buckets[:subquery_join] || []).uniq
126
128
  string_joins = (buckets[:string_join] || []).map { |x|
127
129
  x.strip
128
130
  }.uniq
@@ -144,11 +146,12 @@ module Squeel
144
146
  association.join_to(manager)
145
147
  end
146
148
 
147
- manager.join_sources.concat join_list
149
+ manager.join_sources.concat(join_list)
150
+ manager.join_sources.concat(build_join_from_subquery(subquery_joins))
148
151
 
149
152
  manager
150
153
  end
151
- # For 4.x adapters
154
+ # For 4.0 adapters
152
155
  alias :build_joins :build_join_dependency
153
156
 
154
157
  def includes(*args)
@@ -279,12 +282,12 @@ module Squeel
279
282
  end
280
283
  end
281
284
 
282
- def find_equality_predicates(nodes)
285
+ def find_equality_predicates(nodes, relation_table_name = table_name)
283
286
  nodes.map { |node|
284
287
  case node
285
288
  when Arel::Nodes::Equality
286
289
  if node.left.respond_to?(:relation) &&
287
- node.left.relation.name == table_name
290
+ node.left.relation.name == relation_table_name
288
291
  node
289
292
  end
290
293
  when Arel::Nodes::Grouping
@@ -395,9 +398,8 @@ module Squeel
395
398
  # your model's default scope. We hijack it in order to dig down into
396
399
  # And and Grouping nodes, which are equivalent to seeing top-level
397
400
  # Equality nodes in stock AR terms.
398
- def where_values_hash_with_squeel
399
- equalities = find_equality_predicates(where_visit(with_default_scope.where_values))
400
-
401
+ def where_values_hash_with_squeel(relation_table_name = table_name)
402
+ equalities = find_equality_predicates(where_visit(with_default_scope.where_values), relation_table_name)
401
403
  binds = Hash[bind_values.find_all(&:first).map { |column, v| [column.name, v] }]
402
404
 
403
405
  Hash[equalities.map { |where|
@@ -406,6 +408,34 @@ module Squeel
406
408
  }]
407
409
  end
408
410
 
411
+ def build_join_from_subquery(subquery_joins)
412
+ subquery_joins.map do |join|
413
+ join.type.new(
414
+ Arel::Nodes::TableAlias.new(
415
+ Arel::Nodes::Grouping.new(join.subquery.left.arel.ast),
416
+ join.subquery.right),
417
+ Arel::Nodes::On.new(where_visit(join.constraints))
418
+ )
419
+ end
420
+ end
421
+
422
+ def preprocess_attrs_with_ar(attributes)
423
+ attributes.map do |key, value|
424
+ case key
425
+ when Squeel::Nodes::Node
426
+ {key => value}
427
+ when Symbol
428
+ if value.is_a?(Hash)
429
+ {key => value}
430
+ else
431
+ ::ActiveRecord::PredicateBuilder.build_from_hash(klass, {key => value}, table)
432
+ end
433
+ else
434
+ ::ActiveRecord::PredicateBuilder.build_from_hash(klass, {key => value}, table)
435
+ end
436
+ end
437
+ end
438
+
409
439
  end
410
440
  end
411
441
  end
@@ -15,15 +15,15 @@ class Symbol
15
15
  end
16
16
 
17
17
  def inner
18
- Squeel::Nodes::Join.new(self, Arel::InnerJoin)
18
+ Squeel::Nodes::Join.new(self, Squeel::InnerJoin)
19
19
  end
20
20
 
21
21
  def outer
22
- Squeel::Nodes::Join.new(self, Arel::OuterJoin)
22
+ Squeel::Nodes::Join.new(self, Squeel::OuterJoin)
23
23
  end
24
24
 
25
25
  def of_class(klass)
26
- Squeel::Nodes::Join.new(self, Arel::InnerJoin, klass)
26
+ Squeel::Nodes::Join.new(self, Squeel::InnerJoin, klass)
27
27
  end
28
28
 
29
29
  end
@@ -110,7 +110,7 @@ module Squeel
110
110
  if args.empty?
111
111
  Nodes::Stub.new method_id
112
112
  elsif (args.size == 1) && (Class === args[0])
113
- Nodes::Join.new(method_id, Arel::InnerJoin, args[0])
113
+ Nodes::Join.new(method_id, InnerJoin, args[0])
114
114
  else
115
115
  Nodes::Function.new method_id, args
116
116
  end
@@ -27,3 +27,4 @@ require 'squeel/nodes/as'
27
27
  require 'squeel/nodes/not'
28
28
  require 'squeel/nodes/join'
29
29
  require 'squeel/nodes/grouping'
30
+ require 'squeel/nodes/subquery_join'
@@ -12,6 +12,18 @@ module Squeel
12
12
  def initialize(left, right)
13
13
  @left, @right = left, right
14
14
  end
15
+
16
+ def on(*args)
17
+ raise "only can convert ActiveRecord::Relation to a join node" unless left.is_a?(ActiveRecord::Relation)
18
+ proc =
19
+ if block_given?
20
+ DSL.eval(&Proc.new)
21
+ else
22
+ args
23
+ end
24
+
25
+ SubqueryJoin.new(self, proc)
26
+ end
15
27
  end
16
28
  end
17
29
  end
@@ -14,21 +14,21 @@ module Squeel
14
14
  # @param [Symbol] name The association name
15
15
  # @param [Arel::InnerJoin, Arel::OuterJoin] type The Arel join class
16
16
  # @param [Class, String, Symbol] klass The polymorphic belongs_to class or class name
17
- def initialize(name, type = Arel::InnerJoin, klass = nil)
17
+ def initialize(name, type = InnerJoin, klass = nil)
18
18
  @_join = Polyamorous::Join.new(name, type, klass)
19
19
  end
20
20
 
21
21
  # Set the join type to an inner join
22
22
  # @return [Join] The join, with an updated join type.
23
23
  def inner
24
- self._type = Arel::InnerJoin
24
+ self._type = InnerJoin
25
25
  self
26
26
  end
27
27
 
28
28
  # Set the join type to an outer join
29
29
  # @return [Join] The join, with an updated join type.
30
30
  def outer
31
- self._type = Arel::OuterJoin
31
+ self._type = OuterJoin
32
32
  self
33
33
  end
34
34
 
@@ -61,7 +61,7 @@ module Squeel
61
61
  def method_missing(method_id, *args)
62
62
  super if method_id == :to_ary
63
63
  if (args.size == 1) && (Class === args[0])
64
- KeyPath.new([self, Join.new(method_id, Arel::InnerJoin, args[0])])
64
+ KeyPath.new([self, Join.new(method_id, InnerJoin, args[0])])
65
65
  else
66
66
  KeyPath.new([self, method_id])
67
67
  end
@@ -83,6 +83,10 @@ module Squeel
83
83
  nil
84
84
  end
85
85
 
86
+ def add_to_tree(hash)
87
+ hash[_join] ||= {}
88
+ end
89
+
86
90
  end
87
91
  end
88
92
  end
@@ -177,6 +177,10 @@ module Squeel
177
177
  end
178
178
  alias :to_str :to_s
179
179
 
180
+ def add_to_tree(hash)
181
+ walk_through_path(path.dup, hash)
182
+ end
183
+
180
184
  # Appends to the KeyPath or delegates to the endpoint, as appropriate
181
185
  # @return [KeyPath] The updated KeyPath
182
186
  def method_missing(method_id, *args, &block)
@@ -194,7 +198,7 @@ module Squeel
194
198
  if args.empty?
195
199
  @path << Stub.new(method_id)
196
200
  elsif (args.size == 1) && (Class === args[0])
197
- @path << Join.new(method_id, Arel::InnerJoin, args[0])
201
+ @path << Join.new(method_id, InnerJoin, args[0])
198
202
  else
199
203
  @path << Nodes::Function.new(method_id, args)
200
204
  end
@@ -213,6 +217,11 @@ module Squeel
213
217
  @path = @path.dup
214
218
  end
215
219
 
220
+ def walk_through_path(path, hash)
221
+ cache = path.shift.add_to_tree(hash)
222
+ path.empty? ? cache : walk_through_path(path, cache)
223
+ end
224
+
216
225
  # Raises a NoMethodError manually, bypassing #method_missing.
217
226
  # Used by special-case operator overrides.
218
227
  def no_method_error(method_id)
@@ -1,6 +1,27 @@
1
+ require 'polyamorous/tree_node'
2
+
1
3
  module Squeel
2
4
  module Nodes
3
5
  class Node
6
+ include ::Polyamorous::TreeNode
7
+
8
+ def each(&block)
9
+ return enum_for(:each) unless block_given?
10
+
11
+ Visitors::EnumerationVisitor.new(block).accept(self)
12
+ end
13
+
14
+ # We don't want the full Enumerable method list, because it will mess
15
+ # with stuff like KeyPath
16
+ def grep(object, &block)
17
+ if block_given?
18
+ each { |value| yield value if object === value }
19
+ else
20
+ [].tap do |results|
21
+ each { |value| results << value if object === value }
22
+ end
23
+ end
24
+ end
4
25
  end
5
26
  end
6
27
  end
@@ -79,7 +79,7 @@ module Squeel
79
79
  if args.empty?
80
80
  KeyPath.new([self, method_id])
81
81
  elsif (args.size == 1) && (Class === args[0])
82
- KeyPath.new([self, Join.new(method_id, Arel::InnerJoin, args[0])])
82
+ KeyPath.new([self, Join.new(method_id, InnerJoin, args[0])])
83
83
  else
84
84
  KeyPath.new([self, Nodes::Function.new(method_id, args)])
85
85
  end
@@ -102,7 +102,7 @@ module Squeel
102
102
  # Create an inner Join node for the association named by this Stub
103
103
  # @return [Join] The new inner Join node
104
104
  def inner
105
- Join.new(self.symbol, Arel::InnerJoin)
105
+ Join.new(self.symbol, InnerJoin)
106
106
  end
107
107
 
108
108
  # Create a keypath with a sifter as its endpoint
@@ -114,14 +114,18 @@ module Squeel
114
114
  # Create an outer Join node for the association named by this Stub
115
115
  # @return [Join] The new outer Join node
116
116
  def outer
117
- Join.new(self.symbol, Arel::OuterJoin)
117
+ Join.new(self.symbol, OuterJoin)
118
118
  end
119
119
 
120
120
  # Create a polymorphic Join node for the association named by this Stub,
121
121
  # @param [Class] klass The polymorphic belongs_to class for this Join
122
122
  # @return [Join] The new polymorphic Join node
123
123
  def of_class(klass)
124
- Join.new(self.symbol, Arel::InnerJoin, klass)
124
+ Join.new(self.symbol, InnerJoin, klass)
125
+ end
126
+
127
+ def add_to_tree(hash)
128
+ hash[symbol] ||= {}
125
129
  end
126
130
 
127
131
  end
@@ -0,0 +1,44 @@
1
+ module Squeel
2
+ module Nodes
3
+ class SubqueryJoin < Node
4
+ attr_accessor :subquery, :type, :constraints
5
+
6
+ def initialize(subquery, constraints, type = Squeel::InnerJoin)
7
+ raise ArgumentError,
8
+ "subquery(#{subquery}) isn't an Squeel::Nodes::As" unless subquery.is_a?(As)
9
+
10
+ raise ArgumentError,
11
+ "constraints(#{constraints}) isn't a Squeel::Nodes::Node" unless constraints.is_a?(Node)
12
+
13
+ self.subquery = subquery
14
+ self.constraints = constraints
15
+ self.type = type
16
+ end
17
+
18
+ # Implemented for equality testing
19
+ def hash
20
+ [subquery, type, constraints].hash
21
+ end
22
+
23
+ def inner
24
+ self.type = Squeel::InnerJoin
25
+ self
26
+ end
27
+
28
+ def outer
29
+ self.type = Squeel::OuterJoin
30
+ self
31
+ end
32
+
33
+ # Compare with other objects
34
+ def eql?(other)
35
+ self.class.eql?(other.class) &&
36
+ self.subquery.eql?(other.subquery) &&
37
+ self.type.eql?(other.type) &&
38
+ self.constraints.eql?(other.constraints)
39
+ end
40
+ alias :== :eql?
41
+
42
+ end
43
+ end
44
+ end
@@ -1,3 +1,3 @@
1
1
  module Squeel
2
- VERSION = '1.1.1'
2
+ VERSION = '1.2.1'
3
3
  end
@@ -8,3 +8,5 @@ require 'squeel/visitors/order_visitor'
8
8
  require 'squeel/visitors/select_visitor'
9
9
  require 'squeel/visitors/from_visitor'
10
10
  require 'squeel/visitors/preload_visitor'
11
+
12
+ require 'squeel/visitors/enumeration_visitor'
@@ -0,0 +1,101 @@
1
+ require 'active_support/core_ext/module'
2
+ require 'squeel/nodes'
3
+
4
+ module Squeel
5
+ module Visitors
6
+ # The Enumeration visitor class, used to implement Node#each
7
+ class EnumerationVisitor
8
+
9
+ # Create a new EnumerationVisitor.
10
+ #
11
+ # @param [Proc] block The block to execute against each node.
12
+ def initialize(block = Proc.new)
13
+ @block = block
14
+ end
15
+
16
+ # Accept an object.
17
+ #
18
+ # @param object The object to visit
19
+ # @return The results of the node visitation, which will be the last
20
+ # call to the @block
21
+ def accept(object)
22
+ visit(object)
23
+ end
24
+
25
+ private
26
+ # A hash that caches the method name to use for a visitor for a given
27
+ # class
28
+ DISPATCH = Hash.new do |hash, klass|
29
+ hash[klass] = "visit_#{(klass.name || '').gsub('::', '_')}"
30
+ end
31
+
32
+ # Visit the object.
33
+ #
34
+ # @param object The object to visit
35
+ def visit(object)
36
+ send(DISPATCH[object.class], object)
37
+ @block.call(object)
38
+ rescue NoMethodError => e
39
+ raise e if respond_to?(DISPATCH[object.class], true)
40
+
41
+ superklass = object.class.ancestors.find { |klass|
42
+ respond_to?(DISPATCH[klass], true)
43
+ }
44
+ raise(TypeError, "Cannot visit #{object.class}") unless superklass
45
+ DISPATCH[object.class] = DISPATCH[superklass]
46
+ retry
47
+ end
48
+
49
+ def visit_terminal(o)
50
+ end
51
+ alias :visit_Object :visit_terminal
52
+
53
+ def visit_Array(o)
54
+ o.map { |v| visit(v) }
55
+ end
56
+
57
+ def visit_Hash(o)
58
+ o.each { |k, v| visit(k); visit(v) }
59
+ end
60
+
61
+ def visit_Squeel_Nodes_Nary(o)
62
+ visit(o.children)
63
+ end
64
+
65
+ def visit_Squeel_Nodes_Binary(o)
66
+ visit(o.left)
67
+ visit(o.right)
68
+ end
69
+
70
+ def visit_Squeel_Nodes_Unary(o)
71
+ visit(o.expr)
72
+ end
73
+
74
+ def visit_Squeel_Nodes_Order(o)
75
+ visit(o.expr)
76
+ end
77
+
78
+ def visit_Squeel_Nodes_Function(o)
79
+ visit(o.args)
80
+ end
81
+
82
+ def visit_Squeel_Nodes_Predicate(o)
83
+ visit(o.expr)
84
+ visit(o.value)
85
+ end
86
+
87
+ def visit_Squeel_Nodes_KeyPath(o)
88
+ visit(o.path)
89
+ end
90
+
91
+ def visit_Squeel_Nodes_Join(o)
92
+ visit(o._join)
93
+ end
94
+
95
+ def visit_Squeel_Nodes_Literal(o)
96
+ visit(o.expr)
97
+ end
98
+
99
+ end
100
+ end
101
+ end