hexp 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +15 -0
 - data/.gitignore +4 -0
 - data/.travis.yml +2 -3
 - data/.yardopts +1 -0
 - data/Gemfile.devtools +17 -24
 - data/Gemfile.lock +77 -82
 - data/{LICENSE.md → LICENSE} +0 -0
 - data/README.md +20 -10
 - data/Rakefile +17 -1
 - data/bench/node/rewrite_bench.rb +23 -0
 - data/config/flay.yml +1 -1
 - data/config/flog.yml +1 -1
 - data/config/reek.yml +6 -0
 - data/config/yardstick.yml +7 -2
 - data/hexp.gemspec +6 -5
 - data/lib/hexp.rb +26 -18
 - data/lib/hexp/builder.rb +54 -35
 - data/lib/hexp/css_selector.rb +124 -25
 - data/lib/hexp/css_selector/parser.rb +45 -2
 - data/lib/hexp/css_selector/sass_parser.rb +16 -0
 - data/lib/hexp/dom.rb +2 -0
 - data/lib/hexp/dsl.rb +20 -21
 - data/lib/hexp/errors.rb +2 -2
 - data/lib/hexp/list.rb +14 -11
 - data/lib/hexp/node.rb +89 -38
 - data/lib/hexp/node/attributes.rb +43 -26
 - data/lib/hexp/node/children.rb +59 -4
 - data/lib/hexp/node/css_selection.rb +113 -7
 - data/lib/hexp/node/domize.rb +22 -13
 - data/lib/hexp/node/normalize.rb +3 -9
 - data/lib/hexp/node/pp.rb +13 -9
 - data/lib/hexp/node/rewriter.rb +28 -3
 - data/lib/hexp/node/{selector.rb → selection.rb} +48 -2
 - data/lib/hexp/nokogiri/reader.rb +2 -2
 - data/lib/hexp/text_node.rb +1 -1
 - data/lib/hexp/version.rb +1 -1
 - data/spec/unit/hexp/css_selector/universal_spec.rb +7 -0
 - data/spec/unit/hexp/node/domize_spec.rb +12 -0
 - data/spec/unit/hexp/node/{selector_spec.rb → selection_spec.rb} +9 -9
 - data/spec/unit/hexp/parse_spec.rb +10 -0
 - metadata +40 -44
 - data/SPEC.md +0 -53
 - data/notes +0 -34
 
| 
         @@ -10,35 +10,75 @@ module Hexp 
     | 
|
| 
       10 
10 
     | 
    
         
             
                #
         
     | 
| 
       11 
11 
     | 
    
         
             
                # The classes that make up the parse tree largely mimic the ones from SASS,
         
     | 
| 
       12 
12 
     | 
    
         
             
                # like CommaSequence, SimpleSequence, Class, Id, etc. By having them in our
         
     | 
| 
       13 
     | 
    
         
            -
                # own namespace however we can easily add Hexp-specific  
     | 
| 
      
 13 
     | 
    
         
            +
                # own namespace however we can easily add Hexp-specific functionality to them.
         
     | 
| 
       14 
14 
     | 
    
         
             
                #
         
     | 
| 
       15 
15 
     | 
    
         
             
                class Parser
         
     | 
| 
      
 16 
     | 
    
         
            +
                  # Initialize the parser with the selector to parse
         
     | 
| 
      
 17 
     | 
    
         
            +
                  #
         
     | 
| 
      
 18 
     | 
    
         
            +
                  # @param [String] selector
         
     | 
| 
      
 19 
     | 
    
         
            +
                  #
         
     | 
| 
      
 20 
     | 
    
         
            +
                  # @api private
         
     | 
| 
       16 
21 
     | 
    
         
             
                  def initialize(selector)
         
     | 
| 
       17 
22 
     | 
    
         
             
                    @selector = selector.freeze
         
     | 
| 
       18 
23 
     | 
    
         
             
                  end
         
     | 
| 
       19 
24 
     | 
    
         | 
| 
      
 25 
     | 
    
         
            +
                  # Parse the selector
         
     | 
| 
      
 26 
     | 
    
         
            +
                  #
         
     | 
| 
      
 27 
     | 
    
         
            +
                  # @return [Hexp::CssSelector::CommaSequence]
         
     | 
| 
      
 28 
     | 
    
         
            +
                  #
         
     | 
| 
      
 29 
     | 
    
         
            +
                  # @api private
         
     | 
| 
       20 
30 
     | 
    
         
             
                  def parse
         
     | 
| 
       21 
31 
     | 
    
         
             
                    rewrite_comma_sequence(SassParser.call(@selector))
         
     | 
| 
       22 
32 
     | 
    
         
             
                  end
         
     | 
| 
       23 
33 
     | 
    
         | 
| 
      
 34 
     | 
    
         
            +
                  # Parse a CSS selector in one go
         
     | 
| 
      
 35 
     | 
    
         
            +
                  #
         
     | 
| 
      
 36 
     | 
    
         
            +
                  # @param [String] selector
         
     | 
| 
      
 37 
     | 
    
         
            +
                  # @return [Hexp::CssSelector::CommaSequence]
         
     | 
| 
      
 38 
     | 
    
         
            +
                  #
         
     | 
| 
      
 39 
     | 
    
         
            +
                  # @api private
         
     | 
| 
       24 
40 
     | 
    
         
             
                  def self.call(selector)
         
     | 
| 
       25 
41 
     | 
    
         
             
                    new(selector).parse
         
     | 
| 
       26 
42 
     | 
    
         
             
                  end
         
     | 
| 
       27 
43 
     | 
    
         | 
| 
       28 
44 
     | 
    
         
             
                  private
         
     | 
| 
       29 
45 
     | 
    
         | 
| 
      
 46 
     | 
    
         
            +
                  # Map CommaSequence from the SASS namespace to our own
         
     | 
| 
      
 47 
     | 
    
         
            +
                  #
         
     | 
| 
      
 48 
     | 
    
         
            +
                  # @param [Sass::Selector::CommaSequence] comma_sequence
         
     | 
| 
      
 49 
     | 
    
         
            +
                  # @return [Hexp::CssSelector::CommaSequence]
         
     | 
| 
      
 50 
     | 
    
         
            +
                  #
         
     | 
| 
      
 51 
     | 
    
         
            +
                  # @api private
         
     | 
| 
       30 
52 
     | 
    
         
             
                  def rewrite_comma_sequence(comma_sequence)
         
     | 
| 
       31 
53 
     | 
    
         
             
                    CommaSequence.new(comma_sequence.members.map{|sequence| rewrite_sequence(sequence)})
         
     | 
| 
       32 
54 
     | 
    
         
             
                  end
         
     | 
| 
       33 
55 
     | 
    
         | 
| 
      
 56 
     | 
    
         
            +
                  # Map Sequence from the SASS namespace to our own
         
     | 
| 
      
 57 
     | 
    
         
            +
                  #
         
     | 
| 
      
 58 
     | 
    
         
            +
                  # @param [Sass::Selector::Sequence] comma_sequence
         
     | 
