module CSL::Treelike::ClassMethods
Attributes
Public Class Methods
# File lib/csl/treelike.rb, line 355 def initialize(attrs = {}) super(*attrs.symbolize_keys.values_at(*keys)) @order = [] end
Public Instance Methods
# File lib/csl/treelike.rb, line 275 def constantize_nodename(name) return constantize(name) if respond_to?(:constantize) klass = name.to_s.capitalize.gsub(/(\w)-(\w)/) { [$1, $2.upcase].join } if const_defined?(klass) const_get(klass) else nil end end
Returns a new instance of an Array or Struct
to manage the Node's children. This method is called automatically by the Node's constructor.
# File lib/csl/treelike.rb, line 267 def create_children if const?(:Children) const_get(:Children).new else [] end end
Private Instance Methods
# File lib/csl/treelike.rb, line 471 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
# File lib/csl/treelike.rb, line 290 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 its children that way can only contain nodes of the given types.
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.
# File lib/csl/treelike.rb, line 307 def attr_children(*names) names.each do |name| 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) if klass value = klass.new(value) else # try to force convert value value = (value.is_a?(String) ? TextNode : Node).new(value) value.nodename = name.to_s end rescue => e raise ArgumentError, "failed to convert #{value.inspect} to node: #{e.message}" end unless value.respond_to?(:nodename) add_child value value end alias_method :"#{reader}=", writer unless method_defined?(:"#{reader}=") end end const_set(:Children, Struct.new(*names) { # 1.8 Compatibility @keys = members.map(&:to_sym).freeze class << self attr_reader :keys end def initialize(attrs = {}) super(*attrs.symbolize_keys.values_at(*keys)) @order = [] end def index(node, &block) @order.index(node, &block) end # @return [<Symbol>] a list of symbols representing the names/keys # of the attribute variables. def keys __class__.keys end def count values.reject { |c| c.nil? || c.is_a?(Array) && c.empty? }.length end alias original_each each # Iterates through all children. Nil values are skipped and Arrays # expanded. def each(&block) if block_given? order.each(&block) self else to_enum end end def empty? all?(&:nil?) end def select(&block) each.select(&block) end # Adds the node as a child node. Raises ValidationError if none # of the Struct members matches the node's name. If there is # already a node set with this node name, the node will be pushed # to an array for that name. def push(node) unless node.respond_to?(:nodename) && keys.include?(node.nodename.to_sym) raise ValidationError, "not allowed to add #{node.inspect} to #{inspect}" end current = self[node.nodename] case current when Array current.push(node) when nil self[node.nodename] = node else self[node.nodename] = [current, node] end # Add to @order to keep track of node ordering @order << node self end alias << push # Delete items from self that are equal to node. If any items are # found, returns the deleted items. If the items is not found, # returns nil. If the optional code block is given, returns the # result og block if the item is not found. def delete(node) return nil unless node.respond_to?(:nodename) deleted = resolve(node.nodename) if deleted.kind_of?(Array) deleted = deleted.delete(node) else if deleted == node self[node.nodename] = nil else deleted = nil end end # Delete node from ordered list as well @order.delete(node) if deleted.nil? && block_given? yield else deleted end end def fetch(name, default = nil) if block_given? resolve(name) || yield(key) else resolve(name) || default end end protected attr_reader :order private def resolve(nodename) keys.include?(nodename.to_sym) && self[nodename] end }) end
# File lib/csl/treelike.rb, line 371 def count values.reject { |c| c.nil? || c.is_a?(Array) && c.empty? }.length end
Delete items from self that are equal to node. If any items are found, returns the deleted items. If the items is not found, returns nil. If the optional code block is given, returns the result og block if the item is not found.
# File lib/csl/treelike.rb, line 427 def delete(node) return nil unless node.respond_to?(:nodename) deleted = resolve(node.nodename) if deleted.kind_of?(Array) deleted = deleted.delete(node) else if deleted == node self[node.nodename] = nil else deleted = nil end end # Delete node from ordered list as well @order.delete(node) if deleted.nil? && block_given? yield else deleted end end
Iterates through all children. Nil values are skipped and Arrays expanded.
# File lib/csl/treelike.rb, line 379 def each(&block) if block_given? order.each(&block) self else to_enum end end
# File lib/csl/treelike.rb, line 388 def empty? all?(&:nil?) end
# File lib/csl/treelike.rb, line 451 def fetch(name, default = nil) if block_given? resolve(name) || yield(key) else resolve(name) || default end end
Turns the node into a leaf-node.
# File lib/csl/treelike.rb, line 478 def has_no_children undef_method :add_child undef_method :added_child undef_method :add_children undef_method :<< undef_method :delete_child undef_method :deleted_child undef_method :delete_children private :children define_method(:has_children?) do false end define_method(:empty?) do true end end
# File lib/csl/treelike.rb, line 360 def index(node, &block) @order.index(node, &block) end
@return [<Symbol>] a list of symbols representing the names/keys
of the attribute variables.
# File lib/csl/treelike.rb, line 366 def keys __class__.keys end
Adds the node as a child node. Raises ValidationError
if none of the Struct
members matches the node's name. If there is already a node set with this node name, the node will be pushed to an array for that name.
# File lib/csl/treelike.rb, line 400 def push(node) unless node.respond_to?(:nodename) && keys.include?(node.nodename.to_sym) raise ValidationError, "not allowed to add #{node.inspect} to #{inspect}" end current = self[node.nodename] case current when Array current.push(node) when nil self[node.nodename] = node else self[node.nodename] = [current, node] end # Add to @order to keep track of node ordering @order << node self end
# File lib/csl/treelike.rb, line 465 def resolve(nodename) keys.include?(nodename.to_sym) && self[nodename] end
# File lib/csl/treelike.rb, line 392 def select(&block) each.select(&block) end