erector 0.7.2 → 0.8.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 +17 -3
- data/VERSION.yml +2 -2
- data/bin/erector +1 -1
- data/lib/erector.rb +22 -2
- data/lib/erector/after_initialize.rb +34 -0
- data/lib/erector/caching.rb +93 -0
- data/lib/erector/convenience.rb +58 -0
- data/lib/erector/dependencies.rb +24 -0
- data/lib/erector/dependency.rb +21 -0
- data/lib/erector/{erect.rb → erect/erect.rb} +14 -4
- data/lib/erector/{erected.rb → erect/erected.rb} +6 -4
- data/lib/erector/{indenting.rb → erect/indenting.rb} +0 -0
- data/lib/erector/{rhtml.treetop → erect/rhtml.treetop} +51 -11
- data/lib/erector/errors.rb +12 -0
- data/lib/erector/extensions/hash.rb +21 -0
- data/lib/erector/externals.rb +88 -24
- data/lib/erector/html.rb +352 -0
- data/lib/erector/inline.rb +5 -5
- data/lib/erector/jquery.rb +36 -0
- data/lib/erector/mixin.rb +3 -5
- data/lib/erector/needs.rb +94 -0
- data/lib/erector/output.rb +117 -0
- data/lib/erector/rails.rb +2 -2
- data/lib/erector/rails/extensions/action_controller.rb +5 -3
- data/lib/erector/rails/extensions/rails_helpers.rb +159 -0
- data/lib/erector/rails/extensions/rails_widget.rb +98 -56
- data/lib/erector/rails/rails_form_builder.rb +8 -4
- data/lib/erector/rails/rails_version.rb +2 -2
- data/lib/erector/rails/template_handlers/ert_handler.rb +1 -1
- data/lib/erector/rails/template_handlers/rb_handler.rb +42 -1
- data/lib/erector/raw_string.rb +2 -2
- data/lib/erector/sass.rb +22 -0
- data/lib/erector/widget.rb +100 -653
- data/lib/erector/widgets.rb +1 -0
- data/lib/erector/widgets/external_renderer.rb +51 -0
- data/lib/erector/widgets/page.rb +45 -63
- data/lib/erector/widgets/table.rb +9 -1
- data/spec/erect/erect_rails_spec.rb +19 -17
- data/spec/erect/erect_spec.rb +11 -1
- data/spec/erect/erected_spec.rb +76 -5
- data/spec/erect/rhtml_parser_spec.rb +11 -1
- data/spec/erector/caching_spec.rb +267 -0
- data/spec/erector/convenience_spec.rb +258 -0
- data/spec/erector/dependency_spec.rb +46 -0
- data/spec/erector/externals_spec.rb +233 -0
- data/spec/erector/html_spec.rb +508 -0
- data/spec/erector/indentation_spec.rb +84 -24
- data/spec/erector/inline_spec.rb +19 -8
- data/spec/erector/jquery_spec.rb +35 -0
- data/spec/erector/mixin_spec.rb +1 -1
- data/spec/erector/needs_spec.rb +120 -0
- data/spec/erector/output_spec.rb +199 -0
- data/spec/erector/sample-file.txt +1 -0
- data/spec/erector/sass_spec.rb +33 -0
- data/spec/erector/widget_spec.rb +113 -932
- data/spec/erector/widgets/field_table_spec.rb +6 -6
- data/spec/erector/widgets/form_spec.rb +3 -3
- data/spec/erector/widgets/page_spec.rb +52 -6
- data/spec/erector/widgets/table_spec.rb +4 -4
- data/spec/spec_helper.rb +70 -29
- metadata +56 -19
- data/lib/erector/rails/extensions/rails_widget/rails_helpers.rb +0 -137
- data/spec/core_spec_suite.rb +0 -3
- data/spec/erector/external_spec.rb +0 -110
- data/spec/rails_spec_suite.rb +0 -3
- data/spec/spec.opts +0 -1
- data/spec/spec_suite.rb +0 -40
@@ -0,0 +1,12 @@
|
|
1
|
+
module Erector
|
2
|
+
module Errors
|
3
|
+
class RubyVersionNotSupported < RuntimeError
|
4
|
+
def initialize(version_identifier, explanation=nil)
|
5
|
+
super [
|
6
|
+
"Erector does not support Ruby version(s) #{version_identifier}.",
|
7
|
+
explanation ? "The reason(s) are:\n#{explanation}" : nil
|
8
|
+
].compact.join("\n")
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class Hash
|
2
|
+
module CorrectlyHashedHash
|
3
|
+
def hash
|
4
|
+
out = 0
|
5
|
+
# This sort_by is all kinds of weird...basically, we need a deterministic order here,
|
6
|
+
# but we can't just use "sort", because keys aren't necessarily sortable (they don't
|
7
|
+
# necessarily respond to <=>). Sorting by their hash codes works just as well, and
|
8
|
+
# is guaranteed to work, since everything hashes.
|
9
|
+
keys.sort_by { |k| k.hash }.each { |k| out ^= k.hash; out ^= self[k].hash }
|
10
|
+
out
|
11
|
+
end
|
12
|
+
|
13
|
+
def eql?(o)
|
14
|
+
self == o
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def correctly_hashed
|
19
|
+
extend(CorrectlyHashedHash)
|
20
|
+
end
|
21
|
+
end
|
data/lib/erector/externals.rb
CHANGED
@@ -1,32 +1,96 @@
|
|
1
1
|
module Erector
|
2
|
-
class External < Struct.new(:type, :klass, :text, :options)
|
3
|
-
def initialize(type, klass, text, options = {})
|
4
|
-
super(type.to_sym, klass, text, options)
|
5
|
-
end
|
6
|
-
|
7
|
-
def ==(other)
|
8
|
-
(self.type == other.type and
|
9
|
-
self.text == other.text and
|
10
|
-
self.options == other.options) ? true : false
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
2
|
module Externals
|
15
|
-
def
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
|
9
|
+
# Express a dependency of this widget
|
10
|
+
# Multiple forms:
|
11
|
+
# depends_on(type, text, options = {})
|
12
|
+
# for example
|
13
|
+
# depends_on(:js, '/foo.js', :embed=>true)
|
14
|
+
#
|
15
|
+
# Other variants:
|
16
|
+
# depends_on(type, an_io, ... # file to be read
|
17
|
+
# depends_on('blah.js' ... infer :js
|
18
|
+
# depends_on('blah.css' ... infer :css
|
19
|
+
# depends on :js, 'file1.js', 'file2.js'... [options]
|
20
|
+
# depends_on :js => ["foo.js", "bar.js"], :css=>['file.css']
|
21
|
+
# depends_on :js => ["foo.js", "bar.js"], other_option=>:blah
|
22
|
+
def depends_on(*args)
|
23
|
+
x = interpret_args(*args)
|
24
|
+
my_dependencies.push(x)
|
25
|
+
end
|
26
|
+
|
27
|
+
# deprecated in favor of #depends_on
|
28
|
+
# todo: warning
|
29
|
+
def external(type, value, options = {})
|
30
|
+
type = type.to_sym
|
31
|
+
x = Dependency.new(type, value, options)
|
32
|
+
my_dependencies << x unless my_dependencies.include?(x)
|
33
|
+
end
|
34
|
+
|
35
|
+
# returns all dependencies of the given type from this class and all its
|
36
|
+
# superclasses
|
37
|
+
def dependencies(type)
|
38
|
+
type = type.to_sym
|
39
|
+
deps = Dependencies.new
|
40
|
+
deps.push(*superclass.dependencies(type)) if superclass.respond_to?(:dependencies)
|
41
|
+
deps.push(*my_dependencies.select { |x| x.type == type })
|
42
|
+
deps.uniq
|
43
|
+
end
|
44
|
+
|
45
|
+
def my_dependencies
|
46
|
+
@my_dependencies ||= Dependencies.new
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
INFERABLE_TYPES = [:css, :js]
|
51
|
+
|
52
|
+
def interpret_args(*args)
|
53
|
+
options = {}
|
54
|
+
options = args.pop if args.last.is_a?(::Hash)
|
55
|
+
if args.empty? && options.any?
|
56
|
+
deps = []
|
57
|
+
texts_hash = {}
|
58
|
+
INFERABLE_TYPES.each do |t|
|
59
|
+
texts_hash[t] = options.delete(t) if options.has_key? t
|
60
|
+
end
|
61
|
+
texts_hash.each do |t, texts|
|
62
|
+
texts.each do |text|
|
63
|
+
deps << interpret_args(t, text, options)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
return deps
|
67
|
+
elsif args[0].class == Symbol
|
68
|
+
type = args.shift
|
69
|
+
else
|
70
|
+
type = /.+\.js/.match(args[0]) ? :js : :css
|
71
|
+
end
|
72
|
+
|
73
|
+
deps = args.map do |text|
|
74
|
+
Dependency.new(type, text, options)
|
75
|
+
end
|
76
|
+
deps.size == 1 ? deps.first : deps
|
20
77
|
end
|
21
78
|
end
|
22
79
|
|
23
|
-
def
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
80
|
+
def render_with_externals(options_to_external_renderer = {})
|
81
|
+
output = Erector::Output.new
|
82
|
+
self.to_a(:output => output)
|
83
|
+
nested_widgets = output.widgets.to_a
|
84
|
+
externals = ExternalRenderer.new({:classes => nested_widgets}.merge(options_to_external_renderer)).to_html(:output => output)
|
85
|
+
output.to_a
|
29
86
|
end
|
30
|
-
end
|
31
87
|
|
88
|
+
def render_externals(options_to_external_renderer = {})
|
89
|
+
output_for_externals = Erector::Output.new
|
90
|
+
nested_widgets = output.widgets
|
91
|
+
externalizer = ExternalRenderer.new({:classes => nested_widgets}.merge(options_to_external_renderer))
|
92
|
+
externalizer._render(:output => output_for_externals)
|
93
|
+
output_for_externals.to_a
|
94
|
+
end
|
95
|
+
end
|
32
96
|
end
|
data/lib/erector/html.rb
ADDED
@@ -0,0 +1,352 @@
|
|
1
|
+
module Erector
|
2
|
+
module HTML
|
3
|
+
module ClassMethods
|
4
|
+
# Tags which are always self-closing. Click "[Source]" to see the full list.
|
5
|
+
def empty_tags
|
6
|
+
['area', 'base', 'br', 'col', 'embed', 'frame',
|
7
|
+
'hr', 'img', 'input', 'link', 'meta', 'param']
|
8
|
+
end
|
9
|
+
|
10
|
+
# Tags which can contain other stuff. Click "[Source]" to see the full list.
|
11
|
+
def full_tags
|
12
|
+
[
|
13
|
+
'a', 'abbr', 'acronym', 'address', 'article', 'aside', 'audio',
|
14
|
+
'b', 'bdo', 'big', 'blockquote', 'body', 'button',
|
15
|
+
'canvas', 'caption', 'center', 'cite', 'code', 'colgroup', 'command',
|
16
|
+
'datalist', 'dd', 'del', 'details', 'dfn', 'dialog', 'div', 'dl', 'dt',
|
17
|
+
'em',
|
18
|
+
'fieldset', 'figure', 'footer', 'form', 'frameset',
|
19
|
+
'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hgroup', 'html', 'i',
|
20
|
+
'iframe', 'ins', 'keygen', 'kbd', 'label', 'legend', 'li',
|
21
|
+
'map', 'mark', 'meter',
|
22
|
+
'nav', 'noframes', 'noscript',
|
23
|
+
'object', 'ol', 'optgroup', 'option',
|
24
|
+
'p', 'pre', 'progress',
|
25
|
+
'q', 'ruby', 'rt', 'rp', 's',
|
26
|
+
'samp', 'script', 'section', 'select', 'small', 'source', 'span', 'strike',
|
27
|
+
'strong', 'style', 'sub', 'sup',
|
28
|
+
'table', 'tbody', 'td', 'textarea', 'tfoot',
|
29
|
+
'th', 'thead', 'time', 'title', 'tr', 'tt',
|
30
|
+
'u', 'ul',
|
31
|
+
'var', 'video'
|
32
|
+
]
|
33
|
+
end
|
34
|
+
|
35
|
+
def all_tags
|
36
|
+
full_tags + empty_tags
|
37
|
+
end
|
38
|
+
|
39
|
+
def def_empty_tag_method(tag_name)
|
40
|
+
self.class_eval(<<-SRC, __FILE__, __LINE__ + 1)
|
41
|
+
def #{tag_name}(*args, &block)
|
42
|
+
__empty_element__('#{tag_name}', *args, &block)
|
43
|
+
end
|
44
|
+
SRC
|
45
|
+
end
|
46
|
+
|
47
|
+
def def_full_tag_method(tag_name)
|
48
|
+
self.class_eval(<<-SRC, __FILE__, __LINE__ + 1)
|
49
|
+
def #{tag_name}(*args, &block)
|
50
|
+
__element__(false, '#{tag_name}', *args, &block)
|
51
|
+
end
|
52
|
+
|
53
|
+
def #{tag_name}!(*args, &block)
|
54
|
+
__element__(true, '#{tag_name}', *args, &block)
|
55
|
+
end
|
56
|
+
SRC
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.included(base)
|
61
|
+
base.extend ClassMethods
|
62
|
+
|
63
|
+
base.full_tags.each do |tag_name|
|
64
|
+
base.def_full_tag_method(tag_name)
|
65
|
+
end
|
66
|
+
|
67
|
+
base.empty_tags.each do |tag_name|
|
68
|
+
base.def_empty_tag_method(tag_name)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Internal method used to emit an HTML/XML element, including an open tag,
|
73
|
+
# attributes (optional, via the default hash), contents (also optional),
|
74
|
+
# and close tag.
|
75
|
+
#
|
76
|
+
# Using the arcane powers of Ruby, there are magic methods that call
|
77
|
+
# +element+ for all the standard HTML tags, like +a+, +body+, +p+, and so
|
78
|
+
# forth. Look at the source of #full_tags for the full list.
|
79
|
+
# Unfortunately, this big mojo confuses rdoc, so we can't see each method
|
80
|
+
# in this rdoc page, but trust us, they're there.
|
81
|
+
#
|
82
|
+
# When calling one of these magic methods, put attributes in the default
|
83
|
+
# hash. If there is a string parameter, then it is used as the contents.
|
84
|
+
# If there is a block, then it is executed (yielded), and the string
|
85
|
+
# parameter is ignored. The block will usually be in the scope of the
|
86
|
+
# child widget, which means it has access to all the methods of Widget,
|
87
|
+
# which will eventually end up appending text to the +output+ string. See
|
88
|
+
# how elegant it is? Not confusing at all if you don't think about it.
|
89
|
+
#
|
90
|
+
def element(*args, &block)
|
91
|
+
__element__(false, *args, &block)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Like +element+, but string parameters are not escaped.
|
95
|
+
def element!(*args, &block)
|
96
|
+
__element__(true, *args, &block)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Internal method used to emit a self-closing HTML/XML element, including
|
100
|
+
# a tag name and optional attributes (passed in via the default hash).
|
101
|
+
#
|
102
|
+
# Using the arcane powers of Ruby, there are magic methods that call
|
103
|
+
# +empty_element+ for all the standard HTML tags, like +img+, +br+, and so
|
104
|
+
# forth. Look at the source of #empty_tags for the full list.
|
105
|
+
# Unfortunately, this big mojo confuses rdoc, so we can't see each method
|
106
|
+
# in this rdoc page, but trust us, they're there.
|
107
|
+
#
|
108
|
+
def empty_element(*args, &block)
|
109
|
+
__empty_element__(*args, &block)
|
110
|
+
end
|
111
|
+
|
112
|
+
# Returns an HTML-escaped version of its parameter. Leaves the output
|
113
|
+
# string untouched. This method is idempotent: h(h(text)) will not
|
114
|
+
# double-escape text. This means that it is safe to do something like
|
115
|
+
# text(h("2<4")) -- it will produce "2<4", not "2&lt;4".
|
116
|
+
def h(content)
|
117
|
+
if content.respond_to?(:html_safe?) && content.html_safe?
|
118
|
+
content
|
119
|
+
else
|
120
|
+
raw(CGI.escapeHTML(content.to_s))
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# Emits an open tag, comprising '<', tag name, optional attributes, and '>'
|
125
|
+
def open_tag(tag_name, attributes={})
|
126
|
+
output.newline if newliney?(tag_name) && !output.at_line_start?
|
127
|
+
output << raw("<#{tag_name}#{format_attributes(attributes)}>")
|
128
|
+
output.indent
|
129
|
+
end
|
130
|
+
|
131
|
+
# Emits a close tag, consisting of '<', '/', tag name, and '>'
|
132
|
+
def close_tag(tag_name)
|
133
|
+
output.undent
|
134
|
+
output << raw("</#{tag_name}>")
|
135
|
+
if newliney?(tag_name)
|
136
|
+
output.newline
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# Returns text which will *not* be HTML-escaped.
|
141
|
+
def raw(value)
|
142
|
+
RawString.new(value.to_s)
|
143
|
+
end
|
144
|
+
|
145
|
+
# Emits text. If a string is passed in, it will be HTML-escaped. If the
|
146
|
+
# result of calling methods such as raw is passed in, the HTML will not be
|
147
|
+
# HTML-escaped again. If another kind of object is passed in, the result
|
148
|
+
# of calling its to_s method will be treated as a string would be.
|
149
|
+
#
|
150
|
+
# You shouldn't pass a widget in to this method, as that will cause
|
151
|
+
# performance problems (as well as being semantically goofy). Use the
|
152
|
+
# #widget method instead.
|
153
|
+
def text(value)
|
154
|
+
if value.is_a? Widget
|
155
|
+
widget value
|
156
|
+
else
|
157
|
+
output << h(value)
|
158
|
+
end
|
159
|
+
nil
|
160
|
+
end
|
161
|
+
|
162
|
+
# Emits text which will *not* be HTML-escaped. Same effect as text(raw(s))
|
163
|
+
def text!(value)
|
164
|
+
text raw(value)
|
165
|
+
end
|
166
|
+
|
167
|
+
alias rawtext text!
|
168
|
+
|
169
|
+
# Returns a copy of value with spaces replaced by non-breaking space characters.
|
170
|
+
# With no arguments, return a single non-breaking space.
|
171
|
+
# The output uses the escaping format ' ' since that works
|
172
|
+
# in both HTML and XML (as opposed to ' ' which only works in HTML).
|
173
|
+
def nbsp(value = " ")
|
174
|
+
raw(h(value).gsub(/ /,' '))
|
175
|
+
end
|
176
|
+
|
177
|
+
# Return a character given its unicode code point or unicode name.
|
178
|
+
def character(code_point_or_name)
|
179
|
+
if code_point_or_name.is_a?(Symbol)
|
180
|
+
found = Erector::CHARACTERS[code_point_or_name]
|
181
|
+
if found.nil?
|
182
|
+
raise "Unrecognized character #{code_point_or_name}"
|
183
|
+
end
|
184
|
+
raw("&#x#{sprintf '%x', found};")
|
185
|
+
elsif code_point_or_name.is_a?(Integer)
|
186
|
+
raw("&#x#{sprintf '%x', code_point_or_name};")
|
187
|
+
else
|
188
|
+
raise "Unrecognized argument to character: #{code_point_or_name}"
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
# Emits an XML instruction, which looks like this: <?xml version=\"1.0\" encoding=\"UTF-8\"?>
|
193
|
+
def instruct(attributes={:version => "1.0", :encoding => "UTF-8"})
|
194
|
+
output << raw("<?xml#{format_sorted(sort_for_xml_declaration(attributes))}?>")
|
195
|
+
end
|
196
|
+
|
197
|
+
# Emits an HTML comment (<!-- ... -->) surrounding +text+ and/or the output of +block+.
|
198
|
+
# see http://www.w3.org/TR/html4/intro/sgmltut.html#h-3.2.4
|
199
|
+
#
|
200
|
+
# If +text+ is an Internet Explorer conditional comment condition such as "[if IE]",
|
201
|
+
# the output includes the opening condition and closing "[endif]". See
|
202
|
+
# http://www.quirksmode.org/css/condcom.html
|
203
|
+
#
|
204
|
+
# Since "Authors should avoid putting two or more adjacent hyphens inside comments,"
|
205
|
+
# we emit a warning if you do that.
|
206
|
+
def comment(text = '', &block)
|
207
|
+
puts "Warning: Authors should avoid putting two or more adjacent hyphens inside comments." if text =~ /--/
|
208
|
+
|
209
|
+
conditional = text =~ /\[if .*\]/
|
210
|
+
|
211
|
+
rawtext "<!--"
|
212
|
+
rawtext text
|
213
|
+
rawtext ">" if conditional
|
214
|
+
|
215
|
+
if block
|
216
|
+
rawtext "\n"
|
217
|
+
block.call
|
218
|
+
rawtext "\n"
|
219
|
+
end
|
220
|
+
|
221
|
+
rawtext "<![endif]" if conditional
|
222
|
+
rawtext "-->\n"
|
223
|
+
end
|
224
|
+
|
225
|
+
# Emits a javascript block inside a +script+ tag, wrapped in CDATA
|
226
|
+
# doohickeys like all the cool JS kids do.
|
227
|
+
def javascript(*args, &block)
|
228
|
+
if args.length > 2
|
229
|
+
raise ArgumentError, "Cannot accept more than two arguments"
|
230
|
+
end
|
231
|
+
attributes, value = nil, nil
|
232
|
+
arg0 = args[0]
|
233
|
+
if arg0.is_a?(Hash)
|
234
|
+
attributes = arg0
|
235
|
+
else
|
236
|
+
value = arg0
|
237
|
+
arg1 = args[1]
|
238
|
+
if arg1.is_a?(Hash)
|
239
|
+
attributes = arg1
|
240
|
+
end
|
241
|
+
end
|
242
|
+
attributes ||= {}
|
243
|
+
attributes[:type] = "text/javascript"
|
244
|
+
open_tag 'script', attributes
|
245
|
+
|
246
|
+
# Shouldn't this be a "cdata" HtmlPart?
|
247
|
+
# (maybe, but the syntax is specific to javascript; it isn't
|
248
|
+
# really a generic XML CDATA section. Specifically,
|
249
|
+
# ]]> within value is not treated as ending the
|
250
|
+
# CDATA section by Firefox2 when parsing text/html,
|
251
|
+
# although I guess we could refuse to generate ]]>
|
252
|
+
# there, for the benefit of XML/XHTML parsers).
|
253
|
+
rawtext "\n// <![CDATA[\n"
|
254
|
+
if block
|
255
|
+
instance_eval(&block)
|
256
|
+
else
|
257
|
+
rawtext value
|
258
|
+
end
|
259
|
+
rawtext "\n// ]]>"
|
260
|
+
output.append_newline # this forces a newline even if we're not in pretty mode
|
261
|
+
|
262
|
+
close_tag 'script'
|
263
|
+
rawtext "\n"
|
264
|
+
end
|
265
|
+
|
266
|
+
protected
|
267
|
+
def __element__(raw, tag_name, *args, &block)
|
268
|
+
if args.length > 2
|
269
|
+
raise ArgumentError, "Cannot accept more than four arguments"
|
270
|
+
end
|
271
|
+
attributes, value = nil, nil
|
272
|
+
arg0 = args[0]
|
273
|
+
if arg0.is_a?(Hash)
|
274
|
+
attributes = arg0
|
275
|
+
else
|
276
|
+
value = arg0
|
277
|
+
arg1 = args[1]
|
278
|
+
if arg1.is_a?(Hash)
|
279
|
+
attributes = arg1
|
280
|
+
end
|
281
|
+
end
|
282
|
+
attributes ||= {}
|
283
|
+
open_tag tag_name, attributes
|
284
|
+
if block && value
|
285
|
+
raise ArgumentError, "You can't pass both a block and a value to #{tag_name} -- please choose one."
|
286
|
+
end
|
287
|
+
if block
|
288
|
+
block.call
|
289
|
+
elsif raw
|
290
|
+
text! value
|
291
|
+
else
|
292
|
+
text value
|
293
|
+
end
|
294
|
+
close_tag tag_name
|
295
|
+
end
|
296
|
+
|
297
|
+
def __empty_element__(tag_name, attributes={})
|
298
|
+
output << raw("<#{tag_name}#{format_attributes(attributes)} />")
|
299
|
+
output.newline if newliney?(tag_name)
|
300
|
+
end
|
301
|
+
|
302
|
+
def format_attributes(attributes)
|
303
|
+
if !attributes || attributes.empty?
|
304
|
+
""
|
305
|
+
else
|
306
|
+
format_sorted(sorted(attributes))
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
def format_sorted(sorted)
|
311
|
+
results = ['']
|
312
|
+
sorted.each do |key, value|
|
313
|
+
if value
|
314
|
+
if value.is_a?(Array)
|
315
|
+
value = value.flatten
|
316
|
+
next if value.empty?
|
317
|
+
value = value.join(' ')
|
318
|
+
end
|
319
|
+
results << "#{key}=\"#{h(value)}\""
|
320
|
+
end
|
321
|
+
end
|
322
|
+
return results.join(' ')
|
323
|
+
end
|
324
|
+
|
325
|
+
def sorted(attributes)
|
326
|
+
stringized = []
|
327
|
+
attributes.each do |key, value|
|
328
|
+
stringized << [key.to_s, value]
|
329
|
+
end
|
330
|
+
return stringized.sort
|
331
|
+
end
|
332
|
+
|
333
|
+
def sort_for_xml_declaration(attributes)
|
334
|
+
# correct order is "version, encoding, standalone" (XML 1.0 section 2.8).
|
335
|
+
# But we only try to put version before encoding for now.
|
336
|
+
stringized = []
|
337
|
+
attributes.each do |key, value|
|
338
|
+
stringized << [key.to_s, value]
|
339
|
+
end
|
340
|
+
return stringized.sort{|a, b| b <=> a}
|
341
|
+
end
|
342
|
+
|
343
|
+
NON_NEWLINEY = {'i' => true, 'b' => true, 'small' => true,
|
344
|
+
'img' => true, 'span' => true, 'a' => true,
|
345
|
+
'input' => true, 'textarea' => true, 'button' => true, 'select' => true
|
346
|
+
}
|
347
|
+
|
348
|
+
def newliney?(tag_name)
|
349
|
+
!NON_NEWLINEY.include?(tag_name)
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|