| 
      
 59 
     | 
    
         
            +
                  # @return [Hexp::CssSelector::Sequence]
         
     | 
| 
      
 60 
     | 
    
         
            +
                  #
         
     | 
| 
      
 61 
     | 
    
         
            +
                  # @api private
         
     | 
| 
       34 
62 
     | 
    
         
             
                  def rewrite_sequence(sequence)
         
     | 
| 
       35 
63 
     | 
    
         
             
                    Sequence.new(sequence.members.map{|simple_sequence| rewrite_simple_sequence(simple_sequence)})
         
     | 
| 
       36 
64 
     | 
    
         
             
                  end
         
     | 
| 
       37 
65 
     | 
    
         | 
| 
      
 66 
     | 
    
         
            +
                  # Map SimpleSequence from the SASS namespace to our own
         
     | 
| 
      
 67 
     | 
    
         
            +
                  #
         
     | 
| 
      
 68 
     | 
    
         
            +
                  # @param [Sass::Selector::SimpleSequence] comma_sequence
         
     | 
| 
      
 69 
     | 
    
         
            +
                  # @return [Hexp::CssSelector::SimpleSequence]
         
     | 
| 
      
 70 
     | 
    
         
            +
                  #
         
     | 
| 
      
 71 
     | 
    
         
            +
                  # @api private
         
     | 
| 
       38 
72 
     | 
    
         
             
                  def rewrite_simple_sequence(simple_sequence)
         
     | 
| 
       39 
73 
     | 
    
         
             
                    SimpleSequence.new(simple_sequence.members.map{|simple| rewrite_simple(simple)})
         
     | 
| 
       40 
74 
     | 
    
         
             
                  end
         
     | 
| 
       41 
75 
     | 
    
         | 
| 
      
 76 
     | 
    
         
            +
                  # Map Simple from the SASS namespace to our own
         
     | 
| 
      
 77 
     | 
    
         
            +
                  #
         
     | 
| 
      
 78 
     | 
    
         
            +
                  # @param [Sass::Selector::Simple] comma_sequence
         
     | 
| 
      
 79 
     | 
    
         
            +
                  # @return [Hexp::CssSelector::Simple]
         
     | 
| 
      
 80 
     | 
    
         
            +
                  #
         
     | 
| 
      
 81 
     | 
    
         
            +
                  # @api private
         
     | 
| 
       42 
82 
     | 
    
         
             
                  def rewrite_simple(simple)
         
     | 
| 
       43 
83 
     | 
    
         
             
                    case simple
         
     | 
| 
       44 
84 
     | 
    
         
             
                    when ::Sass::Selector::Element             # span
         
     | 
| 
         @@ -50,7 +90,7 @@ module Hexp 
     | 
|
| 
       50 
90 
     | 
    
         
             
                    when ::Sass::Selector::Attribute           # [href^="http://"]
         
     | 
| 
       51 
91 
     | 
    
         
             
                      raise "CSS attribute selector flags are curently ignored by Hexp (not implemented)" unless simple.flags.nil?
         
     | 
| 
       52 
92 
     | 
    
         
             
                      raise "CSS attribute namespaces are curently ignored by Hexp (not implemented)" unless simple.namespace.nil?
         
     | 
| 
       53 
     | 
    
         
            -
                      raise "CSS attribute operator #{simple.operator} not understood by Hexp" unless %w[= ~= ^=].include?(simple.operator) || simple.operator.nil?
         
     | 
| 
      
 93 
     | 
    
         
            +
                      raise "CSS attribute operator #{simple.operator} not understood by Hexp" unless %w[= ~= ^= |= $= *=].include?(simple.operator) || simple.operator.nil?
         
     | 
| 
       54 
94 
     | 
    
         
             
                      Attribute.new(
         
     | 
| 
       55 
95 
     | 
    
         
             
                        simple.name.first,
         
     | 
| 
       56 
96 
     | 
    
         
             
                        simple.namespace,
         
     | 
| 
         @@ -58,10 +98,13 @@ module Hexp 
     | 
|
| 
       58 
98 
     | 
    
         
             
                        simple.value ? simple.value.first : nil,
         
     | 
| 
       59 
99 
     | 
    
         
             
                        simple.flags
         
     | 
| 
       60 
100 
     | 
    
         
             
                      )
         
     | 
| 
      
 101 
     | 
    
         
            +
                    when ::Sass::Selector::Universal           # *
         
     | 
| 
      
 102 
     | 
    
         
            +
                      Universal.new
         
     | 
| 
       61 
103 
     | 
    
         
             
                    else
         
     | 
| 
       62 
104 
     | 
    
         
             
                      raise "CSS selectors containing #{simple.class} are not implemented in Hexp"
         
     | 
| 
       63 
105 
     | 
    
         
             
                    end
         
     | 
| 
       64 
106 
     | 
    
         | 
| 
      
 107 
     | 
    
         
            +
                    # As of yet unimplemented
         
     | 
| 
       65 
108 
     | 
    
         
             
                    # when ::Sass::Selector::Universal           # *
         
     | 
| 
       66 
109 
     | 
    
         
             
                    # when ::Sass::Selector::Parent              # & in Sass
         
     | 
| 
       67 
110 
     | 
    
         
             
                    # when ::Sass::Selector::Interpolation       # #{} in Sass
         
     | 
| 
         @@ -3,10 +3,20 @@ module Hexp 
     | 
|
| 
       3 
3 
     | 
    
         
             
                # A CSS Parser that only knows how to parse CSS selectors
         
     | 
| 
       4 
4 
     | 
    
         
             
                #
         
     | 
| 
       5 
5 
     | 
    
         
             
                class SassParser < ::Sass::SCSS::CssParser
         
     | 
| 
      
 6 
     | 
    
         
            +
                  # Initialize the parser with the selector to parse
         
     | 
| 
      
 7 
     | 
    
         
            +
                  #
         
     | 
| 
      
 8 
     | 
    
         
            +
                  # @param [String] selector
         
     | 
| 
      
 9 
     | 
    
         
            +
                  #
         
     | 
| 
      
 10 
     | 
    
         
            +
                  # @api private
         
     | 
| 
       6 
11 
     | 
    
         
             
                  def initialize(selector)
         
     | 
| 
       7 
12 
     | 
    
         
             
                    super(selector, '')
         
     | 
| 
       8 
13 
     | 
    
         
             
                  end
         
     | 
| 
       9 
14 
     | 
    
         | 
| 
      
 15 
     | 
    
         
            +
                  # Parse the selector
         
     | 
| 
      
 16 
     | 
    
         
            +
                  #
         
     | 
| 
      
 17 
     | 
    
         
            +
                  # @return [Sass::Selector::CommaSequence]
         
     | 
| 
      
 18 
     | 
    
         
            +
                  #
         
     | 
| 
      
 19 
     | 
    
         
            +
                  # @api private
         
     | 
| 
       10 
20 
     | 
    
         
             
                  def parse
         
     | 
