pakyow-presenter 0.11.3 → 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. checksums.yaml +5 -5
  2. data/{pakyow-presenter/CHANGELOG.md → CHANGELOG.md} +0 -5
  3. data/LICENSE +4 -0
  4. data/{pakyow-presenter/README.md → README.md} +1 -2
  5. data/lib/pakyow/generators/presenter/presenter_generator.rb +28 -0
  6. data/lib/pakyow/generators/presenter/templates/presenter.rb.tt +2 -0
  7. data/lib/pakyow/plugin/helpers/rendering.rb +19 -0
  8. data/lib/pakyow/presenter/actions/auto_render.rb +15 -0
  9. data/lib/pakyow/presenter/attributes/attribute.rb +22 -0
  10. data/lib/pakyow/presenter/attributes/boolean.rb +24 -0
  11. data/lib/pakyow/presenter/attributes/hash.rb +89 -0
  12. data/lib/pakyow/presenter/attributes/set.rb +61 -0
  13. data/lib/pakyow/presenter/attributes/string.rb +38 -0
  14. data/lib/pakyow/presenter/attributes.rb +126 -0
  15. data/lib/pakyow/presenter/behavior/config.rb +29 -0
  16. data/lib/pakyow/presenter/behavior/error_rendering.rb +124 -0
  17. data/lib/pakyow/presenter/behavior/exposures.rb +29 -0
  18. data/lib/pakyow/presenter/behavior/implicit_rendering.rb +21 -0
  19. data/lib/pakyow/presenter/behavior/initializing.rb +37 -0
  20. data/lib/pakyow/presenter/behavior/modes.rb +117 -0
  21. data/lib/pakyow/presenter/behavior/versions.rb +71 -0
  22. data/lib/pakyow/presenter/behavior/watching.rb +21 -0
  23. data/lib/pakyow/presenter/binder.rb +115 -0
  24. data/lib/pakyow/presenter/binding_parts.rb +64 -0
  25. data/lib/pakyow/presenter/component.rb +74 -0
  26. data/lib/pakyow/presenter/composers/component.rb +78 -0
  27. data/lib/pakyow/presenter/composers/view.rb +73 -0
  28. data/lib/pakyow/presenter/errors.rb +63 -0
  29. data/lib/pakyow/presenter/framework.rb +203 -0
  30. data/lib/pakyow/presenter/front_matter_parser.rb +42 -0
  31. data/lib/pakyow/presenter/helpers/rendering.rb +18 -0
  32. data/lib/pakyow/presenter/presentable.rb +28 -0
  33. data/lib/pakyow/presenter/presentable_error.rb +19 -0
  34. data/lib/pakyow/presenter/presenter.rb +766 -0
  35. data/lib/pakyow/presenter/presenters/endpoint.rb +158 -0
  36. data/lib/pakyow/presenter/presenters/form.rb +522 -0
  37. data/lib/pakyow/presenter/processor.rb +61 -0
  38. data/lib/pakyow/presenter/renderable.rb +28 -0
  39. data/lib/pakyow/presenter/renderer.rb +269 -0
  40. data/lib/pakyow/presenter/rendering/actions/cleanup_prototype_nodes.rb +21 -0
  41. data/lib/pakyow/presenter/rendering/actions/cleanup_unbound_bindings.rb +35 -0
  42. data/lib/pakyow/presenter/rendering/actions/create_template_nodes.rb +27 -0
  43. data/lib/pakyow/presenter/rendering/actions/insert_prototype_bar.rb +101 -0
  44. data/lib/pakyow/presenter/rendering/actions/install_authenticity.rb +42 -0
  45. data/lib/pakyow/presenter/rendering/actions/place_in_mode.rb +56 -0
  46. data/lib/pakyow/presenter/rendering/actions/render_components.rb +279 -0
  47. data/lib/pakyow/presenter/rendering/actions/set_page_title.rb +35 -0
  48. data/lib/pakyow/presenter/rendering/actions/setup_endpoints.rb +62 -0
  49. data/lib/pakyow/presenter/rendering/actions/setup_forms.rb +174 -0
  50. data/lib/pakyow/presenter/significant_nodes.rb +309 -0
  51. data/lib/pakyow/presenter/templates.rb +229 -0
  52. data/lib/pakyow/presenter/versioned_view.rb +209 -0
  53. data/lib/pakyow/presenter/view.rb +586 -0
  54. data/lib/pakyow/presenter/views/form.rb +58 -0
  55. data/lib/pakyow/presenter/views/layout.rb +32 -0
  56. data/lib/pakyow/presenter/views/page.rb +72 -0
  57. data/lib/pakyow/presenter/views/partial.rb +28 -0
  58. data/lib/pakyow/presenter.rb +33 -0
  59. data/lib/pakyow/views/errors/layouts/development_error.html +102 -0
  60. data/lib/pakyow/views/errors/layouts/production_error.html +27 -0
  61. data/lib/pakyow/views/errors/pages/404.html +12 -0
  62. data/lib/pakyow/views/errors/pages/500.html +12 -0
  63. data/lib/pakyow/views/errors/pages/development/500.html +39 -0
  64. data/lib/string_doc/attributes.rb +109 -0
  65. data/lib/string_doc/meta_attributes.rb +48 -0
  66. data/lib/string_doc/meta_node.rb +328 -0
  67. data/lib/string_doc/node.rb +377 -0
  68. data/lib/string_doc.rb +642 -0
  69. metadata +95 -75
  70. data/pakyow-presenter/LICENSE +0 -20
  71. data/pakyow-presenter/lib/pakyow/presenter/attributes.rb +0 -228
  72. data/pakyow-presenter/lib/pakyow/presenter/base.rb +0 -38
  73. data/pakyow-presenter/lib/pakyow/presenter/binder.rb +0 -116
  74. data/pakyow-presenter/lib/pakyow/presenter/binder_set.rb +0 -94
  75. data/pakyow-presenter/lib/pakyow/presenter/binding_eval.rb +0 -37
  76. data/pakyow-presenter/lib/pakyow/presenter/config/presenter.rb +0 -42
  77. data/pakyow-presenter/lib/pakyow/presenter/container.rb +0 -6
  78. data/pakyow-presenter/lib/pakyow/presenter/doc_helpers.rb +0 -17
  79. data/pakyow-presenter/lib/pakyow/presenter/exceptions.rb +0 -11
  80. data/pakyow-presenter/lib/pakyow/presenter/ext/app.rb +0 -33
  81. data/pakyow-presenter/lib/pakyow/presenter/ext/call_context.rb +0 -28
  82. data/pakyow-presenter/lib/pakyow/presenter/helpers.rb +0 -46
  83. data/pakyow-presenter/lib/pakyow/presenter/page.rb +0 -110
  84. data/pakyow-presenter/lib/pakyow/presenter/partial.rb +0 -6
  85. data/pakyow-presenter/lib/pakyow/presenter/presenter.rb +0 -232
  86. data/pakyow-presenter/lib/pakyow/presenter/string_doc.rb +0 -380
  87. data/pakyow-presenter/lib/pakyow/presenter/string_doc_parser.rb +0 -144
  88. data/pakyow-presenter/lib/pakyow/presenter/string_doc_renderer.rb +0 -18
  89. data/pakyow-presenter/lib/pakyow/presenter/template.rb +0 -51
  90. data/pakyow-presenter/lib/pakyow/presenter/view.rb +0 -541
  91. data/pakyow-presenter/lib/pakyow/presenter/view_collection.rb +0 -330
  92. data/pakyow-presenter/lib/pakyow/presenter/view_composer.rb +0 -237
  93. data/pakyow-presenter/lib/pakyow/presenter/view_context.rb +0 -111
  94. data/pakyow-presenter/lib/pakyow/presenter/view_store.rb +0 -262
  95. data/pakyow-presenter/lib/pakyow/presenter/view_store_loader.rb +0 -43
  96. data/pakyow-presenter/lib/pakyow/presenter/view_version.rb +0 -113
  97. data/pakyow-presenter/lib/pakyow/presenter.rb +0 -8
  98. data/pakyow-presenter/lib/pakyow/views/errors/404.erb +0 -26
  99. data/pakyow-presenter/lib/pakyow/views/errors/500.erb +0 -23
  100. data/pakyow-presenter/lib/pakyow-presenter.rb +0 -1
