lib/sql_tree/node.rb in sql_tree-0.1.0 vs lib/sql_tree/node.rb in sql_tree-0.1.1

- old
+ new

@@ -1,35 +1,106 @@ module SQLTree::Node + # Auto-loades files for Node subclasses that reside in the + # node directory, based on the classname. def self.const_missing(const) SQLTree.load_default_class_file(SQLTree::Node, const) end + # The SQLTree::Node::Base class is the superclass for all node + # types that are used to represent SQL queries. + # + # This class implements some helper methods, and enables the + # SQLTree::Node::NodeType['SQL fragment'] construct to parse SQL + # queries. class Base + + def initialize(attributes = {}) # :nodoc: + attributes.each { |key, value| send(:"#{key}=", value) } + end # Pretty prints this instance for inspection def inspect "#{self.class.name}[#{self.to_sql}]" end # Quotes a variable name so that it can be safely used within # SQL queries. + # <tt>name</tt>:: The name of the variable to quote. def quote_var(name) - "\"#{name}\"" + "#{SQLTree.identifier_quote_char}#{name}#{SQLTree.identifier_quote_char}" # TODO: MySQL style variable quoting end - # Quotes a string so that it can be used within an SQL query. + # Quotes a string so that it can be used safey within an SQL query. + # <tt>str</tt>:: The string to quote. def quote_str(str) - "'#{str.gsub(/\'/, "''")}'" + "'#{str.gsub("'", "''")}'" end - # This method should be implemented by a subclass. + # Registers the children and leafs attributes in the subclass + def self.inherited(subclass) + class << subclass; attr_accessor :children, :leafs; end + subclass.children = [] + subclass.leafs = [] + end + + # Adds another leaf to this node class. + def self.leaf(name) + self.leafs << name + self.send(:attr_accessor, name) + end + + # Adds another child to this node class. + def self.child(name) + self.children << name + self.send(:attr_accessor, name) + end + + # Compares this node with another node, returns true if the nodes are equal. + def ==(other) + other.kind_of?(self.class) && equal_children?(other) && equal_leafs?(other) + end + + # Returns true if all children of the current object and the other object are equal. + def equal_children?(other) + self.class.children.all? { |child| send(child) == other.send(child) } + end + + # Returns true if all leaf values of the current object and the other object are equal. + def equal_leafs?(other) + self.class.leafs.all? { |leaf| send(leaf) == other.send(leaf) } + end + + # Parses an SQL fragment tree from a stream of tokens. + # + # This method should be implemented by each subclass. + # This method should not be called directly, but the + # <tt>SQLTree::Node::Subclass#[]</tt> should be called to + # parse an SQL fragment provided as a string. + # + # <tt>tokens</tt>:: the token stream to use for parsing. def self.parse(tokens) raise 'Only implemented in subclasses!' end + + def self.parse_list(tokens, item_class = SQLTree::Node::Expression) + items = [item_class.parse(tokens)] + while SQLTree::Token::COMMA === tokens.peek + tokens.consume(SQLTree::Token::COMMA) + items << item_class.parse(tokens) + end + return items + end # Parses a string, expecting it to be parsable to an instance of # the current class. + # + # This method will construct a new parser that will tokenize the + # string, and will then present the stream of tokens to the + # <tt>self.parse</tt> method of the current class. + # + # <tt>sql</tt>:: The SQL string to parse + # <tt>options</tt>:: A Hash of options to send to the parser. def self.[](sql, options = {}) parser = SQLTree::Parser.new(sql, options) self.parse(parser) end end \ No newline at end of file