| 
       11 
21 
     | 
    
         
             
                    init_scanner!
         
     | 
| 
       12 
22 
     | 
    
         
             
                    result = selector_comma_sequence
         
     | 
| 
         @@ -14,6 +24,12 @@ module Hexp 
     | 
|
| 
       14 
24 
     | 
    
         
             
                    result
         
     | 
| 
       15 
25 
     | 
    
         
             
                  end
         
     | 
| 
       16 
26 
     | 
    
         | 
| 
      
 27 
     | 
    
         
            +
                  # Parse a CSS selector in one go
         
     | 
| 
      
 28 
     | 
    
         
            +
                  #
         
     | 
| 
      
 29 
     | 
    
         
            +
                  # @param [String] selector
         
     | 
| 
      
 30 
     | 
    
         
            +
                  # @return [Sass::Selector::CommaSequence]
         
     | 
| 
      
 31 
     | 
    
         
            +
                  #
         
     | 
| 
      
 32 
     | 
    
         
            +
                  # @api private
         
     | 
| 
       17 
33 
     | 
    
         
             
                  def self.call(selector)
         
     | 
| 
       18 
34 
     | 
    
         
             
                    self.new(selector).parse
         
     | 
| 
       19 
35 
     | 
    
         
             
                  end
         
     | 
    
        data/lib/hexp/dom.rb
    CHANGED
    
    
    
        data/lib/hexp/dsl.rb
    CHANGED
    
    | 
         @@ -1,26 +1,25 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            module Hexp
         
     | 
| 
      
 2 
     | 
    
         
            +
              # Make the Hexp::Node DSL available to objects that implement `to_hexp`
         
     | 
| 
      
 3 
     | 
    
         
            +
              #
         
     | 
| 
      
 4 
     | 
    
         
            +
              # Mixing in {Hexp} has the same effect as mixing in {Hexp::DSL}, and is the
         
     | 
| 
      
 5 
     | 
    
         
            +
              # recommended way.
         
     | 
| 
      
 6 
     | 
    
         
            +
              #
         
     | 
| 
       2 
7 
     | 
    
         
             
              module DSL
         
     | 
| 
       3 
     | 
    
         
            -
                 
     | 
| 
       4 
     | 
    
         
            -
             
     | 
| 
       5 
     | 
    
         
            -
             
     | 
| 
       6 
     | 
    
         
            -
             
     | 
| 
       7 
     | 
    
         
            -
             
     | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
       9 
     | 
    
         
            -
             
     | 
| 
       10 
     | 
    
         
            -
             
     | 
| 
       11 
     | 
    
         
            -
                  : 
     | 
| 
       12 
     | 
    
         
            -
                  : 
     | 
| 
       13 
     | 
    
         
            -
             
     | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
       15 
     | 
    
         
            -
             
     | 
| 
       16 
     | 
    
         
            -
                   
     | 
| 
       17 
     | 
    
         
            -
             
     | 
| 
       18 
     | 
    
         
            -
                  :text,
         
     | 
| 
       19 
     | 
    
         
            -
                  :remove_attr,
         
     | 
| 
       20 
     | 
    
         
            -
                  :set_attributes,
         
     | 
| 
       21 
     | 
    
         
            -
                ].each do |meth|
         
     | 
| 
       22 
     | 
    
         
            -
                  define_method meth do |*args, &blk|
         
     | 
| 
       23 
     | 
    
         
            -
                    to_hexp.public_send(meth, *args, &blk)
         
     | 
| 
      
 8 
     | 
    
         
            +
                # The names of methods related to manipulating the list of children of a node
         
     | 
| 
      
 9 
     | 
    
         
            +
                CHILDREN_METHODS   = Hexp::Node::Children.public_instance_methods.freeze
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                # The names of methods related to a node's attributes
         
     | 
| 
      
 12 
     | 
    
         
            +
                ATTRIBUTES_METHODS = Hexp::Node::Attributes.public_instance_methods.freeze
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                # Methods that are defined directly in {Hexp::Node}
         
     | 
| 
      
 15 
     | 
    
         
            +
                NODE_METHODS = Hexp::Node.public_instance_methods(false) - [
         
     | 
| 
      
 16 
     | 
    
         
            +
                  :to_hexp,
         
     | 
| 
      
 17 
     | 
    
         
            +
                  :inspect
         
     | 
| 
      
 18 
     | 
    
         
            +
                ]
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                [CHILDREN_METHODS, ATTRIBUTES_METHODS, NODE_METHODS].flatten.each do |method|
         
     | 
| 
      
 21 
     | 
    
         
            +
                  define_method method do |*args, &blk|
         
     | 
| 
      
 22 
     | 
    
         
            +
                    to_hexp.public_send(method, *args, &blk)
         
     | 
| 
       24 
23 
     | 
    
         
             
                  end
         
     | 
| 
       25 
24 
     | 
    
         
             
                end
         
     | 
| 
       26 
25 
     | 
    
         
             
              end
         
     | 
    
        data/lib/hexp/errors.rb
    CHANGED
    
    | 
         @@ -1,4 +1,6 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            module Hexp
         
     | 
| 
      
 2 
     | 
    
         
            +
              # Base class for exceptions raised by Hexp
         
     | 
| 
      
 3 
     | 
    
         
            +
              #
         
     | 
| 
       2 
4 
     | 
    
         
             
              Error = Class.new(StandardError)
         
     | 
| 
       3 
5 
     | 
    
         | 
| 
       4 
6 
     | 
    
         
             
              # Raised when trying to stick things inside a Hexp where they don't belong
         
     | 
| 
         @@ -7,7 +9,6 @@ module Hexp 
     | 
|
| 
       7 
9 
     | 
    
         
             
                # Create a new FormatError
         
     | 
| 
       8 
10 
     | 
    
         
             
                #
         
     | 
| 
       9 
11 
     | 
    
         
             
                # @api private
         
     | 
| 
       10 
     | 
    
         
            -
                #
         
     | 
| 
       11 
12 
     | 
    
         
             
                def initialize(msg = 'You have illegal contents in your Hexp')
         
     | 
| 
       12 
13 
     | 
    
         
             
                  super
         
     | 
| 
       13 
14 
     | 
    
         
             
                end
         
     | 
| 
         @@ -17,5 +18,4 @@ module Hexp 
     | 
|
| 
       17 
18 
     | 
    
         
             
              #
         
     | 
| 
       18 
19 
     | 
    
         
             
              class ParseError < Error
         
     | 
| 
       19 
20 
     | 
    
         
             
              end
         
     | 
| 
       20 
     | 
    
         
            -
             
     | 
| 
       21 
21 
     | 
    
         
             
            end
         
     | 
    
        data/lib/hexp/list.rb
    CHANGED
    
    | 
         @@ -8,10 +8,10 @@ module Hexp 
     | 
|
| 
       8 
8 
     | 
    
         
             
                # @example
         
     | 
| 
       9 
9 
     | 
    
         
             
                #   Hexp::List.new([H[:p], H[:div]])
         
     | 
