mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
mostly implemented depth-first traversal
This commit is contained in:
parent
f092ae544f
commit
c86c37e5f3
4 changed files with 159 additions and 0 deletions
|
@ -3,6 +3,8 @@ module Arel
|
|||
###
|
||||
# Abstract base class for all AST nodes
|
||||
class Node
|
||||
include Enumerable
|
||||
|
||||
###
|
||||
# Factory method to create a Nodes::Not node that has the recipient of
|
||||
# the caller as a child.
|
||||
|
@ -32,6 +34,11 @@ module Arel
|
|||
viz = Visitors.for engine
|
||||
viz.accept self
|
||||
end
|
||||
|
||||
# Iterate through AST, nodes will be yielded depth-first
|
||||
def each &block
|
||||
Visitors::DepthFirst.new(block).accept self
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
require 'arel/visitors/visitor'
|
||||
require 'arel/visitors/depth_first'
|
||||
require 'arel/visitors/to_sql'
|
||||
require 'arel/visitors/sqlite'
|
||||
require 'arel/visitors/postgresql'
|
||||
|
|
75
lib/arel/visitors/depth_first.rb
Normal file
75
lib/arel/visitors/depth_first.rb
Normal file
|
@ -0,0 +1,75 @@
|
|||
module Arel
|
||||
module Visitors
|
||||
class DepthFirst < Arel::Visitors::Visitor
|
||||
def initialize block = nil
|
||||
@block = block || Proc.new
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def binary o
|
||||
visit o.left
|
||||
visit o.right
|
||||
@block.call o
|
||||
end
|
||||
alias :visit_Arel_Nodes_And :binary
|
||||
alias :visit_Arel_Nodes_Assignment :binary
|
||||
alias :visit_Arel_Nodes_Between :binary
|
||||
alias :visit_Arel_Nodes_DoesNotMatch :binary
|
||||
alias :visit_Arel_Nodes_Equality :binary
|
||||
alias :visit_Arel_Nodes_GreaterThan :binary
|
||||
alias :visit_Arel_Nodes_GreaterThanOrEqual :binary
|
||||
alias :visit_Arel_Nodes_In :binary
|
||||
alias :visit_Arel_Nodes_LessThan :binary
|
||||
alias :visit_Arel_Nodes_LessThanOrEqual :binary
|
||||
alias :visit_Arel_Nodes_Matches :binary
|
||||
alias :visit_Arel_Nodes_NotEqual :binary
|
||||
alias :visit_Arel_Nodes_NotIn :binary
|
||||
alias :visit_Arel_Nodes_Or :binary
|
||||
|
||||
def visit_Arel_Attribute o
|
||||
visit o.relation
|
||||
visit o.name
|
||||
@block.call o
|
||||
end
|
||||
alias :visit_Arel_Attributes_Integer :visit_Arel_Attribute
|
||||
alias :visit_Arel_Attributes_Float :visit_Arel_Attribute
|
||||
alias :visit_Arel_Attributes_String :visit_Arel_Attribute
|
||||
alias :visit_Arel_Attributes_Time :visit_Arel_Attribute
|
||||
alias :visit_Arel_Attributes_Boolean :visit_Arel_Attribute
|
||||
alias :visit_Arel_Attributes_Attribute :visit_Arel_Attribute
|
||||
|
||||
def visit_Arel_Table o
|
||||
visit o.name
|
||||
@block.call o
|
||||
end
|
||||
|
||||
def terminal o
|
||||
@block.call o
|
||||
end
|
||||
alias :visit_Arel_Nodes_SqlLiteral :terminal
|
||||
alias :visit_Arel_SqlLiteral :terminal
|
||||
alias :visit_BigDecimal :terminal
|
||||
alias :visit_Date :terminal
|
||||
alias :visit_DateTime :terminal
|
||||
alias :visit_FalseClass :terminal
|
||||
alias :visit_Fixnum :terminal
|
||||
alias :visit_Float :terminal
|
||||
alias :visit_NilClass :terminal
|
||||
alias :visit_String :terminal
|
||||
alias :visit_Symbol :terminal
|
||||
alias :visit_Time :terminal
|
||||
alias :visit_TrueClass :terminal
|
||||
|
||||
def visit_Array o
|
||||
o.each { |i| visit i }
|
||||
@block.call o
|
||||
end
|
||||
|
||||
def visit_Hash o
|
||||
o.each { |k,v| visit(k); visit(v) }
|
||||
@block.call o
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
76
test/visitors/test_depth_first.rb
Normal file
76
test/visitors/test_depth_first.rb
Normal file
|
@ -0,0 +1,76 @@
|
|||
require 'helper'
|
||||
|
||||
module Arel
|
||||
module Visitors
|
||||
class TestDepthFirst < MiniTest::Unit::TestCase
|
||||
Collector = Struct.new(:calls) do
|
||||
def call object
|
||||
calls << object
|
||||
end
|
||||
end
|
||||
|
||||
def setup
|
||||
@collector = Collector.new []
|
||||
@visitor = Visitors::DepthFirst.new @collector
|
||||
end
|
||||
|
||||
[
|
||||
Arel::Nodes::And,
|
||||
Arel::Nodes::Assignment,
|
||||
Arel::Nodes::Between,
|
||||
Arel::Nodes::DoesNotMatch,
|
||||
Arel::Nodes::Equality,
|
||||
Arel::Nodes::GreaterThan,
|
||||
Arel::Nodes::GreaterThanOrEqual,
|
||||
Arel::Nodes::In,
|
||||
Arel::Nodes::LessThan,
|
||||
Arel::Nodes::LessThanOrEqual,
|
||||
Arel::Nodes::Matches,
|
||||
Arel::Nodes::NotEqual,
|
||||
Arel::Nodes::NotIn,
|
||||
Arel::Nodes::Or,
|
||||
].each do |klass|
|
||||
define_method("test_#{klass.name.gsub('::', '_')}") do
|
||||
binary = klass.new(:a, :b)
|
||||
@visitor.accept binary
|
||||
assert_equal [:a, :b, binary], @collector.calls
|
||||
end
|
||||
end
|
||||
|
||||
[
|
||||
Arel::Attributes::Integer,
|
||||
Arel::Attributes::Float,
|
||||
Arel::Attributes::String,
|
||||
Arel::Attributes::Time,
|
||||
Arel::Attributes::Boolean,
|
||||
Arel::Attributes::Attribute
|
||||
].each do |klass|
|
||||
define_method("test_#{klass.name.gsub('::', '_')}") do
|
||||
binary = klass.new(:a, :b)
|
||||
@visitor.accept binary
|
||||
assert_equal [:a, :b, binary], @collector.calls
|
||||
end
|
||||
end
|
||||
|
||||
def test_table
|
||||
relation = Arel::Table.new(:users)
|
||||
@visitor.accept relation
|
||||
assert_equal ['users', relation], @collector.calls
|
||||
end
|
||||
|
||||
def test_array
|
||||
node = Nodes::Or.new(:a, :b)
|
||||
list = [node]
|
||||
@visitor.accept list
|
||||
assert_equal [:a, :b, node, list], @collector.calls
|
||||
end
|
||||
|
||||
def test_hash
|
||||
node = Nodes::Or.new(:a, :b)
|
||||
hash = { node => node }
|
||||
@visitor.accept hash
|
||||
assert_equal [:a, :b, node, :a, :b, node, hash], @collector.calls
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue