hobo 0.5.3 → 0.6

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.
Files changed (80) hide show
  1. data/bin/hobo +18 -4
  2. data/hobo_files/plugin/CHANGES.txt +511 -0
  3. data/hobo_files/plugin/README +8 -3
  4. data/hobo_files/plugin/Rakefile +81 -0
  5. data/hobo_files/plugin/generators/hobo/hobo_generator.rb +4 -4
  6. data/hobo_files/plugin/generators/hobo/templates/guest.rb +1 -1
  7. data/hobo_files/plugin/generators/hobo_front_controller/hobo_front_controller_generator.rb +1 -1
  8. data/hobo_files/plugin/generators/hobo_front_controller/templates/index.dryml +16 -22
  9. data/hobo_files/plugin/generators/hobo_front_controller/templates/login.dryml +4 -6
  10. data/hobo_files/plugin/generators/hobo_front_controller/templates/search.dryml +6 -5
  11. data/hobo_files/plugin/generators/hobo_front_controller/templates/signup.dryml +4 -6
  12. data/hobo_files/plugin/generators/hobo_migration/hobo_migration_generator.rb +237 -0
  13. data/hobo_files/plugin/generators/hobo_migration/templates/migration.rb +9 -0
  14. data/hobo_files/plugin/generators/hobo_model/USAGE +2 -3
  15. data/hobo_files/plugin/generators/hobo_model/hobo_model_generator.rb +1 -14
  16. data/hobo_files/plugin/generators/hobo_model/templates/fixtures.yml +1 -6
  17. data/hobo_files/plugin/generators/hobo_model/templates/model.rb +10 -4
  18. data/hobo_files/plugin/generators/hobo_rapid/hobo_rapid_generator.rb +7 -6
  19. data/hobo_files/plugin/generators/hobo_rapid/templates/hobo_base.css +68 -0
  20. data/hobo_files/plugin/generators/hobo_rapid/templates/hobo_rapid.css +93 -0
  21. data/hobo_files/plugin/generators/hobo_rapid/templates/hobo_rapid.js +11 -6
  22. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/images/plus.png +0 -0
  23. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/public/stylesheets/application.css +24 -14
  24. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/views/application.dryml +28 -44
  25. data/hobo_files/plugin/generators/hobo_user_model/USAGE +2 -12
  26. data/hobo_files/plugin/generators/hobo_user_model/hobo_user_model_generator.rb +1 -14
  27. data/hobo_files/plugin/generators/hobo_user_model/templates/fixtures.yml +0 -6
  28. data/hobo_files/plugin/generators/hobo_user_model/templates/model.rb +8 -1
  29. data/hobo_files/plugin/init.rb +6 -2
  30. data/hobo_files/plugin/lib/active_record/has_many_association.rb +23 -12
  31. data/hobo_files/plugin/lib/extensions.rb +134 -40
  32. data/hobo_files/plugin/lib/extensions/test_case.rb +0 -1
  33. data/hobo_files/plugin/lib/hobo.rb +77 -46
  34. data/hobo_files/plugin/lib/hobo/authenticated_user.rb +24 -2
  35. data/hobo_files/plugin/lib/hobo/authentication_support.rb +2 -1
  36. data/hobo_files/plugin/lib/hobo/controller.rb +35 -12
  37. data/hobo_files/plugin/lib/hobo/define_tags.rb +4 -4
  38. data/hobo_files/plugin/lib/hobo/dryml.rb +33 -51
  39. data/hobo_files/plugin/lib/hobo/dryml/dryml_builder.rb +47 -34
  40. data/hobo_files/plugin/lib/hobo/dryml/scoped_variables.rb +37 -0
  41. data/hobo_files/plugin/lib/hobo/dryml/taglib.rb +27 -5
  42. data/hobo_files/plugin/lib/hobo/dryml/template.rb +545 -302
  43. data/hobo_files/plugin/lib/hobo/dryml/template_environment.rb +305 -135
  44. data/hobo_files/plugin/lib/hobo/email_address.rb +5 -0
  45. data/hobo_files/plugin/lib/hobo/field_spec.rb +66 -0
  46. data/hobo_files/plugin/lib/hobo/hobo_helper.rb +325 -0
  47. data/hobo_files/plugin/lib/hobo/html_string.rb +2 -0
  48. data/hobo_files/plugin/lib/hobo/lazy_hash.rb +13 -1
  49. data/hobo_files/plugin/lib/hobo/markdown_string.rb +3 -1
  50. data/hobo_files/plugin/lib/hobo/model.rb +185 -66
  51. data/hobo_files/plugin/lib/hobo/model_controller.rb +56 -49
  52. data/hobo_files/plugin/lib/hobo/password_string.rb +2 -0
  53. data/hobo_files/plugin/lib/hobo/plugins.rb +75 -0
  54. data/hobo_files/plugin/lib/hobo/rapid_helper.rb +98 -0
  55. data/hobo_files/plugin/lib/hobo/static_tags +0 -3
  56. data/hobo_files/plugin/lib/hobo/textile_string.rb +11 -1
  57. data/hobo_files/plugin/lib/hobo/undefined.rb +1 -1
  58. data/hobo_files/plugin/lib/rexml.rb +166 -75
  59. data/hobo_files/plugin/spec/fixtures/users.yml +9 -0
  60. data/hobo_files/plugin/spec/spec.opts +6 -0
  61. data/hobo_files/plugin/spec/spec_helper.rb +28 -0
  62. data/hobo_files/plugin/spec/unit/hobo/dryml/template_spec.rb +650 -0
  63. data/hobo_files/plugin/tags/core.dryml +58 -4
  64. data/hobo_files/plugin/tags/rapid.dryml +289 -135
  65. data/hobo_files/plugin/tags/rapid_document_tags.dryml +49 -0
  66. data/hobo_files/plugin/tags/rapid_editing.dryml +92 -69
  67. data/hobo_files/plugin/tags/rapid_forms.dryml +242 -0
  68. data/hobo_files/plugin/tags/rapid_navigation.dryml +65 -65
  69. data/hobo_files/plugin/tags/rapid_pages.dryml +197 -124
  70. data/hobo_files/plugin/tags/rapid_support.dryml +23 -0
  71. metadata +29 -22
  72. data/hobo_files/plugin/generators/hobo_model/templates/migration.rb +0 -13
  73. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/default/default_mapping.rb +0 -11
  74. data/hobo_files/plugin/generators/hobo_user_model/templates/migration.rb +0 -15
  75. data/hobo_files/plugin/lib/hobo/HtmlString +0 -3
  76. data/hobo_files/plugin/lib/hobo/controller_helpers.rb +0 -135
  77. data/hobo_files/plugin/lib/hobo/core.rb +0 -475
  78. data/hobo_files/plugin/lib/hobo/rapid.rb +0 -447
  79. data/hobo_files/plugin/test/hobo_dryml_template_test.rb +0 -7
  80. data/hobo_files/plugin/test/hobo_test.rb +0 -7