| 
       10 
10 
     | 
    
         
             
                #
         
     | 
| 
       11 
     | 
    
         
            -
                # @param  
     | 
| 
      
 11 
     | 
    
         
            +
                # @param [#to_ary] nodes
         
     | 
| 
      
 12 
     | 
    
         
            +
                #   List of nodes
         
     | 
| 
       12 
13 
     | 
    
         
             
                #
         
     | 
| 
       13 
14 
     | 
    
         
             
                # @api public
         
     | 
| 
       14 
     | 
    
         
            -
                #
         
     | 
| 
       15 
15 
     | 
    
         
             
                def initialize(nodes)
         
     | 
| 
       16 
16 
     | 
    
         
             
                  super nodes.to_ary.freeze
         
     | 
| 
       17 
17 
     | 
    
         
             
                end
         
     | 
| 
         @@ -24,11 +24,12 @@ module Hexp 
     | 
|
| 
       24 
24 
     | 
    
         
             
                #     Hexp::Node[:hr],
         
     | 
| 
       25 
25 
     | 
    
         
             
                #   ]
         
     | 
| 
       26 
26 
     | 
    
         
             
                #
         
     | 
| 
       27 
     | 
    
         
            -
                # @param  
     | 
| 
      
 27 
     | 
    
         
            +
                # @param [Array] args
         
     | 
| 
      
 28 
     | 
    
         
            +
                #   individual nodes
         
     | 
| 
       28 
29 
     | 
    
         
             
                #
         
     | 
| 
       29 
30 
     | 
    
         
             
                # @return [Hexp::List]
         
     | 
| 
       30 
     | 
    
         
            -
                # @api public
         
     | 
| 
       31 
31 
     | 
    
         
             
                #
         
     | 
| 
      
 32 
     | 
    
         
            +
                # @api public
         
     | 
| 
       32 
33 
     | 
    
         
             
                def self.[](*args)
         
     | 
| 
       33 
34 
     | 
    
         
             
                  new(args)
         
     | 
| 
       34 
35 
     | 
    
         
             
                end
         
     | 
| 
         @@ -39,21 +40,21 @@ module Hexp 
     | 
|
| 
       39 
40 
     | 
    
         
             
                # that this is a wrapping class. This is convenient when inspecting nested
         
     | 
| 
       40 
41 
     | 
    
         
             
                # hexps, but probably something we want to solve differently.
         
     | 
| 
       41 
42 
     | 
    
         
             
                #
         
     | 
| 
       42 
     | 
    
         
            -
                # @ 
     | 
| 
       43 
     | 
    
         
            -
                # @return string
         
     | 
| 
      
 43 
     | 
    
         
            +
                # @return [String]
         
     | 
| 
       44 
44 
     | 
    
         
             
                #
         
     | 
| 
      
 45 
     | 
    
         
            +
                # @api private
         
     | 
| 
       45 
46 
     | 
    
         
             
                def inspect
         
     | 
| 
       46 
47 
     | 
    
         
             
                  __getobj__.inspect
         
     | 
| 
       47 
48 
     | 
    
         
             
                end
         
     | 
| 
       48 
49 
     | 
    
         | 
| 
       49 
     | 
    
         
            -
                #  
     | 
| 
      
 50 
     | 
    
         
            +
                # Implicit conversion to Array
         
     | 
| 
       50 
51 
     | 
    
         
             
                #
         
     | 
| 
       51 
52 
     | 
    
         
             
                # @example
         
     | 
| 
       52 
53 
     | 
    
         
             
                #   Hexp::List[ H[:p], H[:span] ].to_ary #=> [H[:p], H[:span]]
         
     | 
| 
       53 
54 
     | 
    
         
             
                #
         
     | 
| 
       54 
55 
     | 
    
         
             
                # @return [Array<Hexp::Node>]
         
     | 
| 
       55 
     | 
    
         
            -
                # @api public
         
     | 
| 
       56 
56 
     | 
    
         
             
                #
         
     | 
| 
      
 57 
     | 
    
         
            +
                # @api public
         
     | 
| 
       57 
58 
     | 
    
         
             
                def to_ary
         
     | 
| 
       58 
59 
     | 
    
         
             
                  __getobj__
         
     | 
| 
       59 
60 
     | 
    
         
             
                end
         
     | 
| 
         @@ -72,10 +73,12 @@ module Hexp 
     | 
|
| 
       72 
73 
     | 
    
         
             
                #   H[:div, [[:span]]].children.eql? [H[:span]]           #=> false
         
     | 
| 
       73 
74 
     | 
    
         
             
                #   H[:div, [[:span]]].children.eql? Hexp::List[H[:span]] #=> true
         
     | 
| 
       74 
75 
     | 
    
         
             
                #
         
     | 
| 
       75 
     | 
    
         
            -
                # @param  
     | 
| 
       76 
     | 
    
         
            -
                #  
     | 
| 
       77 
     | 
    
         
            -
                # @return [Boolean]
         
     | 
| 
      
 76 
     | 
    
         
            +
                # @param [Object] other
         
     | 
| 
      
 77 
     | 
    
         
            +
                #   Object to compare with
         
     | 
| 
       78 
78 
     | 
    
         
             
                #
         
     | 
| 
      
 79 
     | 
    
         
            +
                # @return [true,false]
         
     | 
| 
      
 80 
     | 
    
         
            +
                #
         
     | 
| 
      
 81 
     | 
    
         
            +
                # @api public
         
     | 
| 
       79 
82 
     | 
    
         
             
                def eql?(other)
         
     | 
| 
       80 
83 
     | 
    
         
             
                  self == other && self.class == other.class
         
     | 
| 
       81 
84 
     | 
    
         
             
                end
         
     | 
    
        data/lib/hexp/node.rb
    CHANGED
    
    | 
         @@ -1,5 +1,56 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            module Hexp
         
     | 
| 
       2 
     | 
    
         
            -
              # A Hexp 
     | 
| 
      
 2 
     | 
    
         
            +
              # A +Hexp::Node+ represents a single element in a HTML syntax tree. It
         
     | 
| 
      
 3 
     | 
    
         
            +
              # consists of three parts : the {#tag}, the {#attributes} and the {#children}.
         
     | 
| 
      
 4 
     | 
    
         
            +
              #
         
     | 
| 
      
 5 
     | 
    
         
            +
              # Instances of +Hexp::Node+ are immutable. Because of this all methods that
         
     | 
| 
      
 6 
     | 
    
         
            +
              # "alter" the node actually return a new instance, leaving the old one
         
     | 
| 
      
 7 
     | 
    
         
            +
              # untouched.
         
     | 
| 
      
 8 
     | 
    
         
            +
              #
         
     | 
| 
      
 9 
     | 
    
         
            +
              # @example Immutable nodes : the old one is untouched
         
     | 
| 
      
 10 
     | 
    
         
            +
              #   node = Hexp::Node.new(:div)
         
     | 
| 
      
 11 
     | 
    
         
            +
              #   node2 = node.add_class('items')
         
     | 
