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,613 @@
|
|
1
|
+
module Dryml
|
2
|
+
|
3
|
+
class TemplateEnvironment
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def inherited(subclass)
|
7
|
+
subclass.compiled_local_names = []
|
8
|
+
end
|
9
|
+
attr_accessor :load_time, :compiled_local_names
|
10
|
+
|
11
|
+
|
12
|
+
def _register_tag_attrs(tag_name, attrs)
|
13
|
+
@tag_attrs ||= {}
|
14
|
+
@tag_attrs[tag_name] = attrs
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
def tag_attrs
|
19
|
+
@tag_attrs ||= {}
|
20
|
+
end
|
21
|
+
|
22
|
+
alias_method :delayed_alias_method_chain, :alias_method_chain
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
include ActionView::Helpers
|
27
|
+
include Helper ## FIXME remove
|
28
|
+
|
29
|
+
def initialize(view_name=nil, view=nil)
|
30
|
+
unless view_name.nil? && view.nil?
|
31
|
+
@view = view
|
32
|
+
@_view_name = view_name
|
33
|
+
@_erb_binding = binding
|
34
|
+
@_part_contexts = {}
|
35
|
+
@_scoped_variables = ScopedVariables.new
|
36
|
+
@_polymorphic_tag_cache = {}
|
37
|
+
|
38
|
+
# Make sure the "assigns" from the controller are available (instance variables)
|
39
|
+
if view
|
40
|
+
view.assigns.each do |key, value|
|
41
|
+
instance_variable_set("@#{key}", value)
|
42
|
+
end
|
43
|
+
|
44
|
+
# copy view instance variables over
|
45
|
+
view.instance_variables.each do |iv|
|
46
|
+
instance_variable_set(iv, view.instance_variable_get(iv))
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
for attr in [:erb_binding, :part_contexts, :view_name,
|
53
|
+
:this, :this_parent, :this_field, :this_key,
|
54
|
+
:form_this, :form_field_path, :form_field_names]
|
55
|
+
class_eval "def #{attr}; @_#{attr}; end"
|
56
|
+
end
|
57
|
+
|
58
|
+
def path_for_form_field
|
59
|
+
@_form_field_path.nil? and raise Dryml::DrymlException,
|
60
|
+
"DRYML cannot provide the correct form-field name here (this_field = #{this_field.inspect}, this = #{this.inspect})"
|
61
|
+
@_form_field_path
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
def this_key=(key)
|
66
|
+
@_this_key = key
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
# The type of this, or when this is nil, the type that would be expected in the current field
|
71
|
+
def this_type
|
72
|
+
@_this_type ||= if this == false || this == true
|
73
|
+
Hobo::Boolean
|
74
|
+
elsif this
|
75
|
+
this.class
|
76
|
+
elsif this_parent && this_field && (parent_class = this_parent.class).respond_to?(:attr_type)
|
77
|
+
type = parent_class.attr_type(this_field)
|
78
|
+
if type.is_a?(ActiveRecord::Reflection::AssociationReflection)
|
79
|
+
reflection = type
|
80
|
+
if reflection.macro == :has_many
|
81
|
+
Array
|
82
|
+
elsif reflection.options[:polymorphic]
|
83
|
+
# All we know is that it will be some active-record type
|
84
|
+
ActiveRecord::Base
|
85
|
+
else
|
86
|
+
reflection.klass
|
87
|
+
end
|
88
|
+
else
|
89
|
+
type
|
90
|
+
end
|
91
|
+
else
|
92
|
+
# Nothing to go on at all
|
93
|
+
Object
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
def this_field_reflection
|
99
|
+
this.try.proxy_reflection ||
|
100
|
+
(this_parent && this_field && this_parent.class.respond_to?(:reflections) && this_parent.class.reflections[this_field.to_sym])
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
def attrs_for(name)
|
105
|
+
self.class.tag_attrs[name.to_sym]
|
106
|
+
end
|
107
|
+
|
108
|
+
|
109
|
+
def add_classes!(attributes, *classes)
|
110
|
+
classes = classes.flatten.select{|x|x}
|
111
|
+
current = attributes[:class]
|
112
|
+
attributes[:class] = (current ? current.split + classes : classes).uniq.join(' ')
|
113
|
+
attributes
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
def add_classes(attributes, *classes)
|
118
|
+
add_classes!(HashWithIndifferentAccess.new(attributes), classes)
|
119
|
+
end
|
120
|
+
|
121
|
+
|
122
|
+
def merge_attrs(attrs, overriding_attrs)
|
123
|
+
return {}.update(attrs) if overriding_attrs.nil?
|
124
|
+
attrs = attrs.with_indifferent_access unless attrs.is_a?(HashWithIndifferentAccess)
|
125
|
+
classes = overriding_attrs[:class]
|
126
|
+
attrs = add_classes(attrs, *classes.split) if classes
|
127
|
+
attrs.update(overriding_attrs - [:class])
|
128
|
+
end
|
129
|
+
|
130
|
+
|
131
|
+
def scope
|
132
|
+
@_scoped_variables
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
def typed_id(object=nil, attribute=nil)
|
137
|
+
if object.nil?
|
138
|
+
# nothing passed -- use context
|
139
|
+
if this_parent && this_field && !this_parent.respond_to?(:member_class)
|
140
|
+
object, attribute = this_parent, this_field
|
141
|
+
else
|
142
|
+
object = this
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
id = if (typed_id = object.try.typed_id)
|
147
|
+
typed_id
|
148
|
+
elsif object == @this
|
149
|
+
"this"
|
150
|
+
end
|
151
|
+
attribute ? "#{id}:#{attribute}" : id
|
152
|
+
end
|
153
|
+
|
154
|
+
|
155
|
+
def call_part(part_node_id, part_name, *locals)
|
156
|
+
res = ''
|
157
|
+
new_context do
|
158
|
+
@_part_contexts[part_node_id] = PartContext.for_call(part_name, self, locals)
|
159
|
+
res = send("#{part_name}_part", *locals)
|
160
|
+
end
|
161
|
+
res
|
162
|
+
end
|
163
|
+
|
164
|
+
|
165
|
+
def refresh_part(encoded_context, session, dom_id)
|
166
|
+
context = Dryml::PartContext.for_refresh(encoded_context, @this, session)
|
167
|
+
|
168
|
+
with_part_context(context) do
|
169
|
+
send("#{context.part_name}_part", *context.locals)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
|
174
|
+
def with_part_context(context, &block)
|
175
|
+
this, this_field = context.this, context.this_field
|
176
|
+
|
177
|
+
b = if context.form_field_path
|
178
|
+
proc { with_form_context(:unknown, context.form_field_path, &block) }
|
179
|
+
else
|
180
|
+
block
|
181
|
+
end
|
182
|
+
|
183
|
+
if this && this_field
|
184
|
+
new_object_context(this) { new_field_context(this_field, &b) }
|
185
|
+
elsif this
|
186
|
+
new_object_context(this, &b)
|
187
|
+
else
|
188
|
+
new_context(&b)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
|
193
|
+
def call_polymorphic_tag(name, *args)
|
194
|
+
name = name.to_s.gsub('-', '_')
|
195
|
+
type = args.first.is_a?(Class) ? args.shift : nil
|
196
|
+
attributes, parameters = args
|
197
|
+
|
198
|
+
tag = find_polymorphic_tag(name, type)
|
199
|
+
if tag != name
|
200
|
+
send(tag, attributes || {}, parameters || {})
|
201
|
+
else
|
202
|
+
block_given? ? yield : nil
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
|
207
|
+
def find_polymorphic_tag(name, call_type=nil)
|
208
|
+
call_type ||= (this.respond_to?(:member_class) && this.member_class) || this_type
|
209
|
+
|
210
|
+
begin
|
211
|
+
found = nil
|
212
|
+
while true
|
213
|
+
if respond_to?(poly_name = "#{name}__for_#{call_type.name.to_s.underscore.gsub('/', '__')}")
|
214
|
+
found = poly_name
|
215
|
+
break
|
216
|
+
else
|
217
|
+
if call_type == ActiveRecord::Base || call_type == Object
|
218
|
+
found = name
|
219
|
+
break
|
220
|
+
else
|
221
|
+
call_type = call_type.superclass
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
found
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
|
230
|
+
def repeat_attribute(string_or_array)
|
231
|
+
res = nil
|
232
|
+
if string_or_array.instance_of?(String)
|
233
|
+
new_field_context(string_or_array) do
|
234
|
+
res = context_map { yield }
|
235
|
+
end
|
236
|
+
else
|
237
|
+
res = context_map(string_or_array) { yield }
|
238
|
+
end
|
239
|
+
res.join
|
240
|
+
end
|
241
|
+
|
242
|
+
|
243
|
+
def new_context
|
244
|
+
ctx = [ @_this, @_this_parent, @_this_field, @_this_type,
|
245
|
+
@_form_field_path, @_form_field_paths_by_object ]
|
246
|
+
@_this_type = nil
|
247
|
+
res = nil
|
248
|
+
@view.with_output_buffer { res = yield }
|
249
|
+
@_this, @_this_parent, @_this_field, @_this_type, @_form_field_path, @_form_field_paths_by_object = ctx
|
250
|
+
res.to_s
|
251
|
+
end
|
252
|
+
|
253
|
+
|
254
|
+
def new_object_context(new_this)
|
255
|
+
new_context do
|
256
|
+
if new_this.respond_to?(:origin)
|
257
|
+
@_this_parent, @_this_field = new_this.origin, new_this.origin_attribute
|
258
|
+
else
|
259
|
+
@_this_parent, @_this_field = [nil, nil]
|
260
|
+
end
|
261
|
+
@_this = new_this
|
262
|
+
|
263
|
+
# We might have lost track of where 'this' is relative to the form_this
|
264
|
+
# check if this or this_parent are objects we've seen before in this form
|
265
|
+
@_form_field_path = find_form_field_path(new_this) if @_form_field_path
|
266
|
+
|
267
|
+
yield
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
|
272
|
+
def new_field_context(field_path, new_this=nil)
|
273
|
+
new_context do
|
274
|
+
path = if field_path.is_a? String
|
275
|
+
field_path.split('.')
|
276
|
+
else
|
277
|
+
Array(field_path)
|
278
|
+
end
|
279
|
+
if new_this
|
280
|
+
raise ArgumentError, "invlaid context change" unless path.length == 1
|
281
|
+
@_this_parent, @_this_field, @_this = this, path.first, new_this
|
282
|
+
else
|
283
|
+
parent, field, obj = Dryml.get_field_path(this, path)
|
284
|
+
@_this, @_this_parent, @_this_field = obj, parent, field
|
285
|
+
end
|
286
|
+
|
287
|
+
if @_form_field_path
|
288
|
+
@_form_field_path += path
|
289
|
+
@_form_field_paths_by_object[@_this] = @_form_field_path
|
290
|
+
end
|
291
|
+
|
292
|
+
yield
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
|
297
|
+
def find_form_field_path(object)
|
298
|
+
back = []
|
299
|
+
while object
|
300
|
+
path = @_form_field_paths_by_object[object]
|
301
|
+
if path
|
302
|
+
path = path + back unless back.empty?
|
303
|
+
return path
|
304
|
+
end
|
305
|
+
if object.respond_to? :origin
|
306
|
+
back.unshift object.origin_attribute
|
307
|
+
object = object.origin
|
308
|
+
else
|
309
|
+
return nil
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
|
315
|
+
|
316
|
+
|
317
|
+
def _tag_context(attributes)
|
318
|
+
with = attributes[:with]
|
319
|
+
field = attributes[:field]
|
320
|
+
|
321
|
+
if with && field
|
322
|
+
new_object_context(with) { new_field_context(field) { yield } }
|
323
|
+
elsif field
|
324
|
+
new_field_context(field) { yield }
|
325
|
+
elsif attributes.has_key?(:with)
|
326
|
+
new_object_context(with) { yield }
|
327
|
+
else
|
328
|
+
new_context { yield }
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
|
333
|
+
def with_form_context(form_this=this, form_field_path=[form_this.class.name.underscore])
|
334
|
+
@_form_this = form_this
|
335
|
+
@_form_field_path = form_field_path
|
336
|
+
@_form_field_paths_by_object = { form_this => form_field_path }
|
337
|
+
res = scope.new_scope :in_form => true, :form_field_names => [] do
|
338
|
+
yield
|
339
|
+
end
|
340
|
+
@_form_this = @_form_field_path = @_form_field_paths_by_object = nil
|
341
|
+
res
|
342
|
+
end
|
343
|
+
|
344
|
+
|
345
|
+
def register_form_field(name)
|
346
|
+
scope.form_field_names << name
|
347
|
+
end
|
348
|
+
|
349
|
+
|
350
|
+
def _tag_locals(attributes, locals)
|
351
|
+
attributes.symbolize_keys!
|
352
|
+
#ensure with and field are not in attributes
|
353
|
+
attributes.delete(:with)
|
354
|
+
attributes.delete(:field)
|
355
|
+
|
356
|
+
# declared attributes don't appear in the attributes hash
|
357
|
+
stripped_attributes = HashWithIndifferentAccess.new.update(attributes)
|
358
|
+
locals.each {|a| stripped_attributes.delete(a.to_sym) }
|
359
|
+
|
360
|
+
# Return locals declared as local variables (attrs="...")
|
361
|
+
locals.map {|a| attributes[a.to_sym]} + [stripped_attributes]
|
362
|
+
end
|
363
|
+
|
364
|
+
|
365
|
+
def call_tag_parameter_with_default_content(the_tag, attributes, default_content, overriding_content_proc)
|
366
|
+
if the_tag.is_one_of?(String, Symbol) && the_tag.to_s.in?(Dryml.static_tags)
|
367
|
+
body = if overriding_content_proc
|
368
|
+
new_context { overriding_content_proc.call(proc { default_content._?.call(nil) }) }
|
369
|
+
elsif default_content
|
370
|
+
new_context { default_content.call(nil) }
|
371
|
+
else
|
372
|
+
nil
|
373
|
+
end
|
374
|
+
element(the_tag, attributes, body)
|
375
|
+
else
|
376
|
+
d = if overriding_content_proc
|
377
|
+
proc { |default| overriding_content_proc.call(proc { default_content._?.call(default) }) }
|
378
|
+
else
|
379
|
+
proc { |default| default_content._?.call(default) }
|
380
|
+
end
|
381
|
+
send(the_tag, attributes, { :default => d })
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
|
386
|
+
def call_tag_parameter(the_tag, attributes, parameters, caller_parameters, param_name)
|
387
|
+
overriding_proc = caller_parameters[param_name]
|
388
|
+
replacing_proc = caller_parameters[:"#{param_name}_replacement"]
|
389
|
+
|
390
|
+
unless param_name == the_tag || param_name == :default
|
391
|
+
classes = attributes[:class]
|
392
|
+
param_class = param_name.to_s.gsub('_', '-')
|
393
|
+
attributes[:class] = if classes
|
394
|
+
classes =~ /\b#{param_class}\b/ ? classes : "#{classes} #{param_class}"
|
395
|
+
else
|
396
|
+
param_class
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
if param_name == :default && overriding_proc && overriding_proc.arity>0
|
401
|
+
# :default content is handled specially
|
402
|
+
|
403
|
+
call_tag_parameter_with_default_content(the_tag, attributes, parameters[:default], overriding_proc)
|
404
|
+
|
405
|
+
elsif replacing_proc
|
406
|
+
# The caller is replacing this parameter. Don't call the tag
|
407
|
+
# at all, just the overriding proc, but pass the restorable
|
408
|
+
# tag as a parameter to the overriding proc
|
409
|
+
|
410
|
+
tag_restore = proc do |restore_attrs, restore_params|
|
411
|
+
# Call the replaced tag with the attributes and parameters
|
412
|
+
# as given in the original tag definition, and with the
|
413
|
+
# specialisation given on the 'restore' call
|
414
|
+
|
415
|
+
if overriding_proc
|
416
|
+
overriding_attributes, overriding_parameters = overriding_proc.call
|
417
|
+
restore_attrs = overriding_attributes.merge(restore_attrs)
|
418
|
+
restore_params = overriding_parameters.merge(restore_params)
|
419
|
+
end
|
420
|
+
|
421
|
+
override_and_call_tag(the_tag, attributes, parameters, restore_attrs, restore_params)
|
422
|
+
end
|
423
|
+
replacing_proc.call(tag_restore)
|
424
|
+
|
425
|
+
else
|
426
|
+
overriding_attributes, overriding_parameters = overriding_proc._?.call
|
427
|
+
override_and_call_tag(the_tag, attributes, parameters, overriding_attributes, overriding_parameters)
|
428
|
+
end
|
429
|
+
end
|
430
|
+
|
431
|
+
|
432
|
+
def override_and_call_tag(the_tag, general_attributes, general_parameters, overriding_attributes, overriding_parameters)
|
433
|
+
attributes = overriding_attributes ? merge_attrs(general_attributes, overriding_attributes) : general_attributes
|
434
|
+
if overriding_parameters
|
435
|
+
overriding_default_content = overriding_parameters.delete(:default)
|
436
|
+
parameters = general_parameters.merge(overriding_parameters)
|
437
|
+
else
|
438
|
+
parameters = general_parameters
|
439
|
+
end
|
440
|
+
|
441
|
+
default_content = parameters[:default]
|
442
|
+
|
443
|
+
if the_tag.is_one_of?(String, Symbol) && the_tag.to_s.in?(Dryml.static_tags)
|
444
|
+
body = if overriding_default_content
|
445
|
+
new_context { overriding_default_content.call(proc { default_content.call(nil) if default_content }) }
|
446
|
+
elsif default_content
|
447
|
+
new_context { default_content.call(nil) }
|
448
|
+
else
|
449
|
+
nil
|
450
|
+
end
|
451
|
+
element(the_tag, attributes, body)
|
452
|
+
else
|
453
|
+
if default_content || overriding_default_content
|
454
|
+
d = if overriding_default_content
|
455
|
+
proc { |default| overriding_default_content.call(proc { default_content.call(default) if default_content }) }
|
456
|
+
else
|
457
|
+
proc { |default| default_content.call(default) if default_content }
|
458
|
+
end
|
459
|
+
parameters = parameters.merge(:default => d)
|
460
|
+
end
|
461
|
+
|
462
|
+
if the_tag.is_one_of?(String, Symbol)
|
463
|
+
# It's a defined DRYML tag
|
464
|
+
send(the_tag, attributes, parameters)
|
465
|
+
else
|
466
|
+
# It's a proc - restoring a replaced parameter
|
467
|
+
the_tag.call(attributes, parameters)
|
468
|
+
end
|
469
|
+
end
|
470
|
+
end
|
471
|
+
|
472
|
+
|
473
|
+
# This method is used where 'param' is declared on a tag that is
|
474
|
+
# itself a parameter tag. Takes two procs that each return a pair
|
475
|
+
# of hashes (attributes and parameters). Returns a single proc
|
476
|
+
# that also returns a pair of hashes - the merged atributes and
|
477
|
+
# parameters.
|
478
|
+
def merge_tag_parameter(general_proc, overriding_proc)
|
479
|
+
if overriding_proc.nil?
|
480
|
+
general_proc
|
481
|
+
else
|
482
|
+
if overriding_proc.arity == 1
|
483
|
+
# The override is a replace parameter - just pass it on
|
484
|
+
overriding_proc
|
485
|
+
else
|
486
|
+
proc do
|
487
|
+
overriding_attrs, overriding_parameters = overriding_proc.call
|
488
|
+
general_attrs, general_parameters = general_proc.call
|
489
|
+
|
490
|
+
attrs = merge_attrs(general_attrs, overriding_attrs)
|
491
|
+
overriding_default = overriding_parameters.delete(:default)
|
492
|
+
params = merge_parameter_hashes(general_parameters, overriding_parameters)
|
493
|
+
|
494
|
+
# The overrider should provide its :default as the new
|
495
|
+
# 'default_content'
|
496
|
+
if overriding_default
|
497
|
+
params[:default] =
|
498
|
+
if general_parameters[:default]
|
499
|
+
proc do |default|
|
500
|
+
overriding_default.call(proc { new_context { concat(general_parameters[:default].call(default)) } } )
|
501
|
+
end
|
502
|
+
else
|
503
|
+
proc do |default|
|
504
|
+
overriding_default.call(default)
|
505
|
+
end
|
506
|
+
end
|
507
|
+
end
|
508
|
+
|
509
|
+
[attrs, params]
|
510
|
+
end
|
511
|
+
end
|
512
|
+
end
|
513
|
+
end
|
514
|
+
|
515
|
+
|
516
|
+
def merge_parameter_hashes(given_parameters, overriding_parameters)
|
517
|
+
to_merge = given_parameters.keys & overriding_parameters.keys
|
518
|
+
no_merge = overriding_parameters.keys - to_merge
|
519
|
+
result = given_parameters.dup
|
520
|
+
|
521
|
+
no_merge.each { |k| result[k] = overriding_parameters[k] }
|
522
|
+
to_merge.each { |k| result[k] = merge_tag_parameter(given_parameters[k], overriding_parameters[k])}
|
523
|
+
|
524
|
+
result
|
525
|
+
end
|
526
|
+
|
527
|
+
|
528
|
+
|
529
|
+
|
530
|
+
def part_contexts_javascripts
|
531
|
+
storage = part_contexts_storage
|
532
|
+
storage.blank? ? "" : "<script type=\"text/javascript\">\n#{storage}</script>\n"
|
533
|
+
end
|
534
|
+
|
535
|
+
|
536
|
+
def part_contexts_storage
|
537
|
+
PartContext.client_side_storage(@_part_contexts, session)
|
538
|
+
end
|
539
|
+
|
540
|
+
|
541
|
+
def render_tag(tag_name, attributes)
|
542
|
+
method_name = tag_name.to_s.gsub('-', '_')
|
543
|
+
if respond_to?(method_name)
|
544
|
+
res = send(method_name, attributes).strip
|
545
|
+
|
546
|
+
# TODO: Temporary hack to get the dryml metadata comments in the right place
|
547
|
+
if false && RAILS_ENV == "development"
|
548
|
+
res.gsub(/^(.*?)(<!DOCTYPE.*?>).*?(<html.*?>)/m, "\\2\\3\\1")
|
549
|
+
else
|
550
|
+
res
|
551
|
+
end
|
552
|
+
else
|
553
|
+
false
|
554
|
+
end
|
555
|
+
end
|
556
|
+
|
557
|
+
|
558
|
+
def element(name, attributes, content=nil, escape = true, empty = false, &block)
|
559
|
+
unless attributes.blank?
|
560
|
+
attrs = []
|
561
|
+
if escape
|
562
|
+
attributes.each do |key, value|
|
563
|
+
next unless value
|
564
|
+
key = key.to_s.gsub("_", "-")
|
565
|
+
|
566
|
+
value = if ActionView::Helpers::TagHelper::BOOLEAN_ATTRIBUTES.include?(key)
|
567
|
+
key
|
568
|
+
else
|
569
|
+
# escape once
|
570
|
+
value.to_s.gsub(/[\"><]|&(?!([a-zA-Z]+|(#\d+));)/) { |special| ERB::Util::HTML_ESCAPE[special] }
|
571
|
+
end
|
572
|
+
attrs << %(#{key}="#{value}")
|
573
|
+
end
|
574
|
+
|
575
|
+
else
|
576
|
+
attrs = options.map do |key, value|
|
577
|
+
key = key.to_s.gsub("_", "-")
|
578
|
+
%(#{key}="#{value}")
|
579
|
+
end
|
580
|
+
end
|
581
|
+
attr_string = " #{attrs.sort * ' '}" unless attrs.empty?
|
582
|
+
end
|
583
|
+
|
584
|
+
content = new_context { yield } if block_given?
|
585
|
+
res = if empty
|
586
|
+
"<#{name}#{attr_string}#{scope.xmldoctype ? ' /' : ''}>"
|
587
|
+
else
|
588
|
+
"<#{name}#{attr_string}>#{content}</#{name}>"
|
589
|
+
end
|
590
|
+
if block_called_from_erb? block
|
591
|
+
concat res
|
592
|
+
else
|
593
|
+
res
|
594
|
+
end
|
595
|
+
end
|
596
|
+
|
597
|
+
|
598
|
+
def session
|
599
|
+
@view ? @view.session : {}
|
600
|
+
end
|
601
|
+
|
602
|
+
|
603
|
+
def method_missing(name, *args, &b)
|
604
|
+
if @view
|
605
|
+
@view.send(name, *args, &b)
|
606
|
+
else
|
607
|
+
raise NoMethodError, name.to_s
|
608
|
+
end
|
609
|
+
end
|
610
|
+
|
611
|
+
end
|
612
|
+
|
613
|
+
end
|