hexp 0.0.1.pre3 → 0.0.1.pre4

Sign up to get free protection for your applications and to get access to all the features.
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