| 
      
 12 
     | 
    
         
            +
              #   p node  # => H[:div]
         
     | 
| 
      
 13 
     | 
    
         
            +
              #   p node2 # => H[:div, 'class' => 'items']
         
     | 
| 
      
 14 
     | 
    
         
            +
              #
         
     | 
| 
      
 15 
     | 
    
         
            +
              # The +Hexp::Node+ constructor takes a +Symbol+, a +Hash+ and an +Array+ for
         
     | 
| 
      
 16 
     | 
    
         
            +
              # the {#tag}, {#attributes} and {#children} respectively. However the
         
     | 
| 
      
 17 
     | 
    
         
            +
              # attributes and children can each be ommitted if they are empty.
         
     | 
| 
      
 18 
     | 
    
         
            +
              #
         
     | 
| 
      
 19 
     | 
    
         
            +
              # If the node contains a single child node, then it is not necessary to wrap
         
     | 
| 
      
 20 
     | 
    
         
            +
              # that child node in an array. Just put the single node in place of the list
         
     | 
| 
      
 21 
     | 
    
         
            +
              # of children.
         
     | 
| 
      
 22 
     | 
    
         
            +
              #
         
     | 
| 
      
 23 
     | 
    
         
            +
              # One can use +H[tag, attrs, children]+ as a
         
     | 
| 
      
 24 
     | 
    
         
            +
              # shorthand syntax. Finally one can use {Hexp.build Hexp.build} to construct nodes using
         
     | 
| 
      
 25 
     | 
    
         
            +
              # Ruby blocks, not unlike the Builder or Nokogiri gems.
         
     | 
| 
      
 26 
     | 
    
         
            +
              #
         
     | 
| 
      
 27 
     | 
    
         
            +
              # @example Creating Hexp : syntax alternatives and optional parameters
         
     | 
| 
      
 28 
     | 
    
         
            +
              #   Hexp::Node.new(:div, class: 'foo')
         
     | 
| 
      
 29 
     | 
    
         
            +
              #   Hexp::Node.new(:div, {class: 'foo'}, "A text node")
         
     | 
| 
      
 30 
     | 
    
         
            +
              #   Hexp::Node.new(:div, {class: 'foo'}, ["A text node"])
         
     | 
| 
      
 31 
     | 
    
         
            +
              #   H[:div, {class: 'foo'}, H[:span, {class: 'big'}, "good stuff"]]
         
     | 
| 
      
 32 
     | 
    
         
            +
              #   H[:div, {class: 'foo'}, [
         
     | 
| 
      
 33 
     | 
    
         
            +
              #       H[:span, {class: 'big'}, "good stuff"],
         
     | 
| 
      
 34 
     | 
    
         
            +
              #       H[:a, {href: '/index'}, "go home"]
         
     | 
| 
      
 35 
     | 
    
         
            +
              #     ]
         
     | 
| 
      
 36 
     | 
    
         
            +
              #   ]
         
     | 
| 
      
 37 
     | 
    
         
            +
              #   Hexp.build { div.strong { "Hello, world!" } }
         
     | 
| 
      
 38 
     | 
    
         
            +
              #
         
     | 
| 
      
 39 
     | 
    
         
            +
              # Methods that read or alter the attributes Hash are defined in
         
     | 
| 
      
 40 
     | 
    
         
            +
              # {Hexp::Node::Attributes}. Methods that read or alter the list of child nodes
         
     | 
| 
      
 41 
     | 
    
         
            +
              # are defined in {Hexp::Node::Children}
         
     | 
| 
      
 42 
     | 
    
         
            +
              #
         
     | 
| 
      
 43 
     | 
    
         
            +
              # == CSS selectors
         
     | 
| 
      
 44 
     | 
    
         
            +
              #
         
     | 
| 
      
 45 
     | 
    
         
            +
              # When working with large trees of {Hexp::Node} objects, it is convenient to
         
     | 
| 
      
 46 
     | 
    
         
            +
              # be able to target specific nodes using CSS selector syntax. Hexp supports a
         
     | 
| 
      
 47 
     | 
    
         
            +
              # subset of the CSS 3 selector syntax, see {Hexp::Node::CssSelection} for info
         
     | 
| 
      
 48 
     | 
    
         
            +
              # on the supported syntax.
         
     | 
| 
      
 49 
     | 
    
         
            +
              #
         
     | 
| 
      
 50 
     | 
    
         
            +
              # For changing, replacing or removing specific nodes based on a CSS selector
         
     | 
| 
      
 51 
     | 
    
         
            +
              # string, see {Hexp::Node#replace}. To iterate over nodes, see
         
     | 
| 
      
 52 
     | 
    
         
            +
              # {Hexp::Node#select}.
         
     | 
| 
      
 53 
     | 
    
         
            +
              #
         
     | 
| 
       3 
54 
     | 
    
         
             
              class Node
         
     | 
| 
       4 
55 
     | 
    
         
             
                include Equalizer.new(:tag, :attributes, :children)
         
     | 
| 
       5 
56 
     | 
    
         
             
                extend Forwardable
         
     | 
| 
         @@ -149,66 +200,66 @@ module Hexp 
     | 
|
| 
       149 
200 
     | 
    
         
             
                  false
         
     | 
| 
       150 
201 
     | 
    
         
             
                end
         
     | 
| 
       151 
202 
     | 
    
         | 
| 
      
 203 
     | 
    
         
            +
                # Return a new node, with a different tag
         
     | 
| 
      
 204 
     | 
    
         
            +
                #
         
     | 
| 
      
 205 
     | 
    
         
            +
                # @example
         
     | 
| 
      
 206 
     | 
    
         
            +
                #   H[:div, class: 'foo'].set_tag(:bar)
         
     | 
| 
      
 207 
     | 
    
         
            +
                #   # => H[:bar, class: 'foo']
         
     | 
| 
      
 208 
     | 
    
         
            +
                #
         
     | 
| 
      
 209 
     | 
    
         
            +
                # @param tag [#to_sym] The new tag
         
     | 
| 
      
 210 
     | 
    
         
            +
                # @return [Hexp::Node]
         
     | 
| 
      
 211 
     | 
    
         
            +
                #
         
     | 
| 
      
 212 
     | 
    
         
            +
                # @api public
         
     | 
| 
       152 
213 
     | 
    
         
             
                def set_tag(tag)
         
     | 
| 
       153 
214 
     | 
    
         
             
                  H[tag.to_sym, attributes, children]
         
     | 
| 
       154 
215 
     | 
    
         
             
                end
         
     | 
| 
       155 
216 
     | 
    
         | 
| 
       156 
     | 
    
         
            -
                #  
     | 
| 
       157 
     | 
    
         
            -
                #
         
     | 
| 
       158 
     | 
    
         
            -
                # Since nodes are immutable, this is the main entry point for deriving nodes
         
     | 
| 
       159 
     | 
    
         
            -
                # from others.
         
     | 
| 
      
 217 
     | 
    
         
            +
                # Replace nodes in a tree
         
     | 
