hexp 0.0.1.pre3 → 0.0.1.pre4

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.
data/Gemfile CHANGED
@@ -8,4 +8,3 @@ group :development do
8
8
  gem 'json', '~> 1.8.0'
9
9
  eval File.read('Gemfile.devtools')
10
10
  end
11
-
@@ -1,3 +1,3 @@
1
1
  ---
2
2
  threshold: 7
3
- total_score: 75.0
3
+ total_score: 94.0
@@ -1,2 +1,2 @@
1
1
  ---
2
- threshold: 17.4
2
+ threshold: 29.4
@@ -51,7 +51,8 @@ NestedIterators:
51
51
  - any?
52
52
  NilCheck:
53
53
  enabled: true
54
- exclude: []
54
+ exclude:
55
+ - Hexp::Node#rewrite
55
56
  RepeatedConditional:
56
57
  enabled: true
57
58
  exclude: []
@@ -67,8 +68,10 @@ TooManyMethods:
67
68
  TooManyStatements:
68
69
  enabled: true
69
70
  exclude:
70
- - each
71
- max_statements: 4
71
+ - each
72
+ - Hexp::Node#rewrite
73
+ - Hexp::Node::Normalize#normalized_children
74
+ max_statements: 5
72
75
  UncommunicativeMethodName:
73
76
  enabled: true
74
77
  exclude: []
@@ -1,2 +1,2 @@
1
1
  ---
2
- threshold: 81
2
+ threshold: 77.5
@@ -0,0 +1,8 @@
1
+ module Hexp
2
+ # Raised when trying to stick things inside a Hexp where they don't belong
3
+ class FormatError < StandardError
4
+ def initialize(msg = 'You have illegal contents in your Hexp')
5
+ super
6
+ end
7
+ end
8
+ end
@@ -39,17 +39,77 @@ module Hexp
39
39
  end
40
40
 
41
41
  def inspect
42
- self.class.inspect_name + to_a.reject(&:empty?).inspect
43
- end
44
-
45
- def to_a
46
- [tag, attributes, children]
42
+ self.class.inspect_name + [tag, attributes, children ].reject(&:empty?).inspect
47
43
  end
48
44
 
49
45
  def pp
50
46
  self.class::PP.new(self).call
51
47
  end
52
48
 
49
+ # Rewrite a node tree. Since nodes are immutable, this is the main entry point
50
+ # for deriving nodes from others.
51
+ #
52
+ # Rewrite will pass you each node in the tree, and expects something to replace
53
+ # it with. A single node, multiple nodes, or no nodes (remove it).
54
+ #
55
+ # Rewrite will not pass in the root node, since rewrite always returns a single
56
+ # node, so it doesn't allow you to replace the root node with zero or multiple
57
+ # nodes.
58
+ #
59
+ # The block can take one or two parameters, the first being the node that is
60
+ # being replaced, the second (optional) its parent. As a response the block
61
+ # needs to return one of these
62
+ #
63
+ # * a single Hexp::Node
64
+ # * a Hexp::NodeList, or Array of Hexp::Node
65
+ # * an Array representation of a Hexp::Node, [:tag, {attributes}, [children]]
66
+ # * nil
67
+ #
68
+ # The last case (returning nil) means "do nothing", it will simply keep the
69
+ # currently referenced node where it is. This is very handy when you only want
70
+ # to act on certain nodes, just return nothing if you want to do nothing.
71
+ #
72
+ # Some examples :
73
+ #
74
+ # Remove all script tags
75
+ #
76
+ # tree.rewrite{|node| [] if node.tag == :script }
77
+ #
78
+ # Wrap each <input> tag into a <p> tag
79
+ #
80
+ # tree.rewrite do |node|
81
+ # if node.tag == :input
82
+ # [ H[:p, [ child ] ]
83
+ # end
84
+ # end
85
+ #
86
+ # @param blk [Proc] The rewrite action
87
+ # @return [Hexp::Node] The rewritten tree
88
+ def rewrite(&blk)
89
+ return to_enum(:rewrite) unless block_given?
90
+
91
+ H[self.tag,
92
+ self.attributes,
93
+ self.children.flat_map {|child| child.rewrite(&blk) }
94
+ .flat_map do |child|
95
+ response = blk.call(child, self)
96
+ if response.instance_of?(Hexp::Node)
97
+ [ response ]
98
+ elsif response.respond_to?(:to_ary)
99
+ if response.first.instance_of?(Symbol)
100
+ [ response ]
101
+ else
102
+ response
103
+ end
104
+ elsif response.nil?
105
+ [ child ]
106
+ else
107
+ raise FormatError, "invalid rewrite response : #{response.inspect}, expected Hexp::Node or Array, got #{response.class}"
108
+ end
109
+ end
110
+ ]
111
+ end
112
+
53
113
  class << self