@@ -1,51 +1,151 @@
1
1
  module Hobo::Dryml
2
2
 
3
3
  class TemplateEnvironment
4
-
4
+
5
5
  class << self
6
6
  def inherited(subclass)
7
7
  subclass.compiled_local_names = []
8
8
  end
9
9
  attr_accessor :load_time, :compiled_local_names
10
+
11
+ # --- Local Tags --- #
12
+
13
+ def start_redefine_block(method_names)
14
+ @_preserved_methods_for_redefine ||= []
15
+ @_redef_impl_names ||= []
16
+
17
+ methods = {}
18
+ method_names.each {|m| methods[m] = m.in?(self.methods) && instance_method(m) }
19
+ @_preserved_methods_for_redefine.push(methods)
20
+ @_redef_impl_names.push []
21
+ end
22
+
23
+
24
+ def end_redefine_block
25
+ methods = @_preserved_methods_for_redefine.pop
26
+ methods.each_pair do |name, method|
27
+ if method
28
+ define_method(name, method)
29
+ else
30
+ remove_method(name)
31
+ end
32
+ end
33
+ to_remove = @_redef_impl_names.pop
34
+ to_remove.each {|m| remove_method(m) }
35
+ end
36
+
37
+
38
+ def redefine_nesting
39
+ @_preserved_methods_for_redefine.length
40
+ end
41
+
42
+
43
+ def redefine_tag(name, proc)
44
+ impl_name = "#{name}_redefined_#{redefine_nesting}"
45
+ define_method(impl_name, proc)
46
+ class_eval "def #{name}(options={}, &b); #{impl_name}(options, b); end"
47
+ @_redef_impl_names.push(impl_name)
48
+ end
49
+
50
+ def redefine_template(name, proc)
51
+ impl_name = "#{name}_redefined_#{redefine_nesting}"
52
+ define_method(impl_name, proc)
53
+ class_eval "def #{name}(options={}, template_parameters={}, &b); " +
54
+ "#{impl_name}(options, template_parameters, b); end"
55
+ @_redef_impl_names.push(impl_name)
56
+ end
57
+
58
+ # --- end local tags --- #
59
+
60
+
61
+ def _register_tag_attrs(tag_name, attrs)
62
+ @tag_attrs ||= {}
63
+ @tag_attrs[tag_name] = attrs
64
+ end
65
+
66
+ def tag_attrs
67
+ @tag_attrs ||= {}
68
+ end
69
+
10
70
  end
