squeel 1.1.1 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/.rspec +3 -0
- data/.travis.yml +36 -4
- data/CHANGELOG.md +15 -0
- data/Gemfile +1 -1
- data/README.md +47 -6
- data/Rakefile +14 -2
- data/lib/squeel.rb +9 -1
- data/lib/squeel/adapters/active_record.rb +0 -1
- data/lib/squeel/adapters/active_record/3.0/join_dependency_extensions.rb +1 -0
- data/lib/squeel/adapters/active_record/3.0/relation_extensions.rb +12 -1
- data/lib/squeel/adapters/active_record/3.1/join_dependency_extensions.rb +1 -0
- data/lib/squeel/adapters/active_record/3.2/join_dependency_extensions.rb +1 -0
- data/lib/squeel/adapters/active_record/4.0/join_dependency_extensions.rb +1 -0
- data/lib/squeel/adapters/active_record/4.0/relation_extensions.rb +92 -0
- data/lib/squeel/adapters/active_record/4.1/compat.rb +15 -0
- data/lib/squeel/adapters/active_record/4.1/context.rb +88 -0
- data/lib/squeel/adapters/active_record/4.1/preloader_extensions.rb +31 -0
- data/lib/squeel/adapters/active_record/4.1/reflection_extensions.rb +37 -0
- data/lib/squeel/adapters/active_record/4.1/relation_extensions.rb +307 -0
- data/lib/squeel/adapters/active_record/4.2/compat.rb +1 -0
- data/lib/squeel/adapters/active_record/4.2/context.rb +1 -0
- data/lib/squeel/adapters/active_record/4.2/preloader_extensions.rb +1 -0
- data/lib/squeel/adapters/active_record/4.2/relation_extensions.rb +108 -0
- data/lib/squeel/adapters/active_record/context.rb +7 -7
- data/lib/squeel/adapters/active_record/join_dependency_extensions.rb +9 -13
- data/lib/squeel/adapters/active_record/relation_extensions.rb +38 -8
- data/lib/squeel/core_ext/symbol.rb +3 -3
- data/lib/squeel/dsl.rb +1 -1
- data/lib/squeel/nodes.rb +1 -0
- data/lib/squeel/nodes/as.rb +12 -0
- data/lib/squeel/nodes/join.rb +8 -4
- data/lib/squeel/nodes/key_path.rb +10 -1
- data/lib/squeel/nodes/node.rb +21 -0
- data/lib/squeel/nodes/stub.rb +8 -4
- data/lib/squeel/nodes/subquery_join.rb +44 -0
- data/lib/squeel/version.rb +1 -1
- data/lib/squeel/visitors.rb +2 -0
- data/lib/squeel/visitors/enumeration_visitor.rb +101 -0
- data/lib/squeel/visitors/order_visitor.rb +9 -2
- data/lib/squeel/visitors/predicate_visitor.rb +11 -0
- data/lib/squeel/visitors/preload_visitor.rb +12 -0
- data/lib/squeel/visitors/visitor.rb +89 -13
- data/spec/config.travis.yml +13 -0
- data/spec/config.yml +12 -0
- data/spec/console.rb +3 -12
- data/spec/core_ext/symbol_spec.rb +3 -3
- data/spec/helpers/squeel_helper.rb +8 -5
- data/spec/spec_helper.rb +4 -16
- data/spec/squeel/adapters/active_record/context_spec.rb +8 -4
- data/spec/squeel/adapters/active_record/join_dependency_extensions_spec.rb +123 -38
- data/spec/squeel/adapters/active_record/relation_extensions_spec.rb +350 -124
- data/spec/squeel/core_ext/symbol_spec.rb +3 -3
- data/spec/squeel/nodes/join_spec.rb +4 -4
- data/spec/squeel/nodes/stub_spec.rb +3 -3
- data/spec/squeel/nodes/subquery_join_spec.rb +46 -0
- data/spec/squeel/visitors/order_visitor_spec.rb +3 -3
- data/spec/squeel/visitors/predicate_visitor_spec.rb +69 -36
- data/spec/squeel/visitors/select_visitor_spec.rb +1 -1
- data/spec/squeel/visitors/visitor_spec.rb +7 -6
- data/spec/support/models.rb +99 -15
- data/spec/support/schema.rb +109 -4
- data/squeel.gemspec +8 -6
- metadata +89 -107
- data/.ruby-gemset +0 -1
- data/.ruby-version +0 -1
- data/spec/blueprints/articles.rb +0 -5
- data/spec/blueprints/comments.rb +0 -5
- data/spec/blueprints/notes.rb +0 -3
- data/spec/blueprints/people.rb +0 -4
- 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.
|
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
|
-
|
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 =
|
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
|
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.
|
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 ==
|
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,
|
18
|
+
Squeel::Nodes::Join.new(self, Squeel::InnerJoin)
|
19
19
|
end
|
20
20
|
|
21
21
|
def outer
|
22
|
-
Squeel::Nodes::Join.new(self,
|
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,
|
26
|
+
Squeel::Nodes::Join.new(self, Squeel::InnerJoin, klass)
|
27
27
|
end
|
28
28
|
|
29
29
|
end
|
data/lib/squeel/dsl.rb
CHANGED
@@ -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,
|
113
|
+
Nodes::Join.new(method_id, InnerJoin, args[0])
|
114
114
|
else
|
115
115
|
Nodes::Function.new method_id, args
|
116
116
|
end
|
data/lib/squeel/nodes.rb
CHANGED
data/lib/squeel/nodes/as.rb
CHANGED
@@ -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
|
data/lib/squeel/nodes/join.rb
CHANGED
@@ -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 =
|
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 =
|
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 =
|
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,
|
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,
|
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)
|
data/lib/squeel/nodes/node.rb
CHANGED
@@ -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
|
data/lib/squeel/nodes/stub.rb
CHANGED
@@ -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,
|
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,
|
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,
|
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,
|
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
|
data/lib/squeel/version.rb
CHANGED
data/lib/squeel/visitors.rb
CHANGED
@@ -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
|