54
114
  def inspect_name
55
115
  if defined?(H) && H == self
@@ -71,13 +71,17 @@ module Hexp
71
71
  Hexp::List[*
72
72
  children.map do |child|
73
73
  case child
74
+ when Hexp::Node
75
+ child
74
76
  when String, TextNode
75
77
  Hexp::TextNode.new(child)
76
78
  when Array
77
79
  Hexp::Node[*child]
78
80
  else
79
81
  if child.respond_to? :to_hexp
80
- Hexp::Node[*child.to_hexp]
82
+ response = child.to_hexp
83
+ raise FormatError, "to_hexp must return a Hexp::Node, got #{response.inspect}" unless response.instance_of?(Hexp::Node)
84
+ response
81
85
  end
82
86
  end
83
87
  end
@@ -1,3 +1,3 @@
1
1
  module Hexp
2
- VERSION = '0.0.1.pre3'
2
+ VERSION = '0.0.1.pre4'
3
3
  end
@@ -0,0 +1,130 @@
1
+ require 'spec_helper'
2
+
3
+ describe Hexp::Node, 'rewrite' do
4
+ subject { hexp.rewrite(&blk) }
5
+ let :hexp do
6
+ H[:div, [
7
+ [:a],
8
+ [:p],
9
+ [:br]
10
+ ]
11
+ ]
12
+ end
13
+
14
+ context 'without a block' do
15
+ let(:blk) { nil }
16
+
17
+ it 'returns an Enumerator' do
18
+ expect(subject).to be_instance_of(Enumerator)
19
+ end
20
+ end
21
+
22
+ context 'with a block that returns [child]' do
23
+ let(:blk) { proc {|child, parent| [child] } }
24
+
25
+ it 'should return an identical hexp' do
26
+ expect(subject).to eq(hexp)
27
+ end
28
+ end
29
+
30
+ context 'with multiple nestings' do
31
+ let :hexp do
32
+ H[:span, [super()]]
33
+ end
34
+
35
+ let :blk do
36
+ proc do |child, parent|
37
+ @tags << [child.tag, parent.tag]
38
+ end
39
+ end
40
+
41
+ it 'should traverse depth-first' do
42
+ @tags = []
43
+ subject
44
+ expect(@tags).to eq([[:a, :div], [:p, :div], [:br, :div], [:div, :span]])
45
+ end
46
+ end
47
+
48
+ context 'when adding nodes' do
49
+ let :blk do
50
+ proc do |child, parent|
51
+ raise 'got my own node back' if child.tag == :blockquote
52
+ # wrap paragraphs in a <blockquote>
53
+ if child.tag == :p
54
+ [:blockquote, [child]]
55
+ else
56
+ [child]
57
+ end
58
+ end
59
+ end
60
+
61
+ it 'should not pass those nodes again to the block' do
62
+ subject.should == H[:div, [
63
+ [:a],
64
+ [:blockquote, [
65
+ [:p]
66
+ ]
67
+ ],
68
+ [:br]
69
+ ]
70
+ ]
71
+ end
72
+ end
73
+
74
+ context 'with a one parameter block' do
75
+ let :hexp do
76
+ H[:parent, [[:child]]]
77
+ end
78
+
79
+ let :blk do
80
+ proc do |child|
81
+ expect(child).to eq(H[:child])
82
+ [child]
83
+ end
84
+ end
85
+
86
+ it 'should receive the child node as its argument' do
87
+ subject
88
+ end
89
+ end
90
+
91
+ describe 'block response types' do
92
+ context 'when responding with a single node' do
93
+ let :blk do
94
+ proc do |child|
95
+ H[:br]
96
+ end
97
+ end
98
+
99
+ it 'should replace the existing node' do
100
+ expect(subject).to eq H[:div, [ [:br] ]*3 ]
101
+ end
102
+ end
103
+
104
+ context 'when responding with an array that starts with a Symbol' do
105
+ let :blk do
106
+ proc do |child|
107
+ [:br, {class: 'foo'} ]
108
+ end
109
+ end
110
+
111
+ it 'should treat it as a node and replace the existing one' do
112
+ expect(subject).to eq H[:div, [ [:br, {'class' => 'foo'}] ]*3 ]
113
+ end
114
+ end
115
+
116
+ context 'when responding with nil' do
117
+ let :blk do
118
+ proc do |node|
119
+ [] if node.tag == :a
120
+ end
121
+ end
122
+
123
+ it 'should keep the original node' do
124
+ expect(subject).to eq H[:div, [ H[:p], H[:br] ]]
125
+ end
126
+ end
127
+
128
+ end
129
+
130
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hexp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1.pre3
4
+ version: 0.0.1.pre4
5
5
  prerelease: 6
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-06-07 00:00:00.000000000 Z
12
+ date: 2013-06-14 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: nokogiri
@@ -86,6 +86,7 @@ files:
86
86
  - hexp.gemspec
87
87
  - lib/hexp.rb
88
88
  - lib/hexp/dom.rb
89
+ - lib/hexp/format_error.rb
89
90
  - lib/hexp/h.rb
90
91
  - lib/hexp/list.rb
91
92
  - lib/hexp/node.rb
@@ -101,7 +102,7 @@ files:
101
102
  - spec/unit/hexp/node/domize_spec.rb
102
103
  - spec/unit/hexp/node/normalize_spec.rb
103
104
  - spec/unit/hexp/node/pp_spec.rb
104
- - spec/unit/hexp/node/to_a_spec.rb
105
+ - spec/unit/hexp/node/rewrite_spec.rb
105
106
  - spec/unit/hexp/node/to_dom_spec.rb
106
107
  - spec/unit/hexp/node/to_hexp_spec.rb
107
108
  - spec/unit/hexp/node/to_html_spec.rb
@@ -120,7 +121,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
120
121
  version: '0'
121
122
  segments:
122
123
  - 0
123
- hash: -105489159819991926
124
+ hash: 3015653127704886975
124
125
  required_rubygems_version: !ruby/object:Gem::Requirement
125
126
  none: false
126
127
  requirements:
@@ -140,7 +141,7 @@ test_files:
140
141
  - spec/unit/hexp/node/domize_spec.rb
141
142
  - spec/unit/hexp/node/normalize_spec.rb
142
143
  - spec/unit/hexp/node/pp_spec.rb
143
- - spec/unit/hexp/node/to_a_spec.rb
144
+ - spec/unit/hexp/node/rewrite_spec.rb
144
145
  - spec/unit/hexp/node/to_dom_spec.rb
145
146
  - spec/unit/hexp/node/to_hexp_spec.rb
146
147
  - spec/unit/hexp/node/to_html_spec.rb
@@ -1,8 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Hexp::Node, 'to_a' do
4
- subject { object.to_a }
5
- let(:object) { Hexp::Node.new(:p, class: 'foo') }
6
-
7
- it { should == [:p, {'class' => 'foo'}, Hexp::List[]] }
8
- end