erector 0.8.3 → 0.9.0.pre1

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.
Files changed (115) hide show
  1. data/Gemfile +21 -0
  2. data/Rakefile +171 -0
  3. data/VERSION.yml +3 -2
  4. data/lib/erector.rb +3 -5
  5. data/lib/erector/abstract_widget.rb +76 -31
  6. data/lib/erector/attributes.rb +34 -0
  7. data/lib/erector/caching.rb +1 -0
  8. data/lib/erector/convenience.rb +12 -4
  9. data/lib/erector/dependency.rb +2 -1
  10. data/lib/erector/element.rb +113 -0
  11. data/lib/erector/erect/erect.rb +10 -7
  12. data/lib/erector/externals.rb +2 -1
  13. data/lib/erector/html.rb +6 -340
  14. data/lib/erector/html_widget.rb +300 -0
  15. data/lib/erector/inline.rb +1 -1
  16. data/lib/erector/output.rb +39 -12
  17. data/lib/erector/promise.rb +137 -0
  18. data/lib/erector/rails2/extensions/rails_widget.rb +1 -1
  19. data/lib/erector/rails3.rb +1 -1
  20. data/lib/erector/sass.rb +14 -19
  21. data/lib/erector/tag.rb +65 -0
  22. data/lib/erector/text.rb +123 -0
  23. data/lib/erector/version.rb +1 -1
  24. data/lib/erector/widget.rb +52 -12
  25. data/lib/erector/widgets/page.rb +12 -10
  26. data/lib/erector/xml_widget.rb +131 -0
  27. data/spec/erector/caching_spec.rb +1 -0
  28. data/spec/erector/externals_spec.rb +0 -1
  29. data/spec/erector/html_spec.rb +9 -25
  30. data/spec/erector/output_spec.rb +102 -9
  31. data/spec/erector/promise_spec.rb +173 -0
  32. data/spec/erector/sass_spec.rb +1 -1
  33. data/spec/erector/tag_spec.rb +67 -0
  34. data/spec/erector/widget_spec.rb +53 -2
  35. data/spec/erector/xml_widget_spec.rb +74 -0
  36. data/spec/rails2/rails_app/Gemfile +1 -0
  37. data/spec/rails2/rails_app/spec/rails_spec_helper.rb +2 -0
  38. data/spec/rails_root/Gemfile +11 -0
  39. data/spec/rails_root/README +256 -0
  40. data/spec/rails_root/Rakefile +7 -0
  41. data/spec/rails_root/app/controllers/application.rb +6 -0
  42. data/spec/rails_root/app/controllers/application_controller.rb +3 -0
  43. data/spec/rails_root/app/helpers/application_helper.rb +2 -0
  44. data/spec/rails_root/app/views/layouts/application.html.erb +14 -0
  45. data/spec/rails_root/app/views/test/_erb.erb +1 -0
  46. data/spec/rails_root/app/views/test/_erector.rb +5 -0
  47. data/spec/rails_root/app/views/test/_partial_with_locals.rb +7 -0
  48. data/spec/rails_root/app/views/test/bare.rb +5 -0
  49. data/spec/rails_root/app/views/test/erb_from_erector.html.rb +5 -0
  50. data/spec/rails_root/app/views/test/erector_from_erb.html.erb +1 -0
  51. data/spec/rails_root/app/views/test/erector_with_locals_from_erb.html.erb +6 -0
  52. data/spec/rails_root/app/views/test/implicit_assigns.html.rb +5 -0
  53. data/spec/rails_root/app/views/test/needs.html.rb +7 -0
  54. data/spec/rails_root/app/views/test/needs_subclass.html.rb +5 -0
  55. data/spec/rails_root/app/views/test/protected_instance_variable.html.rb +5 -0
  56. data/spec/rails_root/app/views/test/render_default.html.rb +5 -0
  57. data/spec/rails_root/app/views/test/render_partial.html.rb +5 -0
  58. data/spec/rails_root/config.ru +4 -0
  59. data/spec/rails_root/config/application.rb +42 -0
  60. data/spec/rails_root/config/boot.rb +13 -0
  61. data/spec/rails_root/config/database.yml +22 -0
  62. data/spec/rails_root/config/environment.rb +5 -0
  63. data/spec/rails_root/config/environments/development.rb +22 -0
  64. data/spec/rails_root/config/environments/production.rb +49 -0
  65. data/spec/rails_root/config/environments/test.rb +35 -0
  66. data/spec/rails_root/config/initializers/backtrace_silencers.rb +7 -0
  67. data/spec/rails_root/config/initializers/inflections.rb +10 -0
  68. data/spec/rails_root/config/initializers/mime_types.rb +5 -0
  69. data/spec/rails_root/config/initializers/secret_token.rb +7 -0
  70. data/spec/rails_root/config/initializers/session_store.rb +8 -0
  71. data/spec/rails_root/config/locales/en.yml +5 -0
  72. data/spec/rails_root/config/routes.rb +58 -0
  73. data/spec/rails_root/db/seeds.rb +7 -0
  74. data/spec/rails_root/doc/README_FOR_APP +2 -0
  75. data/spec/rails_root/log/development.log +17 -0
  76. data/spec/rails_root/log/test.log +3750 -0
  77. data/spec/rails_root/public/404.html +26 -0
  78. data/spec/rails_root/public/422.html +26 -0
  79. data/spec/rails_root/public/500.html +26 -0
  80. data/spec/rails_root/public/dispatch.cgi +10 -0
  81. data/spec/rails_root/public/dispatch.fcgi +24 -0
  82. data/spec/rails_root/public/dispatch.rb +10 -0
  83. data/spec/rails_root/public/favicon.ico +0 -0
  84. data/spec/rails_root/public/images/rails.png +0 -0
  85. data/spec/rails_root/public/index.html +262 -0
  86. data/spec/rails_root/public/javascripts/application.js +2 -0
  87. data/spec/rails_root/public/javascripts/controls.js +965 -0
  88. data/spec/rails_root/public/javascripts/dragdrop.js +974 -0
  89. data/spec/rails_root/public/javascripts/effects.js +1123 -0
  90. data/spec/rails_root/public/javascripts/prototype.js +6001 -0
  91. data/spec/rails_root/public/javascripts/rails.js +175 -0
  92. data/spec/rails_root/public/robots.txt +5 -0
  93. data/spec/rails_root/script/about +3 -0
  94. data/spec/rails_root/script/console +3 -0
  95. data/spec/rails_root/script/destroy +3 -0
  96. data/spec/rails_root/script/generate +3 -0
  97. data/spec/rails_root/script/performance/benchmarker +3 -0
  98. data/spec/rails_root/script/performance/profiler +3 -0
  99. data/spec/rails_root/script/performance/request +3 -0
  100. data/spec/rails_root/script/plugin +3 -0
  101. data/spec/rails_root/script/process/inspector +3 -0
  102. data/spec/rails_root/script/process/reaper +3 -0
  103. data/spec/rails_root/script/process/spawner +3 -0
  104. data/spec/rails_root/script/rails +6 -0
  105. data/spec/rails_root/script/runner +3 -0
  106. data/spec/rails_root/script/server +3 -0
  107. data/spec/rails_root/spec/form_builder_spec.rb +21 -0
  108. data/spec/rails_root/spec/rails_helpers_spec.rb +220 -0
  109. data/spec/rails_root/spec/rails_spec_helper.rb +10 -0
  110. data/spec/rails_root/spec/rails_widget_spec.rb +83 -0
  111. data/spec/rails_root/spec/render_spec.rb +298 -0
  112. data/spec/rails_root/test/performance/browsing_test.rb +9 -0
  113. data/spec/rails_root/test/test_helper.rb +13 -0
  114. data/spec/spec_helper.rb +3 -1
  115. metadata +202 -66
