erector-rails4 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +2 -0
  3. data/.gitignore +10 -0
  4. data/.rspec +1 -0
  5. data/.ruby-gemset +1 -0
  6. data/.ruby-version +1 -0
  7. data/.travis.yml +5 -0
  8. data/Gemfile +6 -0
  9. data/Gemfile.lock +143 -0
  10. data/README.md +4 -0
  11. data/Rakefile +24 -0
  12. data/erector-rails4.gemspec +36 -0
  13. data/erector-rails4.sublimeproject +20 -0
  14. data/lib/erector/abstract_widget.rb +231 -0
  15. data/lib/erector/after_initialize.rb +29 -0
  16. data/lib/erector/attributes.rb +29 -0
  17. data/lib/erector/cache.rb +37 -0
  18. data/lib/erector/caching.rb +65 -0
  19. data/lib/erector/convenience.rb +98 -0
  20. data/lib/erector/dependencies.rb +24 -0
  21. data/lib/erector/dependency.rb +31 -0
  22. data/lib/erector/element.rb +113 -0
  23. data/lib/erector/externals.rb +104 -0
  24. data/lib/erector/html.rb +12 -0
  25. data/lib/erector/html_widget.rb +220 -0
  26. data/lib/erector/inline.rb +37 -0
  27. data/lib/erector/jquery.rb +28 -0
  28. data/lib/erector/mixin.rb +12 -0
  29. data/lib/erector/needs.rb +95 -0
  30. data/lib/erector/output.rb +144 -0
  31. data/lib/erector/promise.rb +141 -0
  32. data/lib/erector/rails/form_builder.rb +44 -0
  33. data/lib/erector/rails/railtie.rb +13 -0
  34. data/lib/erector/rails/template_handler.rb +16 -0
  35. data/lib/erector/rails/widget_renderer.rb +6 -0
  36. data/lib/erector/rails.rb +221 -0
  37. data/lib/erector/raw_string.rb +12 -0
  38. data/lib/erector/sass.rb +32 -0
  39. data/lib/erector/tag.rb +66 -0
  40. data/lib/erector/text.rb +123 -0
  41. data/lib/erector/unicode.rb +18185 -0
  42. data/lib/erector/unicode_builder.rb +67 -0
  43. data/lib/erector/version.rb +5 -0
  44. data/lib/erector/widget.rb +94 -0
  45. data/lib/erector/widgets.rb +5 -0
  46. data/lib/erector/xml_widget.rb +131 -0
  47. data/lib/erector.rb +28 -0
  48. data/spec/dummy/Rakefile +7 -0
  49. data/spec/dummy/app/controllers/application.rb +6 -0
  50. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  51. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  52. data/spec/dummy/app/views/layouts/erb_as_layout.html.erb +2 -0
  53. data/spec/dummy/app/views/layouts/widget_as_layout.rb +8 -0
  54. data/spec/dummy/app/views/test/_erb.erb +1 -0
  55. data/spec/dummy/app/views/test/_erector.rb +5 -0
  56. data/spec/dummy/app/views/test/_partial_with_locals.rb +7 -0
  57. data/spec/dummy/app/views/test/bare.rb +5 -0
  58. data/spec/dummy/app/views/test/erb_from_erector.html.rb +5 -0
  59. data/spec/dummy/app/views/test/erector_from_erb.html.erb +1 -0
  60. data/spec/dummy/app/views/test/erector_with_locals_from_erb.html.erb +6 -0
  61. data/spec/dummy/app/views/test/implicit_assigns.html.rb +5 -0
  62. data/spec/dummy/app/views/test/needs.html.rb +7 -0
  63. data/spec/dummy/app/views/test/needs_subclass.html.rb +5 -0
  64. data/spec/dummy/app/views/test/protected_instance_variable.html.rb +5 -0
  65. data/spec/dummy/app/views/test/render_default.html.rb +5 -0
  66. data/spec/dummy/app/views/test/render_default_erb_with_layout.html.erb +1 -0
  67. data/spec/dummy/app/views/test/render_default_widget_with_layout.html.rb +5 -0
  68. data/spec/dummy/app/views/test/render_partial.html.rb +5 -0
  69. data/spec/dummy/app/views/test/render_with_widget_as_layout.rb +5 -0
  70. data/spec/dummy/app/views/test/render_with_widget_as_layout_using_content_for.rb +8 -0
  71. data/spec/dummy/config/application.rb +44 -0
  72. data/spec/dummy/config/boot.rb +10 -0
  73. data/spec/dummy/config/database.yml +22 -0
  74. data/spec/dummy/config/environment.rb +5 -0
  75. data/spec/dummy/config/environments/development.rb +22 -0
  76. data/spec/dummy/config/environments/production.rb +49 -0
  77. data/spec/dummy/config/environments/test.rb +36 -0
  78. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  79. data/spec/dummy/config/initializers/inflections.rb +10 -0
  80. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  81. data/spec/dummy/config/initializers/secret_token.rb +12 -0
  82. data/spec/dummy/config/initializers/session_store.rb +8 -0
  83. data/spec/dummy/config/locales/en.yml +5 -0
  84. data/spec/dummy/config/routes.rb +3 -0
  85. data/spec/dummy/config.ru +4 -0
  86. data/spec/dummy/db/seeds.rb +7 -0
  87. data/spec/dummy/script/rails +6 -0
  88. data/spec/dummy/spec/form_builder_spec.rb +21 -0
  89. data/spec/dummy/spec/rails_helpers_spec.rb +236 -0
  90. data/spec/dummy/spec/rails_spec_helper.rb +10 -0
  91. data/spec/dummy/spec/rails_widget_spec.rb +83 -0
  92. data/spec/dummy/spec/render_spec.rb +369 -0
  93. data/spec/erector/after_initialize_spec.rb +45 -0
  94. data/spec/erector/cache_spec.rb +133 -0
  95. data/spec/erector/caching_spec.rb +184 -0
  96. data/spec/erector/convenience_spec.rb +250 -0
  97. data/spec/erector/dependency_spec.rb +67 -0
  98. data/spec/erector/hello_from_readme.rb +18 -0
  99. data/spec/erector/hello_from_readme_spec.rb +11 -0
  100. data/spec/erector/html_spec.rb +585 -0
  101. data/spec/erector/indentation_spec.rb +211 -0
  102. data/spec/erector/inline_spec.rb +94 -0
  103. data/spec/erector/jquery_spec.rb +35 -0
  104. data/spec/erector/mixin_spec.rb +65 -0
  105. data/spec/erector/needs_spec.rb +141 -0
  106. data/spec/erector/output_spec.rb +293 -0
  107. data/spec/erector/promise_spec.rb +173 -0
  108. data/spec/erector/sample-file.txt +1 -0
  109. data/spec/erector/sass_spec.rb +57 -0
  110. data/spec/erector/tag_spec.rb +67 -0
  111. data/spec/erector/unicode_builder_spec.rb +75 -0
  112. data/spec/erector/widget_spec.rb +310 -0
  113. data/spec/erector/xml_widget_spec.rb +73 -0
  114. data/spec/spec_helper.rb +31 -0
  115. metadata +368 -0
