hexp 0.3.3 → 0.4.0.beta1
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 +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +8 -3
- data/Changelog.md +32 -1
- data/Gemfile +0 -5
- data/Rakefile +21 -15
- data/hexp.gemspec +6 -4
- data/lib/hexp.rb +5 -14
- data/lib/hexp/core_ext/nil.rb +9 -0
- data/lib/hexp/css_selector.rb +2 -1
- data/lib/hexp/h.rb +9 -1
- data/lib/hexp/list.rb +6 -1
- data/lib/hexp/node.rb +9 -2
- data/lib/hexp/node/attributes.rb +1 -1
- data/lib/hexp/node/children.rb +4 -2
- data/lib/hexp/node/normalize.rb +24 -28
- data/lib/hexp/nokogiri/reader.rb +1 -1
- data/lib/hexp/text_node.rb +6 -0
- data/lib/hexp/unparser.rb +73 -0
- data/lib/hexp/version.rb +1 -1
- data/spec/integration/literal_syntax_spec.rb +2 -2
- data/spec/shared_helper.rb +1 -1
- data/spec/unit/hexp/builder_spec.rb +2 -2
- data/spec/unit/hexp/css_selector/attribute_spec.rb +24 -24
- data/spec/unit/hexp/css_selector/class_spec.rb +3 -3
- data/spec/unit/hexp/css_selector/comma_sequence_spec.rb +1 -1
- data/spec/unit/hexp/css_selector/element_spec.rb +2 -2
- data/spec/unit/hexp/css_selector/simple_sequence_spec.rb +8 -8
- data/spec/unit/hexp/css_selector/universal_spec.rb +1 -1
- data/spec/unit/hexp/dsl_spec.rb +3 -3
- data/spec/unit/hexp/h_spec.rb +2 -2
- data/spec/unit/hexp/node/attributes_spec.rb +4 -4
- data/spec/unit/hexp/node/class_spec.rb +7 -7
- data/spec/unit/hexp/node/normalize_spec.rb +14 -6
- data/spec/unit/hexp/node/rewrite_spec.rb +1 -1
- data/spec/unit/hexp/node/text_spec.rb +1 -1
- data/spec/unit/hexp/node/to_dom_spec.rb +1 -1
- data/spec/unit/hexp/node/to_html_spec.rb +17 -1
- data/spec/unit/hexp/nokogiri/equality_spec.rb +6 -6
- data/spec/unit/hexp/text_node_spec.rb +2 -2
- metadata +62 -34
- data/Gemfile.devtools +0 -55
- data/Gemfile.lock +0 -186
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 7da79589fadbf74a0c0fa19ad9f409ca9f0dc855
         | 
| 4 | 
            +
              data.tar.gz: 65e4754146881f1d5a23df35ae4b4f7a964ad886
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 82271f2b2f56f71a71b2897b3668c7dbb2b8283a73df551d4ad6ae790e2cec806fb6901e6253c300f5bf1d2b7a4244bdbb0fde09cf3faf414df00d3fd6d1cc84
         | 
| 7 | 
            +
              data.tar.gz: fd56366470868b9c0df687b945e9da1ce17e2d94fbf81b4be38a454810ca684ba1c1d5bcfcee1f28d8fe80b45320b9072bea9b8ed1269b9e93bf1b3dce0272e0
         | 
    
        data/.gitignore
    CHANGED
    
    
    
        data/.travis.yml
    CHANGED
    
    | @@ -1,13 +1,18 @@ | |
| 1 | 
            +
            # Currently fails on JRuby, partly due to Nokogiri behaving differently,
         | 
| 2 | 
            +
            # and partly for some other reason I'm not sure about regarding
         | 
| 3 | 
            +
            # Hexp::Node::Normalize
         | 
| 1 4 | 
             
            language: ruby
         | 
| 2 5 | 
             
            script: "bundle exec rspec"
         | 
| 3 6 | 
             
            rvm:
         | 
| 4 | 
            -
              - 1.9.2
         | 
| 5 7 | 
             
              - 1.9.3
         | 
| 6 8 | 
             
              - 2.0.0
         | 
| 7 | 
            -
              - 2.1. | 
| 9 | 
            +
              - 2.1.1
         | 
| 10 | 
            +
              - 2.1.2
         | 
| 11 | 
            +
              - jruby
         | 
| 12 | 
            +
              - jruby-head
         | 
| 8 13 | 
             
              - ruby-head
         | 
