erector 0.6.3 → 0.6.4
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 +10 -5
- data/VERSION.yml +1 -1
- data/bin/erector +3 -1
- data/lib/erector.rb +1 -0
- data/lib/erector/erect.rb +4 -2
- data/lib/erector/erected.rb +14 -4
- data/lib/erector/mixin.rb +7 -0
- data/lib/erector/rhtml.treetop +48 -11
- data/lib/erector/widget.rb +146 -113
- data/spec/erect/erect_rails_spec.rb +62 -0
- data/spec/erect/erect_spec.rb +165 -0
- data/spec/erect/erected_spec.rb +93 -0
- data/spec/erect/rhtml_parser_spec.rb +351 -0
- data/spec/erector/mixin_spec.rb +54 -0
- data/spec/erector/widget_spec.rb +38 -0
- metadata +8 -2
data/README.txt
CHANGED
@@ -25,20 +25,25 @@ project site at http://erector.rubyforge.org for more documentation.
|
|
25
25
|
end
|
26
26
|
body do
|
27
27
|
text "Hello, "
|
28
|
-
b
|
28
|
+
b target, :class => 'big'
|
29
|
+
text "!"
|
29
30
|
end
|
30
31
|
end
|
31
32
|
end
|
32
33
|
end
|
33
34
|
|
34
35
|
Hello.new(:target => 'world').to_s
|
35
|
-
=> "<html><head><title>Hello</title></head><body>Hello, <b class=\"big\">world
|
36
|
+
=> "<html><head><title>Hello</title></head><body>Hello, <b class=\"big\">world</b>!</body></html>"
|
37
|
+
|
38
|
+
include Erector::Mixin
|
39
|
+
erector { div "love", :class => "big" }
|
40
|
+
=> "<div class=\"big\">love</div>"
|
36
41
|
|
37
42
|
== REQUIREMENTS
|
38
43
|
|
39
|
-
The gem depends on rake and treetop, although this is just for using the
|
40
|
-
so deployed applications won't need these.
|
41
|
-
|
44
|
+
The gem depends on rake and treetop, although this is just for using the command-line tool,
|
45
|
+
so deployed applications won't need these. The Rails-dependent code is now separated so
|
46
|
+
you can use Erector cleanly in a non-Rails app.
|
42
47
|
|
43
48
|
== INSTALL
|
44
49
|
|
data/VERSION.yml
CHANGED
data/bin/erector
CHANGED
data/lib/erector.rb
CHANGED
data/lib/erector/erect.rb
CHANGED
@@ -78,7 +78,9 @@ module Erector
|
|
78
78
|
end
|
79
79
|
|
80
80
|
def run
|
81
|
+
@success = true
|
81
82
|
self.send(mode)
|
83
|
+
@success
|
82
84
|
end
|
83
85
|
|
84
86
|
def to_erector
|
@@ -91,7 +93,7 @@ module Erector
|
|
91
93
|
rescue => e
|
92
94
|
puts e
|
93
95
|
puts e.backtrace.join("\n\t")
|
94
|
-
|
96
|
+
@success = false
|
95
97
|
end
|
96
98
|
end
|
97
99
|
end
|
@@ -103,7 +105,6 @@ module Erector
|
|
103
105
|
begin
|
104
106
|
#todo: fail if file isn't a .rb file
|
105
107
|
require file
|
106
|
-
#todo: understand modulized widgets (e.g. class Foo::Bar::Baz < Erector::Widget in baz.rb)
|
107
108
|
filename = file.split('/').last.gsub(/\.rb$/, '')
|
108
109
|
widget_name = camelize(filename)
|
109
110
|
widget_class = constantize(widget_name)
|
@@ -124,6 +125,7 @@ module Erector
|
|
124
125
|
rescue => e
|
125
126
|
puts e
|
126
127
|
puts e.backtrace.join("\n\t")
|
128
|
+
@success = false
|
127
129
|
end
|
128
130
|
end
|
129
131
|
end
|
data/lib/erector/erected.rb
CHANGED
@@ -6,6 +6,7 @@ Treetop.load("#{dir}/rhtml.treetop")
|
|
6
6
|
|
7
7
|
module Erector
|
8
8
|
class Erected
|
9
|
+
|
9
10
|
def initialize(in_file)
|
10
11
|
@in_file = in_file
|
11
12
|
end
|
@@ -14,14 +15,23 @@ module Erector
|
|
14
15
|
dir + basename + ".rb"
|
15
16
|
end
|
16
17
|
|
17
|
-
def
|
18
|
+
def classnames
|
18
19
|
base = classize(basename)
|
19
20
|
parent = File.dirname(@in_file)
|
20
21
|
grandparent = File.dirname(parent)
|
21
22
|
if File.basename(grandparent) == "views"
|
22
|
-
|
23
|
+
["Views::" + classize(File.basename(parent)) + "::" + base, "Erector::RailsWidget"]
|
24
|
+
else
|
25
|
+
[base, "Erector::Widget"]
|
23
26
|
end
|
24
|
-
|
27
|
+
end
|
28
|
+
|
29
|
+
def classname
|
30
|
+
classnames[0]
|
31
|
+
end
|
32
|
+
|
33
|
+
def parent_class
|
34
|
+
classnames[1]
|
25
35
|
end
|
26
36
|
|
27
37
|
def text
|
@@ -36,7 +46,7 @@ module Erector
|
|
36
46
|
parser.failure_reason
|
37
47
|
else
|
38
48
|
File.open(filename, "w") do |f|
|
39
|
-
f.puts("class #{classname} <
|
49
|
+
f.puts("class #{classname} < #{parent_class}")
|
40
50
|
f.puts(" def content")
|
41
51
|
f.puts(parsed.set_indent(2).convert)
|
42
52
|
f.puts(" end")
|
data/lib/erector/rhtml.treetop
CHANGED
@@ -13,7 +13,35 @@ grammar Rhtml
|
|
13
13
|
end
|
14
14
|
|
15
15
|
rule node
|
16
|
-
hprintlet / printlet / scriptlet / doctype / self_closing_tag / imgtag / closetag / opentag / text
|
16
|
+
yield_with_name / yield / hprintlet / printlet / scriptlet / doctype / directive / self_closing_tag / imgtag / closetag / opentag / text
|
17
|
+
end
|
18
|
+
|
19
|
+
# Argh. For some reason I can't get this to work, so I split it into two rules
|
20
|
+
# rule yield
|
21
|
+
# '<%=' space 'yield' space (':' varname space)? '%>' <Erector::Indenting> {
|
22
|
+
# def convert
|
23
|
+
# var = "@content_for_" + varname.nil? ? "layout" : varname.text_value
|
24
|
+
# line "rawtext #{var} # Note: you must define #{var} elsewhere"
|
25
|
+
# end
|
26
|
+
# }
|
27
|
+
# end
|
28
|
+
|
29
|
+
rule yield_with_name
|
30
|
+
'<%=' space 'yield' space ':' varname space '%>' <Erector::Indenting> {
|
31
|
+
def convert
|
32
|
+
var = "@content_for_" + varname.text_value
|
33
|
+
line "rawtext #{var} # Note: you must define #{var} elsewhere"
|
34
|
+
end
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
rule yield
|
39
|
+
'<%=' space 'yield' space '%>' <Erector::Indenting> {
|
40
|
+
def convert
|
41
|
+
var = "@content_for_layout"
|
42
|
+
line "rawtext #{var} # Note: you must define #{var} elsewhere"
|
43
|
+
end
|
44
|
+
}
|
17
45
|
end
|
18
46
|
|
19
47
|
rule scriptlet
|
@@ -53,10 +81,6 @@ grammar Rhtml
|
|
53
81
|
code = text_value.strip
|
54
82
|
# matches a word, followed by either a word, a string, or a symbol
|
55
83
|
result = code.gsub(/^(\w+) ([\w:"'].*)$/, '\1(\2)')
|
56
|
-
|
57
|
-
# Convert yield, for layouts
|
58
|
-
#result.gsub!(/^yield$/, '@content')
|
59
|
-
|
60
84
|
result
|
61
85
|
end
|
62
86
|
}
|
@@ -69,15 +93,27 @@ grammar Rhtml
|
|
69
93
|
end
|
70
94
|
}
|
71
95
|
end
|
96
|
+
|
97
|
+
rule directive
|
98
|
+
'<!' [^>]* '>' <Erector::Indenting> {
|
99
|
+
def convert
|
100
|
+
line "rawtext '#{text_value}'"
|
101
|
+
end
|
102
|
+
}
|
103
|
+
end
|
72
104
|
|
73
105
|
rule tagname
|
74
106
|
[A-Za-z0-9_:-]+
|
75
107
|
end
|
108
|
+
|
109
|
+
rule varname
|
110
|
+
[A-Za-z0-9_]+
|
111
|
+
end
|
76
112
|
|
77
113
|
rule self_closing_tag
|
78
114
|
'<' tag_name:tagname attrs:attributes? space '/>' <Erector::Indenting> {
|
79
115
|
def convert
|
80
|
-
line "#{tag_name.text_value}#{attrs.
|
116
|
+
line "#{tag_name.text_value}#{attrs.empty? ? "" : attrs.convert}"
|
81
117
|
end
|
82
118
|
}
|
83
119
|
end
|
@@ -85,7 +121,7 @@ grammar Rhtml
|
|
85
121
|
rule opentag
|
86
122
|
'<' tag_name:tagname attrs:attributes? space '>' <Erector::Indenting> {
|
87
123
|
def convert
|
88
|
-
line_in "#{tag_name.text_value}#{attrs.
|
124
|
+
line_in "#{tag_name.text_value}#{attrs.empty? ? "" : attrs.convert} do"
|
89
125
|
end
|
90
126
|
}
|
91
127
|
end
|
@@ -93,7 +129,7 @@ grammar Rhtml
|
|
93
129
|
rule imgtag
|
94
130
|
'<' tag_name:'img' attrs:attributes? space '>' <Erector::Indenting> {
|
95
131
|
def convert
|
96
|
-
line "#{tag_name.text_value}#{attrs.
|
132
|
+
line "#{tag_name.text_value}#{attrs.empty? ? "" : attrs.convert}"
|
97
133
|
end
|
98
134
|
}
|
99
135
|
end
|
@@ -110,10 +146,11 @@ grammar Rhtml
|
|
110
146
|
(([<>] !(tagname / [/%!])) / [^<>])+ <Erector::Indenting> {
|
111
147
|
def convert
|
112
148
|
stripped = text_value.strip
|
113
|
-
if stripped.
|
149
|
+
if stripped.empty?
|
114
150
|
""
|
115
151
|
else
|
116
|
-
line "text '#{
|
152
|
+
line "text '#{stripped.html_unescape.gsub(/\'/, "\\\\'")
|
153
|
+
}'"
|
117
154
|
end
|
118
155
|
end
|
119
156
|
}
|
@@ -123,7 +160,7 @@ grammar Rhtml
|
|
123
160
|
first:attribute rest:attributes* {
|
124
161
|
def convert
|
125
162
|
" " + first.convert +
|
126
|
-
if rest.
|
163
|
+
if rest.empty?
|
127
164
|
""
|
128
165
|
else
|
129
166
|
",#{rest.elements.first.convert}" # this is hacky -- is there a better way?
|
data/lib/erector/widget.rb
CHANGED
@@ -2,45 +2,49 @@ module Erector
|
|
2
2
|
|
3
3
|
# A Widget is the center of the Erector universe.
|
4
4
|
#
|
5
|
-
# To create a widget, extend Erector::Widget and implement
|
6
|
-
#
|
7
|
-
# tags.
|
8
|
-
#
|
9
|
-
# You can also define a widget on the fly by passing a block to +new+. This
|
10
|
-
# +content+ method is called.
|
5
|
+
# To create a widget, extend Erector::Widget and implement the +content+
|
6
|
+
# method. Inside this method you may call any of the tag methods like +span+
|
7
|
+
# or +p+ to emit HTML/XML tags.
|
8
|
+
#
|
9
|
+
# You can also define a widget on the fly by passing a block to +new+. This
|
10
|
+
# block will get executed when the widget's +content+ method is called.
|
11
11
|
#
|
12
|
-
# To render a widget from the outside, instantiate it and call its +to_s+
|
12
|
+
# To render a widget from the outside, instantiate it and call its +to_s+
|
13
|
+
# method.
|
13
14
|
#
|
14
|
-
# A widget's +new+ method optionally accepts an options hash. Entries in
|
15
|
-
# variables, and +attr_reader+ accessors
|
15
|
+
# A widget's +new+ method optionally accepts an options hash. Entries in
|
16
|
+
# this hash are converted to instance variables, and +attr_reader+ accessors
|
17
|
+
# are defined for each.
|
16
18
|
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
# helpers of the enclosing class.
|
27
|
-
#
|
28
|
-
# In this documentation we've tried to keep the distinction clear between
|
29
|
-
# *
|
30
|
-
#
|
19
|
+
# You can add runtime input checking via the +needs+ macro. See #needs.
|
20
|
+
# This mechanism is meant to ameliorate development-time confusion about
|
21
|
+
# exactly what parameters are supported by a given widget, avoiding
|
22
|
+
# confusing runtime NilClass errors.
|
23
|
+
#
|
24
|
+
# To call one widget from another, inside the parent widget's +content+
|
25
|
+
# method, instantiate the child widget and call the +widget+ method. This
|
26
|
+
# assures that the same output stream is used, which gives better
|
27
|
+
# performance than using +capture+ or +to_s+. It also preserves the
|
28
|
+
# indentation and helpers of the enclosing class.
|
29
|
+
#
|
30
|
+
# In this documentation we've tried to keep the distinction clear between
|
31
|
+
# methods that *emit* text and those that *return* text. "Emit" means that
|
32
|
+
# it writes to the output stream; "return" means that it returns a string
|
33
|
+
# like a normal method and leaves it up to the caller to emit that string if
|
34
|
+
# it wants.
|
31
35
|
class Widget
|
32
36
|
class << self
|
33
37
|
def all_tags
|
34
38
|
Erector::Widget.full_tags + Erector::Widget.empty_tags
|
35
39
|
end
|
36
40
|
|
37
|
-
#
|
41
|
+
# Tags which are always self-closing. Click "[Source]" to see the full list.
|
38
42
|
def empty_tags
|
39
43
|
['area', 'base', 'br', 'col', 'frame',
|
40
44
|
'hr', 'img', 'input', 'link', 'meta']
|
41
45
|
end
|
42
46
|
|
43
|
-
#
|
47
|
+
# Tags which can contain other stuff. Click "[Source]" to see the full list.
|
44
48
|
def full_tags
|
45
49
|
[
|
46
50
|
'a', 'abbr', 'acronym', 'address',
|
@@ -81,36 +85,37 @@ module Erector
|
|
81
85
|
end
|
82
86
|
end
|
83
87
|
|
84
|
-
# Class method by which widget classes can declare that they need certain
|
85
|
-
# If needed parameters are not passed in to #new, then an
|
86
|
-
# (with a hopefully useful message about which
|
87
|
-
#
|
88
|
-
# a parameter called '
|
89
|
-
#
|
88
|
+
# Class method by which widget classes can declare that they need certain
|
89
|
+
# parameters. If needed parameters are not passed in to #new, then an
|
90
|
+
# exception will be thrown (with a hopefully useful message about which
|
91
|
+
# parameters are missing). This is intended to catch silly bugs like
|
92
|
+
# passing in a parameter called 'name' to a widget that expects a
|
93
|
+
# parameter called 'title'. Every variable declared in 'needs' will get an
|
94
|
+
# attr_reader accessor declared for it.
|
90
95
|
#
|
91
|
-
# You can also declare default values for parameters using hash syntax.
|
92
|
-
# declarations on multiple lines or on the same line;
|
93
|
-
#
|
94
|
-
# hash parameter).
|
96
|
+
# You can also declare default values for parameters using hash syntax.
|
97
|
+
# You can put #needs declarations on multiple lines or on the same line;
|
98
|
+
# the only caveat is that if there are default values, they all have to be
|
99
|
+
# at the end of the line (so they go into the magic hash parameter).
|
95
100
|
#
|
96
|
-
# If a widget has no #needs declaration then it will accept any
|
97
|
-
# (and make accessors for them) just like
|
98
|
-
# declared.
|
99
|
-
#
|
100
|
-
#
|
101
|
-
#
|
101
|
+
# If a widget has no #needs declaration then it will accept any
|
102
|
+
# combination of parameters (and make accessors for them) just like
|
103
|
+
# normal. In that case there will be no 'attr_reader's declared. If a
|
104
|
+
# widget wants to declare that it takes no parameters, use the special
|
105
|
+
# incantation "needs nil" (and don't declare any other needs, or kittens
|
106
|
+
# will cry).
|
102
107
|
#
|
103
108
|
# Usage:
|
104
109
|
# class FancyForm < Erector::Widget
|
105
110
|
# needs :title, :show_okay => true, :show_cancel => false
|
106
111
|
# ...
|
107
112
|
# end
|
108
|
-
#
|
109
|
-
# That means that
|
113
|
+
#
|
114
|
+
# That means that
|
110
115
|
# FancyForm.new(:title => 'Login')
|
111
|
-
# will succeed, as will
|
116
|
+
# will succeed, as will
|
112
117
|
# FancyForm.new(:title => 'Login', :show_cancel => true)
|
113
|
-
# but
|
118
|
+
# but
|
114
119
|
# FancyForm.new(:name => 'Login')
|
115
120
|
# will fail.
|
116
121
|
#
|
@@ -237,25 +242,38 @@ module Erector
|
|
237
242
|
to_s(:prettyprint => true)
|
238
243
|
end
|
239
244
|
|
240
|
-
# Entry point for rendering a widget (and all its children). This method
|
241
|
-
# calls this widget's #content
|
245
|
+
# Entry point for rendering a widget (and all its children). This method
|
246
|
+
# creates a new output string (if necessary), calls this widget's #content
|
247
|
+
# method and returns the string.
|
242
248
|
#
|
243
249
|
# Options:
|
244
250
|
# output:: the string to output to. Default: a new empty string
|
245
|
-
# prettyprint:: whether Erector should add newlines and indentation.
|
246
|
-
#
|
247
|
-
#
|
248
|
-
#
|
249
|
-
#
|
250
|
-
#
|
251
|
-
#
|
252
|
-
#
|
251
|
+
# prettyprint:: whether Erector should add newlines and indentation.
|
252
|
+
# Default: the value of prettyprint_default (which is false
|
253
|
+
# by default).
|
254
|
+
# indentation:: the amount of spaces to indent. Ignored unless prettyprint
|
255
|
+
# is true.
|
256
|
+
# helpers:: a helpers object containing utility methods. Usually this is a
|
257
|
+
# Rails view object.
|
258
|
+
# content_method_name:: in case you want to call a method other than
|
259
|
+
# #content, pass its name in here.
|
253
260
|
def to_s(options = {}, &blk)
|
254
|
-
|
255
261
|
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
|
-
|
262
|
+
_render(options, &blk).to_s
|
263
|
+
end
|
264
|
+
|
265
|
+
# Entry point for rendering a widget (and all its children). Same as #to_s
|
266
|
+
# only it returns an array, for theoretical performance improvements when using a
|
267
|
+
# Rack server (like Sinatra or Rails Metal).
|
268
|
+
#
|
269
|
+
# # Options: see #to_s
|
270
|
+
def to_a(options = {}, &blk)
|
271
|
+
_render(options, &blk).to_a
|
272
|
+
end
|
273
|
+
|
274
|
+
def _render(options = {}, &blk)
|
257
275
|
options = {
|
258
|
-
:output => "",
|
276
|
+
:output => "", # "" is apparently faster than [] in a long-running process
|
259
277
|
:prettyprint => prettyprint_default,
|
260
278
|
:indentation => 0,
|
261
279
|
:helpers => nil,
|
@@ -263,24 +281,29 @@ module Erector
|
|
263
281
|
}.merge(options)
|
264
282
|
context(options[:output], options[:prettyprint], options[:indentation], options[:helpers]) do
|
265
283
|
send(options[:content_method_name], &blk)
|
266
|
-
output
|
284
|
+
output
|
267
285
|
end
|
268
286
|
end
|
269
287
|
|
270
288
|
alias_method :inspect, :to_s
|
271
289
|
|
272
|
-
# Template method which must be overridden by all widget subclasses.
|
273
|
-
# #element methods which emit HTML
|
290
|
+
# Template method which must be overridden by all widget subclasses.
|
291
|
+
# Inside this method you call the magic #element methods which emit HTML
|
292
|
+
# and text to the output string. If you call "super" (or don't override
|
293
|
+
# +content+) then your widget will render any block that was passed into
|
294
|
+
# its constructor (in the current instance context so it can get access
|
295
|
+
# to parent widget methods via method_missing).
|
274
296
|
def content
|
275
297
|
if @block
|
276
298
|
instance_eval(&@block)
|
277
299
|
end
|
278
300
|
end
|
279
301
|
|
280
|
-
# To call one widget from another, inside the parent widget's +content+
|
281
|
-
#
|
282
|
-
#
|
283
|
-
#
|
302
|
+
# To call one widget from another, inside the parent widget's +content+
|
303
|
+
# method, instantiate the child widget and call its +write_via+ method,
|
304
|
+
# passing in +self+. This assures that the same output string is used,
|
305
|
+
# which gives better performance than using +capture+ or +to_s+. You can
|
306
|
+
# also use the +widget+ method.
|
284
307
|
def write_via(parent)
|
285
308
|
@parent = parent
|
286
309
|
context(parent.output, parent.prettyprint, parent.indentation, parent.helpers) do
|
@@ -288,13 +311,14 @@ module Erector
|
|
288
311
|
end
|
289
312
|
end
|
290
313
|
|
291
|
-
# Emits a (nested) widget onto the current widget's output stream. Accepts
|
292
|
-
# a class or an instance. If the first argument is a class, then
|
293
|
-
# is a hash used to populate its instance variables.
|
294
|
-
# instance then the hash must be unspecified
|
314
|
+
# Emits a (nested) widget onto the current widget's output stream. Accepts
|
315
|
+
# either a class or an instance. If the first argument is a class, then
|
316
|
+
# the second argument is a hash used to populate its instance variables.
|
317
|
+
# If the first argument is an instance then the hash must be unspecified
|
318
|
+
# (or empty).
|
295
319
|
#
|
296
|
-
# The sub-widget will have access to the methods of the parent class, via
|
297
|
-
# magic and a "parent" pointer.
|
320
|
+
# The sub-widget will have access to the methods of the parent class, via
|
321
|
+
# some method_missing magic and a "parent" pointer.
|
298
322
|
def widget(target, assigns={}, &block)
|
299
323
|
child = if target.is_a? Class
|
300
324
|
target.new(assigns, &block)
|
@@ -315,40 +339,46 @@ module Erector
|
|
315
339
|
#-- methods for subclasses to call
|
316
340
|
#++
|
317
341
|
|
318
|
-
# Internal method used to emit an HTML/XML element, including an open tag,
|
319
|
-
# contents (also optional),
|
342
|
+
# Internal method used to emit an HTML/XML element, including an open tag,
|
343
|
+
# attributes (optional, via the default hash), contents (also optional),
|
344
|
+
# and close tag.
|
320
345
|
#
|
321
|
-
# Using the arcane powers of Ruby, there are magic methods that call
|
322
|
-
# HTML tags, like +a+, +body+, +p+, and so
|
323
|
-
#
|
324
|
-
#
|
346
|
+
# Using the arcane powers of Ruby, there are magic methods that call
|
347
|
+
# +element+ for all the standard HTML tags, like +a+, +body+, +p+, and so
|
348
|
+
# forth. Look at the source of #full_tags for the full list.
|
349
|
+
# Unfortunately, this big mojo confuses rdoc, so we can't see each method
|
350
|
+
# in this rdoc page, but trust us, they're there.
|
325
351
|
#
|
326
|
-
# When calling one of these magic methods, put attributes in the default
|
327
|
-
#
|
328
|
-
#
|
329
|
-
#
|
330
|
-
#
|
352
|
+
# When calling one of these magic methods, put attributes in the default
|
353
|
+
# hash. If there is a string parameter, then it is used as the contents.
|
354
|
+
# If there is a block, then it is executed (yielded), and the string
|
355
|
+
# parameter is ignored. The block will usually be in the scope of the
|
356
|
+
# child widget, which means it has access to all the methods of Widget,
|
357
|
+
# which will eventually end up appending text to the +output+ string. See
|
358
|
+
# how elegant it is? Not confusing at all if you don't think about it.
|
331
359
|
#
|
332
360
|
def element(*args, &block)
|
333
361
|
__element__(*args, &block)
|
334
362
|
end
|
335
363
|
|
336
|
-
# Internal method used to emit a self-closing HTML/XML element, including
|
337
|
-
# (passed in via the default hash).
|
338
|
-
#
|
339
|
-
# Using the arcane powers of Ruby, there are magic methods that call
|
340
|
-
# HTML tags, like +img+, +br+, and so
|
341
|
-
#
|
342
|
-
#
|
364
|
+
# Internal method used to emit a self-closing HTML/XML element, including
|
365
|
+
# a tag name and optional attributes (passed in via the default hash).
|
366
|
+
#
|
367
|
+
# Using the arcane powers of Ruby, there are magic methods that call
|
368
|
+
# +empty_element+ for all the standard HTML tags, like +img+, +br+, and so
|
369
|
+
# forth. Look at the source of #empty_tags for the full list.
|
370
|
+
# Unfortunately, this big mojo confuses rdoc, so we can't see each method
|
371
|
+
# in this rdoc page, but trust us, they're there.
|
343
372
|
#
|
344
373
|
def empty_element(*args, &block)
|
345
374
|
__empty_element__(*args, &block)
|
346
375
|
end
|
347
376
|
|
348
|
-
# Returns an HTML-escaped version of its parameter. Leaves the output
|
349
|
-
# the #text method automatically HTML-escapes
|
350
|
-
#
|
351
|
-
#
|
377
|
+
# Returns an HTML-escaped version of its parameter. Leaves the output
|
378
|
+
# string untouched. Note that the #text method automatically HTML-escapes
|
379
|
+
# its parameter, so be careful *not* to do something like text(h("2<4"))
|
380
|
+
# since that will double-escape the less-than sign (you'll get
|
381
|
+
# "2&lt;4" instead of "2<4").
|
352
382
|
def h(content)
|
353
383
|
content.html_escape
|
354
384
|
end
|
@@ -358,20 +388,20 @@ module Erector
|
|
358
388
|
indent_for_open_tag(tag_name)
|
359
389
|
@indentation += SPACES_PER_INDENT
|
360
390
|
|
361
|
-
output
|
391
|
+
output << "<#{tag_name}#{format_attributes(attributes)}>"
|
362
392
|
@at_start_of_line = false
|
363
393
|
end
|
364
394
|
|
365
|
-
# Emits text. If a string is passed in, it will be HTML-escaped.
|
366
|
-
#
|
367
|
-
#
|
368
|
-
#
|
369
|
-
#
|
395
|
+
# Emits text. If a string is passed in, it will be HTML-escaped. If a
|
396
|
+
# widget or the result of calling methods such as raw is passed in, the
|
397
|
+
# HTML will not be HTML-escaped again. If another kind of object is passed
|
398
|
+
# in, the result of calling its to_s method will be treated as a string
|
399
|
+
# would be.
|
370
400
|
def text(value)
|
371
401
|
if value.is_a? Widget
|
372
402
|
widget value
|
373
403
|
else
|
374
|
-
output
|
404
|
+
output <<(value.html_escape)
|
375
405
|
end
|
376
406
|
@at_start_of_line = false
|
377
407
|
nil
|
@@ -410,12 +440,12 @@ module Erector
|
|
410
440
|
end
|
411
441
|
end
|
412
442
|
|
413
|
-
# Emits a close tag, consisting of '<', tag name, and '>'
|
443
|
+
# Emits a close tag, consisting of '<', '/', tag name, and '>'
|
414
444
|
def close_tag(tag_name)
|
415
445
|
@indentation -= SPACES_PER_INDENT
|
416
446
|
indent()
|
417
447
|
|
418
|
-
output
|
448
|
+
output <<("</#{tag_name}>")
|
419
449
|
|
420
450
|
if newliney(tag_name)
|
421
451
|
_newline
|
@@ -438,12 +468,13 @@ module Erector
|
|
438
468
|
|
439
469
|
# Emits an XML instruction, which looks like this: <?xml version=\"1.0\" encoding=\"UTF-8\"?>
|
440
470
|
def instruct(attributes={:version => "1.0", :encoding => "UTF-8"})
|
441
|
-
output
|
471
|
+
output << "<?xml#{format_sorted(sort_for_xml_declaration(attributes))}?>"
|
442
472
|
end
|
443
473
|
|
444
|
-
# Creates a whole new output string, executes the block, then converts the
|
445
|
-
# emits it as raw text. If at all possible
|
446
|
-
#
|
474
|
+
# Creates a whole new output string, executes the block, then converts the
|
475
|
+
# output string to a string and emits it as raw text. If at all possible
|
476
|
+
# you should avoid this method since it hurts performance, and use
|
477
|
+
# +widget+ or +write_via+ instead.
|
447
478
|
def capture(&block)
|
448
479
|
begin
|
449
480
|
original_output = output
|
@@ -475,7 +506,8 @@ module Erector
|
|
475
506
|
)
|
476
507
|
end
|
477
508
|
|
478
|
-
# Emits a javascript block inside a +script+ tag, wrapped in CDATA
|
509
|
+
# Emits a javascript block inside a +script+ tag, wrapped in CDATA
|
510
|
+
# doohickeys like all the cool JS kids do.
|
479
511
|
def javascript(*args, &block)
|
480
512
|
if args.length > 2
|
481
513
|
raise ArgumentError, "Cannot accept more than two arguments"
|
@@ -514,16 +546,17 @@ module Erector
|
|
514
546
|
rawtext "\n"
|
515
547
|
end
|
516
548
|
|
517
|
-
# Convenience method to emit a css file link, which looks like this:
|
549
|
+
# Convenience method to emit a css file link, which looks like this:
|
518
550
|
# <link href="erector.css" rel="stylesheet" type="text/css" />
|
519
|
-
# The parameter is the full contents of the href attribute, including any ".css" extension.
|
551
|
+
# The parameter is the full contents of the href attribute, including any ".css" extension.
|
520
552
|
#
|
521
553
|
# If you want to emit raw CSS inline, use the #style method instead.
|
522
554
|
def css(href)
|
523
555
|
link :rel => 'stylesheet', :type => 'text/css', :href => href
|
524
556
|
end
|
525
557
|
|
526
|
-
# Convenience method to emit an anchor tag whose href and text are the same,
|
558
|
+
# Convenience method to emit an anchor tag whose href and text are the same,
|
559
|
+
# e.g. <a href="http://example.com">http://example.com</a>
|
527
560
|
def url(href)
|
528
561
|
a href, :href => href
|
529
562
|
end
|
@@ -581,7 +614,7 @@ protected
|
|
581
614
|
def __empty_element__(tag_name, attributes={})
|
582
615
|
indent_for_open_tag(tag_name)
|
583
616
|
|
584
|
-
output
|
617
|
+
output << "<#{tag_name}#{format_attributes(attributes)} />"
|
585
618
|
|
586
619
|
if newliney(tag_name)
|
587
620
|
_newline
|
@@ -590,7 +623,7 @@ protected
|
|
590
623
|
|
591
624
|
def _newline
|
592
625
|
return unless @prettyprint
|
593
|
-
output
|
626
|
+
output << "\n"
|
594
627
|
@at_start_of_line = true
|
595
628
|
end
|
596
629
|
|
@@ -604,7 +637,7 @@ protected
|
|
604
637
|
|
605
638
|
def indent()
|
606
639
|
if @at_start_of_line
|
607
|
-
output
|
640
|
+
output << " " * [@indentation, 0].max
|
608
641
|
end
|
609
642
|
end
|
610
643
|
|