@@ -0,0 +1,300 @@
1
+ require "erector/xml_widget"
2
+
3
+ module Erector
4
+
5
+ # A Widget is the center of the Erector universe.
6
+ #
7
+ # To create a widget, extend Erector::Widget and implement the +content+
8
+ # method. Inside this method you may call any of the tag methods like +span+
9
+ # or +p+ to emit HTML/XML tags.
10
+ #
11
+ # You can also define a widget on the fly by passing a block to +new+. This
12
+ # block will get executed when the widget's +content+ method is called. See
13
+ # the userguide for important details about the scope of this block when run --
14
+ # http://erector.rubyforge.org/userguide.html#blocks
15
+ #
16
+ # To render a widget from the outside, instantiate it and call its +to_html+
17
+ # method.
18
+ #
19
+ # A widget's +new+ method optionally accepts an options hash. Entries in
20
+ # this hash are converted to instance variables.
21
+ #
22
+ # You can add runtime input checking via the +needs+ macro. See #needs.
23
+ # This mechanism is meant to ameliorate development-time confusion about
24
+ # exactly what parameters are supported by a given widget, avoiding
25
+ # confusing runtime NilClass errors.
26
+ #
27
+ # To call one widget from another, inside the parent widget's +content+
28
+ # method, instantiate the child widget and call the +widget+ method. This
29
+ # assures that the same output stream is used, which gives better
30
+ # performance than using +capture+ or +to_html+. It also preserves the
31
+ # indentation and helpers of the enclosing class.
32
+ #
33
+ # In this documentation we've tried to keep the distinction clear between
34
+ # methods that *emit* text and those that *return* text. "Emit" means that
35
+ # it writes to the output stream; "return" means that it returns a string
36
+ # like a normal method and leaves it up to the caller to emit that string if
37
+ # it wants.
38
+ #
39
+ # This class extends AbstractWidget and includes several modules,
40
+ # so be sure to check all of those places for API documentation for the
41
+ # various methods of Widget:
42
+ #
43
+ # * AbstractWidget
44
+ # * Element
45
+ # * Attributes
46
+ # * Text
47
+ # * Needs
48
+ # * Caching
49
+ # * Externals
50
+ # * AfterInitialize
51
+ #
52
+ # * HTML
53
+ # * Convenience
54
+ # * JQuery
55
+ # * Sass
56
+ #
57
+ # Also read the API Cheatsheet in the user guide
58
+ # at http://erector.rubyforge.org/userguide#apicheatsheet
59
+ class HTMLWidget < Erector::XMLWidget
60
+
61
+ include Erector::HTML
62
+ include Erector::Convenience
63
+ include Erector::JQuery
64
+ include Erector::Sass if Object.const_defined?(:Sass)
65
+
66
+ tag 'area', :self_closing
67
+ tag 'base', :self_closing
68
+ tag 'br', :self_closing
69
+ tag 'col', :self_closing
70
+ tag 'embed', :self_closing
71
+ tag 'frame', :self_closing
72
+ tag 'hr', :self_closing
73
+ tag 'img', :self_closing, :inline
74
+ tag 'input', :self_closing, :inline
75
+ tag 'link', :self_closing
76
+ tag 'meta', :self_closing
77
+ tag 'param', :self_closing
78
+
79
+ tag 'a', :inline
80
+ tag 'abbr'
81
+ tag 'acronym'
82
+ tag 'address'
83
+ tag 'article'
84
+ tag 'aside'
85
+ tag 'audio'
86
+
87
+ tag 'b', :inline
88
+ tag 'bdo'
89
+ tag 'big'
90
+ tag 'blockquote'
91
+ tag 'body'
92
+ tag 'button', :inline
93
+
94
+ tag 'canvas'
95
+ tag 'caption'
96
+ tag 'center'
97
+ tag 'cite'
98
+ tag 'code'
99
+ tag 'colgroup'
100
+ tag 'command'
101
+
102
+ tag 'datalist'
103
+ tag 'dd'
104
+ tag 'del'
105
+ tag 'details'
106
+ tag 'dfn'
107
+ tag 'dialog'
108
+ tag 'div'
109
+ tag 'dl'
110
+ tag 'dt'
111
+
112
+ tag 'em'
113
+
114
+ tag 'fieldset'
115
+ tag 'figure'
116
+ tag 'footer'
117
+ tag 'form'
118
+ tag 'frameset'
119
+
120
+ tag 'h1'
121
+ tag 'h2'
122
+ tag 'h3'
123
+ tag 'h4'
124
+ tag 'h5'
125
+ tag 'h6'
126
+ tag 'head'
127
+ tag 'header'
128
+ tag 'hgroup'
129
+ tag 'html'
130
+ tag 'i', :inline
131
+
132
+ tag 'iframe'
133
+ tag 'ins'
134
+ tag 'keygen'
135
+ tag 'kbd'
136
+ tag 'label'
137
+ tag 'legend'
138
+ tag 'li'
139
+
140
+ tag 'map'
141
+ tag 'mark'
142
+ tag 'meter'
143
+
144
+ tag 'nav'
145
+ tag 'noframes'
146
+ tag 'noscript'
147
+
148
+ tag 'object'
149
+ tag 'ol'
150
+ tag 'optgroup'
151
+ tag 'option'
152
+
153
+ tag 'p'
154
+ tag 'pre'
155
+ tag 'progress'
156
+
157
+ tag 'q'
158
+ tag 'ruby'
159
+ tag 'rt'
160
+ tag 'rp'
161
+ tag 's'
162
+
163
+ tag 'samp'
164
+ tag 'script'
165
+ tag 'section'
166
+ tag 'select', :inline
167
+ tag 'small', :inline
168
+ tag 'source'
169
+ tag 'span', :inline
170
+ tag 'strike'
171
+
172
+ tag 'strong'
173
+ tag 'style'
174
+ tag 'sub'
175
+ tag 'sup'
176
+
177
+ tag 'table'
178
+ tag 'tbody'
179
+ tag 'td'
180
+ tag 'textarea', :inline
181
+ tag 'tfoot'
182
+
183
+ tag 'th'
184
+ tag 'thead'
185
+ tag 'time'
186
+ tag 'title'
187
+ tag 'tr'
188
+ tag 'tt'
189
+
190
+ tag 'u'
191
+ tag 'ul'
192
+
193
+ tag 'var'
194
+ tag 'video'
195
+
196
+
197
+ # Emits a javascript block inside a +script+ tag, wrapped in CDATA
198
+ # doohickeys like all the cool JS kids do.
199
+ def javascript(value = nil, attributes = {})
200
+ if value.is_a?(Hash)
201
+ attributes = value
202
+ value = nil
203
+ elsif block_given? && value
204
+ raise ArgumentError, "You can't pass both a block and a value to javascript -- please choose one."
205
+ end
206
+
207
+ script(attributes.merge(:type => "text/javascript")) do
208
+ # Shouldn't this be a "cdata" HtmlPart?
209
+ # (maybe, but the syntax is specific to javascript; it isn't
210
+ # really a generic XML CDATA section. Specifically,
211
+ # ]]> within value is not treated as ending the
212
+ # CDATA section by Firefox2 when parsing text/html,
213
+ # although I guess we could refuse to generate ]]>
214
+ # there, for the benefit of XML/XHTML parsers).
215
+ output << raw("\n// <![CDATA[\n")
216
+ if block_given?
217
+ yield
218
+ else
219
+ output << raw(value)
220
+ end
221
+ output << raw("\n// ]]>")
222
+ output.append_newline # this forces a newline even if we're not in pretty mode
223
+ end
224
+
225
+ output << raw("\n")
226
+ end
227
+
228
+
229
+
230
+ # alias for AbstractWidget#render
231
+ def to_html(options = {})
232
+ raise "Erector::Widget#to_html takes an options hash, not a symbol. Try calling \"to_html(:content_method_name=> :#{options})\"" if options.is_a? Symbol
233
+ _render(options).to_s
234
+ end
235
+
236
+ # alias for #to_html
237
+ # @deprecated Please use {#to_html} instead
238
+ def to_s(*args)
239
+ unless defined? @@already_warned_to_s
240
+ $stderr.puts "Erector::Widget#to_s is deprecated. Please use #to_html instead. Called from #{caller.first}"
241
+ @@already_warned_to_s = true
242
+ end
243
+ to_html(*args)
244
+ end
245
+
246
+
247
+ # Emits an XML instruction, which looks like this: <?xml version=\"1.0\" encoding=\"UTF-8\"?>
248
+ def instruct(attributes={:version => "1.0", :encoding => "UTF-8"})
249
+ output << raw("<?xml#{format_sorted(sort_for_xml_declaration(attributes))}?>")
250
+ end
251
+
252
+ # Emits an XML/HTML comment (&lt;!-- ... --&gt;) surrounding +text+ and/or
253
+ # the output of +block+. see
254
+ # http://www.w3.org/TR/html4/intro/sgmltut.html#h-3.2.4
255
+ #
256
+ # If +text+ is an Internet Explorer conditional comment condition such as
257
+ # "[if IE]", the output includes the opening condition and closing
258
+ # "[endif]". See http://www.quirksmode.org/css/condcom.html
259
+ #
260
+ # Since "Authors should avoid putting two or more adjacent hyphens inside
261
+ # comments," we emit a warning if you do that.
262
+ def comment(text = '')
263
+ puts "Warning: Authors should avoid putting two or more adjacent hyphens inside comments." if text =~ /--/
264
+
265
+ conditional = text =~ /\[if .*\]/
266
+
267
+ rawtext "<!--"
268
+ rawtext text
269
+ rawtext ">" if conditional
270
+
271
+ if block_given?
272
+ rawtext "\n"
273
+ yield
274
+ rawtext "\n"
275
+ end
276
+
277
+ rawtext "<![endif]" if conditional
278
+ rawtext "-->\n"
279
+ end
280
+
281
+ protected
282
+
283
+ def sort_for_xml_declaration(attributes)
284
+ # correct order is "version, encoding, standalone" (XML 1.0 section 2.8).
285
+ # But we only try to put version before encoding for now.
286
+ stringized = []
287
+ attributes.each do |key, value|
288
+ stringized << [key.to_s, value]
289
+ end
290
+ stringized.sort{|a, b| b <=> a}
291
+ end
292
+
293
+ end
294
+
295
+ public
296
+
297
+ # Alias to make it easier to spell
298
+ HtmlWidget = HTMLWidget
299
+
300
+ end
@@ -30,7 +30,7 @@ module Erector
30
30
  end
