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.
@@ -8,10 +8,26 @@ module Hexp
8
8
  class Rewriter
9
9
  include Hexp
10
10
 
11
+ # Initialize a rewriter with the node to operate on, and the action
12
+ #
13
+ # @param [Hexp::Node] node
14
+ # The root node of the tree to be altered
15
+ # @param [Proc] block
16
+ # The action to perform on each node
17
+ #
18
+ # @api public
11
19
  def initialize(node, block)
12
20
  @node, @block = node, block
13
21
  end
14
22
 
23
+ # Implicit Hexp conversion protocol
24
+ #
25
+ # A {Rewriter} is lazy, only when one of the {Hexp::DSL} methods is used,
26
+ # does the rewriting happen.
27
+ #
28
+ # @return [Hexp::Node]
29
+ #
30
+ # @api public
15
31
  def to_hexp
16
32
  @hexp ||= H[
17
33
  @node.tag,
@@ -24,16 +40,25 @@ module Hexp
24
40
 
25
41
  # Helper for rewrite
26
42
  #
27
- # @param blk [Proc] the block for rewriting
28
43
  # @return [Array<Hexp::Node>]
29
- # @api private
30
44
  #
45
+ # @api private
31
46
  def rewrite_children
32
47
  @node.children
33
- .flat_map {|child| child.rewrite &@block }
48
+ .flat_map {|child| child.rewrite(&@block) }
34
49
  .flat_map {|child| coerce_rewrite_response(@block.(child.to_hexp, @node)) || [child] }
35
50
  end
36
51
 
52
+ # Turn the response of a rewrite operation into something value
53
+ #
54
+ # The response can be a list of nodes, or a single node. If the response is
55
+ # `nil`, that is interpreted as removing the node.
56
+ #
57
+ # @param [nil,#to_hexp,#to_str,#to_ary] response
58
+ #
59
+ # @return [Array<Hexp::Node>]
60
+ #
61
+ # @api private
37
62
  def coerce_rewrite_response(response)
38
63
  return [] if response.nil?
39
64
 
@@ -1,6 +1,6 @@
1
1
  module Hexp
2
2
  class Node
3
- # Select nodes from a Hexp tree
3
+ # Subset of nodes from a Hexp tree
4
4
  #
5
5
  # This is what is backing the {Hexp::Node#select} method. It serves a double
6
6
  # purpose. At it's core it's an Enumerable for iterating over nodes that
@@ -17,13 +17,32 @@ module Hexp
17
17
  # # stick all links inside a <span class="link> ... </span>
18
18
  # hexp.select {|el| el.tag == 'a' }.wrap(:span, class: 'link')
19
19
  #
20
- class Selector
20
+ class Selection
21
21
  include Enumerable
22
22
 
23
+ # Initialize a selection with the root node, and the selection block used
24
+ # as the filtering criterion
25
+ #
26
+ # @param [Hexp::Node] node
27
+ # The root of the tree to select in
28
+ #
29
+ # @param [Proc] block
30
+ # A block that for a given node returns a truthy or falsey value
31
+ #
32
+ # @api private
23
33
  def initialize(node, block)
24
34
  @node, @select_block = node, block
25
35
  end
26
36
 
37
+ # Replace matching nodes
38
+ #
39
+ # Analogues to the main {Hexp::Node#rewrite} operation.
40
+ #
41
+ # @yieldparam [Hexp::Node]
42
+ #
43
+ # @return [Hexp::Node]
44
+ #
45
+ # @api public
27
46
  def rewrite(&block)
28
47
  @node.rewrite do |node, parent|
29
48
  if @select_block.(node)
@@ -34,18 +53,45 @@ module Hexp
34
53
  end
35
54
  end
36
55
 
56
+ # Set an attribute on all matching nodes
57
+ #
58
+ # @param [#to_s] name
59
+ # The attribute name
60
+ # @param [#to_s] value
61
+ # The attribute value
62
+ #
63
+ # @return [Hexp::Node]
64
+ # The new tree
65
+ #
66
+ # @api public
37
67
  def attr(name, value)
38
68
  rewrite do |node|
39
69
  node.attr(name, value)
40
70
  end
41
71
  end
42
72
 
73
+ # Wrap each matching node in a specific node
74
+ #
75
+ # @param [Symbol] tag
76
+ # The tag of the wrapping node
77
+ # @paeam [Hash] attributes
78
+ # The attributes of the node
79
+ #
80
+ # @return [Hexp::Node]
81
+ # The new tree of nodes
82
+ #
83
+ # @api public
43
84
  def wrap(tag, attributes = {})
44
85
  rewrite do |node|
45
86
  H[tag, attributes, [node]]
46
87
  end
47
88
  end
48
89
 
90
+ # Yield each matching node
91
+ #
92
+ # @yieldparam [Hexp::Node]
93
+ #
94
+ # @api public
49
95
  def each(&block)
50
96
  return to_enum(:each) unless block_given?
51
97
 
@@ -10,7 +10,7 @@ module Hexp
10
10
  # @api public
11
11
  #
12
12
  def call(node)
13
- return node.text if node.text?
13
+ return node.text if node.text? || node.cdata?
14
14
 
15
15
  unless node.attributes.empty?
16
16
  attrs = node.attributes.map do |key, value|
@@ -19,7 +19,7 @@ module Hexp
19
19
  attrs = Hash[attrs]
20
20
  end
21
21
 
22
- recurse = ->(node) { call(node) }
22
+ recurse = ->(next_node) { call(next_node) }
23
23
  H[node.name.to_sym, attrs, node.children.map(&recurse)]
24
24
  end
25
25
  end
@@ -140,7 +140,7 @@ module Hexp
140
140
  end
141
141
 
142
142
  def select(&block)
143
- Node::Selector.new(self, block)
143
+ Node::Selection.new(self, block)
144
144
  end
145
145
  end
146
146
  end
data/lib/hexp/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Hexp
2
- VERSION = '0.2.0'
2
+ VERSION = '0.3.0'
3
3
  end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hexp::CssSelector::Universal do
4
+ it 'should match everything' do
5
+ expect(subject.matches? H[:section]).to be_true
6
+ end
7
+ end
@@ -63,4 +63,16 @@ describe Hexp::Node::Domize do
63
63
 
64
64
  it { should_not dom_eq(dom) }
65
65
  end
66
+
67
+ context 'with the :html5 option' do
68
+ let(:hexp) { Hexp::Node[:html] }
69
+
70
+ it 'should set a HTML5 style doctype' do
71
+ dtd = hexp.to_dom(html5: true).children.first
72
+ expect(dtd).to be_a Nokogiri::XML::DTD
73
+ expect(dtd.name).to be_nil
74
+ expect(dtd.external_id).to be_nil
75
+ expect(dtd.system_id).to be_nil
76
+ end
77
+ end
66
78
  end
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Hexp::Node::Selector do
4
- subject(:selector) { Hexp::Node::Selector.new(hexp, block) }
3
+ describe Hexp::Node::Selection do
4
+ subject(:selection) { Hexp::Node::Selection.new(hexp, block) }
5
5
  let(:yielded_elements) { [] }
6
6
  let(:block) { proc {|el| yielded_elements << el } }
7
7
  let(:hexp) { H[:div, [[:span]]] }
@@ -10,7 +10,7 @@ describe Hexp::Node::Selector do
10
10
  let(:block) { proc {|el| el.tag == :span} }
11
11
 
12
12
  it 'should enumerate elements for which the block returns trueish' do
13
- expect(selector.to_a).to eq [H[:span]]
13
+ expect(selection.to_a).to eq [H[:span]]
14
14
  end
15
15
  end
16
16
 
@@ -18,7 +18,7 @@ describe Hexp::Node::Selector do
18
18
  let(:block) { proc {|el| el.tag == :span} }
19
19
 
20
20
  it 'should perform them on elements that match' do
21
- expect(selector.attr('class', 'matched').to_hexp).to eq(
21
+ expect(selection.attr('class', 'matched').to_hexp).to eq(
22
22
  H[:div, [[:span, {class: 'matched'}]]]
23
23
  )
24
24
  end
@@ -28,7 +28,7 @@ describe Hexp::Node::Selector do
28
28
  let(:block) { proc {|el| el.tag == :a} }
29
29
 
30
30
  it 'should be able to wrap element' do
31
- expect(selector.wrap(:li).to_hexp).to eq(
31
+ expect(selection.wrap(:li).to_hexp).to eq(
32
32
  H[:ul, [[:li, H[:a, href: 'foo']], [:li, H[:a, href: 'bar']]]]
33
33
  )
34
34
  end
@@ -39,7 +39,7 @@ describe Hexp::Node::Selector do
39
39
  let(:block) { proc {|el| el.tag == :a} }
40
40
 
41
41
  it 'should work on matching elements, and skip the rest' do
42
- expect(selector.rewrite{ H[:br] }.to_hexp).to eq H[:ul, [[:br], [:span]]]
42
+ expect(selection.rewrite{ H[:br] }.to_hexp).to eq H[:ul, [[:br], [:span]]]
43
43
  end
44
44
  end
45
45
  end
@@ -49,12 +49,12 @@ describe Hexp::Node::Selector do
49
49
 
50
50
  it 'should be lazy' do
51
51
  expect(block).to_not receive(:call)
52
- selector
52
+ selection
53
53
  end
54
54
 
55
55
  it 'should yield the root element when realized' do
56
56
  expect(block).to receive(:call).once.with(H[:div])
57
- selector.each {}
57
+ selection.each {}
58
58
  end
59
59
  end
60
60
 
@@ -65,7 +65,7 @@ describe Hexp::Node::Selector do
65
65
  [:span, "world"]]]}
