dryml 1.1.0.pre0
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/CHANGES.txt +9 -0
- data/LICENSE.txt +22 -0
- data/README +36 -0
- data/Rakefile +47 -0
- data/TODO.txt +6 -0
- data/lib/dryml/dryml_builder.rb +140 -0
- data/lib/dryml/dryml_doc.rb +155 -0
- data/lib/dryml/dryml_generator.rb +271 -0
- data/lib/dryml/dryml_support_controller.rb +13 -0
- data/lib/dryml/helper.rb +69 -0
- data/lib/dryml/parser/attribute.rb +41 -0
- data/lib/dryml/parser/base_parser.rb +254 -0
- data/lib/dryml/parser/document.rb +57 -0
- data/lib/dryml/parser/element.rb +27 -0
- data/lib/dryml/parser/elements.rb +21 -0
- data/lib/dryml/parser/source.rb +58 -0
- data/lib/dryml/parser/text.rb +13 -0
- data/lib/dryml/parser/tree_parser.rb +67 -0
- data/lib/dryml/parser.rb +3 -0
- data/lib/dryml/part_context.rb +133 -0
- data/lib/dryml/scoped_variables.rb +42 -0
- data/lib/dryml/static_tags +98 -0
- data/lib/dryml/tag_parameters.rb +32 -0
- data/lib/dryml/taglib.rb +124 -0
- data/lib/dryml/template.rb +1021 -0
- data/lib/dryml/template_environment.rb +613 -0
- data/lib/dryml/template_handler.rb +187 -0
- data/lib/dryml.rb +291 -0
- data/taglibs/core.dryml +136 -0
- data/test/dryml.rdoctest +68 -0
- metadata +131 -0
@@ -0,0 +1,1021 @@
|
|
1
|
+
require 'rexml/document'
|
2
|
+
require 'digest/sha1'
|
3
|
+
require 'pathname'
|
4
|
+
|
5
|
+
module Dryml
|
6
|
+
|
7
|
+
class Template
|
8
|
+
|
9
|
+
DRYML_NAME = "[a-zA-Z\-][a-zA-Z0-9\-]*"
|
10
|
+
DRYML_NAME_RX = /^#{DRYML_NAME}$/
|
11
|
+
|
12
|
+
RUBY_NAME = "[a-zA-Z_][a-zA-Z0-9_]*"
|
13
|
+
RUBY_NAME_RX = /^#{RUBY_NAME}$/
|
14
|
+
|
15
|
+
CODE_ATTRIBUTE_CHAR = "&"
|
16
|
+
|
17
|
+
NO_METADATA_TAGS = %w(doctype if else unless repeat do with name type-name)
|
18
|
+
|
19
|
+
SPECIAL_ATTRIBUTES = %w(param merge merge-params merge-attrs
|
20
|
+
for-type
|
21
|
+
if unless repeat
|
22
|
+
part part-locals
|
23
|
+
restore)
|
24
|
+
|
25
|
+
VALID_PARAMETER_TAG_ATTRIBUTES = %w(param replace)
|
26
|
+
|
27
|
+
@build_cache = {}
|
28
|
+
|
29
|
+
class << self
|
30
|
+
attr_reader :build_cache
|
31
|
+
|
32
|
+
def clear_build_cache
|
33
|
+
@build_cache.clear()
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize(src, environment, template_path)
|
38
|
+
@src = src
|
39
|
+
@environment = environment # a class or a module
|
40
|
+
@template_path = template_path
|
41
|
+
@template_path = @template_path.sub(%r(^#{Regexp.escape(RAILS_ROOT)}/), "") if Object.const_defined? :RAILS_ROOT
|
42
|
+
|
43
|
+
@builder = Template.build_cache[@template_path] || DRYMLBuilder.new(self)
|
44
|
+
@builder.set_environment(environment)
|
45
|
+
|
46
|
+
@last_element = nil
|
47
|
+
end
|
48
|
+
|
49
|
+
attr_reader :tags, :template_path
|
50
|
+
|
51
|
+
def compile(local_names=[], auto_taglibs=[])
|
52
|
+
now = Time.now
|
53
|
+
|
54
|
+
unless @template_path.ends_with?(EMPTY_PAGE)
|
55
|
+
p = Pathname.new template_path
|
56
|
+
p = Pathname.new(RAILS_ROOT) + p unless p.absolute? || !Object.const_defined?(:RAILS_ROOT)
|
57
|
+
mtime = p.mtime rescue Time.now
|
58
|
+
|
59
|
+
if !@builder.ready?(mtime)
|
60
|
+
@builder.start
|
61
|
+
parsed = true
|
62
|
+
# parse the DRYML file creating a list of build instructions
|
63
|
+
if is_taglib?
|
64
|
+
process_src
|
65
|
+
else
|
66
|
+
create_render_page_method
|
67
|
+
end
|
68
|
+
|
69
|
+
# store build instructions in the cache
|
70
|
+
Template.build_cache[@template_path] = @builder
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# compile the build instructions
|
75
|
+
@builder.build(local_names, auto_taglibs, mtime)
|
76
|
+
|
77
|
+
logger.try.info(" DRYML: Compiled #{template_path} in #{'%.2fs' % (Time.now - now)}") if parsed
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
def create_render_page_method
|
82
|
+
erb_src = process_src
|
83
|
+
|
84
|
+
@builder.add_build_instruction(:render_page, :src => erb_src, :line_num => 1)
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
def is_taglib?
|
89
|
+
@environment.class == Module
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
def process_src
|
94
|
+
@doc = Dryml::Parser::Document.new(@src, @template_path)
|
95
|
+
result = children_to_erb(@doc.root)
|
96
|
+
restore_erb_scriptlets(result)
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
def restore_erb_scriptlets(src)
|
101
|
+
@doc.restore_erb_scriptlets(src)
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
def children_to_erb(nodes)
|
106
|
+
nodes.map { |x| node_to_erb(x) }.join
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
def node_to_erb(node)
|
111
|
+
case node
|
112
|
+
|
113
|
+
# v important this comes before REXML::Text, as REXML::CData < REXML::Text
|
114
|
+
when REXML::CData
|
115
|
+
REXML::CData::START + node.to_s + REXML::CData::STOP
|
116
|
+
|
117
|
+
when REXML::Comment
|
118
|
+
REXML::Comment::START + node.to_s + REXML::Comment::STOP
|
119
|
+
|
120
|
+
when REXML::Text
|
121
|
+
strip_suppressed_whiteaspace(node.to_s)
|
122
|
+
|
123
|
+
when REXML::Element
|
124
|
+
element_to_erb(node)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
|
129
|
+
def strip_suppressed_whiteaspace(s)
|
130
|
+
s # s.gsub(/ -(\s*\n\s*)/, '<% \1 %>')
|
131
|
+
end
|
132
|
+
|
133
|
+
|
134
|
+
def element_to_erb(el)
|
135
|
+
dryml_exception("old-style parameter tag (<#{el.name}>)", el) if el.name.starts_with?(":")
|
136
|
+
|
137
|
+
@last_element = el
|
138
|
+
case el.dryml_name
|
139
|
+
|
140
|
+
when "include"
|
141
|
+
include_element(el)
|
142
|
+
# return just the newlines to keep line-number matching - the
|
143
|
+
# include has no presence in the erb source
|
144
|
+
tag_newlines(el)
|
145
|
+
|
146
|
+
when "set-theme"
|
147
|
+
require_attribute(el, "name", /^#{DRYML_NAME}$/)
|
148
|
+
@builder.add_build_instruction(:set_theme, :name => el.attributes['name'])
|
149
|
+
|
150
|
+
# return nothing - set_theme has no presence in the erb source
|
151
|
+
tag_newlines(el)
|
152
|
+
|
153
|
+
when "def"
|
154
|
+
def_element(el)
|
155
|
+
|
156
|
+
when "extend"
|
157
|
+
extend_element(el)
|
158
|
+
|
159
|
+
when "set"
|
160
|
+
set_element(el)
|
161
|
+
|
162
|
+
when "set-scoped"
|
163
|
+
set_scoped_element(el)
|
164
|
+
|
165
|
+
when "param-content"
|
166
|
+
param_content_element(el)
|
167
|
+
|
168
|
+
else
|
169
|
+
if el.dryml_name.not_in?(Dryml.static_tags) || el.attributes['param'] || el.attributes['restore']
|
170
|
+
tag_call(el)
|
171
|
+
else
|
172
|
+
static_element_to_erb(el)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
|
178
|
+
def include_element(el)
|
179
|
+
require_toplevel(el)
|
180
|
+
require_attribute(el, "as", /^#{DRYML_NAME}$/, true)
|
181
|
+
options = {}
|
182
|
+
%w(src module plugin as).each do |attr|
|
183
|
+
options[attr.to_sym] = el.attributes[attr] if el.attributes[attr]
|
184
|
+
end
|
185
|
+
@builder.add_build_instruction(:include, options)
|
186
|
+
end
|
187
|
+
|
188
|
+
|
189
|
+
def import_module(mod, as=nil)
|
190
|
+
@builder.import_module(mod, as)
|
191
|
+
end
|
192
|
+
|
193
|
+
|
194
|
+
def set_element(el)
|
195
|
+
assigns = el.attributes.map do |name, value|
|
196
|
+
next if name.in?(SPECIAL_ATTRIBUTES)
|
197
|
+
dryml_exception("invalid name in <set> (remember to use '-' rather than '_')", el) unless name =~ /^#{DRYML_NAME}(\.#{DRYML_NAME})*$/
|
198
|
+
"#{ruby_name name} = #{attribute_to_ruby(value)}; "
|
199
|
+
end.join
|
200
|
+
code = apply_control_attributes("begin; #{assigns}; end", el)
|
201
|
+
"<% #{code}#{tag_newlines(el)} %>"
|
202
|
+
end
|
203
|
+
|
204
|
+
|
205
|
+
def set_scoped_element(el)
|
206
|
+
variables = el.attributes.map do |name, value|
|
207
|
+
dryml_exception("invalid name in <set-scoped> (remember to use '-' rather than '_')", el) unless name =~ DRYML_NAME_RX
|
208
|
+
":#{ruby_name name} => #{attribute_to_ruby(value)} "
|
209
|
+
end
|
210
|
+
"<% scope.new_scope(#{variables * ', '}) { #{tag_newlines(el)} %>#{children_to_erb(el)}<% } %>"
|
211
|
+
end
|
212
|
+
|
213
|
+
|
214
|
+
def declared_attributes(def_element)
|
215
|
+
attrspec = def_element.attributes["attrs"]
|
216
|
+
attr_names = attrspec ? attrspec.split(/\s*,\s*/).map{ |n| n.underscore.to_sym } : []
|
217
|
+
invalids = attr_names & ([:with, :field, :this] + SPECIAL_ATTRIBUTES.*.to_sym)
|
218
|
+
dryml_exception("invalid attrs in def: #{invalids * ', '}", def_element) unless invalids.empty?
|
219
|
+
attr_names
|
220
|
+
end
|
221
|
+
|
222
|
+
|
223
|
+
def ruby_name(dryml_name)
|
224
|
+
dryml_name.gsub('-', '_')
|
225
|
+
end
|
226
|
+
|
227
|
+
|
228
|
+
def with_containing_tag_name(el)
|
229
|
+
old = @containing_tag_name
|
230
|
+
@containing_tag_name = el.dryml_name
|
231
|
+
yield
|
232
|
+
@containing_tag_name = old
|
233
|
+
end
|
234
|
+
|
235
|
+
|
236
|
+
def define_polymorphic_dispatcher(el, name)
|
237
|
+
# FIXME: The new erb context ends up being set-up twice
|
238
|
+
src = %(
|
239
|
+
def #{name}(attributes={}, parameters={})
|
240
|
+
_tag_context(attributes) do
|
241
|
+
attributes.delete :with
|
242
|
+
attributes.delete :field
|
243
|
+
call_polymorphic_tag('#{name}', attributes, parameters) { #{name}__base(attributes.except, parameters) }
|
244
|
+
end
|
245
|
+
end
|
246
|
+
)
|
247
|
+
@builder.add_build_instruction(:eval, :src => src, :line_num => element_line_num(el))
|
248
|
+
end
|
249
|
+
|
250
|
+
|
251
|
+
def extend_element(el)
|
252
|
+
def_element(el, true)
|
253
|
+
end
|
254
|
+
|
255
|
+
|
256
|
+
def type_specific_suffix
|
257
|
+
el = @def_element
|
258
|
+
for_type = el.attributes['for']
|
259
|
+
if for_type
|
260
|
+
type_name = if defined?(HoboFields) && for_type =~ /^[a-z]/
|
261
|
+
# It's a symbolic type name - look up the Ruby type name
|
262
|
+
klass = HoboFields.to_class(for_type) or
|
263
|
+
dryml_exception("No such type in polymorphic tag definition: '#{for_type}'", el)
|
264
|
+
klass.name
|
265
|
+
else
|
266
|
+
for_type
|
267
|
+
end.underscore.gsub('/', '__')
|
268
|
+
"__for_#{type_name}"
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
|
273
|
+
def def_element(el, extend_tag=false)
|
274
|
+
require_toplevel(el)
|
275
|
+
require_attribute(el, "tag", DRYML_NAME_RX)
|
276
|
+
require_attribute(el, "attrs", /^\s*#{DRYML_NAME}(\s*,\s*#{DRYML_NAME})*\s*$/, true)
|
277
|
+
require_attribute(el, "alias-of", DRYML_NAME_RX, true)
|
278
|
+
|
279
|
+
@def_element = el
|
280
|
+
|
281
|
+
unsafe_name = el.attributes["tag"]
|
282
|
+
name = Dryml.unreserve(unsafe_name)
|
283
|
+
suffix = type_specific_suffix
|
284
|
+
if suffix
|
285
|
+
name += suffix
|
286
|
+
unsafe_name += suffix
|
287
|
+
end
|
288
|
+
|
289
|
+
if el.attributes['polymorphic']
|
290
|
+
%w(for alias-of).each do |attr|
|
291
|
+
dryml_exception("def cannot have both 'polymorphic' and '#{attr}' attributes") if el.attributes[attr]
|
292
|
+
end
|
293
|
+
|
294
|
+
define_polymorphic_dispatcher(el, ruby_name(name))
|
295
|
+
name += "__base"
|
296
|
+
unsafe_name += "__base"
|
297
|
+
end
|
298
|
+
|
299
|
+
alias_of = el.attributes['alias-of']
|
300
|
+
dryml_exception("def with alias-of must be empty", el) if alias_of and el.size > 0
|
301
|
+
|
302
|
+
alias_of and @builder.add_build_instruction(:alias_method,
|
303
|
+
:new => ruby_name(name).to_sym,
|
304
|
+
:old => ruby_name(Dryml.unreserve(alias_of)).to_sym)
|
305
|
+
|
306
|
+
res = if alias_of
|
307
|
+
"<% #{tag_newlines(el)} %>"
|
308
|
+
else
|
309
|
+
src = tag_method(name, el, extend_tag) +
|
310
|
+
"<% _register_tag_attrs(:#{ruby_name name}, #{declared_attributes(el).inspect.underscore}) %>"
|
311
|
+
|
312
|
+
logger.debug(restore_erb_scriptlets(src)) if el.attributes["debug-source"]
|
313
|
+
|
314
|
+
@builder.add_build_instruction(:def,
|
315
|
+
:src => restore_erb_scriptlets(src),
|
316
|
+
:line_num => element_line_num(el))
|
317
|
+
# keep line numbers matching up
|
318
|
+
"<% #{"\n" * src.count("\n")} %>"
|
319
|
+
end
|
320
|
+
@def_element = nil
|
321
|
+
res
|
322
|
+
end
|
323
|
+
|
324
|
+
def self.descendents(el,&block)
|
325
|
+
return if el.elements.empty?
|
326
|
+
el.elements.each do |child|
|
327
|
+
block.call(child)
|
328
|
+
descendents(child,&block)
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
# Using REXML::XPath is slow
|
333
|
+
def self.descendent_select(el)
|
334
|
+
result = []
|
335
|
+
descendents(el) { |desc|
|
336
|
+
result << desc if yield(desc)
|
337
|
+
}
|
338
|
+
result
|
339
|
+
end
|
340
|
+
|
341
|
+
def param_names_in_definition(el)
|
342
|
+
self.class.descendent_select(el) { |el| el.attribute 'param' }.map do |e|
|
343
|
+
name = get_param_name(e)
|
344
|
+
dryml_exception("invalid param name: #{name.inspect}", e) unless
|
345
|
+
is_code_attribute?(name) || name =~ RUBY_NAME_RX || name =~ /#\{/
|
346
|
+
name.to_sym unless is_code_attribute?(name)
|
347
|
+
end.compact
|
348
|
+
end
|
349
|
+
|
350
|
+
|
351
|
+
def tag_method(name, el, extend_tag=false)
|
352
|
+
name = ruby_name name
|
353
|
+
param_names = param_names_in_definition(el)
|
354
|
+
|
355
|
+
if extend_tag
|
356
|
+
@extend_key = 'a' + Digest::SHA1.hexdigest(el.to_s)[0..10]
|
357
|
+
alias_statement = "; alias_method_chain_on_include :#{name}, :#{@extend_key}"
|
358
|
+
name = "#{name}_with_#{@extend_key}"
|
359
|
+
end
|
360
|
+
|
361
|
+
src = "<% def #{name}(all_attributes={}, all_parameters={}); " +
|
362
|
+
"parameters = Dryml::TagParameters.new(all_parameters, #{param_names.inspect.underscore}); " +
|
363
|
+
"all_parameters = Dryml::TagParameters.new(all_parameters); " +
|
364
|
+
tag_method_body(el) +
|
365
|
+
"; end#{alias_statement} %>"
|
366
|
+
@extend_key = nil
|
367
|
+
src
|
368
|
+
end
|
369
|
+
|
370
|
+
|
371
|
+
def tag_method_body(el)
|
372
|
+
attrs = declared_attributes(el)
|
373
|
+
|
374
|
+
# A statement to assign values to local variables named after the tag's attrs
|
375
|
+
# The trailing comma on `attributes` is supposed to be there!
|
376
|
+
setup_locals = attrs.map{|a| "#{Dryml.unreserve(a).underscore}, "}.join + "attributes, = " +
|
377
|
+
"_tag_locals(all_attributes, #{attrs.inspect})"
|
378
|
+
|
379
|
+
start = "_tag_context(all_attributes) do #{setup_locals}"
|
380
|
+
|
381
|
+
"#{start} " +
|
382
|
+
# reproduce any line breaks in the start-tag so that line numbers are preserved
|
383
|
+
tag_newlines(el) + "%>" +
|
384
|
+
wrap_tag_method_body_with_metadata(children_to_erb(el)) +
|
385
|
+
"<% output_buffer; end"
|
386
|
+
end
|
387
|
+
|
388
|
+
|
389
|
+
def wrap_source_with_metadata(content, kind, name, *args)
|
390
|
+
if (!include_source_metadata) || name.in?(NO_METADATA_TAGS)
|
391
|
+
content
|
392
|
+
else
|
393
|
+
metadata = [kind, name] + args + [@template_path]
|
394
|
+
"<!--[DRYML|#{metadata * '|'}[-->" + content + "<!--]DRYML]-->"
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
|
399
|
+
def wrap_tag_method_body_with_metadata(content)
|
400
|
+
name = @def_element.attributes['tag']
|
401
|
+
for_ = @def_element.attributes['for']
|
402
|
+
name += " for #{for_}" if for_
|
403
|
+
wrap_source_with_metadata(content, "def", name, element_line_num(@def_element))
|
404
|
+
end
|
405
|
+
|
406
|
+
|
407
|
+
def wrap_tag_call_with_metadata(el, content)
|
408
|
+
name = el.expanded_name
|
409
|
+
param = el.attributes['param']
|
410
|
+
|
411
|
+
if param == "&true"
|
412
|
+
name += " param"
|
413
|
+
elsif param
|
414
|
+
name += " param='#{param}'"
|
415
|
+
end
|
416
|
+
|
417
|
+
wrap_source_with_metadata(content, "call", name, element_line_num(el))
|
418
|
+
end
|
419
|
+
|
420
|
+
|
421
|
+
def param_content_local_name(name)
|
422
|
+
"_#{ruby_name name}__default_content"
|
423
|
+
end
|
424
|
+
|
425
|
+
|
426
|
+
def param_content_element(name_or_el)
|
427
|
+
name = if name_or_el.is_a?(String)
|
428
|
+
name_or_el
|
429
|
+
else
|
430
|
+
el = name_or_el
|
431
|
+
el.attributes['for'] || @containing_tag_name
|
432
|
+
end
|
433
|
+
local_name = param_content_local_name(name)
|
434
|
+
"<%= #{local_name} && #{local_name}.call %>"
|
435
|
+
end
|
436
|
+
|
437
|
+
|
438
|
+
def part_element(el, content)
|
439
|
+
require_attribute(el, "part", DRYML_NAME_RX)
|
440
|
+
|
441
|
+
if contains_param?(el)
|
442
|
+
delegated_part_element(el, content)
|
443
|
+
else
|
444
|
+
simple_part_element(el, content)
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
|
449
|
+
def simple_part_element(el, content)
|
450
|
+
part_name = el.attributes['part']
|
451
|
+
dom_id = el.attributes['id'] || part_name
|
452
|
+
part_name = ruby_name(part_name)
|
453
|
+
part_locals = el.attributes["part-locals"]
|
454
|
+
|
455
|
+
part_src = "<% def #{part_name}_part(#{part_locals._?.gsub('@', '')}) #{tag_newlines(el)}; new_context do %>" +
|
456
|
+
content +
|
457
|
+
"<% end; end %>"
|
458
|
+
@builder.add_part(part_name, restore_erb_scriptlets(part_src), element_line_num(el))
|
459
|
+
|
460
|
+
newlines = "\n" * part_src.count("\n")
|
461
|
+
args = [attribute_to_ruby(dom_id), ":#{part_name}", part_locals].compact
|
462
|
+
"<%= call_part(#{args * ', '}) #{newlines} %>"
|
463
|
+
end
|
464
|
+
|
465
|
+
|
466
|
+
def delegated_part_element(el, content)
|
467
|
+
# TODO
|
468
|
+
end
|
469
|
+
|
470
|
+
|
471
|
+
def contains_param?(el)
|
472
|
+
# TODO
|
473
|
+
false
|
474
|
+
end
|
475
|
+
|
476
|
+
|
477
|
+
def part_delegate_tag_name(el)
|
478
|
+
"#{@def_name}_#{el.attributes['part']}__part_delegate"
|
479
|
+
end
|
480
|
+
|
481
|
+
|
482
|
+
def current_def_name
|
483
|
+
@def_element && @def_element.attributes['tag']
|
484
|
+
end
|
485
|
+
|
486
|
+
|
487
|
+
def get_param_name(el)
|
488
|
+
param_name = el.attributes["param"]
|
489
|
+
|
490
|
+
if param_name
|
491
|
+
def_tag = find_ancestor(el) {|e| e.name == "def" || e.name == "extend" }
|
492
|
+
dryml_exception("param is not allowed outside of tag definitions", el) if def_tag.nil?
|
493
|
+
|
494
|
+
ruby_name(param_name == "&true" ? el.dryml_name : param_name)
|
495
|
+
else
|
496
|
+
nil
|
497
|
+
end
|
498
|
+
end
|
499
|
+
|
500
|
+
|
501
|
+
def inside_def_for_type?
|
502
|
+
@def_element && @def_element.attributes['for']
|
503
|
+
end
|
504
|
+
|
505
|
+
|
506
|
+
def call_name(el)
|
507
|
+
dryml_exception("invalid tag name (remember to use '-' rather than '_')", el) unless el.dryml_name =~ /^#{DRYML_NAME}(\.#{DRYML_NAME})*$/
|
508
|
+
|
509
|
+
name = Dryml.unreserve(ruby_name(el.dryml_name))
|
510
|
+
if call_to_self_from_type_specific_def?(el)
|
511
|
+
"#{name}__base"
|
512
|
+
elsif old_tag_call?(el)
|
513
|
+
name = name[4..-1] # remove 'old-' prefix
|
514
|
+
name += type_specific_suffix if inside_def_for_type?
|
515
|
+
"#{name}_without_#{@extend_key}"
|
516
|
+
else
|
517
|
+
name
|
518
|
+
end
|
519
|
+
end
|
520
|
+
|
521
|
+
|
522
|
+
def old_tag_call?(el)
|
523
|
+
@def_element && el.dryml_name == "old-#{current_def_name}"
|
524
|
+
end
|
525
|
+
|
526
|
+
|
527
|
+
def call_to_self_from_type_specific_def?(el)
|
528
|
+
inside_def_for_type? && el.dryml_name == current_def_name &&!el.attributes['for-type']
|
529
|
+
end
|
530
|
+
|
531
|
+
|
532
|
+
def polymorphic_call_type(el)
|
533
|
+
t = el.attributes['for-type']
|
534
|
+
if t.nil?
|
535
|
+
nil
|
536
|
+
elsif t == "&true"
|
537
|
+
'this_type'
|
538
|
+
elsif t =~ /^[A-Z]/
|
539
|
+
t
|
540
|
+
elsif t =~ /^[a-z]/ && defined? HoboFields.to_class
|
541
|
+
klass = HoboFields.to_class(t)
|
542
|
+
klass.name
|
543
|
+
elsif is_code_attribute?(t)
|
544
|
+
t[1..-1]
|
545
|
+
else
|
546
|
+
dryml_exception("invalid for-type attribute", el)
|
547
|
+
end
|
548
|
+
end
|
549
|
+
|
550
|
+
|
551
|
+
def tag_call(el)
|
552
|
+
name = call_name(el)
|
553
|
+
param_name = get_param_name(el)
|
554
|
+
attributes = tag_attributes(el)
|
555
|
+
newlines = tag_newlines(el)
|
556
|
+
|
557
|
+
parameters = tag_newlines(el) + parameter_tags_hash(el)
|
558
|
+
|
559
|
+
is_param_restore = el.attributes['restore']
|
560
|
+
|
561
|
+
call = if param_name
|
562
|
+
param_name = attribute_to_ruby(param_name, :symbolize => true)
|
563
|
+
args = "#{attributes}, #{parameters}, all_parameters, #{param_name}"
|
564
|
+
to_call = if is_param_restore
|
565
|
+
# The tag is available in a local variable
|
566
|
+
# holding a proc
|
567
|
+
param_restore_local_name(name)
|
568
|
+
elsif (call_type = polymorphic_call_type(el))
|
569
|
+
"find_polymorphic_tag(:#{ruby_name name}, #{call_type})"
|
570
|
+
else
|
571
|
+
":#{ruby_name name}"
|
572
|
+
end
|
573
|
+
"call_tag_parameter(#{to_call}, #{args})"
|
574
|
+
else
|
575
|
+
if is_param_restore
|
576
|
+
# The tag is a proc available in a local variable
|
577
|
+
"#{param_restore_local_name(name)}.call(#{attributes}, #{parameters})"
|
578
|
+
elsif (call_type = polymorphic_call_type(el))
|
579
|
+
"send(find_polymorphic_tag(:#{ruby_name name}, #{call_type}), #{attributes}, #{parameters})"
|
580
|
+
elsif attributes == "{}" && parameters == "{}"
|
581
|
+
if name =~ /^[A-Z]/
|
582
|
+
# it's a tag with a cap name - not a local
|
583
|
+
"#{ruby_name name}()"
|
584
|
+
else
|
585
|
+
# could be a tag or a local variable
|
586
|
+
"#{ruby_name name}.to_s"
|
587
|
+
end
|
588
|
+
else
|
589
|
+
"#{ruby_name name}(#{attributes}, #{parameters})"
|
590
|
+
end
|
591
|
+
end
|
592
|
+
|
593
|
+
call = apply_control_attributes(call, el)
|
594
|
+
call = maybe_make_part_call(el, "<% concat(#{call}) %>")
|
595
|
+
wrap_tag_call_with_metadata(el, call)
|
596
|
+
end
|
597
|
+
|
598
|
+
|
599
|
+
def merge_attribute(el)
|
600
|
+
merge = el.attributes['merge']
|
601
|
+
dryml_exception("merge cannot have a RHS", el) if merge && merge != "&true"
|
602
|
+
merge
|
603
|
+
end
|
604
|
+
|
605
|
+
|
606
|
+
def parameter_tags_hash(el, containing_tag_name=nil)
|
607
|
+
call_type = nil
|
608
|
+
|
609
|
+
metadata_name = containing_tag_name || el.expanded_name
|
610
|
+
|
611
|
+
param_items = el.map do |node|
|
612
|
+
case node
|
613
|
+
when REXML::Text
|
614
|
+
text = node.to_s
|
615
|
+
unless text.blank?
|
616
|
+
dryml_exception("mixed content and parameter tags", el) if call_type == :named_params
|
617
|
+
call_type = :default_param_only
|
618
|
+
end
|
619
|
+
text
|
620
|
+
when REXML::Element
|
621
|
+
e = node
|
622
|
+
is_parameter_tag = e.parameter_tag?
|
623
|
+
|
624
|
+
# Make sure there isn't a mix of parameter tags and normal content
|
625
|
+
case call_type
|
626
|
+
when nil
|
627
|
+
call_type = is_parameter_tag ? :named_params : :default_param_only
|
628
|
+
when :named_params
|
629
|
+
dryml_exception("mixed parameter tags and non-parameter tags (did you forget a ':'?)", el) unless is_parameter_tag
|
630
|
+
when :default_param_only
|
631
|
+
dryml_exception("mixed parameter tags and non-parameter tags (did you forget a ':'?)", el) if is_parameter_tag
|
632
|
+
end
|
633
|
+
|
634
|
+
if is_parameter_tag
|
635
|
+
parameter_tag_hash_item(e, metadata_name) + ", "
|
636
|
+
end
|
637
|
+
end
|
638
|
+
end.join
|
639
|
+
|
640
|
+
if call_type == :default_param_only || (call_type.nil? && param_items.length > 0) || (el.children.empty? && el.has_end_tag?)
|
641
|
+
with_containing_tag_name(el) do
|
642
|
+
param_items = " :default => #{default_param_proc(el, containing_tag_name)}, "
|
643
|
+
end
|
644
|
+
end
|
645
|
+
|
646
|
+
param_items.concat without_parameters(el)
|
647
|
+
|
648
|
+
merge_params = el.attributes['merge-params'] || merge_attribute(el)
|
649
|
+
if merge_params
|
650
|
+
extra_params = if merge_params == "&true"
|
651
|
+
"parameters"
|
652
|
+
elsif is_code_attribute?(merge_params)
|
653
|
+
merge_params[1..-1]
|
654
|
+
else
|
655
|
+
merge_param_names = merge_params.split(/\s*,\s*/).*.gsub("-", "_")
|
656
|
+
"all_parameters & #{merge_param_names.inspect}"
|
657
|
+
end
|
658
|
+
"merge_parameter_hashes({#{param_items}}, (#{extra_params}) || {})"
|
659
|
+
else
|
660
|
+
"{#{param_items}}"
|
661
|
+
end
|
662
|
+
end
|
663
|
+
|
664
|
+
|
665
|
+
def without_parameters(el)
|
666
|
+
without_names = el.attributes.keys.map { |name| name =~ /^without-(.*)/ and $1 }.compact
|
667
|
+
without_names.map { |name| ":#{ruby_name name}_replacement => proc {|__discard__| '' }, " }.join
|
668
|
+
end
|
669
|
+
|
670
|
+
|
671
|
+
def parameter_tag_hash_item(el, metadata_name)
|
672
|
+
name = el.name.dup
|
673
|
+
if name.sub!(/^before-/, "")
|
674
|
+
before_parameter_tag_hash_item(name, el, metadata_name)
|
675
|
+
elsif name.sub!(/^after-/, "")
|
676
|
+
after_parameter_tag_hash_item(name, el, metadata_name)
|
677
|
+
elsif name.sub!(/^prepend-/, "")
|
678
|
+
prepend_parameter_tag_hash_item(name, el, metadata_name)
|
679
|
+
elsif name.sub!(/^append-/, "")
|
680
|
+
append_parameter_tag_hash_item(name, el, metadata_name)
|
681
|
+
else
|
682
|
+
hash_key = ruby_name name
|
683
|
+
hash_key += "_replacement" if el.attribute("replace")
|
684
|
+
if (param_name = get_param_name(el))
|
685
|
+
":#{hash_key} => merge_tag_parameter(#{param_proc(el, metadata_name)}, all_parameters[:#{param_name}])"
|
686
|
+
else
|
687
|
+
":#{hash_key} => #{param_proc(el, metadata_name)}"
|
688
|
+
end
|
689
|
+
end
|
690
|
+
end
|
691
|
+
|
692
|
+
|
693
|
+
def before_parameter_tag_hash_item(name, el, metadata_name)
|
694
|
+
param_name = get_param_name(el)
|
695
|
+
dryml_exception("param declaration not allowed on 'before' parameters", el) if param_name
|
696
|
+
content = children_to_erb(el) + "<% concat(#{param_restore_local_name(name)}.call({}, {})) %>"
|
697
|
+
":#{ruby_name name}_replacement => #{replace_parameter_proc(el, metadata_name, content)}"
|
698
|
+
end
|
699
|
+
|
700
|
+
|
701
|
+
def after_parameter_tag_hash_item(name, el, metadata_name)
|
702
|
+
param_name = get_param_name(el)
|
703
|
+
dryml_exception("param declaration not allowed on 'after' parameters", el) if param_name
|
704
|
+
content = "<% concat(#{param_restore_local_name(name)}.call({}, {})) %>" + children_to_erb(el)
|
705
|
+
":#{ruby_name name}_replacement => #{replace_parameter_proc(el, metadata_name, content)}"
|
706
|
+
end
|
707
|
+
|
708
|
+
|
709
|
+
def append_parameter_tag_hash_item(name, el, metadata_name)
|
710
|
+
":#{ruby_name name} => proc { [{}, { :default => proc { |#{param_content_local_name(name)}| new_context { %>" +
|
711
|
+
param_content_element(name) + children_to_erb(el) +
|
712
|
+
"<% } } } ] }"
|
713
|
+
end
|
714
|
+
|
715
|
+
|
716
|
+
def prepend_parameter_tag_hash_item(name, el, metadata_name)
|
717
|
+
":#{ruby_name name} => proc { [{}, { :default => proc { |#{param_content_local_name(name)}| new_context { %>" +
|
718
|
+
children_to_erb(el) + param_content_element(name) +
|
719
|
+
"<% } } } ] }"
|
720
|
+
end
|
721
|
+
|
722
|
+
|
723
|
+
def default_param_proc(el, containing_param_name=nil)
|
724
|
+
content = children_to_erb(el)
|
725
|
+
content = wrap_source_with_metadata(content, "param", containing_param_name,
|
726
|
+
element_line_num(el)) if containing_param_name
|
727
|
+
"proc { |#{param_content_local_name(el.dryml_name)}| new_context { %>#{content}<% } #{tag_newlines(el)}}"
|
728
|
+
end
|
729
|
+
|
730
|
+
|
731
|
+
def param_restore_local_name(name)
|
732
|
+
"_#{ruby_name name}_restore"
|
733
|
+
end
|
734
|
+
|
735
|
+
|
736
|
+
def wrap_replace_parameter(el, name)
|
737
|
+
wrap_source_with_metadata(children_to_erb(el), "replace", name, element_line_num(el))
|
738
|
+
end
|
739
|
+
|
740
|
+
|
741
|
+
def param_proc(el, metadata_name_prefix)
|
742
|
+
metadata_name = "#{metadata_name_prefix}><#{el.name}"
|
743
|
+
|
744
|
+
nl = tag_newlines(el)
|
745
|
+
|
746
|
+
if (repl = el.attribute("replace"))
|
747
|
+
dryml_exception("replace attribute must not have a value", el) if repl.has_rhs?
|
748
|
+
dryml_exception("replace parameters must not have attributes", el) if el.attributes.length > 1
|
749
|
+
|
750
|
+
replace_parameter_proc(el, metadata_name)
|
751
|
+
else
|
752
|
+
attributes = el.attributes.dup
|
753
|
+
# Providing one of 'with' or 'field' but not the other should cancel out the other
|
754
|
+
attributes[:with] = "&nil" if attributes.key?(:field) && !attributes.key?(:with)
|
755
|
+
attributes[:field] = "&nil" if !attributes.key?(:field) && attributes.key?(:with)
|
756
|
+
attribute_items = attributes.map do |name, value|
|
757
|
+
if name.in?(VALID_PARAMETER_TAG_ATTRIBUTES)
|
758
|
+
# just ignore
|
759
|
+
elsif name.in?(SPECIAL_ATTRIBUTES)
|
760
|
+
dryml_exception("attribute '#{name}' is not allowed on parameter tags (<#{el.name}:>)", el)
|
761
|
+
else
|
762
|
+
":#{ruby_name name} => #{attribute_to_ruby(value, el)}"
|
763
|
+
end
|
764
|
+
end.compact
|
765
|
+
|
766
|
+
nested_parameters_hash = parameter_tags_hash(el, metadata_name)
|
767
|
+
"proc { [{#{attribute_items * ', '}}, #{nested_parameters_hash}] #{nl}}"
|
768
|
+
end
|
769
|
+
end
|
770
|
+
|
771
|
+
|
772
|
+
def replace_parameter_proc(el, metadata_name, content=nil)
|
773
|
+
content ||= wrap_replace_parameter(el, metadata_name)
|
774
|
+
param_name = el.dryml_name.sub(/^(before|after|append|prepend)-/, "")
|
775
|
+
"proc { |#{param_restore_local_name(param_name)}| new_context { %>#{content}<% } #{tag_newlines(el)}}"
|
776
|
+
end
|
777
|
+
|
778
|
+
|
779
|
+
def maybe_make_part_call(el, call)
|
780
|
+
part_name = el.attributes['part']
|
781
|
+
if part_name
|
782
|
+
part_id = el.attributes['id'] || part_name
|
783
|
+
"<div class='part-wrapper' id='<%= #{attribute_to_ruby part_id} %>'>#{part_element(el, call)}</div>"
|
784
|
+
else
|
785
|
+
call
|
786
|
+
end
|
787
|
+
end
|
788
|
+
|
789
|
+
|
790
|
+
def field_shorthand_element?(el)
|
791
|
+
el.expanded_name =~ /:./
|
792
|
+
end
|
793
|
+
|
794
|
+
|
795
|
+
def tag_attributes(el)
|
796
|
+
attributes = el.attributes
|
797
|
+
items = attributes.map do |n,v|
|
798
|
+
dryml_exception("invalid attribute name '#{n}' (remember to use '-' rather than '_')", el) unless n =~ DRYML_NAME_RX
|
799
|
+
|
800
|
+
next if n.in?(SPECIAL_ATTRIBUTES) || n =~ /^without-/
|
801
|
+
next if el.attributes['part'] && n == 'id' # The id is rendered on the <div class="part-wrapper"> instead
|
802
|
+
|
803
|
+
":#{ruby_name n} => #{attribute_to_ruby(v)}"
|
804
|
+
end.compact
|
805
|
+
|
806
|
+
# if there's a ':' el.name is just the part after the ':'
|
807
|
+
items << ":field => \"#{ruby_name el.name}\"" if field_shorthand_element?(el)
|
808
|
+
|
809
|
+
hash = "{#{items.join(", ")}}"
|
810
|
+
|
811
|
+
if merge_attribute(el)
|
812
|
+
"merge_attrs(#{hash}, attributes)"
|
813
|
+
elsif el.attributes['merge-attrs']
|
814
|
+
merge_attrs = compile_merge_attrs(el)
|
815
|
+
"merge_attrs(#{hash}, #{merge_attrs} || {})"
|
816
|
+
else
|
817
|
+
hash
|
818
|
+
end
|
819
|
+
end
|
820
|
+
|
821
|
+
|
822
|
+
def compile_merge_attrs(el)
|
823
|
+
merge_attrs = el.attributes['merge-attrs']
|
824
|
+
if merge_attrs == "&true"
|
825
|
+
"attributes"
|
826
|
+
elsif is_code_attribute?(merge_attrs)
|
827
|
+
"(#{merge_attrs[1..-1]})"
|
828
|
+
else
|
829
|
+
merge_attr_names = merge_attrs.split(/\s*,\s*/).*.gsub("-", "_").*.to_sym
|
830
|
+
"(all_attributes & #{merge_attr_names.inspect})"
|
831
|
+
end
|
832
|
+
end
|
833
|
+
|
834
|
+
|
835
|
+
def static_tag_to_method_call(el)
|
836
|
+
part = el.attributes["part"]
|
837
|
+
attrs = el.attributes.map do |n, v|
|
838
|
+
next if n.in? SPECIAL_ATTRIBUTES
|
839
|
+
val = restore_erb_scriptlets(v).gsub('"', '\"').gsub(/<%=(.*?)%>/, '#{\1}')
|
840
|
+
%('#{n}' => "#{val}")
|
841
|
+
end.compact
|
842
|
+
|
843
|
+
# If there's a part but no id, the id defaults to the part name
|
844
|
+
if part && !el.attributes["id"]
|
845
|
+
attrs << ":id => '#{part}'"
|
846
|
+
end
|
847
|
+
|
848
|
+
# Convert the attributes hash to a call to merge_attrs if
|
849
|
+
# there's a merge-attrs attribute
|
850
|
+
attrs = if el.attributes['merge-attrs']
|
851
|
+
merge_attrs = compile_merge_attrs(el)
|
852
|
+
"merge_attrs({#{attrs * ', '}}, #{merge_attrs} || {})"
|
853
|
+
else
|
854
|
+
"{" + attrs.join(', ') + "}"
|
855
|
+
end
|
856
|
+
|
857
|
+
if el.children.empty?
|
858
|
+
dryml_exception("part attribute on empty static tag", el) if part
|
859
|
+
|
860
|
+
"<%= " + apply_control_attributes("element(:#{el.name}, #{attrs}, nil, true, #{!el.has_end_tag?} #{tag_newlines(el)})", el) + " %>"
|
861
|
+
else
|
862
|
+
if part
|
863
|
+
body = part_element(el, children_to_erb(el))
|
864
|
+
else
|
865
|
+
body = children_to_erb(el)
|
866
|
+
end
|
867
|
+
|
868
|
+
output_tag = "element(:#{el.name}, #{attrs}, new_context { %>#{body}<% })"
|
869
|
+
"<% concat(" + apply_control_attributes(output_tag, el) + ") %>"
|
870
|
+
end
|
871
|
+
end
|
872
|
+
|
873
|
+
|
874
|
+
def static_element_to_erb(el)
|
875
|
+
if promote_static_tag_to_method_call?(el)
|
876
|
+
static_tag_to_method_call(el)
|
877
|
+
else
|
878
|
+
start_tag_src = el.start_tag_source.gsub(REXML::CData::START, "").gsub(REXML::CData::STOP, "")
|
879
|
+
|
880
|
+
# Allow #{...} as an alternate to <%= ... %>
|
881
|
+
start_tag_src.gsub!(/=\s*('.*?'|".*?")/) do |s|
|
882
|
+
s.gsub(/#\{(.*?)\}/, '<%= \1 %>')
|
883
|
+
end
|
884
|
+
|
885
|
+
if el.has_end_tag?
|
886
|
+
start_tag_src + children_to_erb(el) + "</#{el.name}>"
|
887
|
+
else
|
888
|
+
start_tag_src
|
889
|
+
end
|
890
|
+
end
|
891
|
+
end
|
892
|
+
|
893
|
+
|
894
|
+
def promote_static_tag_to_method_call?(el)
|
895
|
+
%w(part merge-attrs if unless repeat).any? {|x| el.attributes[x]}
|
896
|
+
end
|
897
|
+
|
898
|
+
|
899
|
+
def apply_control_attributes(expression, el)
|
900
|
+
controls = %w(if unless repeat).map_hash { |x| el.attributes[x] }.compact
|
901
|
+
|
902
|
+
dryml_exception("You can't have multiple control attributes on the same element", el) if
|
903
|
+
controls.length > 1
|
904
|
+
|
905
|
+
attr = controls.keys.first
|
906
|
+
val = controls.values.first
|
907
|
+
if val.nil?
|
908
|
+
expression
|
909
|
+
else
|
910
|
+
control = if !el.attribute(attr).has_rhs?
|
911
|
+
"this"
|
912
|
+
elsif is_code_attribute?(val)
|
913
|
+
"#{val[1..-1]}"
|
914
|
+
else
|
915
|
+
val.gsub!('-', '_')
|
916
|
+
attr == "repeat" ? %("#{val}") : "this.#{val}"
|
917
|
+
end
|
918
|
+
|
919
|
+
x = gensym
|
920
|
+
case attr
|
921
|
+
when "if"
|
922
|
+
"(if !(#{control}).blank?; (#{x} = #{expression}; Dryml.last_if = true; #{x}) " +
|
923
|
+
"else (Dryml.last_if = false; ''); end)"
|
924
|
+
when "unless"
|
925
|
+
"(if (#{control}).blank?; (#{x} = #{expression}; Dryml.last_if = true; #{x}) " +
|
926
|
+
"else (Dryml.last_if = false; ''); end)"
|
927
|
+
when "repeat"
|
928
|
+
"repeat_attribute(#{control}) { #{expression} }"
|
929
|
+
end
|
930
|
+
end
|
931
|
+
end
|
932
|
+
|
933
|
+
|
934
|
+
def attribute_to_ruby(*args)
|
935
|
+
options = args.extract_options!
|
936
|
+
attr, el = args
|
937
|
+
|
938
|
+
dryml_exception('erb scriptlet not allowed in this attribute (use #{ ... } instead)', el) if
|
939
|
+
attr.is_a?(String) && attr.index("[![HOBO-ERB")
|
940
|
+
|
941
|
+
if options[:symbolize] && attr =~ /^[a-zA-Z_][^a-zA-Z0-9_]*[\?!]?/
|
942
|
+
":#{attr}"
|
943
|
+
else
|
944
|
+
res = if attr.nil?
|
945
|
+
"nil"
|
946
|
+
elsif is_code_attribute?(attr)
|
947
|
+
"(#{attr[1..-1]})"
|
948
|
+
else
|
949
|
+
if attr !~ /"/
|
950
|
+
'"' + attr + '"'
|
951
|
+
elsif attr !~ /'/
|
952
|
+
"'#{attr}'"
|
953
|
+
else
|
954
|
+
dryml_exception("invalid quote(s) in attribute value")
|
955
|
+
end
|
956
|
+
end
|
957
|
+
options[:symbolize] ? (res + ".to_sym") : res
|
958
|
+
end
|
959
|
+
end
|
960
|
+
|
961
|
+
def find_ancestor(el)
|
962
|
+
e = el.parent
|
963
|
+
until e.is_a? REXML::Document
|
964
|
+
return e if yield(e)
|
965
|
+
e = e.parent
|
966
|
+
end
|
967
|
+
return nil
|
968
|
+
end
|
969
|
+
|
970
|
+
def require_toplevel(el, message=nil)
|
971
|
+
message ||= "can only be at the top level"
|
972
|
+
dryml_exception("<#{el.dryml_name}> #{message}", el) if el.parent != @doc.root
|
973
|
+
end
|
974
|
+
|
975
|
+
def require_attribute(el, name, rx=nil, optional=false)
|
976
|
+
val = el.attributes[name]
|
977
|
+
if val
|
978
|
+
dryml_exception("invalid #{name}=\"#{val}\" attribute on <#{el.dryml_name}>", el) unless rx && val =~ rx
|
979
|
+
else
|
980
|
+
dryml_exception("missing #{name} attribute on <#{el.dryml_name}>", el) unless optional
|
981
|
+
end
|
982
|
+
end
|
983
|
+
|
984
|
+
def dryml_exception(message, el=nil)
|
985
|
+
el ||= @last_element
|
986
|
+
raise DrymlException.new(message, template_path, element_line_num(el))
|
987
|
+
end
|
988
|
+
|
989
|
+
def element_line_num(el)
|
990
|
+
@doc.element_line_num(el)
|
991
|
+
end
|
992
|
+
|
993
|
+
def tag_newlines(el)
|
994
|
+
src = el.start_tag_source
|
995
|
+
"\n" * src.count("\n")
|
996
|
+
end
|
997
|
+
|
998
|
+
def is_code_attribute?(attr_value)
|
999
|
+
attr_value =~ /^\&/ && attr_value !~ /^\&\S+;/
|
1000
|
+
end
|
1001
|
+
|
1002
|
+
def logger
|
1003
|
+
ActionController::Base.logger rescue nil
|
1004
|
+
end
|
1005
|
+
|
1006
|
+
def gensym(name="__tmp")
|
1007
|
+
@gensym_counter ||= 0
|
1008
|
+
@gensym_counter += 1
|
1009
|
+
"#{name}_#{@gensym_counter}"
|
1010
|
+
end
|
1011
|
+
|
1012
|
+
def include_source_metadata
|
1013
|
+
# disabled for now -- we're still getting broken rendering with this feature on
|
1014
|
+
return false
|
1015
|
+
@include_source_metadata = RAILS_ENV == "development" && !ENV['DRYML_EDITOR'].blank? if @include_source_metadata.nil?
|
1016
|
+
@include_source_metadata
|
1017
|
+
end
|
1018
|
+
|
1019
|
+
end
|
1020
|
+
|
1021
|
+
end
|