31
31
  end
32
32
 
33
- class InlineWidget < Erector::Widget
33
+ class InlineWidget < Widget
34
34
  include Inline
35
35
  end
36
36
 
@@ -1,22 +1,25 @@
1
+ require 'erector/abstract_widget'
2
+
1
3
  module Erector
2
4
  class Output
3
5
  SPACES_PER_INDENT = 2
4
6
 
5
7
  attr_reader :prettyprint, :widgets, :indentation, :max_length
6
8
 
7
- def initialize(options = {}, & get_buffer)
8
- @prettyprint = options.fetch(:prettyprint, Widget.prettyprint_default)
9
+ def initialize(options = {})
10
+ @prettyprint = options.fetch(:prettyprint, AbstractWidget.prettyprint_default)
9
11
  @indentation = options.fetch(:indentation, 0)
10
12
  @current_line_length = 0
11
13
  @max_length = options[:max_length]
12
14
  @widgets = []
13
- if get_buffer
14
- @get_buffer = get_buffer
15
- elsif buffer = options[:output]
16
- @get_buffer = lambda { buffer }
15
+
16
+ @get_buffer = if options[:buffer] and options[:buffer].respond_to? :call
17
+ options[:buffer]
18
+ elsif options[:buffer]
19
+ lambda { options[:buffer] }
17
20
  else
