lib/csl/treelike.rb in csl-1.0.0.pre2 vs lib/csl/treelike.rb in csl-1.0.0.pre3
- old
+ new
@@ -10,11 +10,11 @@
def self.included(base)
base.extend(ClassMethods)
end
- # @eturn [String] the node's name.
+ # @return [String] the node's name.
def nodename
@nodename ||= self.class.name.split(/::/)[-1].gsub(/([[:lower:]])([[:upper:]])/, '\1-\2').downcase
end
def each_child
@@ -61,10 +61,11 @@
def add_children(*nodes)
nodes.each do |node|
add_child node
end
+ self
end
def add_child(node)
node.unlink
@@ -84,32 +85,44 @@
add_child node
self
end
# Returns the first immediate child node whose nodename matches the
- # passed-in name or pattern; returns nil there is no match.
- def find_child_by_name(name)
+ # passed-in name/pattern and attribute conditions.
+ #
+ # @param name [String,Regexp] the node name to match
+ # @param conditions [Hash] the attributes to match
+ #
+ # @return [Node,nil] the first matching child node
+ def find_child(name, conditions = {})
children.detect do |child|
- name === child.nodename
+ child.match?(name, conditions)
end
end
- alias > find_child_by_name
+ alias > find_child
# Returns all immediate child nodes whose nodename matches the passed-in
- # name or pattern; returns an empty array if there is no match.
- def find_children_by_name(name)
+ # name/pattern and attribute conditions; returns an empty array if there
+ # is no match.
+ #
+ # @param name [String,Regexp] the node name to match
+ # @param conditions [Hash] the attributes to match
+ #
+ # @return [Array<Node>] all matching child nodes
+ def find_children(name, conditions = {})
children.select do |child|
- name === child.nodename
+ child.match?(name, conditions)
end
end
- alias >> find_children_by_name
+ alias >> find_children
- # Returns true if the node has child nodes; false otherwise.
+ # @return [Boolean] true if this node has child nodes; false otherwise.
def has_children?
!empty?
end
+ # @return [Boolean] true if this node has no child nodes; false otherwise.
def empty?
children.empty?
end
# Unlinks the node and all its children from its parent node. Returns
@@ -141,10 +154,11 @@
def siblings
@siblings = each_sibling.to_a
end
+ # Traverses the node's sub-tree in depth-first order.
def each_descendant
if block_given?
each_child do |child|
yield child
child.each_descendant(&Proc.new)
@@ -154,12 +168,12 @@
else
enum_for :each_descendant
end
end
- # Returns all descendants of the node. See #descendants1 for a memoized
- # version.
+ # Returns all descendants of the node. See {#descendants!}
+ # for a memoized version.
def descendants
@descendants = each_descendant.to_a
end
def each_ancestor
@@ -175,26 +189,26 @@
else
enum_for :each_ancestor
end
end
- # Returns this node's ancestors as an array.
+ # @returns this node's ancestors as an array
def ancestors
@ancestors = each_ancestor.to_a
end
- # Returns the node's current depth in the tree.
+ # @return [Fixnum] the node's current depth in the tree
def depth
@depth = ancestors.length
end
- # Returns the root node.
+ # @return [Node] the root node
def root
@root = root? ? self : parent.root!
end
- # Returns true if the node is a root node, or false.
+ # @returns [Boolean] whether or not the node is the tree's root node
def root?
parent.nil?
end
@@ -247,47 +261,55 @@
[]
end
end
def constantize_nodename(name)
+ return constantize(name) if respond_to?(:constantize)
+
klass = name.to_s.capitalize.gsub(/(\w)-(\w)/) { [$1, $2.upcase].join }
-
- case
- when respond_to?(:constantize)
- constantize(klass)
- when const_defined?(klass)
- const_get(klass)
+
+ if const_defined?(klass)
+ const_get(klass)
else
nil
end
end
private
+ def attr_child_names_for(name)
+ reader = name.to_s.downcase.tr('-', '_')
+ [name.to_sym, reader, "set_child_#{reader}", "has_#{reader}?"]
+ end
+
# Creates a Struct for the passed-in child node names that will be
# used internally by the Node to manage its children. The Struct
# will be automatically initialized and is used similarly to the
# standard Array that normally holds the child nodes. The benefit of
# using the Struct is that all child nodes are accessible by name and
# need not be looked up; this improves performance, however, note that
# a node defining it's children that way can only contain nodes of the
# given types.
#
- # This method also generates accessors for each child.
+ # This method also generates accessors for each child. The writer
+ # method will try to coerce the passed-in value into the correct
+ # node type automatically.
def attr_children(*names)
names.each do |name|
- name = name.to_sym
- reader = name.to_s.downcase.tr('-', '_')
- writer = "set_child_#{reader}"
-
+ name, reader, writer, predicate = attr_child_names_for(name)
define_method(reader) do
children[name]
end unless method_defined?(reader)
+ define_method(predicate) do
+ c = children[name]
+ !(c.nil? || c.is_a?(Array) && c.empty?)
+ end unless method_defined?(predicate)
+
unless method_defined?(writer)
define_method(writer) do |value|
begin
klass = self.class.constantize_nodename(name)
@@ -302,14 +324,14 @@
rescue => e
raise ArgumentError, "failed to convert #{value.inspect} to node: #{e.message}"
end unless value.respond_to?(:nodename)
children << value
+ value
end
- alias_method :"#{reader}=", writer
-
+ alias_method :"#{reader}=", writer unless method_defined?(:"#{reader}=")
end
end
const_set(:Children, Struct.new(*names) {
@@ -325,15 +347,19 @@
end
# @return [<Symbol>] a list of symbols representing the names/keys
# of the attribute variables.
def keys
- self.class.keys
+ __class__.keys
end
alias original_each each
+ def count
+ values.reject { |c| c.nil? || c.empty? }.length
+ end
+
# Iterates through all children. Nil values are skipped and Arrays
# expanded.
def each
if block_given?
original_each do |node|
@@ -415,11 +441,17 @@
keys.include?(nodename.to_sym) && send(nodename)
end
})
end
- # Turns the Node into a leaf-node.
+ def alias_child(new_name, old_name)
+ attr_child_names_for(new_name).zip(attr_child_names_for(old_name)).each do |nn, on|
+ alias_method nn, on if method_defined?(on)
+ end
+ end
+
+ # Turns the node into a leaf-node.
def has_no_children
undef_method :add_child
undef_method :added_child
undef_method :add_children
undef_method :<<
@@ -430,9 +462,13 @@
private :children
define_method(:has_children?) do
false
+ end
+
+ define_method(:empty?) do
+ true
end
end
end
\ No newline at end of file