1
0
Fork 0
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:
Aaron Patterson 2010-11-29 14:11:28 -08:00
parent f092ae544f
commit c86c37e5f3
4 changed files with 159 additions and 0 deletions

View file

@ -3,6 +3,8 @@ module Arel
### ###
# Abstract base class for all AST nodes # Abstract base class for all AST nodes
class Node class Node
include Enumerable
### ###
# Factory method to create a Nodes::Not node that has the recipient of # Factory method to create a Nodes::Not node that has the recipient of
# the caller as a child. # the caller as a child.
@ -32,6 +34,11 @@ module Arel
viz = Visitors.for engine viz = Visitors.for engine
viz.accept self viz.accept self
end end
# Iterate through AST, nodes will be yielded depth-first
def each &block
Visitors::DepthFirst.new(block).accept self
end
end end
end end
end end

View file

@ -1,4 +1,5 @@
require 'arel/visitors/visitor' require 'arel/visitors/visitor'
require 'arel/visitors/depth_first'
require 'arel/visitors/to_sql' require 'arel/visitors/to_sql'
require 'arel/visitors/sqlite' require 'arel/visitors/sqlite'
require 'arel/visitors/postgresql' require 'arel/visitors/postgresql'

View 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

View 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