| 
       160 
218 
     | 
    
         
             
                #
         
     | 
| 
       161 
     | 
    
         
            -
                #  
     | 
| 
       162 
     | 
    
         
            -
                #  
     | 
| 
      
 219 
     | 
    
         
            +
                # With a CSS selector string like +"form.checkout"+ you specify the nodes
         
     | 
| 
      
 220 
     | 
    
         
            +
                # you want to operate on. These will be passed one by one into the block.
         
     | 
| 
      
 221 
     | 
    
         
            +
                # The block returns the {Hexp::Node} that will replace the old node, or it
         
     | 
| 
      
 222 
     | 
    
         
            +
                # can replace an +Array+ of nodes to fill the place of the old node.
         
     | 
| 
       163 
223 
     | 
    
         
             
                #
         
     | 
| 
       164 
     | 
    
         
            -
                #  
     | 
| 
       165 
     | 
    
         
            -
                #  
     | 
| 
       166 
     | 
    
         
            -
                # nodes.
         
     | 
| 
      
 224 
     | 
    
         
            +
                # Because of this you can add one or more nodes, or remove nodes by
         
     | 
| 
      
 225 
     | 
    
         
            +
                # returning an empty array.
         
     | 
| 
       167 
226 
     | 
    
         
             
                #
         
     | 
| 
       168 
     | 
    
         
            -
                #  
     | 
| 
       169 
     | 
    
         
            -
                # 
     | 
| 
       170 
     | 
    
         
            -
                # needs to return one of these
         
     | 
| 
      
 227 
     | 
    
         
            +
                # @example Remove all script tags
         
     | 
| 
      
 228 
     | 
    
         
            +
                #   tree.replace('script') {|_| [] }
         
     | 
| 
       171 
229 
     | 
    
         
             
                #
         
     | 
| 
       172 
     | 
    
         
            -
                # 
     | 
| 
       173 
     | 
    
         
            -
                # 
     | 
| 
       174 
     | 
    
         
            -
                # 
     | 
| 
       175 
     | 
    
         
            -
                # 
     | 
| 
      
 230 
     | 
    
         
            +
                # @example Wrap each +<input>+ tag into a +<p>+ tag
         
     | 
| 
      
 231 
     | 
    
         
            +
                #   tree.replace('input') do |input|
         
     | 
| 
      
 232 
     | 
    
         
            +
                #     H[:p, input]
         
     | 
| 
      
 233 
     | 
    
         
            +
                #   end
         
     | 
| 
       176 
234 
     | 
    
         
             
                #
         
     | 
| 
       177 
     | 
    
         
            -
                #  
     | 
| 
       178 
     | 
    
         
            -
                # currently referenced node where it is. This is very handy when you only want
         
     | 
| 
       179 
     | 
    
         
            -
                # to act on certain nodes, just return nothing if you want to do nothing.
         
     | 
| 
      
 235 
     | 
    
         
            +
                # @param [String] css_selector
         
     | 
| 
       180 
236 
     | 
    
         
             
                #
         
     | 
| 
       181 
     | 
    
         
            -
                #  
     | 
| 
      
 237 
     | 
    
         
            +
                # @yieldparam [Hexp::Node]
         
     | 
| 
      
 238 
     | 
    
         
            +
                #   The matching nodes
         
     | 
| 
       182 
239 
     | 
    
         
             
                #
         
     | 
| 
       183 
     | 
    
         
            -
                #  
     | 
| 
       184 
     | 
    
         
            -
                #
         
     | 
| 
       185 
     | 
    
         
            -
                # @example
         
     | 
| 
       186 
     | 
    
         
            -
                #    tree.rewrite{|node| [] if node.tag == :script }
         
     | 
| 
       187 
     | 
    
         
            -
                #
         
     | 
| 
       188 
     | 
    
         
            -
                # Wrap each <input> tag into a <p> tag
         
     | 
| 
      
 240 
     | 
    
         
            +
                # @return [Hexp::Node]
         
     | 
| 
      
 241 
     | 
    
         
            +
                #   The rewritten tree
         
     | 
| 
       189 
242 
     | 
    
         
             
                #
         
     | 
| 
       190 
     | 
    
         
            -
                # @example
         
     | 
| 
       191 
     | 
    
         
            -
                #    tree.rewrite do |node|
         
     | 
| 
       192 
     | 
    
         
            -
                #      if node.tag == :input
         
     | 
| 
       193 
     | 
    
         
            -
                #        [ H[:p, [ child ] ]
         
     | 
| 
       194 
     | 
    
         
            -
                #      end
         
     | 
| 
       195 
     | 
    
         
            -
                #    end
         
     | 
| 
       196 
     | 
    
         
            -
                #
         
     | 
| 
       197 
     | 
    
         
            -
                # @param blk [Proc] The rewrite action
         
     | 
| 
       198 
     | 
    
         
            -
                # @return [Hexp::Node] The rewritten tree
         
     | 
| 
       199 
243 
     | 
    
         
             
                # @api public
         
     | 
| 
       200 
     | 
    
         
            -
                #
         
     | 
| 
       201 
244 
     | 
    
         
             
                def rewrite(css_selector = nil, &block)
         
     | 
| 
       202 
245 
     | 
    
         
             
                  return Rewriter.new(self, block) if css_selector.nil?
         
     | 
| 
       203 
246 
     | 
    
         
             
                  CssSelection.new(self, css_selector).rewrite(&block)
         
     | 
| 
       204 
247 
     | 
    
         
             
                end
         
     | 
| 
       205 
248 
     | 
    
         
             
                alias :replace :rewrite
         
     | 
| 
       206 
249 
     | 
    
         | 
| 
      
 250 
     | 
    
         
            +
                # Select nodes based on a css selector
         
     | 
| 
      
 251 
     | 
    
         
            +
                #
         
     | 
| 
      
 252 
     | 
    
         
            +
                # @param [String] css_selector
         
     | 
| 
      
 253 
     | 
    
         
            +
                # @yieldparam [Hexp::Node]
         
     | 
| 
      
 254 
     | 
    
         
            +
                #
         
     | 
| 
      
 255 
     | 
    
         
            +
                # @return [Hexp::Selector,Hexp::CssSelector]
         
     | 
| 
      
 256 
     | 
    
         
            +
                #
         
     | 
| 
      
 257 
     | 
    
         
            +
                # @api public
         
     | 
| 
       207 
258 
     | 
    
         
             
                def select(css_selector = nil, &block)
         
     | 
| 
       208 
259 
     | 
    
         
             
                  if css_selector
         
     | 
| 
       209 
260 
     | 
    
         
             
                    CssSelection.new(self, css_selector).each(&block)
         
     | 
| 
       210 
261 
     | 
    
         
             
                  else
         
     | 
| 
       211 
     | 
    
         
            -
                     
     | 
| 
      
 262 
     | 
    
         
            +
                    Selection.new(self, block)
         
     | 
| 
       212 
263 
     | 
    
         
             
                  end
         
     | 