11
71
 
12
72
  for mod in ActionView::Helpers.constants.grep(/Helper$/).map {|m| ActionView::Helpers.const_get(m)}
13
73
  include mod
14
74
  end
15
75
 
16
- def initialize(view_name, view)
17
- @view = view
18
- @view_name = view_name
19
- @erb_binding = binding
20
- @part_contexts = {}
21
- @stack = [nil]
22
-
23
- # Make sure the "assigns" from the controller are available (instance variables)
24
- if view
25
- view.assigns.each do |key, value|
26
- instance_variable_set("@#{key}", value)
27
- end
28
-
29
- # copy view instance variables over
30
- view.instance_variables.each do |iv|
31
- instance_variable_set(iv, @view.instance_variable_get(iv))
76
+ def initialize(view_name=nil, view=nil)
77
+ unless view_name.nil? && view.nil?
78
+ @view = view
79
+ @_view_name = view_name
80
+ @_erb_binding = binding
81
+ @_part_contexts = {}
82
+ @_scoped_variables = ScopedVariables.new
83
+
84
+ # Make sure the "assigns" from the controller are available (instance variables)
85
+ if view
86
+ view.assigns.each do |key, value|
87
+ instance_variable_set("@#{key}", value)
88
+ end
89
+
90
+ # copy view instance variables over
91
+ view.instance_variables.each do |iv|
92
+ instance_variable_set(iv, view.instance_variable_get(iv))
93
+ end
32
94
  end
33
95
  end
34
96
  end
35
97
 
36
- attr_accessor :erb_binding, :part_contexts, :view_name
98
+ attr_accessor
99
+
100
+ for attr in [:erb_binding, :part_contexts, :view_name,
101
+ :this, :this_parent, :this_field, :this_type,
102
+ :form_field_path, :form_this, :form_field_names]
103
+ class_eval "def #{attr}; @_#{attr}; end"
104
+ end
105
+
106
+
107
+ def attrs_for(name)
108
+ self.class.tag_attrs[name.to_sym]
109
+ end
110
+
111
+
112
+ def add_classes!(attributes, *classes)
113
+ classes = classes.flatten.select{|x|x}.every(:to_s)
114
+ current = attributes[:class]
115
+ attributes[:class] = (current ? current.split + classes : classes).uniq.join(' ')
116
+ attributes
117
+ end
118
+
119
+
120
+ def add_classes(attributes, *classes)
121
+ add_classes!(HashWithIndifferentAccess.new(attributes), classes)
122
+ end
37
123
 
