lib/csl/node.rb in csl-1.0.0.pre3 vs lib/csl/node.rb in csl-1.0.0.pre4

- old
+ new

@@ -1,39 +1,39 @@ module CSL - + class Node - + extend Forwardable - + include Enumerable include Comparable - + include Treelike include PrettyPrinter - - + + class << self def inherited(subclass) types << subclass subclass.nesting.each do |klass| klass.types << subclass if klass < Node end end - + def types @types ||= Set.new end - + def default_attributes @default_attributes ||= {} end - + def constantize(name) - pattern = /#{name.to_s.tr('-', '')}$/i + pattern = /:#{name.to_s.tr('-', '')}$/i klass = types.detect { |t| t.matches?(pattern) } - + case when !klass.nil? klass when nesting[-2].respond_to?(:constantize) nesting[-2].constantize(name) @@ -46,20 +46,20 @@ # passed-in name pattern def match?(name_pattern) name_pattern === name end alias matches? match? - + # Returns a new node with the passed in name and attributes. def create(name, attributes = {}, &block) klass = constantize(name) - node = (klass || Node).new(attributes, &block) + node = (klass || Node).new(attributes, &block) node.nodename = name node end - + def create_attributes(attributes) if const?(:Attributes) const_get(:Attributes).new(default_attributes.merge(attributes)) else default_attributes.merge(attributes) @@ -99,31 +99,31 @@ end def values super.compact end - + # def to_a # keys.zip(values_at(*keys)).reject { |k,v| v.nil? } # end - + # @return [Boolean] true if all the attribute values are nil; # false otherwise. def empty? values.compact.empty? end - + def fetch(key, default = nil) value = keys.include?(key.to_sym) && send(:'[]', key) - - if block_given? + + if block_given? value || yield(key) else value || default end end - + # Merges the current with the passed-in attributes. # # @param other [#each_pair] the other attributes # @return [self] def merge(other) @@ -160,18 +160,18 @@ attr_reader :attributes def_delegators :attributes, :[], :[]=, :values, :values_at, :length, :size - + def initialize(attributes = {}) @attributes = self.class.create_attributes(attributes) @children = self.class.create_children - + yield self if block_given? end - + # Iterates through the Node's attributes def each if block_given? attributes.each_pair(&Proc.new) self @@ -184,11 +184,11 @@ # Returns true if the node contains an attribute with the passed-in name; # false otherwise. def attribute?(name) attributes.fetch(name, false) end - + # Returns true if the node contains any attributes (ignores nil values); # false otherwise. def has_attributes? !attributes.empty? end @@ -200,11 +200,11 @@ def save_to(path, options = {}) File.open(path, 'w:UTF-8') do |f| f << (options[:compact] ? to_xml : pretty_print) end - + self end # Tests whether or not the Name matches the passed-in node name and # attribute conditions; if a Hash is passed as a single argument, @@ -229,11 +229,11 @@ # @param conditions [Hash] the conditions # # @return [Boolean] whether or not the query matches the node def match?(name = nodename, conditions = {}) name, conditions = match_conditions_for(name, conditions) - + return false unless name === nodename return true if conditions.empty? conditions.values.zip( attributes.values_at(*conditions.keys)).all? do |condition, value| @@ -263,14 +263,14 @@ # @param conditions [Hash] the conditions # # @return [Boolean] whether or not the query matches the node exactly def exact_match?(name = nodename, conditions = {}) name, conditions = match_conditions_for(name, conditions) - + return false unless name === nodename return true if conditions.empty? - + conditions.values_at(*attributes.keys).zip( attributes.values_at(*attributes.keys)).all? do |condition, value| condition === value end end @@ -279,44 +279,44 @@ def <=>(other) [nodename, attributes, children] <=> [other.nodename, other.attributes, other.children] rescue nil end - + # Returns the node' XML tags (including attribute assignments) as an # array of strings. def tags if has_children? tags = [] tags << "<#{[nodename, *attribute_assignments].join(' ')}>" - + tags << children.map { |node| node.respond_to?(:tags) ? node.tags : [node.to_s] }.flatten(1) - + tags << "</#{nodename}>" tags else ["<#{[nodename, *attribute_assignments].join(' ')}/>"] end end - + def inspect "#<#{[self.class.name, *attribute_assignments].join(' ')} children=[#{children.count}]>" end - + alias to_s pretty_print - + private - + def attribute_assignments each_pair.map { |name, value| value.nil? ? nil: [name, value.to_s.inspect].join('=') }.compact end - + def match_conditions_for(name, conditions) case name when Hash conditions, name = name, nodename when Symbol @@ -325,19 +325,19 @@ [name, conditions.symbolize_keys] end end - - + + class TextNode < Node - + has_no_children class << self undef_method :attr_children - + # @override def create(name, attributes = {}, &block) klass = constantize(name) node = (klass || TextNode).new(attributes, &block) @@ -345,23 +345,26 @@ node end end attr_accessor :text - alias to_s text + def to_s + text.to_s.strip + end + # TextNodes quack like a string. # def_delegators :to_s, *String.instance_methods(false).reject do |m| # m.to_s =~ /^\W|!$|(?:^(?:hash|eql?|to_s|length|size|inspect)$)/ # end - # + # # String.instance_methods(false).select { |m| m.to_s =~ /!$/ }.each do |m| # define_method(m) do # content.send(m) if content.respond_to?(m) # end # end - + def initialize(argument = '') case when argument.is_a?(Hash) super when argument.respond_to?(:to_s) @@ -370,21 +373,25 @@ yield self if block_given? else raise ArgumentError, "failed to create text node from #{argument.inspect}" end end - + def textnode? true end - + + def empty? + text.nil? || text.empty? + end + def tags ["<#{attribute_assignments.unshift(nodename).join(' ')}>#{text}</#{nodename}>"] end - + def inspect "#<#{[self.class.name, text.inspect, *attribute_assignments].join(' ')}>" end - + end - + end \ No newline at end of file