18
21
  buffer = []
19
- @get_buffer = lambda { buffer }
22
+ lambda { buffer }
20
23
  end
21
24
  end
22
25
 
@@ -25,11 +28,13 @@ module Erector
25
28
  end
26
29
 
27
30
  def <<(s)
31
+ # raise s.inspect unless s.is_a? String
32
+ #
28
33
  s = s.to_s unless s.is_a? String
29
34
  append_indentation
30
35
  if @max_length && s.length + @current_line_length > @max_length
31
36
  leading_spaces = s =~ /^( +)/ ? $1.size : 0
32
- trailing_spaces = s =~ /( +)$/ ? $1.size : 0
37
+ trailing_spaces = s =~ /( +)$/ ? $1.size : 0
33
38
 
34
39
  append(" " * leading_spaces)
35
40
  need_space = false
@@ -51,10 +56,11 @@ module Erector
51
56
  self
52
57
  end
53
58
 
54
- # Inserts a blank string into the output stream and returns a pointer to it.
55
- # If the caller holds on to this pointer, she can later go back and insert text
56
- # earlier in the stream. This is used for, e.g., inserting stuff inside the
57
- # HEAD element that is not known until after the entire page renders.
59
+ # Inserts a blank string into the output stream and returns a pointer to
60
+ # it. If the caller holds on to this pointer, she can later go back and
61
+ # insert text earlier in the stream. This is used for, e.g., inserting
62
+ # stuff inside the HEAD element that is not known until after the entire
63
+ # page renders.
58
64
  def placeholder