38
- attr_reader :this_parent, :this_field, :this_type, :form_field_path, :form_this, :form_field_names
39
124
 
40
- def this; @_this; end
125
+ def merge_attrs(attrs, overriding_attrs)
126
+ attrs = attrs.with_indifferent_access unless attrs.is_a?(HashWithIndifferentAccess)
127
+ classes = overriding_attrs[:class]
128
+ attrs = add_classes(attrs, *classes.split) if classes
129
+ attrs.update(overriding_attrs - [:class])
130
+ end
131
+
41
132
 
133
+ def scope
134
+ @_scoped_variables
135
+ end
136
+
137
+
42
138
  def attr_extension(s)
43
139
  AttributeExtensionString.new(s)
44
140
  end
45
-
141
+
46
142
 
47
143
  def this_field_dom_id
48
- Hobo.dom_id(this_parent, this_field)
144
+ if this_parent && this_field
145
+ Hobo.dom_id(this_parent, this_field)
146
+ else
147
+ Hobo.dom_id(this)
148
+ end
49
149
  end
50
150
 
51
151
 
@@ -66,36 +166,81 @@ module Hobo::Dryml
66
166
  res = ''
67
167
  if part_this
68
168
  new_object_context(part_this) do
69
- @part_contexts[dom_id] = [part_id, part_context_model_id]
169
+ @_part_contexts[dom_id] = [part_id, part_context_model_id]
70
170
  res = send("#{part_id}_part")
71
171
  end
72
172
  else
73
173
  new_context do
74
- @part_contexts[dom_id] = [part_id, part_context_model_id]
174
+ @_part_contexts[dom_id] = [part_id, part_context_model_id]
75
175
  res = send("#{part_id}_part")
76
176
  end
77
177
  end
78
178
  res
79
179
  end
180
+
181
+ def call_polymorphic_tag(*args, &b)
182
+ attributes = extract_options_from_args!(args)
183
+ name, type = args
184
+
185
+ tag = find_polymorphic_tag(name, type)
186
+ if tag != name
187
+ send(tag, attributes, &b)
188
+ else
189
+ nil
190
+ end
191
+ end
80
192
 
193
+
194
+ def find_polymorphic_tag(name, call_type=nil)
195
+ call_type ||= this_type
196
+ return name if call_type.is_a?(ActiveRecord::Reflection::AssociationReflection)
197
+ call_type = TrueClass if call_type == FalseClass
198
+
199
+ while true
200
+ if call_type == ActiveRecord::Base || call_type == Object
201
+ return name
202
+ elsif respond_to?(poly_name = "#{name}__for_#{call_type.name.to_s.underscore.gsub('/', '__')}")
203
+ return poly_name
204
+ else
205
+ call_type = call_type.superclass
206
+ end
207
+ end
208
+ end
209
+ alias_method :find_polymorphic_template, :find_polymorphic_tag
210
+
211
+
212
+ def repeat_attribute(array, &b)
213
+ res = array.map { |x| new_object_context(x, &b) }.join
214
+ Hobo::Dryml.last_if = !array.empty?
215
+ res
216
+ end
81
217
 
218
+
82
219
  def _erbout
83
- @output
220
+ @_erb_output
221
+ end
222
+
223
+
224
+ def _output(s)
225
+ @_erb_output.concat(s)
84
226
  end
85
227
 
86
228
 
87
229
  def new_context
88
- ctx = @output, @_this, @this_parent, @this_field, @this_type, @form_field_path
89
- @output = ""
230
+ ctx = [ @_erb_output,
231
+ @_this, @_this_parent, @_this_field, @_this_type,
232
+ @_form_field_path]
233
+ @_erb_output = ""
90
234
  res = yield