| 
       213 
264 
     | 
    
         
             
                end
         
     | 
| 
       214 
265 
     | 
    
         | 
    
        data/lib/hexp/node/attributes.rb
    CHANGED
    
    | 
         @@ -14,9 +14,10 @@ module Hexp 
     | 
|
| 
       14 
14 
     | 
    
         
             
                  #    H[:p, class: 'hello'].attr('id', 'para1') # => H[:p, {"class"=>"hello", "id"=>"para1"}]
         
     | 
| 
       15 
15 
     | 
    
         
             
                  #    H[:p, class: 'hello'].attr('class', nil)  # => H[:p]
         
     | 
| 
       16 
16 
     | 
    
         
             
                  #
         
     | 
| 
      
 17 
     | 
    
         
            +
                  # @param [Array<#to_s>] args
         
     | 
| 
       17 
18 
     | 
    
         
             
                  # @return [String|Hexp::Node]
         
     | 
| 
       18 
     | 
    
         
            -
                  # @api private
         
     | 
| 
       19 
19 
     | 
    
         
             
                  #
         
     | 
| 
      
 20 
     | 
    
         
            +
                  # @api private
         
     | 
| 
       20 
21 
     | 
    
         
             
                  def attr(*args)
         
     | 
| 
       21 
22 
     | 
    
         
             
                    arity     = args.count
         
     | 
| 
       22 
23 
     | 
    
         
             
                    attr_name = args[0].to_s
         
     | 
| 
         @@ -38,10 +39,12 @@ module Hexp 
     | 
|
| 
       38 
39 
     | 
    
         
             
                  # @example
         
     | 
| 
       39 
40 
     | 
    
         
             
                  #   H[:option].has_attr?('selected') #=> false
         
     | 
| 
       40 
41 
     | 
    
         
             
                  #
         
     | 
| 
       41 
     | 
    
         
            -
                  # @param  
     | 
| 
       42 
     | 
    
         
            -
                  #  
     | 
| 
       43 
     | 
    
         
            -
                  # @api public
         
     | 
| 
      
 42 
     | 
    
         
            +
                  # @param [String|Symbol] name
         
     | 
| 
      
 43 
     | 
    
         
            +
                  #   The name of the attribute
         
     | 
| 
       44 
44 
     | 
    
         
             
                  #
         
     | 
| 
      
 45 
     | 
    
         
            +
                  # @return [true,false]
         
     | 
| 
      
 46 
     | 
    
         
            +
                  #
         
     | 
| 
      
 47 
     | 
    
         
            +
                  # @api public
         
     | 
| 
       45 
48 
     | 
    
         
             
                  def has_attr?(name)
         
     | 
| 
       46 
49 
     | 
    
         
             
                    attributes.has_key? name.to_s
         
     | 
| 
       47 
50 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -51,10 +54,13 @@ module Hexp 
     | 
|
| 
       51 
54 
     | 
    
         
             
                  # @example
         
     | 
| 
       52 
55 
     | 
    
         
             
                  #   H[:span, class: "banner strong"].class?("strong") #=> true
         
     | 
| 
       53 
56 
     | 
    
         
             
                  #
         
     | 
| 
       54 
     | 
    
         
            -
                  # @param  
     | 
| 
       55 
     | 
    
         
            -
                  #  
     | 
| 
       56 
     | 
    
         
            -
                  # 
     | 
| 
      
 57 
     | 
    
         
            +
                  # @param [String] klass
         
     | 
| 
      
 58 
     | 
    
         
            +
                  #   The name of the class to check for
         
     | 
| 
      
 59 
     | 
    
         
            +
                  #
         
     | 
| 
      
 60 
     | 
    
         
            +
                  # @return [Boolean]
         
     | 
| 
      
 61 
     | 
    
         
            +
                  #   True if the class is present, false otherwise
         
     | 
| 
       57 
62 
     | 
    
         
             
                  #
         
     | 
| 
      
 63 
     | 
    
         
            +
                  # @api public
         
     | 
| 
       58 
64 
     | 
    
         
             
                  def class?(klass)
         
     | 
| 
       59 
65 
     | 
    
         
             
                    attr('class') && attr('class').split(' ').include?(klass.to_s)
         
     | 
| 
       60 
66 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -64,10 +70,12 @@ module Hexp 
     | 
|
| 
       64 
70 
     | 
    
         
             
                  # @example
         
     | 
| 
       65 
71 
     | 
    
         
             
                  #   H[:div].add_class('foo') #=> H[:div, class: 'foo']
         
     | 
| 
       66 
72 
     | 
    
         
             
                  #
         
     | 
| 
       67 
     | 
    
         
            -
                  # @param  
     | 
| 
      
 73 
     | 
    
         
            +
                  # @param [#to_s] klass
         
     | 
| 
      
 74 
     | 
    
         
            +
                  #   The class to add
         
     | 
| 
      
 75 
     | 
    
         
            +
                  #
         
     | 
| 
       68 
76 
     | 
    
         
             
                  # @return [Hexp::Node]
         
     | 
| 
       69 
     | 
    
         
            -
                  # @api public
         
     | 
| 
       70 
77 
     | 
    
         
             
                  #
         
     | 
| 
      
 78 
     | 
    
         
            +
                  # @api public
         
     | 
| 
       71 
79 
     | 
    
         
             
                  def add_class(klass)
         
     | 
| 
       72 
80 
     | 
    
         
             
                    attr('class', [attr('class'), klass].compact.join(' '))
         
     | 
| 
       73 
81 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -77,8 +85,8 @@ module Hexp 
     | 
|
| 
       77 
85 
     | 
    
         
             
                  # Convenience method so you don't have to split the class list yourself.
         
     | 
| 
       78 
86 
     | 
    
         
             
                  #
         
     | 
| 
       79 
87 
     | 
    
         
             
                  # @return [Array<String>]
         
     | 
| 
       80 
     | 
    
         
            -
                  # @api public
         
     | 
| 
       81 
88 
     | 
    
         
             
                  #
         
     | 
| 
      
 89 
     | 
    
         
            +
                  # @api public
         
     | 
| 
       82 
90 
     | 
    
         
             
                  def class_list
         
     | 
| 
       83 
91 
     | 
    
         
             
                    @class_list ||= (attr('class') || '').split(' ').freeze
         
     | 
| 
       84 
92 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -93,11 +101,12 @@ module Hexp 
     | 
|
| 
       93 
101 
     | 
    
         
             
                  # Calling this on a node with a class attribute that is equal to an
         
     | 
| 
       94 
102 
     | 
    
         
             
                  # empty string will result in the class attribute being removed.
         
     | 
| 
       95 
103 
     | 
    
         
             
                  #
         
     | 
| 
       96 
     | 
    
         
            -
                  # @param  
     | 
| 
       97 
     | 
    
         
            -
                  #  
     | 
| 
       98 
     | 
    
         
            -
                  # 
     | 
| 
       99 
     | 
    
         
            -
                  #  
     | 