@@ -0,0 +1,328 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "string_doc/meta_attributes"
4
+
5
+ class StringDoc
6
+ # Lets two or more nodes to be represented as a single node in a doc, then manipulated together.
7
+ #
8
+ class MetaNode
9
+ # @api private
10
+ attr_reader :doc, :transforms, :internal_nodes
11
+
12
+ def initialize(nodes)
13
+ nodes.first.parent.replace_node(nodes.first, self)
14
+
15
+ nodes[1..-1].each do |node|
16
+ # Remove the node, but don't make it appear to have been removed for transforms.
17
+ #
18
+ node.remove; node.delete_label(:removed)
19
+ end
20
+
21
+ nodes.each do |node|
22
+ node.set_label(:__meta_node, true)
23
+ end
24
+
25
+ @doc = StringDoc.from_nodes(nodes)
26
+ @transforms = { high: [], default: [], low: [] }
27
+
28
+ @internal_nodes = nodes.select { |node|
29
+ !node.is_a?(MetaNode) && node.labeled?(:__meta_node)
30
+ }
31
+
32
+ @pipeline = nil
33
+ end
34
+
35
+ # @api private
36
+ def initialize_copy(_)
37
+ super
38
+
39
+ @doc = @doc.dup
40
+
41
+ @transforms = @transforms.each_with_object({}) { |(key, value), hash|
42
+ hash[key] = value.dup
43
+ }
44
+
45
+ @internal_nodes = nodes.select { |node|
46
+ !node.is_a?(MetaNode) && node.labeled?(:__meta_node)
47
+ }
48
+
49
+ @pipeline = nil
50
+ end
51
+
52
+ # @api private
53
+ def soft_copy
54
+ instance = self.class.allocate
55
+
56
+ new_doc = @doc.soft_copy
57
+ instance.instance_variable_set(:@doc, new_doc)
58
+ instance.instance_variable_set(:@transforms, @transforms)
59
+
60
+ instance.instance_variable_set(:@internal_nodes, new_doc.nodes.select { |node|
61
+ !node.is_a?(MetaNode) && node.labeled?(:__meta_node)
62
+ })
63
+
64
+ instance.instance_variable_set(:@pipeline, @pipeline.dup)
65
+
66
+ instance
67
+ end
68
+
69
+ def freeze(*)
70
+ pipeline
71
+ super
72
+ end
73
+
74
+ # @api private
75
+ def parent=(parent)
76
+ @parent = parent
77
+ end
78
+
79
+ # @api private
80
+ def nodes
81
+ @doc.nodes
82
+ end
83
+
84
+ def children
85
+ internal_nodes.each_with_object(StringDoc.empty) { |node, children|
86
+ case node.children
87
+ when StringDoc
88
+ children.nodes.concat(node.children.nodes)
89
+ end
90
+ }
91
+ end
92
+
93
+ def attributes
94
+ MetaAttributes.new(internal_nodes.map(&:attributes))
95
+ end
96
+
97
+ def next_transform
98
+ pipeline.shift
99
+ end
100
+
101
+ def transform(priority: :default, &block)
102
+ @transforms[priority] << block
103
+ @pipeline = nil
104
+ end
105
+
106
+ def transforms?
107
+ transforms_itself? || children.transforms?
108
+ end
109
+
110
+ def transforms_itself?
111
+ pipeline.any?
112
+ end
113
+
114
+ def significant?(type = nil)
115
+ internal_nodes.any? { |node|
116
+ node.significant?(type)
117
+ }
118
+ end
119
+
120
+ def significance?(*types)
121
+ internal_nodes.any? { |node|
122
+ node.significance?(*types)
123
+ }
124
+ end
125
+
126
+ def replace(replacement)
127
+ internal_nodes.each do |each_node|
128
+ each_node.replace(replacement)
129
+ end
130
+ end
131
+
132
+ def remove
133
+ internal_nodes.each(&:remove)
134
+ end
135
+
136
+ def text
137
+ internal_nodes[0].text
138
+ end
139
+
140
+ def html
141
+ internal_nodes[0].html
142
+ end
143
+
144
+ def html=(html)
145
+ internal_nodes.each do |node|
146
+ node.html = html
147
+ end
148
+ end
149
+
150
+ def replace_children(children)
151
+ internal_nodes.each do |node|
152
+ node.replace_children(children)
153
+ end
154
+ end
155
+
156
+ def tagname
157
+ internal_nodes[0].tagname
158
+ end
159
+
160
+ def clear
161
+ internal_nodes.each(&:clear)
162
+ end
163
+
164
+ def after(node)
165
+ @doc.append(node)
166
+ end
167
+
168
+ def before(node)
169
+ @doc.prepend(node)
170
+ end
171
+
172
+ def append(node)
173
+ internal_nodes.each do |each_node|
174
+ each_node.append(node)
175
+ end
176
+ end
177
+
178
+ def append_html(html)
179
+ internal_nodes.each do |each_node|
180
+ each_node.append_html(html)
181
+ end
182
+ end
183
+
184
+ def prepend(node)
185
+ internal_nodes.each do |each_node|
186
+ each_node.prepend(node)
187
+ end
188
+ end
189
+
190
+ def label(name)
191
+ if node = internal_nodes.first
192
+ node.label(name)
193
+ else
194
+ nil
195
+ end
196
+ end
197
+
198
+ def labeled?(name)
199
+ if node = internal_nodes.first
200
+ node.labeled?(name)
201
+ else
202
+ false
203
+ end
204
+ end
205
+
206
+ def set_label(name, value)
207
+ internal_nodes.each do |each_node|
208
+ each_node.set_label(name, value)
209
+ end
210
+ end
211
+
212
+ def delete_label(name)
213
+ internal_nodes.each do |each_node|
214
+ each_node.delete_label(name)
215
+ end
216
+ end
217
+
218
+ def each(descend: false, &block)
219
+ internal_nodes.each do |node|
220
+ node.each(descend: descend, &block)
221
+ end
222
+ end
223
+
224
+ def each_significant_node(type, descend: false, &block)
225
+ return enum_for(:each_significant_node, type, descend: descend) unless block_given?
226
+
227
+ internal_nodes.each do |node|
228
+ node.each_significant_node(type, descend: descend, &block)
229
+ end
230
+ end
231
+
232
+ def each_significant_node_without_descending_into_type(type, descend: false, &block)
233
+ return enum_for(:each_significant_node_without_descending_into_type, type, descend: descend) unless block_given?
234
+
235
+ internal_nodes.each do |node|
236
+ node.each_significant_node_without_descending_into_type(type, descend: descend, &block)
237
+ end
238
+ end
239
+
240
+ def each_significant_node_with_name(type, name, descend: false, &block)
241
+ return enum_for(:each_significant_node_with_name, type, name, descend: descend) unless block_given?
242
+
243
+ internal_nodes.each do |node|
244
+ node.each_significant_node_with_name(type, name, descend: descend, &block)
245
+ end
246
+ end
247
+
248
+ def find_first_significant_node(type, descend: false)
249
+ internal_nodes.each do |node|
250
+ if found = node.find_first_significant_node(type, descend: descend)
251
+ return found
252
+ end
253
+ end
254
+
255
+ nil
256
+ end
257
+
258
+ def find_significant_nodes(type, descend: false)
259
+ internal_nodes.each_with_object([]) { |node, collected|
260
+ collected.concat(node.find_significant_nodes(type, descend: descend))
261
+ }
262
+ end
263
+
264
+ def find_significant_nodes_with_name(type, name, descend: false)
265
+ internal_nodes.each_with_object([]) { |node, collected|
266
+ collected.concat(node.find_significant_nodes_with_name(type, name, descend: descend))
267
+ }
268
+ end
269
+
270
+ # Converts the node to an xml string.
271
+ #
272
+ def render(output = String.new, context: nil)
273
+ if transforms_itself?
274
+ __transform(output, context: context)
275
+ else
276
+ nodes.each do |each_node|
277
+ each_node.render(output, context: context)
278
+ end
279
+ end
280
+ end
281
+ alias :to_html :render
282
+ alias :to_xml :render
283
+
284
+ # Returns the node as an xml string, without transforming.
285
+ #
286
+ def to_s
287
+ nodes.each_with_object(String.new) do |node, string|
288
+ string << node.to_s
289
+ end
290
+ end
291
+
292
+ private
293
+
294
+ def pipeline
295
+ @pipeline ||= @transforms.values.flatten
296
+ end
297
+
298
+ def __transform(string, context:)
299
+ node = if frozen?
300
+ soft_copy
301
+ else
302
+ self
303
+ end
304
+
305
+ current = node
306
+ while transform = node.next_transform
307
+ return_value = transform.call(node, context, string)
308
+
309
+ case return_value
310
+ when NilClass
311
+ return
312
+ when StringDoc
313
+ return_value.render(string, context: context); return
314
+ when Node, MetaNode
315
+ current = return_value
316
+ else
317
+ string << return_value.to_s; return
318
+ end
319
+ end
320
+
321
+ # Don't render if the node was removed during the transform.
322
+ #
323
+ if !current.is_a?(Node) || !current.labeled?(:removed)
324
+ current.render(string, context: context)
325
+ end
326
+ end
327
+ end
328
+ end
@@ -0,0 +1,377 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pakyow/support/deep_dup"
4
+ require "pakyow/support/inspectable"
5
+
6
+ class StringDoc
7
+ # String-based XML node.
8
+ #
9
+ class Node
10
+ class << self
11
+ SELF_CLOSING = %w[area base basefont br hr input img link meta].freeze
12
+ FORM_INPUTS = %w[input select textarea button].freeze
13
+ VALUELESS = %w[select].freeze
14
+
15
+ # Returns true if +tag+ is self-closing.
16
+ #
17
+ def self_closing?(tag)
18
+ SELF_CLOSING.include?(tag)
19
+ end
20
+
21
+ # Returns true if +tag+ is a form input.
22
+ #
23
+ def form_input?(tag)
24
+ FORM_INPUTS.include?(tag)
25
+ end
26
+
27
+ # Returns true if +tag+ does not contain a value.
28
+ #
29
+ def without_value?(tag)
30
+ VALUELESS.include?(tag)
31
+ end
32
+ end
33
+
34
+ attr_reader :attributes
35
+
36
+ # @api private
37
+ attr_reader :node, :parent, :children, :tag_open_start, :tag_open_end, :tag_close, :transforms, :significance, :labels
38
+
39
+ # @api private
40
+ attr_writer :parent
41
+
42
+ include Pakyow::Support::Inspectable
43
+ inspectable :@attributes, :@children, :@significance, :@labels
44
+
45
+ using Pakyow::Support::DeepDup
46
+
47
+ def initialize(tag_open_start = "", attributes = Attributes.new, tag_open_end = "", children = StringDoc.empty, tag_close = "", parent: nil, significance: [], labels: {})
48
+ @tag_open_start, @attributes, @tag_open_end, @children, @tag_close = tag_open_start, attributes, tag_open_end, children, tag_close
49
+ @parent, @labels, @significance = parent, labels, significance
50
+ @transforms = { high: [], default: [], low: [] }
51
+ @pipeline = nil
52
+ end
53
+
54
+ # @api private
55
+ def initialize_copy(_)
56
+ super
57
+
58
+ @labels = @labels.deep_dup
59
+ @attributes = @attributes.dup
60
+ @children = @children.dup
61
+ @significance = @significance.dup
62
+
63
+ @transforms = @transforms.each_with_object({}) { |(key, value), hash|
64
+ hash[key] = value.dup
65
+ }
66
+
67
+ @pipeline = nil
68
+ end
69
+
70
+ # @api private
71
+ def soft_copy
72
+ instance = self.class.allocate
73
+
74
+ instance.instance_variable_set(:@tag_open_start, @tag_open_start)
75
+ instance.instance_variable_set(:@tag_open_end, @tag_open_end)
76
+ instance.instance_variable_set(:@tag_close, @tag_close)
77
+ instance.instance_variable_set(:@parent, @parent)
78
+ instance.instance_variable_set(:@significance, @significance)
79
+ instance.instance_variable_set(:@transforms, @transforms)
80
+
81
+ instance.instance_variable_set(:@attributes, @attributes.dup)
82
+ instance.instance_variable_set(:@children, @children.is_a?(StringDoc) ? @children.soft_copy : @children.dup)
83
+ instance.instance_variable_set(:@labels, @labels.deep_dup)
84
+ instance.instance_variable_set(:@pipeline, @pipeline.dup)
85
+
86
+ instance
87
+ end
88
+
89
+ def freeze(*)
90
+ pipeline
91
+ super
92
+ end
93
+
94
+ # @api private
95
+ def empty?
96
+ to_s.strip.empty?
97
+ end
98
+
99
+ # Close self with +tag+ and a child.
100
+ #
101
+ # @api private
102
+ def close(tag, child)
103
+ tap do
104
+ @children = StringDoc.from_nodes(child)
105
+ @tag_open_end = tag ? ">" : ""
106
+ @tag_close = (tag && !self.class.self_closing?(tag)) ? "</#{tag}>" : ""
107
+ end
108
+ end
109
+
110
+ def next_transform
111
+ pipeline.shift
112
+ end
113
+
114
+ def transform(priority: :default, &block)
115
+ @transforms[priority] << block
116
+ @pipeline = nil
117
+ end
118
+
119
+ def transforms?
120
+ transforms_itself? || @children.transforms?
121
+ end
122
+
123
+ def transforms_itself?
124
+ pipeline.any?
125
+ end
126
+
127
+ def significant?(type = nil)
128
+ if type
129
+ @significance.include?(type.to_sym)
130
+ else
131
+ @significance.any?
132
+ end
133
+ end
134
+
135
+ def significance?(*types)
136
+ (@significance & types).any?
137
+ end
138
+
139
+ # Replaces the current node.
140
+ #
141
+ def replace(replacement)
142
+ @parent.replace_node(self, replacement)
143
+ end
144
+
145
+ # Removes the node.
146
+ #
147
+ def remove
148
+ set_label(:removed, true)
149
+ @parent.remove_node(self)
150
+ end
151
+
152
+ REGEX_TAGS = /<[^>]*>/
153
+
154
+ # Returns the text of this node and all children, joined together.
155
+ #
156
+ def text
157
+ html.gsub(REGEX_TAGS, "")
158
+ end
159
+
160
+ # Returns the html contained within self.
161
+ #
162
+ def html
163
+ children.to_s
164
+ end
165
+
166
+ # Replaces self's inner html, without making it available for further manipulation.
167
+ #
168
+ def html=(html)
169
+ @children = html.to_s
170
+ end
171
+
172
+ # Replaces self's children.
173
+ #
174
+ def replace_children(children)
175
+ @children.replace(children)
176
+ end
177
+
178
+ # Returns the node's tagname.
179
+ #
180
+ def tagname
181
+ @tag_open_start.gsub(/[^a-zA-Z0-9]/, "")
182
+ end
183
+
184
+ # Removes all children.
185
+ #
186
+ def clear
187
+ children.clear
188
+ end
189
+
190
+ # Inserts +node+ after +self+.
191
+ #
192
+ def after(node)
193
+ @parent.insert_after(node, self)
194
+ end
195
+
196
+ # Inserts +node+ before +self+.
197
+ #
198
+ def before(node)
199
+ @parent.insert_before(node, self)
200
+ end
201
+
202
+ # Appends +node+ as a child.
203
+ #
204
+ def append(node)
205
+ children.append(node)
206
+ end
207
+
208
+ # Appends +html+ as a child.
209
+ #
210
+ def append_html(html)
211
+ children.append_html(html)
212
+ end
213
+
214
+ # Prepends +node+ as a child.
215
+ #
216
+ def prepend(node)
217
+ children.prepend(node)
218
+ end
219
+
220
+ # Returns the value for label with +name+.
221
+ #
222
+ def label(name)
223
+ @labels[name.to_sym]
224
+ end
225
+
226
+ # Returns true if label exists with +name+.
227
+ #
228
+ def labeled?(name)
229
+ @labels.key?(name.to_sym)
230
+ end
231
+
232
+ # Sets the label with +name+ and +value+.
233
+ #
234
+ def set_label(name, value)
235
+ @labels[name.to_sym] = value
236
+ end
237
+
238
+ # Delete the label with +name+.
239
+ #
240
+ def delete_label(name)
241
+ @labels.delete(name.to_sym)
242
+ end
243
+
244
+ def render(output = String.new, context: nil)
245
+ if transforms_itself?
246
+ __transform(output, context: context)
247
+ else
248
+ output << tag_open_start
249
+
250
+ attributes.each_string do |attribute_string|
251
+ output << attribute_string
252
+ end
253
+
254
+ output << tag_open_end
255
+
256
+ case children
257
+ when StringDoc
258
+ children.render(output, context: context)
259
+ else
260
+ output << children.to_s
261
+ end
262
+
263
+ output << tag_close
264
+ end
265
+ end
266
+ alias :to_html :render
267
+ alias :to_xml :render
268
+
269
+ # Returns the node as an xml string, without transforming.
270
+ #
271
+ def to_s
272
+ string_nodes.flatten.map(&:to_s).join
273
+ end
274
+
275
+ def ==(other)
276
+ other.is_a?(Node) &&
277
+ @tag_open_start == other.tag_open_start &&
278
+ @attributes == other.attributes &&
279
+ @tag_open_end == other.tag_open_end &&
280
+ @children == other.children &&
281
+ @tag_close == other.tag_close
282
+ end
283
+
284
+ def each(descend: false)
285
+ return enum_for(:each, descend: descend) unless block_given?
286
+ yield self
287
+ end
288
+
289
+ def each_significant_node(type, descend: false, &block)
290
+ return enum_for(:each_significant_node, type, descend: descend) unless block_given?
291
+
292
+ if @children.is_a?(StringDoc)
293
+ @children.each_significant_node(type, descend: descend, &block)
294
+ end
295
+ end
296
+
297
+ def each_significant_node_without_descending_into_type(type, descend: false, &block)
298
+ return enum_for(:each_significant_node_without_descending_into_type, type, descend: descend) unless block_given?
299
+
300
+ if @children.is_a?(StringDoc)
301
+ @children.each_significant_node_without_descending_into_type(type, descend: descend, &block)
302
+ end
303
+ end
304
+
305
+ def each_significant_node_with_name(type, name, descend: false, &block)
306
+ return enum_for(:each_significant_node_with_name, type, name, descend: descend) unless block_given?
307
+
308
+ if @children.is_a?(StringDoc)
309
+ @children.each_significant_node_with_name(type, name, descend: descend, &block)
310
+ end
311
+ end
312
+
313
+ def find_first_significant_node(type, descend: false)
314
+ if @children.is_a?(StringDoc)
315
+ @children.find_first_significant_node(type, descend: descend)
316
+ else
317
+ nil
318
+ end
319
+ end
320
+
321
+ def find_significant_nodes(type, descend: false)
322
+ if @children.is_a?(StringDoc)
323
+ @children.find_significant_nodes(type, descend: descend)
324
+ else
325
+ []
326
+ end
327
+ end
328
+
329
+ def find_significant_nodes_with_name(type, name, descend: false)
330
+ if @children.is_a?(StringDoc)
331
+ @children.find_significant_nodes_with_name(type, name, descend: descend)
332
+ else
333
+ []
334
+ end
335
+ end
336
+
337
+ private
338
+
339
+ def pipeline
340
+ @pipeline ||= @transforms.values.flatten
341
+ end
342
+
343
+ def __transform(string, context:)
344
+ node = if frozen?
345
+ soft_copy
346
+ else
347
+ self
348
+ end
349
+
350
+ current = node
351
+ while transform = node.next_transform
352
+ return_value = transform.call(node, context, string)
353
+
354
+ case return_value
355
+ when NilClass
356
+ return
357
+ when StringDoc
358
+ return_value.render(string, context: context); return
359
+ when Node, MetaNode
360
+ current = return_value
361
+ else
362
+ string << return_value.to_s; return
363
+ end
364
+ end
365
+
366
+ # Don't render if the node was removed during the transform.
367
+ #
368
+ if !current.is_a?(Node) || !current.labeled?(:removed)
369
+ current.render(string, context: context)
370
+ end
371
+ end
372
+
373
+ def string_nodes
374
+ [@tag_open_start, @attributes, @tag_open_end, @children, @tag_close]
375
+ end
376
+ end
377
+ end