91
- @output, @_this, @this_parent, @this_field, @this_type, @form_field_path = ctx
235
+ @_erb_output, @_this, @_this_parent, @_this_field, @_this_type,
236
+ @_form_field_path = ctx
92
237
  res.to_s
93
238
  end
94
239
 
95
240
 
96
241
  def new_object_context(new_this)
97
242
  new_context do
98
- @this_parent,@this_field,@this_type = if new_this.respond_to?(:proxy_reflection)
243
+ @_this_parent,@_this_field,@_this_type = if new_this.respond_to?(:proxy_reflection)
99
244
  refl = new_this.proxy_reflection
100
245
  [new_this.proxy_owner, refl.name, refl]
101
246
  else
@@ -129,10 +274,9 @@ module Hobo::Dryml
129
274
  else
130
275
  obj.class
131
276
  end
132
-
133
277
 
134
- @_this, @this_parent, @this_field, @this_type = obj, parent, field, type
135
- @form_field_path += path if @form_field_path
278
+ @_this, @_this_parent, @_this_field, @_this_type = obj, parent, field, type
279
+ @_form_field_path += path if @_form_field_path
136
280
  yield
137
281
  end
138
282
  end
@@ -141,23 +285,25 @@ module Hobo::Dryml
141
285
  def _tag_context(options, tagbody_proc)
142
286
  tagbody = tagbody_proc && proc do |*args|
143
287
  res = ''
144
- block_options = args.length > 0 && args.first
145
- if block_options and block_options.has_key?(:obj)
146
- new_object_context(block_options[:obj]) { res = tagbody_proc.call }
147
- elsif block_options and block_options.has_key?(:attr)
148
- new_field_context(block_options[:attr]) { res = tagbody_proc.call }
288
+
289
+ block_options, default_tagbody = args
290
+ block_with = block_options && block_options[:with]
291
+ if block_options && block_options.has_key?(:field)
292
+ new_field_context(block_options[:field], block_with) { res = tagbody_proc.call(default_tagbody) }
293
+ elsif block_options && block_options.has_key?(:with)
294
+ new_object_context(block_with) { res = tagbody_proc.call(default_tagbody) }
149
295
  else
150
- new_context { res = tagbody_proc.call }
296
+ new_context { res = tagbody_proc.call(default_tagbody) }
151
297
  end
152
298
  res
153
299
  end
154
300
 
155
- obj = options[:obj] == "page" ? @this : options[:obj]
156
-
157
- if options.has_key?(:attr)
158
- new_field_context(options[:attr], obj) { yield tagbody }
159
- elsif options.has_key?(:obj)
160
- new_object_context(obj) { yield tagbody }
301
+ with = options[:with] == "page" ? @this : options[:with]
302
+
303
+ if options.has_key?(:field)
304
+ new_field_context(options[:field], with) { yield tagbody }
305
+ elsif options.has_key?(:with)
306
+ new_object_context(with) { yield tagbody }
161
307
  else
162
308
  new_context { yield tagbody }
163
309
  end
@@ -165,18 +311,18 @@ module Hobo::Dryml
165
311
 
166
312
 
167
313
  def with_form_context
168
- @form_this = this
169
- @form_field_path = []
170
- @form_field_names = []
314
+ @_form_this = this
315
+ @_form_field_path = []
316
+ @_form_field_names = []
171
317
  res = yield
172
- field_names = @form_field_names
173
- @form_this = @form_field_path = @form_field_names = nil
318
+ field_names = @_form_field_names
319
+ @_form_this = @_form_field_path = @_form_field_names = nil
174
320
  [res, field_names]
175
321
  end
176
322
 
177
323
 
178
324
  def register_form_field(name)
179
- @form_field_names << name
325
+ @_form_field_names << name
180
326
  end
181
327
 
182
328
 
@@ -192,107 +338,131 @@ module Hobo::Dryml
192
338
  end
193
339
 
194
340
 