66
66
 
67
67
  it 'should traverse the whole tree once, depth-first' do
68
- selector.each {}
68
+ selection.each {}
69
69
  expect(yielded_elements).to eq [
70
70
  Hexp::TextNode.new("hello"),
71
71
  H[:span, "hello"],
@@ -20,4 +20,14 @@ describe Hexp, 'parse' do
20
20
  it 'should parse attributes' do
21
21
  expect(Hexp.parse('<a href="pretty">Ciao Bella</a>')).to eq H[:a, {href: 'pretty'}, 'Ciao Bella']
22
22
  end
23
+
24
+ it 'should parse style tags' do
25
+ expect(Hexp.parse('<html><head><style type="text/css">h1 {font-weigth: 400;}</style></head></html>')).to eq(
26
+ H[:html,
27
+ H[:head,
28
+ H[:style, {type: 'text/css'}, 'h1 {font-weigth: 400;}']
29
+ ]
30
+ ]
31
+ )
32
+ end
23
33
  end
metadata CHANGED
@@ -1,112 +1,113 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hexp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
5
- prerelease:
4
+ version: 0.3.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Arne Brasseur
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-08-18 00:00:00.000000000 Z
11
+ date: 2014-01-28 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
14
+ prerelease: false
15
15
  name: sass
16
- type: :runtime
17
16
  requirement: !ruby/object:Gem::Requirement
18
- none: false
19
17
  requirements:
20
18
  - - ~>
21
19
  - !ruby/object:Gem::Version
22
20
  version: '3.2'
21
+ type: :runtime
23
22
  version_requirements: !ruby/object:Gem::Requirement
24
- none: false
25
23
  requirements:
26
24
  - - ~>
27
25
  - !ruby/object:Gem::Version
28
26
  version: '3.2'
29
- prerelease: false
30
27
  - !ruby/object:Gem::Dependency
28
+ prerelease: false
31
29
  name: nokogiri
32
- type: :runtime
33
30
  requirement: !ruby/object:Gem::Requirement
34
- none: false
35
31
  requirements:
36
32
  - - ~>
37
33
  - !ruby/object:Gem::Version
38
34
  version: '1.6'
35
+ type: :runtime
39
36
  version_requirements: !ruby/object:Gem::Requirement
40
- none: false
41
37
  requirements:
42
38
  - - ~>
43
39
  - !ruby/object:Gem::Version
44
40
  version: '1.6'
45
- prerelease: false
46
41
  - !ruby/object:Gem::Dependency
42
+ prerelease: false
47
43
  name: ice_nine
48
- type: :runtime
49
44
  requirement: !ruby/object:Gem::Requirement
50
- none: false
51
45
  requirements:
52
46
  - - ~>
53
47
  - !ruby/object:Gem::Version
54
- version: '0.8'
48
+ version: '0.9'
49
+ type: :runtime
55
50
  version_requirements: !ruby/object:Gem::Requirement
56
- none: false
57
51
  requirements:
58
52
  - - ~>
59
53
  - !ruby/object:Gem::Version
60
- version: '0.8'
61
- prerelease: false
54
+ version: '0.9'
62
55
  - !ruby/object:Gem::Dependency
56
+ prerelease: false
63
57
  name: equalizer
64
- type: :runtime
65
58
  requirement: !ruby/object:Gem::Requirement
66
- none: false
67
59
  requirements:
68
60
  - - ~>
69
61
  - !ruby/object:Gem::Version
70
62
  version: '0.0'
63
+ type: :runtime
71
64
  version_requirements: !ruby/object:Gem::Requirement
72
- none: false
73
65
  requirements:
74
66
  - - ~>
75
67
  - !ruby/object:Gem::Version
76
68
  version: '0.0'
77
- prerelease: false
78
69
  - !ruby/object:Gem::Dependency
70
+ prerelease: false
79
71
  name: rake
80
- type: :development
81
72
  requirement: !ruby/object:Gem::Requirement
82
- none: false
83
73
  requirements:
84
74
  - - ~>
85
75
  - !ruby/object:Gem::Version
86
76
  version: '10.1'
77
+ type: :development
87
78
  version_requirements: !ruby/object:Gem::Requirement
88
- none: false
89
79
  requirements:
90
80
  - - ~>
91
81
  - !ruby/object:Gem::Version
92
82
  version: '10.1'
93
- prerelease: false
94
83
  - !ruby/object:Gem::Dependency
84
+ prerelease: false
95
85
  name: rspec
96
- type: :development
97
86
  requirement: !ruby/object:Gem::Requirement
98
- none: false
99
87
  requirements:
100
88
  - - ~>
101
89
  - !ruby/object:Gem::Version
102
90
  version: '2.14'
91
+ type: :development
103
92
  version_requirements: !ruby/object:Gem::Requirement
104
- none: false
105
93
  requirements:
106
94
  - - ~>
107
95
  - !ruby/object:Gem::Version
108
96
  version: '2.14'
97
+ - !ruby/object:Gem::Dependency
109
98
  prerelease: false
99
+ name: benchmark_suite
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ~>
103
+ - !ruby/object:Gem::Version
104
+ version: '1.0'
105
+ type: :development
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ~>
109
+ - !ruby/object:Gem::Version
110
+ version: '1.0'
110
111
  description: HTML expressions
111
112
  email:
112
113
  - arne@arnebrasseur.net
@@ -117,14 +118,15 @@ extra_rdoc_files:
117
118
  files:
118
119
  - .gitignore
119
120
  - .travis.yml
121
+ - .yardopts
120
122
  - Changelog.md
121
123
  - Gemfile
122
124
  - Gemfile.devtools
123
125
  - Gemfile.lock
124
- - LICENSE.md
126
+ - LICENSE
125
127
  - README.md
126
128
  - Rakefile
127
- - SPEC.md
129
+ - bench/node/rewrite_bench.rb
128
130
  - config/devtools.yml
129
131
  - config/flay.yml
130
132
  - config/flog.yml
@@ -157,13 +159,12 @@ files:
157
159
  - lib/hexp/node/normalize.rb
158
160
  - lib/hexp/node/pp.rb
159
161
  - lib/hexp/node/rewriter.rb
160
- - lib/hexp/node/selector.rb
162
+ - lib/hexp/node/selection.rb
161
163
  - lib/hexp/nokogiri/equality.rb
162
164
  - lib/hexp/nokogiri/reader.rb
163
165
  - lib/hexp/sass/selector_parser.rb
164
166
  - lib/hexp/text_node.rb
165
167
  - lib/hexp/version.rb
166
- - notes
167
168
  - spec/integration/literal_syntax_spec.rb
168
169
  - spec/shared_helper.rb
169
170
  - spec/spec_helper.rb
@@ -174,6 +175,7 @@ files:
174
175
  - spec/unit/hexp/css_selector/element_spec.rb
175
176
  - spec/unit/hexp/css_selector/parser_spec.rb
176
177
  - spec/unit/hexp/css_selector/simple_sequence_spec.rb
178
+ - spec/unit/hexp/css_selector/universal_spec.rb
177
179
  - spec/unit/hexp/dsl_spec.rb
178
180
  - spec/unit/hexp/h_spec.rb
179
181
  - spec/unit/hexp/list_spec.rb
@@ -186,7 +188,7 @@ files:
186
188
  - spec/unit/hexp/node/normalize_spec.rb
187
189
  - spec/unit/hexp/node/pp_spec.rb
188
190
  - spec/unit/hexp/node/rewrite_spec.rb
189
- - spec/unit/hexp/node/selector_spec.rb
191
+ - spec/unit/hexp/node/selection_spec.rb
190
192
  - spec/unit/hexp/node/text_spec.rb
191
193
  - spec/unit/hexp/node/to_dom_spec.rb
192
194
  - spec/unit/hexp/node/to_hexp_spec.rb
@@ -199,33 +201,26 @@ files:
199
201
  homepage: https://github.com/plexus/hexp
200
202
  licenses:
201
203
  - MIT
204
+ metadata: {}
202
205
  post_install_message:
203
206
  rdoc_options: []
204
207
  require_paths:
205
208
  - lib
206
209
  required_ruby_version: !ruby/object:Gem::Requirement
207
- none: false
208
210
  requirements:
209
211
  - - ! '>='
210
212
  - !ruby/object:Gem::Version
211
213
  version: '0'
212
- segments:
213
- - 0
214
- hash: -1985616243020717322
215
214
  required_rubygems_version: !ruby/object:Gem::Requirement
216
- none: false
217
215
  requirements:
218
216
  - - ! '>='
219
217
  - !ruby/object:Gem::Version
220
218
  version: '0'
221
- segments:
222
- - 0
223
- hash: -1985616243020717322
224
219
  requirements: []
225
220
  rubyforge_project:
226
- rubygems_version: 1.8.23
221
+ rubygems_version: 2.2.1
227
222
  signing_key:
228
- specification_version: 3
223
+ specification_version: 4
229
224
  summary: HTML expressions
230
225
  test_files:
231
226
  - spec/integration/literal_syntax_spec.rb
@@ -238,6 +233,7 @@ test_files:
238
233
  - spec/unit/hexp/css_selector/element_spec.rb
239
234
  - spec/unit/hexp/css_selector/parser_spec.rb
240
235
  - spec/unit/hexp/css_selector/simple_sequence_spec.rb
236
+ - spec/unit/hexp/css_selector/universal_spec.rb
241
237
  - spec/unit/hexp/dsl_spec.rb
242
238
  - spec/unit/hexp/h_spec.rb
243
239
  - spec/unit/hexp/list_spec.rb
@@ -250,7 +246,7 @@ test_files:
250
246
  - spec/unit/hexp/node/normalize_spec.rb
251
247
  - spec/unit/hexp/node/pp_spec.rb
252
248
  - spec/unit/hexp/node/rewrite_spec.rb
253
- - spec/unit/hexp/node/selector_spec.rb
249
+ - spec/unit/hexp/node/selection_spec.rb
254
250
  - spec/unit/hexp/node/text_spec.rb
255
251
  - spec/unit/hexp/node/to_dom_spec.rb
256
252
  - spec/unit/hexp/node/to_hexp_spec.rb