| 
      
 104 
     | 
    
         
            +
                  # @param [#to_s] klass
         
     | 
| 
      
 105 
     | 
    
         
            +
                  #   The class to be removed
         
     | 
| 
      
 106 
     | 
    
         
            +
                  # @return [Hexp::Node]
         
     | 
| 
      
 107 
     | 
    
         
            +
                  #   A node that is identical to this one, but with the given class removed
         
     | 
| 
       100 
108 
     | 
    
         
             
                  #
         
     | 
| 
      
 109 
     | 
    
         
            +
                  # @api public
         
     | 
| 
       101 
110 
     | 
    
         
             
                  def remove_class(klass)
         
     | 
| 
       102 
111 
     | 
    
         
             
                    return self unless has_attr?('class')
         
     | 
| 
       103 
112 
     | 
    
         
             
                    new_list = class_list - [klass.to_s]
         
     | 
| 
         @@ -107,10 +116,11 @@ module Hexp 
     | 
|
| 
       107 
116 
     | 
    
         | 
| 
       108 
117 
     | 
    
         
             
                  # Set or override multiple attributes using a hash syntax
         
     | 
| 
       109 
118 
     | 
    
         
             
                  #
         
     | 
| 
       110 
     | 
    
         
            -
                  # @param  
     | 
| 
      
 119 
     | 
    
         
            +
                  # @param [Hash<#to_s,#to_s>] attrs
         
     | 
| 
      
 120 
     | 
    
         
            +
                  #
         
     | 
| 
       111 
121 
     | 
    
         
             
                  # @return [Hexp::Node]
         
     | 
| 
       112 
     | 
    
         
            -
                  # @api public
         
     | 
| 
       113 
122 
     | 
    
         
             
                  #
         
     | 
| 
      
 123 
     | 
    
         
            +
                  # @api public
         
     | 
| 
       114 
124 
     | 
    
         
             
                  def set_attrs(attrs)
         
     | 
| 
       115 
125 
     | 
    
         
             
                    H[
         
     | 
| 
       116 
126 
     | 
    
         
             
                      self.tag,
         
     | 
| 
         @@ -123,10 +133,13 @@ module Hexp 
     | 
|
| 
       123 
133 
     | 
    
         | 
| 
       124 
134 
     | 
    
         
             
                  # Remove an attribute by name
         
     | 
| 
       125 
135 
     | 
    
         
             
                  #
         
     | 
| 
       126 
     | 
    
         
            -
                  # @param  
     | 
| 
       127 
     | 
    
         
            -
                  #  
     | 
| 
       128 
     | 
    
         
            -
                  # 
     | 
| 
      
 136 
     | 
    
         
            +
                  # @param [#to_s] name
         
     | 
| 
      
 137 
     | 
    
         
            +
                  #   The attribute to be removed
         
     | 
| 
      
 138 
     | 
    
         
            +
                  #
         
     | 
| 
      
 139 
     | 
    
         
            +
                  # @return [Hexp::Node]
         
     | 
| 
      
 140 
     | 
    
         
            +
                  #   A new node with the attribute removed
         
     | 
| 
       129 
141 
     | 
    
         
             
                  #
         
     | 
| 
      
 142 
     | 
    
         
            +
                  # @api public
         
     | 
| 
       130 
143 
     | 
    
         
             
                  def remove_attr(name)
         
     | 
| 
       131 
144 
     | 
    
         
             
                    H[
         
     | 
| 
       132 
145 
     | 
    
         
             
                      self.tag,
         
     | 
| 
         @@ -137,10 +150,13 @@ module Hexp 
     | 
|
| 
       137 
150 
     | 
    
         | 
| 
       138 
151 
     | 
    
         
             
                  # Attribute accessor
         
     | 
| 
       139 
152 
     | 
    
         
             
                  #
         
     | 
| 
       140 
     | 
    
         
            -
                  # @ 
     | 
| 
       141 
     | 
    
         
            -
                  # 
     | 
| 
       142 
     | 
    
         
            -
                  # @api public
         
     | 
| 
      
 153 
     | 
    
         
            +
                  # @param_name [#to_s] attr
         
     | 
| 
      
 154 
     | 
    
         
            +
                  #   The name of the attribute
         
     | 
| 
       143 
155 
     | 
    
         
             
                  #
         
     | 
| 
      
 156 
     | 
    
         
            +
                  # @return [String]
         
     | 
| 
      
 157 
     | 
    
         
            +
                  #   The value of the attribute
         
     | 
| 
      
 158 
     | 
    
         
            +
                  #
         
     | 
| 
      
 159 
     | 
    
         
            +
                  # @api public
         
     | 
| 
       144 
160 
     | 
    
         
             
                  def [](attr_name)
         
     | 
| 
       145 
161 
     | 
    
         
             
                    self.attributes[attr_name.to_s]
         
     | 
| 
       146 
162 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -148,16 +164,17 @@ module Hexp 
     | 
|
| 
       148 
164 
     | 
    
         
             
                  # Merge attributes into this Hexp
         
     | 
| 
       149 
165 
     | 
    
         
             
                  #
         
     | 
| 
       150 
166 
     | 
    
         
             
                  # Class attributes are treated special : the class lists are merged, rather
         
     | 
| 
       151 
     | 
    
         
            -
                  # than being overwritten. See {set_attrs} for a more basic version.
         
     | 
| 
      
 167 
     | 
    
         
            +
                  # than being overwritten. See {#set_attrs} for a more basic version.
         
     | 
| 
       152 
168 
     | 
    
         
             
                  #
         
     | 
| 
       153 
     | 
    
         
            -
                  # This method is analoguous with  
     | 
| 
      
 169 
     | 
    
         
            +
                  # This method is analoguous with `Hash#merge`. As argument it can take a
         
     | 
| 
       154 
170 
     | 
    
         
             
                  # Hash, or another Hexp element, in which case that element's attributes
         
     | 
| 
       155 
171 
     | 
    
         
             
                  # are used.
         
     | 
| 
       156 
172 
     | 
    
         
             
                  #
         
     | 
| 
       157 
     | 
    
         
            -
                  # @ 
     | 
| 
      
 173 
     | 
    
         
            +
                  # @param_or_hash [#to_hexp|Hash] node
         
     | 
| 
      
 174 
     | 
    
         
            +
                  #
         
     | 
| 
       158 
175 
     | 
    
         
             
                  # @return [Hexp::Node]
         
     | 
| 
       159 
     | 
    
         
            -
                  # @api public
         
     | 
| 
       160 
176 
     | 
    
         
             
                  #
         
     | 
| 
      
 177 
     | 
    
         
            +
                  # @api public
         
     | 
| 
       161 
178 
     | 
    
         
             
                  def merge_attrs(node_or_hash)
         
     | 
| 
       162 
179 
     | 
    
         
             
                    hash = node_or_hash.respond_to?(:to_hexp) ?
         
     | 
| 
       163 
180 
     | 
    
         
             
                             node_or_hash.to_hexp.attributes : node_or_hash
         
     |