195
- def _tag_locals(options, attrs, inner_tag_names)
196
- options = Hobo::Dryml.hashify_options(options)
197
- options.symbolize_keys!
198
- #ensure obj and attr are not in options
199
- options.delete(:obj)
200
- options.delete(:attr)
341
+ def _tag_locals(attributes, locals)
342
+ attributes.symbolize_keys!
343
+ #ensure with and field are not in attributes
344
+ attributes.delete(:with)
345
+ attributes.delete(:field)
201
346
 
202
- inner_tag_options, options = options.partition_hash(inner_tag_names.omap{to_sym})
203
-
204
- # positional arguments never appear in the options hash
205
- stripped_options = {}.update(options)
206
- attrs.each {|a| stripped_options.delete(a.to_sym) }
207
- attrs.map {|a| options[a.to_sym]} + [stripped_options, inner_tag_options]
347
+ # positional arguments never appear in the attributes hash
348
+ stripped_attributes = HashWithIndifferentAccess.new.update(attributes)
349
+ locals.each {|a| stripped_attributes.delete(a.to_sym) }
350
+
351
+ # Return locals declared as local variables (attrs="...")
352
+ locals.map {|a| attributes[a.to_sym]} + [stripped_attributes]
208
353
  end
209
354
 
210
355
 
211
- def call_replaceable_tag(name, options, external_param, &b)
212
- options.delete(:replace_option)
213
-
214
- if external_param.is_a? Hash
215
- before = external_param.delete(:before_content)
216
- after = external_param.delete(:after_content)
217
- top = external_param.delete(:top_content)
218
- bottom = external_param.delete(:bottom_content)
219
- content = external_param.delete(:content)
220
- options = Hobo::Dryml.merge_tag_options(options, external_param)
221
- elsif !external_param.nil?
222
- return external_param.to_s
223
- end
224
-
225
- tag = if respond_to?(name)
226
- body = if content
227
- proc { content }
228
- elsif b && (top || bottom)
229
- proc { top.to_s + b.call + bottom.to_s }
230
- else
231
- b
232
- end
233
- send(name, options, &body)
356
+ def do_tagbody(tagbody, attributes, default_tagbody)
357
+ res = if tagbody
358
+ tagbody.call(attributes, default_tagbody)
234
359
  else
235
- body = if content
236
- content
237
- elsif b
238
- top.to_s + new_context { b.call } + bottom.to_s
239
- else
240
- top.to_s + bottom.to_s
241
- end
242
- content_tag(name, body, options)
360
+ default_tagbody ? new_context { default_tagbody.call } : ""
243
361
  end
244
- before.to_s + tag.to_s + after.to_s
362
+ Hobo::Dryml.last_if = !!tagbody
363
+ res
245
364
  end
246
365
 
247
366
 
248
- def call_replaceable_content_tag(name, options, external_param, &b)
249
- options.delete(:content_option)
250
-
251
- if external_param.is_a? Hash
252
- content = external_param.delete(:content)
253
- top = external_param.delete(:top_content)
254
- bottom = external_param.delete(:bottom_content)
255
- external_param.delete(:before_content)
256
- external_param.delete(:after_content)
257
- options = Hobo::Dryml.merge_tag_options(options, external_param)
258
- elsif !external_param.nil?
259
- content = external_param.to_s
260
- end
367
+ def call_block_tag_parameter(the_tag, attributes, overriding_proc, &b)
368
+ if overriding_proc && overriding_proc.arity == 1
369
+ # This is a 'replace' parameter
370
+
371
+ template_default = proc do |attrs, body_block|
372
+ tagbody_proc = body_block && proc {|_| new_context { body_block.call(b) } }
373
+ call_block_tag_parameter(the_tag, attributes, proc { attrs.update(:tagbody => tagbody_proc) }, &b)
374
+ end
375
+ overriding_proc.call(template_default)
376
+ else
377
+ if overriding_proc
378
+ overriding_attributes = overriding_proc.call
379
+ tagbody = overriding_attributes.delete(:tagbody)
380
+ attributes = merge_attrs(attributes, overriding_attributes)
381
+ end
261
382
 