@@ -0,0 +1,29 @@
1
+ module Erector
2
+ module AfterInitialize
3
+ def self.included(base)
4
+ base.extend ClassMethods
5
+ end
6
+
7
+ module ClassMethods
8
+ def after_initialize(instance=nil, &blk)
9
+ if blk
10
+ after_initialize_parts << blk
11
+ elsif instance
12
+ if superclass.respond_to?(:after_initialize)
13
+ superclass.after_initialize instance
14
+ end
15
+ after_initialize_parts.each do |part|
16
+ instance.instance_eval &part
17
+ end
18
+ else
19
+ raise ArgumentError, "You must provide either an instance or a block"
20
+ end
21
+ end
22
+
23
+ protected
24
+ def after_initialize_parts
25
+ @after_initialize_parts ||= []
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,29 @@
1
+ module Erector
2
+ module Attributes
3
+ def format_attributes(attributes)
4
+ return "" if !attributes || attributes.empty?
5
+
6
+ results = ['']
7
+
8
+ attributes.each do |key, value|
9
+ if value
10
+ if value.is_a?(Array)
11
+ value = value.flatten
12
+ next if value.empty?
13
+ value = value.join(' ')
14
+ end
15
+
16
+ if value.is_a?(TrueClass)
17
+ results << "#{key}"
18
+ elsif value.nil? || value.is_a?(FalseClass)
19
+ # Nothing is generated in this case
20
+ else
21
+ results << "#{key}=\"#{h(value)}\""
22
+ end
23
+ end
24
+ end
25
+
26
+ results.join(' ')
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,37 @@
1
+ require 'singleton'
2
+
3
+ module Erector
4
+ class Cache
5
+ include Singleton
6
+
7
+ def []=(*args)
8
+ value = args.pop
9
+ ::Rails.cache.write(transform_key(args), value.to_s)
10
+ end
11
+
12
+ def [](*args)
13
+ ::Rails.cache.read(transform_key(args))
14
+ end
15
+
16
+ def delete(*args)
17
+ ::Rails.cache.delete(transform_key(args))
18
+ end
19
+
20
+ def transform_key(args)
21
+ ['erector'] + args.reject { |x| x.nil? }.map { |x|
22
+ if x.is_a?(Hash)
23
+ transformed = {}
24
+
25
+ x.each do |k, v|
26
+ transformed[k] = v.respond_to?(:cache_key) ? v.cache_key : v
27
+ end
28
+
29
+ transformed
30
+ else
31
+ x
32
+ end
33
+ }
34
+ end
35
+
36
+ end
37
+ end
@@ -0,0 +1,65 @@
1
+ module Erector
2
+ module Caching
3
+ def self.included(base)
4
+ base.extend ClassMethods
5
+ end
6
+
7
+ module ClassMethods
8
+ def cacheable(value = true)
9
+ @cachable = value
10
+
11
+ if value && value != true
12
+ @cache_version = value
13
+ end
14
+ end
15
+
16
+ alias_method :cachable, :cacheable
17
+
18
+ def cachable?
19
+ if @cachable.nil?
20
+ superclass.respond_to?(:cachable?) && superclass.cachable?
21
+ else
22
+ @cachable
23
+ end
24
+ end
25
+
26
+ def cache_version
27
+ @cache_version || nil
28
+ end
29
+
30
+ def cache
31
+ Erector::Cache.instance
32
+ end
33
+ end
34
+
35
+ def cache
36
+ self.class.cache
37
+ end
38
+
39
+ def should_cache?
40
+ if block.nil? && self.class.cachable?
41
+ true
42
+ else
43
+ false
44
+ end
45
+ end
46
+
47
+ protected
48
+ def _emit(options = {})
49
+ if should_cache?
50
+ cache[self.class, self.class.cache_version, assigns, options[:content_method_name]] ||= super
51
+ else
52
+ super
53
+ end
54
+ end
55
+
56
+ def _emit_via(parent, options = {})
57
+ if should_cache?
58
+ parent.output << cache[self.class, self.class.cache_version, assigns, options[:content_method_name]] ||= parent.capture_content { super }
59
+ parent.output.widgets << self.class # todo: test!!!
60
+ else
61
+ super
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,98 @@
1
+ module Erector
2
+ module Convenience
3
+ # Render (like to_html) but adding newlines and indentation.
4
+ # You may just want to call to_html(:prettyprint => true)
5
+ # so you can pass in other rendering options as well.
6
+ def to_pretty(options = {})
7
+ emit(options.merge(:prettyprint => true))
8
+ end
9
+
10
+ # Render (like to_html) but stripping all tags and inserting some
11
+ # appropriate formatting. Currently we format p, br, ol, ul, and li
12
+ # tags.
13
+ def to_text(options = {})
14
+ # TODO: make text output a first class rendering strategy, like HTML is now,
15
+ # so we can do things like nested lists and numbered lists
16
+ html = to_html(options.merge(:prettyprint => false))
17
+ html.gsub!(/^<p[^>]*>/m, '')
18
+ html.gsub!(/(<(ul|ol)>)?<li>/, "\n* ")
19
+ html.gsub!(/<(\/?(ul|ol|p|br))[^>]*( \/)?>/, "\n")
20
+ CGI.unescapeHTML(html.gsub(/<[^>]*>/, ''))
21
+ end
22
+
23
+ # Emits the result of joining the elements in array with the separator.
24
+ # The array elements and separator can be Erector::Widget objects,
25
+ # which are rendered, or strings, which are html-escaped and output.
26
+ def join(array, separator)
27
+ first = true
28
+ array.each do |item|
29
+ if !first
30
+ if separator.is_a? Widget
31
+ widget separator
32
+ else
33
+ text separator
34
+ end
35
+ end
36
+ first = false
37
+ if item.is_a? Widget
38
+ widget item
39
+ else
40
+ text item
41
+ end
42
+ end
43
+ end
44
+
45
+ # Convenience method to emit a css file link, which looks like this:
46
+ # <link href="erector.css" rel="stylesheet" type="text/css" />
47
+ # The parameter is the full contents of the href attribute, including any ".css" extension.
48
+ #
49
+ # If you want to emit raw CSS inline, use the #style method instead.
50
+ def css(href, options = {})
51
+ link({:rel => 'stylesheet', :type => 'text/css', :href => href}.merge(options))
52
+ end
53
+
54
+ # Convenience method to emit an anchor tag whose href and text are the same,
55
+ # e.g. <a href="http://example.com">http://example.com</a>
56
+ def url(href, options = {})
57
+ a href, ({:href => href}.merge(options))
58
+ end
59
+
60
+ # Emits a javascript block inside a +script+ tag, wrapped in CDATA
61
+ # doohickeys like all the cool JS kids do.
62
+ def javascript(value = nil, attributes = {})
63
+ if value.is_a?(Hash)
64
+ attributes = value
65
+ value = nil
66
+ elsif block_given? && value
67
+ raise ArgumentError, "You can't pass both a block and a value to javascript -- please choose one."
68
+ end
69
+
70
+ script(attributes.merge(:type => "text/javascript")) do
71
+ # Shouldn't this be a "cdata" HtmlPart?
72
+ # (maybe, but the syntax is specific to javascript; it isn't
73
+ # really a generic XML CDATA section. Specifically,
74
+ # ]]> within value is not treated as ending the
75
+ # CDATA section by Firefox2 when parsing text/html,
76
+ # although I guess we could refuse to generate ]]>
77
+ # there, for the benefit of XML/XHTML parsers).
78
+ output << raw("\n// <![CDATA[\n")
79
+ if block_given?
80
+ yield
81
+ else
82
+ output << raw(value)
83
+ end
84
+ output << raw("\n// ]]>")
85
+ output.append_newline # this forces a newline even if we're not in pretty mode
86
+ end
87
+
88
+ output << raw("\n")
89
+ end
90
+
91
+
92
+ # makes a unique id based on the widget's class name and object id
93
+ # that you can use as the HTML id of an emitted element
94
+ def dom_id
95
+ "#{self.class.name.gsub(/:+/,"_")}_#{self.object_id}"
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,24 @@
1
+ module Erector
2
+ class Dependencies < Array
3
+ def push(*new_dependencies_args)
4
+ new_dependencies = new_dependencies_args.select do |new_dependency|
5
+ !include?(new_dependency)
6
+ end
7
+ new_dependencies.each do |dep|
8
+ unless dep.is_a? Erector::Dependency
9
+ raise "expected Dependency, got #{dep.class}: #{dep.inspect}"
10
+ end
11
+ end
12
+ super(*new_dependencies)
13
+ end
14
+
15
+ alias_method :<<, :push
16
+
17
+ def uniq
18
+ inject(self.class.new) do |memo, item|
19
+ memo << item unless memo.any? {|memo_item| memo_item == item}
20
+ memo
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,31 @@
1
+ module Erector
2
+ class Dependency
3
+ attr_reader :type, :text, :options
4
+
5
+ def initialize(type, text, options = {})
6
+ text = text.read if text.is_a? IO
7
+ text = self.class.interpolate(text) if options[:interpolate] # todo: test
8
+ @type, @text, @options = type, text, options
9
+ end
10
+
11
+ def self.interpolate(s)
12
+ eval("<<INTERPOLATE\n" + s + "\nINTERPOLATE").chomp
13
+ end
14
+
15
+ def ==(other)
16
+ (self.type == other.type and
17
+ self.text == other.text and
18
+ self.options == other.options) ? true : false
19
+ end
20
+
21
+ def eql?(other)
22
+ self == other
23
+ end
24
+
25
+ def hash
26
+ # this is a fairly inefficient hash function but it does the trick for
27
+ # now
28
+ "#{type}#{text}#{options}".hash
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,113 @@
1
+ require 'erector/abstract_widget'
2
+ require 'erector/promise'
3
+
4
+ # todo: unit test
5
+ module Erector
6
+ module Element
7
+ # Internal method used to emit an HTML/XML element, including an open tag,
8
+ # attributes (optional, via the default hash), contents (also optional),
9
+ # and close tag.
10
+ #
11
+ # Using the arcane powers of Ruby, there are magic methods that call
12
+ # +element+ for all the standard HTML tags, like +a+, +body+, +p+, and so
13
+ # forth. Look at the source of erector/html.rb for the full list.
14
+ # Unfortunately, this big mojo confuses rdoc, so we can't see each method
15
+ # in this rdoc page, but trust us, they're there.
16
+ #
17
+ # When calling one of these magic methods, put attributes in the default
18
+ # hash. If there is a string parameter, then it is used as the contents.
19
+ # If there is a block, then it is executed (yielded), and the string
20
+ # parameter is ignored. The block will usually be in the scope of the
21
+ # child widget, which means it has access to all the methods of Widget,
22
+ # which will eventually end up appending text to the +output+ string. See
23
+ # how elegant it is? Not confusing at all if you don't think about it.
24
+ #
25
+ def element(*args, &block)
26
+ _element(*args, &block)
27
+ end
28
+
29
+ # Internal method used to emit a self-closing HTML/XML element, including
30
+ # a tag name and optional attributes (passed in via the default hash).
31
+ #
32
+ # Using the arcane powers of Ruby, there are magic methods that call
33
+ # +empty_element+ for all the standard HTML tags, like +img+, +br+, and so
34
+ # forth. Look at the source of #self_closing_tags for the full list.
35
+ # Unfortunately, this big mojo confuses rdoc, so we can't see each method
36
+ # in this rdoc page, but trust us, they're there.
37
+ #
38
+ def empty_element(*args, &block)
39
+ _empty_element(*args, &block)
40
+ end
41
+
42
+ # moved to Promise
43
+ # # Emits an open tag, comprising '<', tag name, optional attributes, and '>'
44
+ # def open_tag(promise)
45
+ # output.newline if newliney?(promise._tag_name) && !output.at_line_start?
46
+ # output << promise._open_tag
47
+ # output.indent
48
+ # end
49
+ #
50
+ # # Emits a close tag, consisting of '<', '/', tag name, and '>'
51
+ # def close_tag(promise)
52
+ # output.undent
53
+ # output << promise._close_tag
54
+ # if newliney?(promise._tag_name)
55
+ # output.newline
56
+ # end
57
+ # end
58
+ #
59
+ # def inside_tag value, block
60
+ # if block
61
+ # block.call
62
+ # else
63
+ # text value
64
+ # end
65
+ # end
66
+
67
+ def _element(tag_name, *args, &block)
68
+ if args.length > 2
69
+ raise ArgumentError, "too many args"
70
+ end
71
+ attributes, value = nil, nil
72
+ arg0 = args[0]
73
+ if arg0.is_a?(Hash)
74
+ attributes = arg0
75
+ else
76
+ value = arg0
77
+ arg1 = args[1]
78
+ if arg1.is_a?(Hash)
79
+ attributes = arg1
80
+ end
81
+ end
82
+
83
+ if block && value
84
+ raise ArgumentError, "You can't pass both a block and a value to #{tag_name} -- please choose one."
85
+ end
86
+
87
+ attributes ||= {}
88
+ promise = if !value.nil?
89
+ Promise.new(output, tag_name, attributes, false, newliney?(tag_name)) do
90
+ if value.is_a? AbstractWidget
91
+ widget value
92
+ else
93
+ text value
94
+ end
95
+ end
96
+ elsif block
97
+ Promise.new(output, tag_name, attributes, false, newliney?(tag_name), &block)
98
+ else
99
+ Promise.new(output, tag_name, attributes, false, newliney?(tag_name))
100
+ end
101
+ promise._render
102
+ promise
103
+ end
104
+
105
+ def _empty_element(tag_name, attributes={})
106
+ promise = Promise.new(output, tag_name, attributes, true, newliney?(tag_name))
107
+ promise._render
108
+ promise
109
+ end
110
+
111
+
112
+ end
113
+ end
@@ -0,0 +1,104 @@
1
+ module Erector
2
+
3
+ # Externals are a mechanism by which a widget can declare page-level
4
+ # resources upon which it depends. They are not emitted during the widget's
5
+ # normal emiting process. Rather, the Erector::Widget::Page keeps track of
6
+ # all the widgets it emits, then goes back and inserts the proper tags for
7
+ # all the externals inside its HEAD element.
8
+ module Externals
9
+ def self.included(base)
10
+ base.extend ClassMethods
11
+ end
12
+
13
+ module ClassMethods
14
+
15
+ # Express a dependency of this widget
16
+ # Multiple forms:
17
+ # depends_on(type, text, options = {})
18
+ # for example
19
+ # depends_on(:js, '/foo.js', :embed=>true)
20
+ #
21
+ # Other variants:
22
+ # depends_on(type, an_io, ... # file to be read
23
+ # depends_on('blah.js' ... infer :js
24
+ # depends_on('blah.css' ... infer :css
25
+ # depends on :js, 'file1.js', 'file2.js'... [options]
26
+ # depends_on :js => ["foo.js", "bar.js"], :css=>['file.css']
27
+ # depends_on :js => ["foo.js", "bar.js"], other_option=>:blah
28
+ def depends_on(*args)
29
+ x = interpret_args(*args)
30
+ my_dependencies.push(x)
31
+ end
32
+
33
+ # deprecated in favor of #depends_on
34
+ # todo: warning
35
+ def external(type, value, options = {})
36
+ type = type.to_sym
37
+ x = Dependency.new(type, value, options)
38
+ my_dependencies << x unless my_dependencies.include?(x)
39
+ end
40
+
41
+ # returns all dependencies of the given type from this class and all its
42
+ # superclasses
43
+ def dependencies(type)
44
+ type = type.to_sym
45
+ deps = Dependencies.new
46
+ deps.push(*superclass.dependencies(type)) if superclass.respond_to?(:dependencies)
47
+ deps.push(*my_dependencies.select { |x| x.type == type })
48
+ deps.uniq
49
+ end
50
+
51
+ def my_dependencies
52
+ @my_dependencies ||= Dependencies.new
53
+ end
54
+
55
+ private
56
+ INFERABLE_TYPES = [:css, :js]
57
+
58
+ def interpret_args(*args)
59
+ options = {}
60
+ options = args.pop if args.last.is_a?(::Hash)
61
+ if args.empty? && options.any?
62
+ deps = []
63
+ texts_hash = {}
64
+ INFERABLE_TYPES.each do |t|
65
+ texts_hash[t] = options.delete(t) if options.has_key? t
66
+ end
67
+ texts_hash.each do |t, texts|
68
+ [texts].flatten.each do |text|
69
+ deps << interpret_args(t, text, options)
70
+ end
71
+ end
72
+ return deps
73
+ elsif args[0].class == Symbol
74
+ type = args.shift
75
+ else
76
+ type = /.+\.js/.match(args[0]) ? :js : :css
77
+ end
78
+
79
+ deps = args.map do |text|
80
+ Dependency.new(type, text, options)
81
+ end
82
+ deps.size == 1 ? deps.first : deps
83
+ end
84
+ end
85
+
86
+ def render_with_externals(options_to_external_emiter = {})
87
+ output = Erector::Output.new
88
+ self.to_a(:output => output) # emit all the externals onto this new output buffer
89
+ nested_widgets = output.widgets.to_a
90
+ options_to_external_emiter = {:classes => nested_widgets}.merge(options_to_external_emiter)
91
+ emiter = ExternalRenderer.new(options_to_external_emiter)
92
+ externals = emiter.to_a(:output => output)
93
+ output.to_a
94
+ end
95
+
96
+ def render_externals(options_to_external_emiter = {})
97
+ output_for_externals = Erector::Output.new
98
+ nested_widgets = output.widgets
99
+ externalizer = ExternalRenderer.new({:classes => nested_widgets}.merge(options_to_external_emiter))
100
+ externalizer._emit(:output => output_for_externals)
101
+ output_for_externals.to_a
102
+ end
103
+ end
104
+ end