undies 2.2.0 → 2.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +4 -1
- data/README.rdoc +22 -1
- data/bench/bench_runner.rb +42 -61
- data/lib/undies/element.rb +51 -32
- data/lib/undies/node.rb +62 -11
- data/lib/undies/node_stack.rb +111 -0
- data/lib/undies/output.rb +9 -32
- data/lib/undies/template.rb +83 -25
- data/lib/undies/version.rb +1 -1
- data/test/element_test.rb +69 -48
- data/test/fixtures/write_thing.rb +21 -0
- data/test/node_stack_test.rb +109 -0
- data/test/node_test.rb +66 -16
- data/test/output_test.rb +13 -73
- data/test/template_builder_render_test.rb +32 -0
- data/test/template_source_render_test.rb +115 -0
- data/test/template_test.rb +149 -167
- data/undies.gemspec +1 -0
- metadata +32 -11
- data/lib/undies/node_buffer.rb +0 -35
- data/test/node_buffer_test.rb +0 -54
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
undies (2.2.
|
4
|
+
undies (2.2.1)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: http://rubygems.org/
|
@@ -15,6 +15,8 @@ GEM
|
|
15
15
|
erubis (2.7.0)
|
16
16
|
rake (0.9.2)
|
17
17
|
ruby-prof (0.10.8)
|
18
|
+
whysoslow (0.0.2)
|
19
|
+
ansi (~> 1.4)
|
18
20
|
|
19
21
|
PLATFORMS
|
20
22
|
ruby
|
@@ -27,3 +29,4 @@ DEPENDENCIES
|
|
27
29
|
rake (~> 0.9.2)
|
28
30
|
ruby-prof
|
29
31
|
undies!
|
32
|
+
whysoslow (~> 0.0)
|
data/README.rdoc
CHANGED
@@ -153,9 +153,30 @@ Now just interact with the Undies API directly.
|
|
153
153
|
*Note:* there is one extra caveat to be aware of using this approach. You need to be sure and flush the template when content processing is complete. Just pass the template to the Undies::Template#flush method:
|
154
154
|
|
155
155
|
# ensures all content is streamed to the template's output stream
|
156
|
-
# this is
|
156
|
+
# this is necessary when not using the source approach above
|
157
157
|
Undies::Template.flush(template)
|
158
158
|
|
159
|
+
=== Manual approach
|
160
|
+
|
161
|
+
There is another method you can use to render output: the Manual approach. Like the Builder approach, this method is ideal when you don't know the source until render time. The key difference is that blocks are not used to imply nesting relationships. Using this approach, you manually 'push' and 'pop' to move up and down nesting relationship contexts. So a push on an element would move the template context to the element pushed. A pop would move back to the current context's parent element. As you would expect, pop'ing on the root of a template has no effect on the context and pushing a non-element node has no effect on the context.
|
162
|
+
|
163
|
+
To render using this approach, create a Template as you would with the Builder approach. Interact with the Undies API directly. Use the Template#push and Template#pop class methods to change the template scope.
|
164
|
+
|
165
|
+
# this is the equivalent to the Builder approach example above
|
166
|
+
|
167
|
+
template = Undies::Template.new(Undies::Output.new(@io))
|
168
|
+
something = "Some Thing!"
|
169
|
+
current = template._div.something!
|
170
|
+
|
171
|
+
template.__push(current)
|
172
|
+
template._ something.to_s
|
173
|
+
template.__pop
|
174
|
+
|
175
|
+
# alternate method for flushing a template
|
176
|
+
template.__flush
|
177
|
+
|
178
|
+
*Note:* as with the Builder approach, you must flush the template when content processing is complete.
|
179
|
+
|
159
180
|
== License
|
160
181
|
|
161
182
|
Copyright (c) 2011-Present Kelly Redding
|
data/bench/bench_runner.rb
CHANGED
@@ -1,118 +1,99 @@
|
|
1
|
-
require '
|
1
|
+
require 'whysoslow'
|
2
2
|
require 'stringio'
|
3
|
-
require 'ansi'
|
4
3
|
|
5
|
-
require 'undies'
|
6
4
|
require 'erb'
|
7
5
|
require 'erubis'
|
6
|
+
require 'undies'
|
8
7
|
|
9
|
-
class
|
8
|
+
class UndiesBenchResults
|
10
9
|
|
11
|
-
|
12
|
-
attr_accessor :name, :ext, :size, :out, :outstream
|
10
|
+
attr_accessor :name, :ext, :size
|
13
11
|
|
14
|
-
def initialize(name, ext, size)
|
12
|
+
def initialize(name, ext, size, build)
|
15
13
|
@name = name.to_s
|
16
14
|
@ext = ext.to_s
|
17
15
|
@size = size.to_s
|
18
|
-
@
|
19
|
-
|
16
|
+
@build = build
|
17
|
+
|
18
|
+
@printer = Whysoslow::DefaultPrinter.new({
|
19
|
+
:title => "#{@name}, #{@size}",
|
20
|
+
:verbose => true
|
21
|
+
})
|
22
|
+
@runner = Whysoslow::Runner.new(@printer)
|
20
23
|
end
|
21
24
|
|
22
25
|
def file
|
23
26
|
File.expand_path("bench/#{@size}.html#{@ext}")
|
24
27
|
end
|
25
28
|
|
26
|
-
def
|
27
|
-
|
28
|
-
def total=(value_in_secs); @total = value_in_secs.to_f * 1000; end
|
29
|
-
def real=(value_in_secs); @real = value_in_secs.to_f * 1000; end
|
30
|
-
|
31
|
-
def to_s(meas=:real, basis=nil)
|
32
|
-
"#{name_s} (#{size_s}): #{time_s(meas)} ms #{"(#{basis_s(meas, basis)})" if basis}"
|
29
|
+
def run
|
30
|
+
@runner.run &@build
|
33
31
|
end
|
34
32
|
|
35
|
-
|
36
|
-
|
37
|
-
def measure(&block)
|
38
|
-
Benchmark.measure(&block).to_s.strip.gsub(/[^\s|0-9|\.]/, '').split(/\s+/).tap do |values|
|
39
|
-
self.user, self.system, self.total, self.real = values
|
40
|
-
end
|
41
|
-
end
|
33
|
+
end
|
42
34
|
|
43
|
-
def name_s; @name.ljust(6); end
|
44
|
-
def size_s; @size.to_s; end
|
45
|
-
def time_s(meas); self.send(meas).to_s.rjust(7); end
|
46
|
-
|
47
|
-
def basis_s(meas, time)
|
48
|
-
diff = (time - self.send(meas))
|
49
|
-
perc = ((diff / time) * 100).round
|
50
|
-
if diff >= 0
|
51
|
-
ANSI.green + "+#{diff} ms, +#{perc}%" + ANSI.reset
|
52
|
-
else
|
53
|
-
ANSI.red + "#{diff} ms, #{perc}%" + ANSI.reset
|
54
|
-
end
|
55
|
-
end
|
56
35
|
|
57
|
-
end
|
58
36
|
|
59
|
-
class UndiesResults <
|
37
|
+
class UndiesResults < UndiesBenchResults
|
60
38
|
|
61
39
|
def initialize(size='large')
|
62
|
-
|
63
|
-
|
40
|
+
@outstream = StringIO.new(@out = "")
|
41
|
+
super(:undies, '.rb', size, Proc.new do
|
64
42
|
Undies::Template.new(
|
65
43
|
Undies::Source.new(self.file),
|
66
44
|
{},
|
67
|
-
Undies::Output.new(
|
45
|
+
Undies::Output.new(@outstream, :pp => 2)
|
68
46
|
)
|
69
|
-
end
|
47
|
+
end)
|
70
48
|
end
|
71
49
|
|
72
50
|
end
|
73
51
|
|
74
|
-
class ErbResults <
|
52
|
+
class ErbResults < UndiesBenchResults
|
75
53
|
|
76
54
|
def initialize(size='large')
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
end
|
55
|
+
@out = ""
|
56
|
+
super(:erb, '.erb', size, Proc.new do
|
57
|
+
@out = ERB.new(File.read(self.file), 0, "%<>").result(binding)
|
58
|
+
end)
|
81
59
|
end
|
82
60
|
|
83
61
|
end
|
84
62
|
|
85
|
-
class ErubisResults <
|
63
|
+
class ErubisResults < UndiesBenchResults
|
86
64
|
|
87
65
|
def initialize(size='large')
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
end
|
66
|
+
@out = ""
|
67
|
+
super(:erubis, '.erb', size, Proc.new do
|
68
|
+
@out = Erubis::Eruby.new(File.read(self.file)).result(binding)
|
69
|
+
end)
|
92
70
|
end
|
93
71
|
|
94
72
|
end
|
95
73
|
|
74
|
+
|
75
|
+
|
96
76
|
class UndiesBenchRunner
|
97
77
|
|
98
78
|
SIZES = {
|
99
|
-
:small
|
100
|
-
:large
|
79
|
+
# :small => "~20 nodes",
|
80
|
+
# :large => "~2000 nodes",
|
101
81
|
:verylarge => "~20000 nodes"
|
102
82
|
}
|
103
83
|
|
84
|
+
|
104
85
|
def initialize
|
105
86
|
puts "Benchmark Results:"
|
106
87
|
puts
|
107
|
-
|
108
|
-
|
109
|
-
puts
|
110
|
-
|
111
|
-
puts
|
112
|
-
|
113
|
-
puts ErubisResults.new(size).to_s(:real, basis.real)
|
88
|
+
SIZES.each do |size, desc|
|
89
|
+
ErbResults.new(size).run
|
90
|
+
puts
|
91
|
+
ErubisResults.new(size).run
|
92
|
+
puts
|
93
|
+
UndiesResults.new(size).run
|
114
94
|
puts
|
115
95
|
end
|
96
|
+
puts
|
116
97
|
end
|
117
98
|
|
118
99
|
end
|
data/lib/undies/element.rb
CHANGED
@@ -29,35 +29,59 @@ module Undies
|
|
29
29
|
gsub('"', '"')
|
30
30
|
end
|
31
31
|
|
32
|
-
def self.
|
33
|
-
|
32
|
+
def self.set_children(element, value)
|
33
|
+
element.instance_variable_set("@children", value)
|
34
34
|
end
|
35
35
|
|
36
|
-
def self.
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
36
|
+
def self.children(element)
|
37
|
+
element.instance_variable_get("@children")
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.prefix(element, meth, level, indent)
|
41
|
+
"".tap do |value|
|
42
|
+
if indent > 0
|
43
|
+
if meth == 'start_tag'
|
44
|
+
value << "#{level > 0 ? "\n": ''}#{' '*level*indent}"
|
45
|
+
elsif meth == 'end_tag'
|
46
|
+
value << "\n#{' '*level*indent}" if children(element)
|
47
|
+
end
|
48
|
+
end
|
45
49
|
end
|
46
|
-
output << element.instance_variable_get("@end_tag") if element.instance_variable_get("@end_tag")
|
47
|
-
output.pp_use_indent = true
|
48
50
|
end
|
49
51
|
|
50
|
-
def
|
52
|
+
def self.set_start_tag(element)
|
53
|
+
element.instance_variable_set(
|
54
|
+
"@start_tag",
|
55
|
+
"<#{node_name(element)}#{hash_attrs(attrs(element))}" + (builds(element).size > 0 ? ">" : " />")
|
56
|
+
)
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.set_end_tag(element)
|
60
|
+
element.instance_variable_set(
|
61
|
+
"@end_tag",
|
62
|
+
builds(element).size > 0 ? "</#{node_name(element)}>" : nil
|
63
|
+
)
|
64
|
+
end
|
65
|
+
|
66
|
+
def initialize(name, attrs={}, &build)
|
67
|
+
super(nil)
|
68
|
+
@start_tag = ""
|
69
|
+
@end_tag = ""
|
70
|
+
@content = nil
|
71
|
+
@builds = []
|
72
|
+
@children = false
|
73
|
+
|
51
74
|
if !attrs.kind_of?(::Hash)
|
52
75
|
raise ArgumentError, "#{name.inspect} attrs must be provided as a Hash."
|
53
76
|
end
|
54
77
|
|
55
78
|
@name = name.to_s
|
56
79
|
@attrs = attrs
|
57
|
-
@
|
58
|
-
|
59
|
-
|
60
|
-
|
80
|
+
@builds << build if build
|
81
|
+
|
82
|
+
# cache in an instance variable for fast access with flush and pop
|
83
|
+
self.class.set_start_tag(self)
|
84
|
+
self.class.set_end_tag(self)
|
61
85
|
end
|
62
86
|
|
63
87
|
# CSS proxy methods ============================================
|
@@ -89,35 +113,30 @@ module Undies
|
|
89
113
|
|
90
114
|
def ==(other)
|
91
115
|
other.instance_variable_get("@name") == @name &&
|
92
|
-
other.instance_variable_get("@attrs") == @attrs
|
93
|
-
other.instance_variable_get("@nodes") == @nodes
|
116
|
+
other.instance_variable_get("@attrs") == @attrs
|
94
117
|
end
|
95
118
|
|
96
119
|
# overriding this because the base Node class defines a 'to_s' method that
|
97
120
|
# needs to be honored
|
98
121
|
def to_str(*args)
|
99
122
|
"Undies::Element:#{self.object_id} " +
|
100
|
-
"@name=#{@name.inspect}, @attrs=#{@attrs.inspect}
|
123
|
+
"@name=#{@name.inspect}, @attrs=#{@attrs.inspect}"
|
101
124
|
end
|
102
125
|
alias_method :inspect, :to_str
|
103
126
|
|
104
127
|
private
|
105
128
|
|
106
|
-
def proxy(value, attrs,
|
129
|
+
def proxy(value, attrs, build)
|
107
130
|
yield value if block_given?
|
108
131
|
@attrs.merge!(attrs)
|
109
|
-
@
|
110
|
-
@start_tag = start_tag
|
111
|
-
@end_tag = end_tag
|
112
|
-
self
|
113
|
-
end
|
132
|
+
@builds << build if build
|
114
133
|
|
115
|
-
|
116
|
-
|
117
|
-
|
134
|
+
# cache in an instance variable for fast access with flush and pop
|
135
|
+
self.class.set_start_tag(self)
|
136
|
+
self.class.set_end_tag(self)
|
118
137
|
|
119
|
-
|
120
|
-
|
138
|
+
# return self so you can chain proxy method calls
|
139
|
+
self
|
121
140
|
end
|
122
141
|
|
123
142
|
end
|
data/lib/undies/node.rb
CHANGED
@@ -2,28 +2,79 @@ module Undies
|
|
2
2
|
|
3
3
|
class Node
|
4
4
|
|
5
|
-
# have as many methods to the class level as
|
5
|
+
# have as many methods to the class level as possible to keep from
|
6
6
|
# polluting the public instance methods and to maximize the effectiveness
|
7
7
|
# of the Element#method_missing logic
|
8
8
|
|
9
|
+
def self.start_tag(node)
|
10
|
+
node.instance_variable_get("@start_tag") || ""
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.end_tag(node)
|
14
|
+
node.instance_variable_get("@end_tag") || ""
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.set_start_tag(node); end
|
18
|
+
def self.set_end_tag(node); end
|
19
|
+
|
20
|
+
def self.builds(node)
|
21
|
+
node.instance_variable_get("@builds") || []
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.add_build(node, build_block)
|
25
|
+
node.instance_variable_set("@builds", builds(node) + [build_block])
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.children(node)
|
29
|
+
node.instance_variable_get("@children")
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.set_children(node, value)
|
33
|
+
node.instance_variable_set("@children", value)
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.attrs(element)
|
37
|
+
element.instance_variable_get("@attrs")
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.merge_attrs(element, value={})
|
41
|
+
attrs(element).merge(value).tap do |a|
|
42
|
+
element.instance_variable_set("@attrs", a)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.node_name(node)
|
47
|
+
node.instance_variable_get("@name") || ""
|
48
|
+
end
|
49
|
+
|
9
50
|
def self.content(node)
|
10
|
-
node.instance_variable_get("@content")
|
51
|
+
node.instance_variable_get("@content") || ""
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.mode(node)
|
55
|
+
node.instance_variable_get("@mode") || :inline
|
11
56
|
end
|
12
57
|
|
13
|
-
def self.
|
14
|
-
|
15
|
-
|
58
|
+
def self.prefix(node, meth, level, indent)
|
59
|
+
"".tap do |value|
|
60
|
+
if mode(node) != :inline && indent > 0
|
61
|
+
if meth == 'start_tag'
|
62
|
+
value << "#{level > 0 ? "\n": ''}#{' '*level*indent}"
|
63
|
+
elsif meth == 'end_tag'
|
64
|
+
value << "\n#{' '*(level > 0 ? level-1 : level)*indent}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
16
68
|
end
|
17
69
|
|
18
|
-
def initialize(content,
|
70
|
+
def initialize(content, mode=:inline)
|
19
71
|
@start_tag = nil
|
20
72
|
@end_tag = nil
|
21
|
-
@force_pp = opts[:force_pp]
|
22
73
|
@content = content
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
74
|
+
@builds = []
|
75
|
+
@attrs = {}
|
76
|
+
@children = false
|
77
|
+
@mode = mode
|
27
78
|
end
|
28
79
|
|
29
80
|
def ==(other_node)
|
@@ -0,0 +1,111 @@
|
|
1
|
+
module Undies
|
2
|
+
class NodeStack
|
3
|
+
|
4
|
+
# the node stack handles caching nodes for processing, running node
|
5
|
+
# builds, and buffering node output for writing. I want to treat this
|
6
|
+
# as a stack of nodes for the template API to reference. I need to push
|
7
|
+
# a node onto the stack, reference it using the 'current' method,
|
8
|
+
# and pop it off the stack when I'm done.
|
9
|
+
|
10
|
+
# the stack first caches new nodes before pushing them onto the stack
|
11
|
+
# and node output is buffered as new nodes are pushed
|
12
|
+
|
13
|
+
def self.create(output)
|
14
|
+
output.kind_of?(NodeStack) ? output : NodeStack.new(output)
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_reader :stack, :output, :buffer
|
18
|
+
attr_accessor :cached_node
|
19
|
+
|
20
|
+
def initialize(output)
|
21
|
+
@stack = []
|
22
|
+
@cached_node = nil
|
23
|
+
@output = output
|
24
|
+
@written_level = 0
|
25
|
+
end
|
26
|
+
|
27
|
+
def empty?; @stack.empty?; end
|
28
|
+
def size; @stack.size; end
|
29
|
+
def first; @stack.first; end
|
30
|
+
def last; @stack.last; end
|
31
|
+
|
32
|
+
alias_method :current, :last
|
33
|
+
alias_method :level, :size
|
34
|
+
|
35
|
+
def push(node)
|
36
|
+
if current
|
37
|
+
current.class.set_children(current, node.kind_of?(Element))
|
38
|
+
end
|
39
|
+
if @written_level < level
|
40
|
+
open(current)
|
41
|
+
end
|
42
|
+
@stack.push(node)
|
43
|
+
end
|
44
|
+
|
45
|
+
def pop(*args)
|
46
|
+
if !empty?
|
47
|
+
node = if @written_level < level
|
48
|
+
@stack.pop.tap { |node| write(node) }
|
49
|
+
else
|
50
|
+
@stack.pop.tap { |node| close(node) }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def node(node)
|
56
|
+
clear_cached
|
57
|
+
self.cached_node = node
|
58
|
+
end
|
59
|
+
|
60
|
+
def flush
|
61
|
+
clear_cached
|
62
|
+
end
|
63
|
+
|
64
|
+
def clear_cached
|
65
|
+
node_to_push, self.cached_node = self.cached_node, nil
|
66
|
+
if node_to_push
|
67
|
+
push(node_to_push)
|
68
|
+
node_to_push.class.builds(node_to_push).each { |build| build.call }
|
69
|
+
clear_cached
|
70
|
+
pop
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def open(node)
|
77
|
+
start_tag(node, @written_level)
|
78
|
+
@written_level += 1
|
79
|
+
content(node, @written_level)
|
80
|
+
end
|
81
|
+
|
82
|
+
def write(node)
|
83
|
+
start_tag(node, @written_level)
|
84
|
+
content(node, @written_level+1)
|
85
|
+
end_tag(node, @written_level)
|
86
|
+
end
|
87
|
+
|
88
|
+
def close(node)
|
89
|
+
@written_level -= 1
|
90
|
+
end_tag(node, @written_level)
|
91
|
+
end
|
92
|
+
|
93
|
+
# TODO: Fill up an output write buffer with arg arrays
|
94
|
+
# then empty it
|
95
|
+
|
96
|
+
def start_tag(node, level)
|
97
|
+
@output.write(node, 'start_tag', level)
|
98
|
+
end
|
99
|
+
|
100
|
+
def content(node, level)
|
101
|
+
@output.write(node, 'content', level)
|
102
|
+
end
|
103
|
+
|
104
|
+
def end_tag(node, level)
|
105
|
+
@output.write(node, 'end_tag', level)
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
|
111
|
+
end
|
data/lib/undies/output.rb
CHANGED
@@ -1,18 +1,15 @@
|
|
1
|
-
require 'undies/node_buffer'
|
2
|
-
|
3
1
|
module Undies
|
4
2
|
class Output
|
5
3
|
|
6
|
-
attr_reader :io, :options, :pp, :node_buffer
|
7
|
-
attr_accessor :pp_use_indent
|
8
|
-
|
9
4
|
# the output class wraps an IO stream, gathers pretty printing options,
|
10
|
-
# and handles
|
5
|
+
# and handles writing out buffered node stack items
|
6
|
+
|
7
|
+
attr_reader :io, :pp
|
8
|
+
attr_accessor :pp_level
|
11
9
|
|
12
10
|
def initialize(io, opts={})
|
13
11
|
@io = io
|
14
12
|
self.options = opts
|
15
|
-
@node_buffer = NodeBuffer.new
|
16
13
|
end
|
17
14
|
|
18
15
|
def options=(opts)
|
@@ -21,33 +18,13 @@ module Undies
|
|
21
18
|
end
|
22
19
|
|
23
20
|
# setup any pretty printing
|
24
|
-
@pp = opts[:pp]
|
25
|
-
|
26
|
-
self.pp_use_indent = true
|
27
|
-
|
28
|
-
@options = opts
|
29
|
-
end
|
30
|
-
|
31
|
-
def pp_level
|
32
|
-
@pp_level
|
33
|
-
end
|
34
|
-
|
35
|
-
def pp_level=(value)
|
36
|
-
@pp_indent = @pp ? "\n#{' '*value*@pp}" : ""
|
37
|
-
@pp_level = value
|
38
|
-
end
|
39
|
-
|
40
|
-
def <<(data)
|
41
|
-
@io << (@pp_use_indent ? "#{@pp_indent}#{data}" : data.to_s)
|
42
|
-
end
|
43
|
-
|
44
|
-
def node(obj)
|
45
|
-
self.node_buffer.pull(self)
|
46
|
-
self.node_buffer.push(obj)
|
21
|
+
@pp = opts[:pp] || 0
|
22
|
+
@pp_level = opts[:pp_level] || 0
|
47
23
|
end
|
48
24
|
|
49
|
-
def
|
50
|
-
|
25
|
+
def write(node, meth, level)
|
26
|
+
@io << node.class.prefix(node, meth, level+@pp_level, @pp)
|
27
|
+
@io << node.class.send(meth, node)
|
51
28
|
end
|
52
29
|
|
53
30
|
end
|