| 9 14 | 
             
            matrix:
         | 
| 10 15 | 
             
              allow_failures:
         | 
| 11 | 
            -
                - rvm: jruby | 
| 16 | 
            +
                - rvm: jruby
         | 
| 12 17 | 
             
                - rvm: ruby-head
         | 
| 13 18 | 
             
                - rvm: jruby-head
         | 
    
        data/Changelog.md
    CHANGED
    
    | @@ -1,5 +1,36 @@ | |
| 1 1 | 
             
            ### Development
         | 
| 2 | 
            -
             | 
| 2 | 
            +
             | 
| 3 | 
            +
            [full diff](http://github.com/plexus/hexp/compare/v0.3.3...master)
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            ### v0.4.0.beta1
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            [full diff](http://github.com/plexus/hexp/compare/v0.3.3...v0.4.0.beta1)
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            * Do our own HTML unparsing, instead of going through Nokogiri,
         | 
| 10 | 
            +
              causing a big speed improvement.
         | 
| 11 | 
            +
            * Make H[] notation more lenient
         | 
| 12 | 
            +
              * Make array around list of children optional
         | 
| 13 | 
            +
                `H[:p, [H[:span, 'foo'], ' ', H[:span, 'bar']]]` =>
         | 
| 14 | 
            +
                `H[:p, H[:span, 'foo'], ' ', H[:span, 'bar']]`
         | 
| 15 | 
            +
              * Allow creating node lists without a wrapping node, e.g.
         | 
| 16 | 
            +
                `H[H[:span, 'foo'], ' ', H[:span, 'bar']]`
         | 
| 17 | 
            +
            * Make Hexp::List and Hexp::TextNode respond to to_html
         | 
| 18 | 
            +
            * Add Hexp::Node#tag? as a complement to Hexp::Node#text?
         | 
| 19 | 
            +
             | 
| 20 | 
            +
            ### v0.3.3
         | 
| 21 | 
            +
             | 
| 22 | 
            +
            [full diff](http://github.com/plexus/hexp/compare/v0.3.0...v0.3.3)
         | 
| 23 | 
            +
             | 
| 24 | 
            +
            * Bugfix regarding string values in attribute CSS selectors
         | 
| 25 | 
            +
            * Update dependencies
         | 
| 26 | 
            +
             | 
| 27 | 
            +
            ### v0.3.0
         | 
| 28 | 
            +
             | 
| 29 | 
            +
            [full diff](http://github.com/plexus/hexp/compare/v0.2.0...v0.3.0)
         | 
| 30 | 
            +
             | 
| 31 | 
            +
            * Improved CSS selector support
         | 
| 32 | 
            +
            * Handle CDATA sections when parsing through Nokogiri
         | 
| 33 | 
            +
            * Improved documentation
         | 
| 3 34 |  | 
| 4 35 | 
             
            ### v0.2.0
         | 
| 5 36 |  | 
    
        data/Gemfile
    CHANGED
    
    
    
        data/Rakefile
    CHANGED
    
    | @@ -1,22 +1,19 @@ | |
| 1 | 
            -
            require 'devtools'
         | 
| 2 1 | 
             
            require 'rubygems/package_task'
         | 
| 3 2 |  | 
| 4 | 
            -
            Devtools.init_rake_tasks
         | 
| 5 | 
            -
             | 
| 6 3 | 
             
            # Redefine rake:ci:metrics to disable rubocop, will tackle that laundry list
         | 
| 7 4 | 
             
            # some other time
         | 
| 8 | 
            -
            namespace :ci do
         | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 13 | 
            -
             | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 19 | 
            -
            end
         | 
| 5 | 
            +
            # namespace :ci do
         | 
| 6 | 
            +
            #   desc 'Run metrics (except mutant, rubocop) and spec'
         | 
| 7 | 
            +
            #   task travis: %w[
         | 
| 8 | 
            +
            #     metrics:coverage
         | 
| 9 | 
            +
            #     spec:integration
         | 
| 10 | 
            +
            #     metrics:yardstick:verify
         | 
| 11 | 
            +
            #     metrics:flog
         | 
| 12 | 
            +
            #     metrics:flay
         | 
| 13 | 
            +
            #   ]
         | 
| 14 | 
            +
            #   # metrics:reek
         | 
| 15 | 
            +
            #   # metrics:rubocop
         | 
| 16 | 
            +
            # end
         | 
| 20 17 |  | 
| 21 18 |  | 
| 22 19 | 
             
            spec = Gem::Specification.load(File.expand_path('../hexp.gemspec', __FILE__))
         | 
| @@ -45,3 +42,12 @@ task :doc2gh do | |
| 45 42 | 
             
              sh "git push origin gh-pages"
         | 
| 46 43 | 
             
              sh "git co master"
         | 
| 47 44 | 
             
            end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
            require 'mutant'
         | 
| 47 | 
            +
            task :default => :mutant
         | 
| 48 | 
            +
             | 
| 49 | 
            +
            task :mutant do
         | 
| 50 | 
            +
              pattern = ENV.fetch('PATTERN', 'Hexp*')
         | 
| 51 | 
            +
              result  = Mutant::CLI.run(%w[-Ilib -rhexp --use rspec --score 100] + [pattern])
         | 
| 52 | 
            +
              fail unless result == Mutant::CLI::EXIT_SUCCESS
         | 
| 53 | 
            +
            end
         | 
    
        data/hexp.gemspec
    CHANGED
    
    | @@ -19,10 +19,12 @@ Gem::Specification.new do |gem| | |
| 19 19 |  | 
| 20 20 | 
             
              gem.add_runtime_dependency 'sass', '~> 3.2.19'
         | 
| 21 21 | 
             
              gem.add_runtime_dependency 'nokogiri', '~> 1.6'
         | 
| 22 | 
            -
              gem.add_runtime_dependency ' | 
| 22 | 
            +
              gem.add_runtime_dependency 'adamantium', '~> 0.2'
         | 
| 23 23 | 
             
              gem.add_runtime_dependency 'equalizer', '~> 0.0'
         | 
| 24 24 |  | 
| 25 | 
            -
              gem.add_development_dependency 'rake' | 
| 26 | 
            -
              gem.add_development_dependency 'rspec' | 
| 27 | 
            -
              gem.add_development_dependency 'benchmark_suite' | 
| 25 | 
            +
              gem.add_development_dependency 'rake'
         | 
| 26 | 
            +
              gem.add_development_dependency 'rspec'
         | 
| 27 | 
            +
              gem.add_development_dependency 'benchmark_suite'
         | 
| 28 | 
            +
              gem.add_development_dependency 'mutant-rspec'
         | 
| 29 | 
            +
              gem.add_development_dependency 'rspec-its'
         | 
| 28 30 | 
             
            end
         | 
    
        data/lib/hexp.rb
    CHANGED
    
    | @@ -1,12 +1,15 @@ | |
| 1 1 | 
             
            require 'delegate'
         | 
| 2 2 | 
             
            require 'forwardable'
         | 
| 3 | 
            +
            require 'pathname'
         | 
| 3 4 |  | 
| 4 5 | 
             
            require 'nokogiri' # TODO => replace with Builder
         | 
| 5 6 | 
             
            require 'sass'
         | 
| 6 | 
            -
            require ' | 
| 7 | 
            +
            require 'adamantium'
         | 
| 7 8 | 
             
            require 'equalizer'
         | 
| 8 9 |  | 
| 9 10 | 
             
            module Hexp
         | 
| 11 | 
            +
              ROOT = Pathname(__FILE__).dirname.parent
         | 
| 12 | 
            +
             | 
| 10 13 | 
             
              # Inject the Hexp::DSL module into classes that include Hexp
         | 
| 11 14 | 
             
              #
         | 
| 12 15 | 
             
              # @param [Class] klazz
         | 
| @@ -19,19 +22,6 @@ module Hexp | |
| 19 22 | 
             
                klazz.send(:include, Hexp::DSL)
         | 
| 20 23 | 
             
              end
         | 
| 21 24 |  | 
| 22 | 
            -
              # Deep freeze an object
         | 
| 23 | 
            -
              #
         | 
| 24 | 
            -
              # Delegates to IceNine
         | 
| 25 | 
            -
              #
         | 
| 26 | 
            -
              # @param [Array] args
         | 
| 27 | 
            -
              #   arguments to pass on
         | 
| 28 | 
            -
              # @return [Object]
         | 
| 29 | 
            -
              #
         | 
| 30 | 
            -
              # @api private
         | 
| 31 | 
            -
              def self.deep_freeze(*args)
         | 
| 32 | 
            -
                IceNine.deep_freeze(*args)
         | 
| 33 | 
            -
              end
         | 
| 34 | 
            -
             | 
| 35 25 | 
             
              # Variant of ::Array with slightly modified semantics
         | 
| 36 26 | 
             
              #
         | 
| 37 27 | 
             
              # `Array()` is often used to wrap a value in an Array, unless it's already
         | 
| @@ -134,3 +124,4 @@ require 'hexp/sass/selector_parser' | |
| 134 124 | 
             
            require 'hexp/h'
         | 
| 135 125 |  | 
| 136 126 | 
             
            require 'hexp/builder'
         | 
| 127 | 
            +
            require 'hexp/unparser'
         | 
    
        data/lib/hexp/css_selector.rb
    CHANGED
    
    | @@ -4,6 +4,7 @@ module Hexp | |
| 4 4 | 
             
                #
         | 
| 5 5 | 
             
                module Members
         | 
| 6 6 | 
             
                  include Equalizer.new(:members)
         | 
| 7 | 
            +
                  include Adamantium
         | 
| 7 8 |  | 
| 8 9 | 
             
                  extend Forwardable
         | 
| 9 10 | 
             
                  def_delegator :@members, :empty?
         | 
| @@ -19,7 +20,7 @@ module Hexp | |
| 19 20 | 
             
                  #
         | 
| 20 21 | 
             
                  # @api private
         | 
| 21 22 | 
             
                  def initialize(members)
         | 
| 22 | 
            -
                    @members =  | 
| 23 | 
            +
                    @members = members
         | 
| 23 24 | 
             
                  end
         | 
| 24 25 |  | 
| 25 26 | 
             
                  # Create a class level collection constructor
         | 
    
        data/lib/hexp/h.rb
    CHANGED
    
    | @@ -1,5 +1,13 @@ | |
| 1 1 | 
             
            if defined?(::H) && ::H != Hexp::Node
         | 
| 2 2 | 
             
              $stderr.puts "WARN: H is already defined, Hexp H[] shorthand not available"
         | 
| 3 3 | 
             
            else
         | 
| 4 | 
            -
              H | 
| 4 | 
            +
              module H
         | 
| 5 | 
            +
                def self.[](*args)
         | 
| 6 | 
            +
                  if args.first.is_a? Symbol
         | 
| 7 | 
            +
                    Hexp::Node[*args]
         | 
| 8 | 
            +
                  else
         | 
| 9 | 
            +
                    Hexp::List[*args]
         | 
| 10 | 
            +
                  end
         | 
| 11 | 
            +
                end
         | 
| 12 | 
            +
              end
         | 
| 5 13 | 
             
            end
         | 
    
        data/lib/hexp/list.rb
    CHANGED
    
    | @@ -2,6 +2,7 @@ module Hexp | |
| 2 2 | 
             
              # A list of nodes
         | 
| 3 3 | 
             
              #
         | 
| 4 4 | 
             
              class List < DelegateClass(Array)
         | 
| 5 | 
            +
                include Adamantium
         | 
| 5 6 |  | 
| 6 7 | 
             
                # Create new Hexp::List
         | 
| 7 8 | 
             
                #
         | 
| @@ -13,7 +14,7 @@ module Hexp | |
| 13 14 | 
             
                #
         | 
| 14 15 | 
             
                # @api public
         | 
| 15 16 | 
             
                def initialize(nodes)
         | 
| 16 | 
            -
                  super nodes.to_ary.freeze
         | 
| 17 | 
            +
                  super nodes.to_ary.map(&Node::Normalize.method(:coerce_node)).freeze
         | 
| 17 18 | 
             
                end
         | 
| 18 19 |  | 
| 19 20 | 
             
                # Convenience constructor
         | 
| @@ -82,5 +83,9 @@ module Hexp | |
| 82 83 | 
             
                def eql?(other)
         | 
| 83 84 | 
             
                  self == other && self.class == other.class
         | 
| 84 85 | 
             
                end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                def to_html
         | 
| 88 | 
            +
                  each_with_object('') {|n,s| s << n.to_html}
         | 
| 89 | 
            +
                end
         | 
| 85 90 | 
             
              end
         | 
| 86 91 | 
             
            end
         | 
    
        data/lib/hexp/node.rb
    CHANGED
    
    | @@ -53,11 +53,14 @@ module Hexp | |
| 53 53 | 
             
              #
         | 
| 54 54 | 
             
              class Node
         | 
| 55 55 | 
             
                include Equalizer.new(:tag, :attributes, :children)
         | 
| 56 | 
            +
                include Adamantium
         | 
| 56 57 | 
             
                extend Forwardable
         | 
| 57 58 |  | 
| 58 59 | 
             
                include Hexp::Node::Attributes
         | 
| 59 60 | 
             
                include Hexp::Node::Children
         | 
| 60 61 |  | 
| 62 | 
            +
                memoize :class_list
         | 
| 63 | 
            +
             | 
| 61 64 | 
             
                # The HTML tag of this node
         | 
| 62 65 | 
             
                #
         | 
| 63 66 | 
             
                # @example
         | 
| @@ -146,7 +149,7 @@ module Hexp | |
| 146 149 | 
             
                # @api public
         | 
| 147 150 | 
             
                #
         | 
| 148 151 | 
             
                def to_html(options = {})
         | 
| 149 | 
            -
                   | 
| 152 | 
            +
                  Unparser.new(options).call(self)
         | 
| 150 153 | 
             
                end
         | 
| 151 154 |  | 
| 152 155 | 
             
                # Convert this node into a Nokogiri Document
         | 
| @@ -200,6 +203,10 @@ module Hexp | |
| 200 203 | 
             
                  false
         | 
| 201 204 | 
             
                end
         | 
| 202 205 |  | 
| 206 | 
            +
                def tag?(tag)
         | 
| 207 | 
            +
                  self.tag == tag
         | 
| 208 | 
            +
                end
         | 
| 209 | 
            +
             | 
| 203 210 | 
             
                # Return a new node, with a different tag
         | 
| 204 211 | 
             
                #
         | 
| 205 212 | 
             
                # @example
         | 
| @@ -318,7 +325,7 @@ module Hexp | |
| 318 325 | 
             
                  # @api private
         | 
| 319 326 | 
             
                  #
         | 
| 320 327 | 
             
                  def inspect_name
         | 
| 321 | 
            -
                    if defined?(H) | 
| 328 | 
            +
                    if defined?(H)
         | 
| 322 329 | 
             
                      'H'
         | 
| 323 330 | 
             
                    else
         | 
| 324 331 | 
             
                      self.name
         | 
    
        data/lib/hexp/node/attributes.rb
    CHANGED
    
    
    
        data/lib/hexp/node/children.rb
    CHANGED
    
    | @@ -72,9 +72,10 @@ module Hexp | |
| 72 72 | 
             
                  # @return [Hexp::Node]
         | 
| 73 73 | 
             
                  #
         | 
| 74 74 | 
             
                  # @api public
         | 
| 75 | 
            -
                  def  | 
| 76 | 
            -
                    H[tag, attributes,  | 
| 75 | 
            +
                  def content(*args)
         | 
| 76 | 
            +
                    H[tag, attributes, *args]
         | 
| 77 77 | 
             
                  end
         | 
| 78 | 
            +
                  alias set_children content
         | 
| 78 79 |  | 
| 79 80 | 
             
                  # Perform an action on each child node, and replace the node with the result
         | 
| 80 81 | 
             
                  #
         | 
| @@ -94,6 +95,7 @@ module Hexp | |
| 94 95 | 
             
                    return to_enum(:map_children) unless block_given?
         | 
| 95 96 | 
             
                    H[tag, attributes, children.map(&blk)]
         | 
| 96 97 | 
             
                  end
         | 
| 98 | 
            +
             | 
| 97 99 | 
             
                end
         | 
| 98 100 | 
             
              end
         | 
| 99 101 | 
             
            end
         | 
    
        data/lib/hexp/node/normalize.rb
    CHANGED
    
    | @@ -11,8 +11,8 @@ module Hexp | |
| 11 11 | 
             
                  #     Hexp::Node::Normalize.new([:p, {class:'foo'}])
         | 
| 12 12 | 
             
                  #
         | 
| 13 13 | 
             
                  # @api public
         | 
| 14 | 
            -
                  def initialize( | 
| 15 | 
            -
                    @raw =  | 
| 14 | 
            +
                  def initialize(args)
         | 
| 15 | 
            +
                    @raw = args
         | 
| 16 16 | 
             
                  end
         | 
| 17 17 |  | 
| 18 18 | 
             
                  # Normalize to strict hexp nodes, cfr SPEC.md for details
         | 
| @@ -56,14 +56,10 @@ module Hexp | |
| 56 56 | 
             
                  #
         | 
| 57 57 | 
             
                  # @api private
         | 
| 58 58 | 
             
                  def children
         | 
| 59 | 
            -
                     | 
| 60 | 
            -
                    if  | 
| 61 | 
            -
             | 
| 62 | 
            -
                     | 
| 63 | 
            -
                      []
         | 
| 64 | 
            -
                    else
         | 
| 65 | 
            -
                      [last]
         | 
| 66 | 
            -
                    end
         | 
| 59 | 
            +
                    children = @raw.drop(1)
         | 
| 60 | 
            +
                    children = children.drop(1)      if children.first.instance_of?(Hash)
         | 
| 61 | 
            +
                    children = children.first.to_ary if children.first.respond_to?(:to_ary)
         | 
| 62 | 
            +
                    children
         | 
| 67 63 | 
             
                  end
         | 
| 68 64 |  | 
| 69 65 | 
             
                  # Normalize the third element of a hexp node, the list of children
         | 
| @@ -72,24 +68,24 @@ module Hexp | |
| 72 68 | 
             
                  #
         | 
| 73 69 | 
             
                  # @api private
         | 
| 74 70 | 
             
                  def normalized_children
         | 
| 75 | 
            -
                    Hexp::List[*
         | 
| 76 | 
            -
             | 
| 77 | 
            -
             | 
| 78 | 
            -
             | 
| 79 | 
            -
             | 
| 80 | 
            -
             | 
| 81 | 
            -
             | 
| 82 | 
            -
             | 
| 83 | 
            -
             | 
| 84 | 
            -
             | 
| 85 | 
            -
             | 
| 86 | 
            -
             | 
| 87 | 
            -
             | 
| 88 | 
            -
             | 
| 89 | 
            -
             | 
| 90 | 
            -
             | 
| 91 | 
            -
                       | 
| 92 | 
            -
                     | 
| 71 | 
            +
                    Hexp::List[* children ]
         | 
| 72 | 
            +
                  end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                  def self.coerce_node(node)
         | 
| 75 | 
            +
                    case node
         | 
| 76 | 
            +
                    when Hexp::Node, Hexp::TextNode
         | 
| 77 | 
            +
                      node
         | 
| 78 | 
            +
                    when String
         | 
| 79 | 
            +
                      Hexp::TextNode.new(node)
         | 
| 80 | 
            +
                    when ->(ch) { ch.respond_to? :to_hexp }
         | 
| 81 | 
            +
                      response = node.to_hexp
         | 
| 82 | 
            +
                      raise FormatError, "to_hexp must return a Hexp::Node, got #{response.inspect}" unless response.instance_of?(Hexp::Node) || response.instance_of?(Hexp::TextNode)
         | 
| 83 | 
            +
                      response
         | 
| 84 | 
            +
                    when Array
         | 
| 85 | 
            +
                      Hexp::Node[*node]
         | 
| 86 | 
            +
                    else
         | 
| 87 | 
            +
                      raise FormatError, "Invalid value in Hexp literal : #{node.inspect} (#{node.class}) does not implement #to_hexp"
         | 
| 88 | 
            +
                    end
         | 
| 93 89 | 
             
                  end
         | 
| 94 90 | 
             
                end
         | 
| 95 91 |  | 
    
        data/lib/hexp/nokogiri/reader.rb
    CHANGED
    
    
    
        data/lib/hexp/text_node.rb
    CHANGED
    
    | @@ -7,6 +7,8 @@ module Hexp | |
| 7 7 | 
             
              # converted to `TextNode` instances, so there is usually no reason to instantiate
         | 
| 8 8 | 
             
              # these yourself.
         | 
| 9 9 | 
             
              class TextNode < DelegateClass(String)
         | 
| 10 | 
            +
                include Adamantium
         | 
| 11 | 
            +
             | 
| 10 12 | 
             
                # Inspect the TextNode
         | 
| 11 13 | 
             
                #
         | 
| 12 14 | 
             
                # This delegates to the underlying String, making it
         | 
| @@ -142,5 +144,9 @@ module Hexp | |
| 142 144 | 
             
                def select(&block)
         | 
| 143 145 | 
             
                  Node::Selection.new(self, block)
         | 
| 144 146 | 
             
                end
         | 
| 147 | 
            +
             | 
| 148 | 
            +
                def to_html(opts = {})
         | 
| 149 | 
            +
                  Unparser.new(opts).call(self)
         | 
| 150 | 
            +
                end
         | 
| 145 151 | 
             
              end
         | 
| 146 152 | 
             
            end
         | 
| @@ -0,0 +1,73 @@ | |
| 1 | 
            +
            module Hexp
         | 
| 2 | 
            +
              class Unparser
         | 
| 3 | 
            +
                APOS   = ?'.freeze
         | 
| 4 | 
            +
                QUOT   = ?".freeze
         | 
| 5 | 
            +
                LT     = '<'.freeze
         | 
| 6 | 
            +
                GT     = '>'.freeze
         | 
| 7 | 
            +
                SPACE  = ' '.freeze
         | 
| 8 | 
            +
                EQ     = '='.freeze
         | 
| 9 | 
            +
                AMP    = '&'.freeze
         | 
| 10 | 
            +
                FSLASH = '/'.freeze
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                E_AMP  = '&'.freeze
         | 
| 13 | 
            +
                E_APOS = '''.freeze
         | 
| 14 | 
            +
                E_QUOT = '"'.freeze
         | 
| 15 | 
            +
                E_LT   = '<'.freeze
         | 
| 16 | 
            +
                E_GT   = '>'.freeze
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                ESCAPE_ATTR_APOS = {AMP => E_AMP, APOS => E_APOS}
         | 
| 19 | 
            +
                ESCAPE_ATTR_QUOT = {AMP => E_AMP, QUOT => E_QUOT}
         | 
| 20 | 
            +
                ESCAPE_TEXT      = {AMP => E_AMP, APOS => E_APOS, QUOT => E_QUOT, LT => E_LT, GT => E_GT}
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                ESCAPE_ATTR_APOS_REGEX = Regexp.new("[#{ESCAPE_ATTR_APOS.keys.join}]")
         | 
| 23 | 
            +
                ESCAPE_ATTR_QUOT_REGEX = Regexp.new("[#{ESCAPE_ATTR_QUOT.keys.join}]")
         | 
| 24 | 
            +
                ESCAPE_TEXT_REGEX      = Regexp.new("[#{ESCAPE_TEXT.keys.join}]")
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                DEFAULT_OPTIONS = {
         | 
| 27 | 
            +
                  encoding: Encoding.default_external
         | 
| 28 | 
            +
                }
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                def initialize(options)
         | 
| 31 | 
            +
                  @options = DEFAULT_OPTIONS.merge(options)
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                def call(node)
         | 
| 35 | 
            +
                  @buffer = String.new.force_encoding(@options[:encoding])
         | 
| 36 | 
            +
                  add_node(node)
         | 
| 37 | 
            +
                  @buffer.freeze
         | 
| 38 | 
            +
                end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                private
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                def add_node(node)
         | 
| 43 | 
            +
                  if node.text?
         | 
| 44 | 
            +
                    @buffer << escape_text(node)
         | 
| 45 | 
            +
                  else
         | 
| 46 | 
            +
                    add_tag(node.tag, node.attributes, node.children)
         | 
| 47 | 
            +
                  end
         | 
| 48 | 
            +
                end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                def add_tag(tag, attrs, children)
         | 
| 51 | 
            +
                  @buffer << LT << tag.to_s
         | 
| 52 | 
            +
                  unless attrs.empty?
         | 
| 53 | 
            +
                    attrs.each {|k,v| add_attr(k,v)}
         | 
| 54 | 
            +
                  end
         | 
| 55 | 
            +
                  @buffer << GT
         | 
| 56 | 
            +
                  children.each(&method(:add_node))
         | 
| 57 | 
            +
                  @buffer << LT << FSLASH << tag.to_s << GT
         | 
| 58 | 
            +
                end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                def add_attr(key, value)
         | 
| 61 | 
            +
                  @buffer << SPACE << key << EQ
         | 
| 62 | 
            +
                  add_attr_value(value)
         | 
| 63 | 
            +
                end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                def add_attr_value(value)
         | 
| 66 | 
            +
                  @buffer << APOS << value.gsub(ESCAPE_ATTR_APOS_REGEX, ESCAPE_ATTR_APOS) << APOS
         | 
| 67 | 
            +
                end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                def escape_text(text)
         | 
| 70 | 
            +
                  text.gsub(ESCAPE_TEXT_REGEX, ESCAPE_TEXT)
         | 
| 71 | 
            +
                end
         | 
| 72 | 
            +
              end
         | 
| 73 | 
            +
            end
         |