262
- # If there's no body, and no content provided externally, remove
263
- # the tag altogether
264
- return if b.nil? and content.nil?
265
-
266
- tag = if respond_to?(name)
267
- body = if content
268
- proc { content }
269
- elsif b && (top || bottom)
270
- proc { top.to_s + b.call + bottom.to_s }
271
- else
272
- b
273
- end
274
- send(name, options, &body)
275
- else
276
- body = if content
277
- content
278
- elsif b
279
- top.to_s + new_context { b.call } + bottom.to_s
280
- else
281
- top.to_s + bottom.to_s
282
- end
283
- content_tag(name, body, options)
383
+ if the_tag.is_a?(String, Symbol) && the_tag.to_s.in?(Hobo.static_tags)
384
+ body = if tagbody
385
+ new_context { tagbody.call(proc {b.call(nil)}) }
386
+ elsif b
387
+ new_context { b.call(nil) }
388
+ else
389
+ nil
390
+ end
391
+ if body.blank?
392
+ tag(the_tag, attributes)
393
+ else
394
+ content_tag(the_tag, body, attributes)
395
+ end
396
+ else
397
+ if the_tag.is_a?(String, Symbol)
398
+ body = proc do |default|
399
+ if tagbody
400
+ tagbody.call(proc { b ? b.call(default) : "" })
401
+ else
402
+ b ? b.call(default) : ""
403
+ end
284
404
  end
285
- tag.to_s
405
+
406
+ send(the_tag, attributes, &body)
407
+ else
408
+ # It's a proc - a template default
409
+ the_tag.call(attributes, tagbody || b)
410
+ end
411
+ end
412
+ end
286
413
  end
287
414
 
288
-
289
- def render_tag(tag_name, options)
290
- (send(tag_name, options) + part_contexts_js).strip
415
+ def call_template_parameter(the_template, attributes, template_procs, overriding_proc)
416
+ if overriding_proc && overriding_proc.arity == 1
417
+ # It's a replace parameter
418
+
419
+ template_default = proc do |attributes, parameters|
420
+ call_template_parameter(the_template, attributes, template_procs, proc { [attributes, parameters] })
421
+ end
422
+ overriding_proc.call(template_default)
423
+ else
424
+ if overriding_proc
425
+ overriding_attributes, overriding_template_procs = overriding_proc.call
426
+
427
+ attributes = merge_attrs(attributes, overriding_attributes)
428
+ template_procs = template_procs.merge(overriding_template_procs)
429
+ end
430
+
431
+ send(the_template, attributes, template_procs)
432
+ end
433
+ end
434
+
435
+ # Takes two procs that each returh hashes and returns a single
436
+ # proc that calls these in turn and merges the results into a
437
+ # single hash
438
+ def merge_option_procs(general_proc, overriding_proc)
439
+ if overriding_proc
440
+ proc { general_proc.call.merge(overriding_proc.call) }
441
+ else
442
+ general_proc
443
+ end
444
+ end
445
+
446
+ # Same as merge_option_procs, except these procs return a pair of
447
+ # hashes rather than a single hash. The first hash is the tag
448
+ # attributes, the second is a hash of procs -- the template
449
+ # parameters.
450
+ def merge_template_parameter_procs(general_proc, overriding_proc)
451
+ proc do
452
+ general_attributes, general_template_procs = general_proc.call
453
+ overriding_attributes, overriding_template_procs = overriding_proc.call
454
+ [merge_attrs(general_attributes, overriding_attributes), general_template_procs.merge(overriding_template_procs)]
455
+ end
291
456
  end
292
457
 
293
458
 
294
- def method_missing(name, *args)
295
- @view.send(name, *args)
459
+ def render_tag(tag_name, attributes)
460
+ (send(tag_name, attributes) + part_contexts_js).strip
461
+ end
462
+
463
+
464
+ def method_missing(name, *args, &b)
465
+ @view.send(name, *args, &b)
296
466
  end
297
467
 
298
468