59
65
  s = ""
60
66
  buffer << s
@@ -94,6 +100,27 @@ module Erector
94
100
  @current_line_length = 0
95
101
  end
96
102
 
103
+ def mark
104
+ @mark = buffer.size
105
+ end
106
+
107
+ def rewind pos = @mark
108
+ if buffer.kind_of?(Array)
109
+ buffer.slice!(pos..-1)
110
+ elsif (Object.const_defined?(:ActiveSupport) and
111
+ buffer.kind_of?(ActiveSupport::SafeBuffer))
112
+ # monkey patch to get around SafeBuffer's well-meaning paranoia
113
+ # see http://yehudakatz.com/2010/02/01/safebuffers-and-rails-3-0/
114
+ # and http://weblog.rubyonrails.org/2011/6/8/potential-xss-vulnerability-in-ruby-on-rails-applications
115
+ String.instance_method(:slice!).bind(buffer).call(pos..-1)
116
+ elsif buffer.kind_of?(String)
117
+ buffer.slice!(pos..-1)
118
+ else
119
+ raise "Don't know how to rewind a #{buffer.class}"
120
+ end
121
+
122
+ end
123
+
97
124
  protected
98
125
 
99
126
  def append(s)
@@ -0,0 +1,137 @@
1
+ require "erector/attributes"
2
+ require "erector/text"
3
+
4
+ module Erector
5
+ class Promise
6
+ extend Attributes
7
+ extend Text
8
+
9
+ def initialize(output, tag_name, attributes = {}, self_closing = false, newliney = true, &inside_renderer)
10
+ raise "bad output: #{output.inspect}" unless output.is_a? Output
11
+ raise "forgot self-closing" unless [false, true].include? self_closing
12
+
13
+ @output = output
14
+
15
+ # todo: pointer to Tag object?
16
+ @tag_name = tag_name
17
+ @self_closing = self_closing
18
+ @newliney = newliney
19
+
20
+ @attributes = {}
21
+ _set_attributes attributes
22
+ @text = nil
23
+ @inside_renderer = inside_renderer
24
+ _mark
25
+ end
26
+
27
+ def _set_attributes attributes
28
+ attributes.each_pair do |k,v|
29
+ @attributes[k.to_s] = v
30
+ end
31
+ end
32
+
33
+ def _mark
34
+ @mark = @output.mark
35
+ end
36
+
37
+ def _rewind
38
+ @output.rewind @mark
39
+ end
40
+
41
+ def _render
42
+ _rewind
43
+ _render_open_tag
44
+ begin
45
+ _render_inside_tag
46
+ ensure
47
+ _render_close_tag
48
+ end
49
+ end
50
+
51
+ def _render_open_tag
52
+
53
+ @output.newline if !@self_closing and @newliney and !@output.at_line_start?
54
+
55
+ @output << RawString.new( "<#{@tag_name}#{Promise.format_attributes(@attributes)}")
56
+ if @self_closing
57
+ @output << RawString.new( " />")
58
+ @output.newline if @newliney
59
+ else
60
+ @output << RawString.new( ">")
61
+ @output.indent
62
+ end
63
+ end
64
+
65
+ def _render_inside_tag
66
+ return if @self_closing
67
+ if @text
68
+ @output << @text
69
+ end
70
+ if @inside_renderer
71
+ @inside_renderer.call
72
+ end
73
+ end
74
+
75
+ def _render_close_tag
76
+ return if @self_closing
77
+
78
+ @output.undent
79
+ @output<< RawString.new("</#{@tag_name}>")
80
+ if @newliney
81
+ @output.newline
82
+ end
83
+ end
84
+
85
+ def method_missing(method_name, *args, &block)
86
+ method_name = method_name.to_s
87
+ if method_name =~ /\!$/
88
+ id_str = method_name[0...-1]
89
+ raise ArgumentError, "setting id #{id_str} but id #{@attributes["id"]} already present" if @attributes["id"]
90
+ @attributes["id"] = id_str
91
+ else
92
+ if @attributes["class"]
93
+ @attributes["class"] += " "
94
+ else
95
+ @attributes["class"] = ""
96
+ end
97
+ @attributes["class"] += method_name.to_s
98
+ end
99
+
100
+ if block_given?
101
+ @inside_renderer = block
102
+ end
103
+
104
+ if args.last.is_a? Hash
105
+ attributes = args.pop
106
+ _set_attributes attributes
107
+ end
108
+
109
+ # todo: allow multiple args
110
+ # todo: allow promise args
111
+ @text = args.first
112
+
113
+ _render
114
+
115
+ self
116
+ end
117
+
118
+ # are these accessors necessary?
119
+
120
+ def _tag_name
121
+ @tag_name
122
+ end
123
+
124
+ def _attributes
125
+ @attributes
126
+ end
127
+
128
+ def _open_tag
129
+ @open_tag
130
+ end
131
+
132
+ def _close_tag
133
+ @close_tag
134
+ end
135
+
136
+ end
137
+ end