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

- old
+ new

@@ -27,23 +27,29 @@ def default_attributes @default_attributes ||= {} end def constantize(name) - klass = types.detect { |t| t.matches?(name) } + pattern = /#{name.to_s.tr('-', '')}$/i + klass = types.detect { |t| t.matches?(pattern) } - if klass || !superclass.respond_to?(:constantize) + case + when !klass.nil? klass + when nesting[-2].respond_to?(:constantize) + nesting[-2].constantize(name) else - superclass.constantize(name) + nil end end - # @return [Boolean] whether or not the node's name matches the passed-in name - def matches?(nodename) - name.split(/::/)[-1].gsub(/([[:lower:]])([[:upper:]])/, '\1-\2').downcase == nodename + # @return [Boolean] whether or not the node's name matches the + # 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) @@ -197,11 +203,81 @@ 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, + # it is taken as the conditions parameter (the name parameter is + # automatically matches in this case). + # + # Whether or not the arguments match the node is determined as + # follows: + # + # 1. The name must match {#nodename} + # 2. All attribute name/value pairs passed as conditions must match + # the corresponding attributes of the node + # + # Note that only attributes present in the passed-in conditions + # influence the match – if you want to match only nodes that contain + # no other attributes than specified by the conditions, {#exact_match?} + # should be used instead. + # + # @see #exact_match? + # + # @param name [String,Regexp] must match the nodename + # @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| + condition === value + end + end + alias matches? match? + + # Tests whether or not the Name matches the passed-in node name and + # attribute conditions exactly; if a Hash is passed as a single argument, + # it is taken as the conditions parameter (the name parameter is + # automatically matches in this case). + # + # Whether or not the arguments match the node is determined as + # follows: + # + # 1. The name must match {#nodename} + # 2. All attribute name/value pairs of the node must match the + # corresponding pairs in the passed-in Hash + # + # Note that all node attributes are used by this method – if you want + # to match only a subset of attributes {#match?} should be used instead. + # + # @see #match? + # + # @param name [String,Regexp] must match the nodename + # @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 + alias matches_exactly? exact_match? + def <=>(other) [nodename, attributes, children] <=> [other.nodename, other.attributes, other.children] rescue nil end @@ -223,11 +299,11 @@ ["<#{[nodename, *attribute_assignments].join(' ')}/>"] end end def inspect - "#<#{[self.class.name, *attribute_assignments].join(' ')} children=[#{children.length}]>" + "#<#{[self.class.name, *attribute_assignments].join(' ')} children=[#{children.count}]>" end alias to_s pretty_print @@ -237,9 +313,20 @@ 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 + name = name.to_s + end + + [name, conditions.symbolize_keys] + end + end class TextNode < Node \ No newline at end of file