pivotal-erector 0.5.1 → 0.6.0
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.
- 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
|
|