pivotal-erector 0.5.1 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.txt +5 -5
- data/VERSION.yml +2 -2
- data/bin/{erect → erector} +0 -0
- data/lib/erector/erect.rb +1 -1
- data/lib/erector/erected.rb +1 -1
- data/lib/erector/rails/extensions/action_controller.rb +25 -7
- data/lib/erector/rails/extensions/{widget.rb → rails_widget/helpers.rb} +2 -9
- data/lib/erector/rails/extensions/{widget/2.2.0/widget.rb → rails_widget.rb} +4 -2
- data/lib/erector/rails/rails_version.rb +6 -0
- data/lib/erector/rails/template_handlers/action_view_template_handler.rb +43 -11
- data/lib/erector/rails.rb +2 -1
- data/lib/erector/version.rb +1 -1
- data/lib/erector/widget.rb +213 -73
- data/lib/erector/widgets/table.rb +3 -3
- data/spec/erector/indentation_spec.rb +39 -24
- data/spec/erector/widget_spec.rb +197 -64
- data/spec/erector/widgets/table_spec.rb +3 -3
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +1 -4
- data/spec/spec_suite.rb +6 -12
- metadata +18 -30
- data/lib/erector/rails/extensions/action_controller/1.2.5/action_controller.rb +0 -17
- data/lib/erector/rails/extensions/action_controller/2.2.0/action_controller.rb +0 -26
- data/lib/erector/rails/extensions/widget/1.2.5/widget.rb +0 -18
- data/lib/erector/rails/supported_rails_versions.rb +0 -14
- data/lib/erector/rails/template_handlers/1.2.5/action_view_template_handler.rb +0 -32
- data/lib/erector/rails/template_handlers/2.0.0/action_view_template_handler.rb +0 -36
- data/lib/erector/rails/template_handlers/2.1.0/action_view_template_handler.rb +0 -31
- data/lib/erector/rails/template_handlers/2.2.0/action_view_template_handler.rb +0 -46
- data/spec/erect/erect_spec.rb +0 -145
- data/spec/erect/erected_spec.rb +0 -80
- data/spec/erect/rhtml_parser_spec.rb +0 -318
data/README.txt
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
= Erector
|
2
2
|
|
3
3
|
* http://erector.rubyforge.org
|
4
|
-
* mailto:erector
|
4
|
+
* mailto:erector@googlegroups.com
|
5
5
|
* http://www.pivotaltracker.com/projects/482
|
6
6
|
|
7
7
|
== DESCRIPTION
|
@@ -18,20 +18,20 @@ project site at http://erector.rubyforge.org for more documentation.
|
|
18
18
|
require 'erector'
|
19
19
|
|
20
20
|
class Hello < Erector::Widget
|
21
|
-
def
|
21
|
+
def content
|
22
22
|
html do
|
23
23
|
head do
|
24
24
|
title "Hello"
|
25
25
|
end
|
26
26
|
body do
|
27
27
|
text "Hello, "
|
28
|
-
b "
|
28
|
+
b "#{target}!", :class => 'big'
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
-
Hello.new.to_s
|
34
|
+
Hello.new(:target => 'world').to_s
|
35
35
|
=> "<html><head><title>Hello</title></head><body>Hello, <b class=\"big\">world!</b></body></html>"
|
36
36
|
|
37
37
|
== REQUIREMENTS
|
@@ -59,7 +59,7 @@ When installing this way, erector is automatically available to your Rails code
|
|
59
59
|
|
60
60
|
(The MIT License)
|
61
61
|
|
62
|
-
Copyright (c) 2007-
|
62
|
+
Copyright (c) 2007-2009 Pivotal Labs
|
63
63
|
|
64
64
|
Permission is hereby granted, free of charge, to any person obtaining
|
65
65
|
a copy of this software and associated documentation files (the
|
data/VERSION.yml
CHANGED
data/bin/{erect → erector}
RENAMED
File without changes
|
data/lib/erector/erect.rb
CHANGED
@@ -11,7 +11,7 @@ module Erector
|
|
11
11
|
@output_dir = nil
|
12
12
|
|
13
13
|
opts = OptionParser.new do |opts|
|
14
|
-
opts.banner = "Usage:
|
14
|
+
opts.banner = "Usage: erector [options] [file|dir]*"
|
15
15
|
|
16
16
|
opts.separator "Converts from html/rhtml files to erector widgets, or from erector widgets to html files"
|
17
17
|
opts.separator ""
|
data/lib/erector/erected.rb
CHANGED
@@ -1,8 +1,26 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
else
|
7
|
-
|
1
|
+
ActionController::Base.class_eval do
|
2
|
+
def render_widget(widget_class, assigns=nil)
|
3
|
+
@__widget_class = widget_class
|
4
|
+
if assigns
|
5
|
+
@__widget_assigns = assigns
|
6
|
+
else
|
7
|
+
@__widget_assigns = {}
|
8
|
+
variables = instance_variable_names
|
9
|
+
variables -= protected_instance_variables
|
10
|
+
variables.each do |name|
|
11
|
+
@__widget_assigns[name.sub('@', "")] = instance_variable_get(name)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
response.template.send(:_evaluate_assigns_and_ivars)
|
15
|
+
render :inline => "<% @__widget_class.new(@__widget_assigns).to_s(:output => output_buffer, :helpers => self) %>"
|
16
|
+
end
|
17
|
+
|
18
|
+
def render_with_erector_widget(*options, &block)
|
19
|
+
if options.first.is_a?(Hash) && widget = options.first.delete(:widget)
|
20
|
+
render_widget widget, @assigns, &block
|
21
|
+
else
|
22
|
+
render_without_erector_widget *options, &block
|
23
|
+
end
|
24
|
+
end
|
25
|
+
alias_method_chain :render, :erector_widget
|
8
26
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module Erector
|
2
|
-
Widget
|
2
|
+
class RailsWidget < Widget
|
3
3
|
include ActionController::UrlWriter
|
4
4
|
|
5
5
|
# helpers returning raw text
|
@@ -105,13 +105,6 @@ module Erector
|
|
105
105
|
|
106
106
|
def pluralize(*args)
|
107
107
|
helpers.pluralize(*args)
|
108
|
-
end
|
108
|
+
end
|
109
109
|
end
|
110
110
|
end
|
111
|
-
|
112
|
-
dir = File.dirname(__FILE__)
|
113
|
-
if ActionView::Base.instance_methods.include?("output_buffer")
|
114
|
-
require "#{dir}/widget/2.2.0/widget"
|
115
|
-
else
|
116
|
-
require "#{dir}/widget/1.2.5/widget"
|
117
|
-
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module Erector
|
2
|
-
Widget
|
2
|
+
class RailsWidget < Widget
|
3
3
|
def output
|
4
4
|
process_output_buffer || @output
|
5
5
|
end
|
@@ -7,7 +7,7 @@ module Erector
|
|
7
7
|
def capture_with_helpers(&block)
|
8
8
|
helpers ? helpers.capture(&block) : capture_without_helpers(&block)
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
alias_method_chain :capture, :helpers
|
12
12
|
|
13
13
|
# This is here to force #helpers.capture to return the output
|
@@ -34,3 +34,5 @@ module Erector
|
|
34
34
|
end
|
35
35
|
end
|
36
36
|
end
|
37
|
+
|
38
|
+
require "#{File.dirname(__FILE__)}/rails_widget/helpers"
|
@@ -1,14 +1,46 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
1
|
+
module ActionView #:nodoc:
|
2
|
+
module TemplateHandlers #:nodoc:
|
3
|
+
class Erector < TemplateHandler
|
4
|
+
include Compilable
|
5
|
+
def self.line_offset
|
6
|
+
2
|
7
|
+
end
|
8
|
+
|
9
|
+
ActionView::Template.instance_eval do
|
10
|
+
register_template_handler :rb, ActionView::TemplateHandlers::Erector
|
11
|
+
end
|
12
|
+
|
13
|
+
def compile(template)
|
14
|
+
relative_path_parts = template.path.split('/')
|
15
|
+
|
16
|
+
is_partial = relative_path_parts.last =~ /^_/
|
17
|
+
require_dependency File.expand_path(template.filename)
|
18
|
+
|
19
|
+
widget_class_parts = relative_path_parts.inject(['Views']) do |class_parts, node|
|
20
|
+
class_parts << node.gsub(/^_/, "").gsub(/(\.html)?\.rb$/, '').camelize
|
21
|
+
class_parts
|
22
|
+
end
|
23
|
+
widget_class_name = widget_class_parts.join("::")
|
24
|
+
render_method = is_partial ? 'render_partial' : 'content'
|
25
|
+
|
26
|
+
erb_template = <<-ERB
|
27
|
+
<%
|
28
|
+
assigns = instance_variables.inject({}) do |hash, name|
|
29
|
+
hash[name.sub('@', "")] = instance_variable_get(name)
|
30
|
+
hash
|
31
|
+
end
|
32
|
+
|
33
|
+
widget = #{widget_class_name}.new(assigns)
|
34
|
+
widget.to_s(:output => output_buffer, :helpers => self, :content_method_name => :#{render_method})
|
35
|
+
%>
|
36
|
+
ERB
|
37
|
+
::ERB.new(
|
38
|
+
erb_template,
|
39
|
+
nil,
|
40
|
+
::ActionView::TemplateHandlers::ERB.erb_trim_mode,
|
41
|
+
"@output_buffer"
|
42
|
+
).src
|
43
|
+
end
|
8
44
|
end
|
9
|
-
else
|
10
|
-
require File.expand_path("#{dir}/2.0.0/action_view_template_handler")
|
11
45
|
end
|
12
|
-
else
|
13
|
-
require File.expand_path("#{dir}/1.2.5/action_view_template_handler")
|
14
46
|
end
|
data/lib/erector/rails.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
dir = File.dirname(__FILE__)
|
2
2
|
require "action_controller"
|
3
|
-
require "#{dir}/rails/
|
3
|
+
require "#{dir}/rails/rails_version"
|
4
|
+
require "#{dir}/rails/extensions/rails_widget"
|
4
5
|
require "#{dir}/rails/extensions/action_controller"
|
5
6
|
require "#{dir}/rails/extensions/action_view"
|
6
7
|
require "#{dir}/rails/template_handlers/action_view_template_handler"
|
data/lib/erector/version.rb
CHANGED
@@ -4,7 +4,7 @@ module Erector
|
|
4
4
|
if !Erector.const_defined?(:VERSION)
|
5
5
|
dir = File.dirname(__FILE__)
|
6
6
|
version = YAML.load_file(File.expand_path("#{dir}/../../VERSION.yml"))
|
7
|
-
VERSION = "#{version[
|
7
|
+
VERSION = "#{version[:major]}.#{version[:minor]}.#{version[:patch]}"
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
data/lib/erector/widget.rb
CHANGED
@@ -3,17 +3,27 @@ module Erector
|
|
3
3
|
# A Widget is the center of the Erector universe.
|
4
4
|
#
|
5
5
|
# To create a widget, extend Erector::Widget and implement
|
6
|
-
# the +
|
6
|
+
# the +content+ method. Inside this method you may call any of the tag methods like +span+ or +p+ to emit HTML/XML
|
7
7
|
# tags.
|
8
8
|
#
|
9
9
|
# You can also define a widget on the fly by passing a block to +new+. This block will get executed when the widget's
|
10
|
-
# +
|
10
|
+
# +content+ method is called.
|
11
11
|
#
|
12
12
|
# To render a widget from the outside, instantiate it and call its +to_s+ method.
|
13
|
+
#
|
14
|
+
# A widget's +new+ method optionally accepts an options hash. Entries in this hash are converted to instance
|
15
|
+
# variables, and +attr_reader+ accessors are defined for each.
|
16
|
+
#
|
17
|
+
# TODO: You can add runtime input checking via the +needs+ macro. If any of the variables named via
|
18
|
+
# +needs+ are absent, an exception is thrown. Optional variables are specified with +wants+. If a variable appears
|
19
|
+
# in the options hash that is in neither the +needs+ nor +wants+ lists, then that too provokes an exception.
|
20
|
+
# This mechanism is meant to ameliorate development-time confusion about exactly what parameters are supported
|
21
|
+
# by a given widget, avoiding confusing runtime NilClass errors.
|
13
22
|
#
|
14
|
-
# To call one widget from another, inside the parent widget's
|
15
|
-
#
|
16
|
-
# is used, which gives better performance than using +capture+ or +to_s+.
|
23
|
+
# To call one widget from another, inside the parent widget's +content+ method, instantiate the child widget and call
|
24
|
+
# the +widget+ method. This assures that the same output stream
|
25
|
+
# is used, which gives better performance than using +capture+ or +to_s+. It also preserves the indentation and
|
26
|
+
# helpers of the enclosing class.
|
17
27
|
#
|
18
28
|
# In this documentation we've tried to keep the distinction clear between methods that *emit* text and those that
|
19
29
|
# *return* text. "Emit" means that it writes to the output stream; "return" means that it returns a string
|
@@ -64,13 +74,64 @@ module Erector
|
|
64
74
|
raise ArgumentError, "You must provide either an instance or a block"
|
65
75
|
end
|
66
76
|
end
|
67
|
-
|
77
|
+
|
68
78
|
protected
|
69
79
|
def after_initialize_parts
|
70
80
|
@after_initialize_parts ||= []
|
71
81
|
end
|
72
82
|
end
|
73
83
|
|
84
|
+
# Class method by which widget classes can declare that they need certain parameters.
|
85
|
+
# If needed parameters are not passed in to #new, then an exception will be thrown
|
86
|
+
# (with a hopefully useful message about which parameters are missing). This is intended
|
87
|
+
# to catch silly bugs like passing in a parameter called 'name' to a widget that expects
|
88
|
+
# a parameter called 'title'. Every variable declared in 'needs' will get an attr_reader
|
89
|
+
# accessor declared for it.
|
90
|
+
#
|
91
|
+
# You can also declare default values for parameters using hash syntax. You can put #needs
|
92
|
+
# declarations on multiple lines or on the same line; the only caveat is that if there are
|
93
|
+
# default values, they all have to be at the end of the line (so they go into the magic
|
94
|
+
# hash parameter).
|
95
|
+
#
|
96
|
+
# If a widget has no #needs declaration then it will accept any combination of parameters
|
97
|
+
# (and make accessors for them) just like normal. In that case there will be no 'attr_reader's
|
98
|
+
# declared.
|
99
|
+
# If a widget wants to declare that it
|
100
|
+
# takes no parameters, use the special incantation "needs nil" (and don't declare any other
|
101
|
+
# needs, or kittens will cry).
|
102
|
+
#
|
103
|
+
# Usage:
|
104
|
+
# class FancyForm < Erector::Widget
|
105
|
+
# needs :title, :show_okay => true, :show_cancel => false
|
106
|
+
# ...
|
107
|
+
# end
|
108
|
+
#
|
109
|
+
# That means that
|
110
|
+
# FancyForm.new(:title => 'Login')
|
111
|
+
# will succeed, as will
|
112
|
+
# FancyForm.new(:title => 'Login', :show_cancel => true)
|
113
|
+
# but
|
114
|
+
# FancyForm.new(:name => 'Login')
|
115
|
+
# will fail.
|
116
|
+
#
|
117
|
+
def self.needs(*args)
|
118
|
+
args.each do |arg|
|
119
|
+
(@needs ||= []) << (arg.nil? ? nil : (arg.is_a? Hash) ? arg : arg.to_sym)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
protected
|
124
|
+
def self.get_needs
|
125
|
+
@needs ||= []
|
126
|
+
parent = self.ancestors[1]
|
127
|
+
if parent.respond_to? :get_needs
|
128
|
+
parent.get_needs + @needs
|
129
|
+
else
|
130
|
+
@needs
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
public
|
74
135
|
@@prettyprint_default = false
|
75
136
|
def prettyprint_default
|
76
137
|
@@prettyprint_default
|
@@ -87,95 +148,163 @@ module Erector
|
|
87
148
|
|
88
149
|
SPACES_PER_INDENT = 2
|
89
150
|
|
90
|
-
attr_reader :helpers, :assigns, :block, :parent, :output
|
91
|
-
attr_accessor :enable_prettyprint
|
151
|
+
attr_reader :helpers, :assigns, :block, :parent, :output, :prettyprint, :indentation
|
92
152
|
|
93
|
-
def initialize(
|
153
|
+
def initialize(assigns={}, &block)
|
154
|
+
unless assigns.is_a? Hash
|
155
|
+
raise "Erector's API has changed. Now you should pass only an options hash into Widget.new; the rest come in via to_s, or by using #widget."
|
156
|
+
end
|
157
|
+
if (respond_to? :render) &&
|
158
|
+
!self.method(:render).to_s.include?("(RailsWidget)")
|
159
|
+
raise "Erector's API has changed. You should rename #{self.class}#render to #content."
|
160
|
+
end
|
94
161
|
@assigns = assigns
|
95
162
|
assign_locals(assigns)
|
96
|
-
@helpers = helpers
|
97
163
|
@parent = block ? eval("self", block.binding) : nil
|
98
|
-
@output = output
|
99
164
|
@block = block
|
100
|
-
@at_start_of_line = true
|
101
|
-
@indent = 0
|
102
|
-
@enable_prettyprint = prettyprint_default
|
103
165
|
self.class.after_initialize self
|
104
166
|
end
|
105
167
|
|
106
168
|
#-- methods for other classes to call, left public for ease of testing and documentation
|
107
169
|
#++
|
108
170
|
|
171
|
+
protected
|
172
|
+
def context(output, prettyprint = false, indentation = 0, helpers = nil)
|
173
|
+
#TODO: pass in options hash, maybe, instead of parameters
|
174
|
+
original_output = @output
|
175
|
+
original_indendation = @indentation
|
176
|
+
original_helpers = @helpers
|
177
|
+
original_prettyprint = @prettyprint
|
178
|
+
@output = output
|
179
|
+
@at_start_of_line = true
|
180
|
+
raise "indentation must be a number, not #{indentation.inspect}" unless indentation.is_a? Fixnum
|
181
|
+
@indentation = indentation
|
182
|
+
@helpers = helpers
|
183
|
+
@prettyprint = prettyprint
|
184
|
+
yield
|
185
|
+
ensure
|
186
|
+
@output = original_output
|
187
|
+
@indentation = original_indendation
|
188
|
+
@helpers = original_helpers
|
189
|
+
@prettyprint = original_prettyprint
|
190
|
+
end
|
191
|
+
|
192
|
+
public
|
109
193
|
def assign_locals(local_assigns)
|
110
|
-
|
111
|
-
|
194
|
+
needed = self.class.get_needs.map{|need| need.is_a?(Hash) ? need.keys : need}.flatten
|
195
|
+
assigned = []
|
196
|
+
local_assigns.each do |name, value|
|
197
|
+
unless needed.empty? || needed.include?(name)
|
198
|
+
raise "Unknown parameter '#{name}'. #{self.class.name} only accepts #{needed.join(', ')}"
|
199
|
+
end
|
200
|
+
assign_local(name, value)
|
201
|
+
assigned << name
|
202
|
+
end
|
203
|
+
|
204
|
+
# set variables with default values
|
205
|
+
self.class.get_needs.select{|var| var.is_a? Hash}.each do |hash|
|
206
|
+
hash.each_pair do |name, value|
|
207
|
+
unless assigned.include?(name)
|
208
|
+
assign_local(name, value)
|
209
|
+
assigned << name
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
missing = needed - assigned
|
215
|
+
unless missing.empty? || missing == [nil]
|
216
|
+
raise "Missing parameter#{missing.size == 1 ? '' : 's'}: #{missing.join(', ')}"
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
def assign_local(name, value)
|
221
|
+
instance_variable_set("@#{name}", value)
|
222
|
+
if any_are_needed?
|
112
223
|
metaclass.module_eval do
|
113
224
|
attr_reader name
|
114
225
|
end
|
115
226
|
end
|
116
227
|
end
|
117
228
|
|
118
|
-
|
119
|
-
|
120
|
-
# (either in terms of how it is enabled, or in terms of
|
121
|
-
# what decisions Erector makes about where to add whitespace).
|
122
|
-
# This flag should be set prior to any rendering being done
|
123
|
-
# (for example, calls to to_s or to_pretty).
|
124
|
-
def enable_prettyprint(enable)
|
125
|
-
self.enable_prettyprint = enable
|
126
|
-
self
|
229
|
+
def any_are_needed?
|
230
|
+
!self.class.get_needs.empty?
|
127
231
|
end
|
128
|
-
|
232
|
+
|
129
233
|
# Render (like to_s) but adding newlines and indentation.
|
234
|
+
# This is a convenience method; you may just want to call to_s(:prettyprint => true)
|
235
|
+
# so you can pass in other rendering options as well.
|
130
236
|
def to_pretty
|
131
|
-
|
237
|
+
to_s(:prettyprint => true)
|
132
238
|
end
|
133
239
|
|
134
|
-
# Entry point for rendering a widget (and all its children). This method creates a new output string,
|
135
|
-
# calls this widget's #
|
240
|
+
# Entry point for rendering a widget (and all its children). This method creates a new output string (if necessary),
|
241
|
+
# calls this widget's #content method and returns the string.
|
242
|
+
#
|
243
|
+
# Options:
|
244
|
+
# output:: the string to output to. Default: a new empty string
|
245
|
+
# prettyprint:: whether Erector should add newlines and indentation. Default: the value of prettyprint_default (which is false by default).
|
246
|
+
# indentation:: the amount of spaces to indent. Ignored unless prettyprint is true.
|
247
|
+
# helpers:: a helpers object containing utility methods. Usually this is a Rails view object.
|
248
|
+
# content_method_name:: in case you want to call a method other than #content, pass its name in here.
|
136
249
|
#
|
137
|
-
#
|
138
|
-
#
|
139
|
-
#
|
140
|
-
|
141
|
-
|
142
|
-
#
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
250
|
+
# Note: Prettyprinting is an experimental feature and is subject to change
|
251
|
+
# (either in terms of how it is enabled, or in terms of
|
252
|
+
# what decisions Erector makes about where to add whitespace).
|
253
|
+
def to_s(options = {}, &blk)
|
254
|
+
|
255
|
+
raise "Erector::Widget#to_s now takes an options hash, not a symbol. Try calling \"to_s(:content_method_name=> :#{options})\"" if options.is_a? Symbol
|
256
|
+
|
257
|
+
options = {
|
258
|
+
:output => "",
|
259
|
+
:prettyprint => prettyprint_default,
|
260
|
+
:indentation => 0,
|
261
|
+
:helpers => nil,
|
262
|
+
:content_method_name => :content,
|
263
|
+
}.merge(options)
|
264
|
+
context(options[:output], options[:prettyprint], options[:indentation], options[:helpers]) do
|
265
|
+
send(options[:content_method_name], &blk)
|
266
|
+
output.to_s
|
267
|
+
end
|
147
268
|
end
|
148
269
|
|
149
270
|
alias_method :inspect, :to_s
|
150
|
-
|
271
|
+
|
151
272
|
# Template method which must be overridden by all widget subclasses. Inside this method you call the magic
|
152
273
|
# #element methods which emit HTML and text to the output string.
|
153
|
-
def
|
274
|
+
def content
|
154
275
|
if @block
|
155
276
|
instance_eval(&@block)
|
156
277
|
end
|
157
278
|
end
|
158
279
|
|
159
|
-
# To call one widget from another, inside the parent widget's
|
160
|
-
# its +
|
280
|
+
# To call one widget from another, inside the parent widget's +content+ method, instantiate the child widget and call
|
281
|
+
# its +write_via+ method, passing in +self+. This assures that the same output string
|
161
282
|
# is used, which gives better performance than using +capture+ or +to_s+.
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
@output = output_or_widget
|
283
|
+
# You can also use the +widget+ method.
|
284
|
+
def write_via(parent)
|
285
|
+
@parent = parent
|
286
|
+
context(parent.output, parent.prettyprint, parent.indentation, parent.helpers) do
|
287
|
+
content
|
168
288
|
end
|
169
|
-
render
|
170
289
|
end
|
171
290
|
|
172
|
-
#
|
173
|
-
# a
|
174
|
-
#
|
175
|
-
#
|
176
|
-
|
177
|
-
|
178
|
-
|
291
|
+
# Emits a (nested) widget onto the current widget's output stream. Accepts either
|
292
|
+
# a class or an instance. If the first argument is a class, then the second argument
|
293
|
+
# is a hash used to populate its instance variables. If the first argument is an
|
294
|
+
# instance then the hash must be unspecified (or empty).
|
295
|
+
#
|
296
|
+
# The sub-widget will have access to the methods of the parent class, via some method_missing
|
297
|
+
# magic and a "parent" pointer.
|
298
|
+
def widget(target, assigns={}, &block)
|
299
|
+
child = if target.is_a? Class
|
300
|
+
target.new(assigns, &block)
|
301
|
+
else
|
302
|
+
unless assigns.empty?
|
303
|
+
raise "Unexpected second parameter. Did you mean to pass in variables when you instantiated the #{target.class.to_s}?"
|
304
|
+
end
|
305
|
+
target
|
306
|
+
end
|
307
|
+
child.write_via(self)
|
179
308
|
end
|
180
309
|
|
181
310
|
# (Should we make this hidden?)
|
@@ -227,7 +356,7 @@ module Erector
|
|
227
356
|
# Emits an open tag, comprising '<', tag name, optional attributes, and '>'
|
228
357
|
def open_tag(tag_name, attributes={})
|
229
358
|
indent_for_open_tag(tag_name)
|
230
|
-
@
|
359
|
+
@indentation += SPACES_PER_INDENT
|
231
360
|
|
232
361
|
output.concat "<#{tag_name}#{format_attributes(attributes)}>"
|
233
362
|
@at_start_of_line = false
|
@@ -239,7 +368,11 @@ module Erector
|
|
239
368
|
# If another kind of object is passed in, the result of calling
|
240
369
|
# its to_s method will be treated as a string would be.
|
241
370
|
def text(value)
|
242
|
-
|
371
|
+
if value.is_a? Widget
|
372
|
+
widget value
|
373
|
+
else
|
374
|
+
output.concat(value.html_escape)
|
375
|
+
end
|
243
376
|
@at_start_of_line = false
|
244
377
|
nil
|
245
378
|
end
|
@@ -279,20 +412,19 @@ module Erector
|
|
279
412
|
|
280
413
|
# Emits a close tag, consisting of '<', tag name, and '>'
|
281
414
|
def close_tag(tag_name)
|
282
|
-
@
|
415
|
+
@indentation -= SPACES_PER_INDENT
|
283
416
|
indent()
|
284
417
|
|
285
418
|
output.concat("</#{tag_name}>")
|
286
419
|
|
287
420
|
if newliney(tag_name)
|
288
|
-
|
289
|
-
@at_start_of_line = true
|
421
|
+
_newline
|
290
422
|
end
|
291
423
|
end
|
292
424
|
|
293
425
|
# Emits the result of joining the elements in array with the separator.
|
294
426
|
# The array elements and separator can be Erector::Widget objects,
|
295
|
-
# which are rendered, or strings, which are
|
427
|
+
# which are rendered, or strings, which are html-escaped and output.
|
296
428
|
def join(array, separator)
|
297
429
|
first = true
|
298
430
|
array.each do |widget_or_text|
|
@@ -311,7 +443,7 @@ module Erector
|
|
311
443
|
|
312
444
|
# Creates a whole new output string, executes the block, then converts the output string to a string and
|
313
445
|
# emits it as raw text. If at all possible you should avoid this method since it hurts performance,
|
314
|
-
# and use
|
446
|
+
# and use +content+ or +write_via+ instead.
|
315
447
|
def capture(&block)
|
316
448
|
begin
|
317
449
|
original_output = output
|
@@ -382,10 +514,11 @@ module Erector
|
|
382
514
|
rawtext "\n"
|
383
515
|
end
|
384
516
|
|
385
|
-
# Convenience method to emit a css file link, which looks like this:
|
517
|
+
# Convenience method to emit a css file link, which looks like this:
|
518
|
+
# <link href="erector.css" rel="stylesheet" type="text/css" />
|
386
519
|
# The parameter is the full contents of the href attribute, including any ".css" extension.
|
387
520
|
#
|
388
|
-
# If you want to emit raw CSS inline, use the #
|
521
|
+
# If you want to emit raw CSS inline, use the #style method instead.
|
389
522
|
def css(href)
|
390
523
|
link :rel => 'stylesheet', :type => 'text/css', :href => href
|
391
524
|
end
|
@@ -396,7 +529,7 @@ module Erector
|
|
396
529
|
end
|
397
530
|
|
398
531
|
def newliney(tag_name)
|
399
|
-
if @
|
532
|
+
if @prettyprint
|
400
533
|
!NON_NEWLINEY.include?(tag_name)
|
401
534
|
else
|
402
535
|
false
|
@@ -434,6 +567,9 @@ protected
|
|
434
567
|
end
|
435
568
|
attributes ||= {}
|
436
569
|
open_tag tag_name, attributes
|
570
|
+
if block && value
|
571
|
+
raise ArgumentError, "You can't pass both a block and a value to #{tag_name} -- please choose one."
|
572
|
+
end
|
437
573
|
if block
|
438
574
|
instance_eval(&block)
|
439
575
|
else
|
@@ -448,23 +584,27 @@ protected
|
|
448
584
|
output.concat "<#{tag_name}#{format_attributes(attributes)} />"
|
449
585
|
|
450
586
|
if newliney(tag_name)
|
451
|
-
|
452
|
-
@at_start_of_line = true
|
587
|
+
_newline
|
453
588
|
end
|
454
589
|
end
|
590
|
+
|
591
|
+
def _newline
|
592
|
+
return unless @prettyprint
|
593
|
+
output.concat "\n"
|
594
|
+
@at_start_of_line = true
|
595
|
+
end
|
455
596
|
|
456
597
|
def indent_for_open_tag(tag_name)
|
598
|
+
return unless @prettyprint
|
457
599
|
if !@at_start_of_line && newliney(tag_name)
|
458
|
-
|
459
|
-
@at_start_of_line = true
|
600
|
+
_newline
|
460
601
|
end
|
461
|
-
|
462
602
|
indent()
|
463
603
|
end
|
464
604
|
|
465
605
|
def indent()
|
466
606
|
if @at_start_of_line
|
467
|
-
output.concat " " * @
|
607
|
+
output.concat " " * @indentation
|
468
608
|
end
|
469
609
|
end
|
470
610
|
|