hobo 0.7.2 → 0.7.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. data/bin/hobo +24 -7
  2. data/hobo_files/plugin/CHANGES.txt +501 -0
  3. data/hobo_files/plugin/generators/hobo/hobo_generator.rb +8 -6
  4. data/hobo_files/plugin/generators/hobo/templates/application.dryml +3 -0
  5. data/hobo_files/plugin/generators/hobo/templates/dryml-support.js +132 -0
  6. data/hobo_files/plugin/generators/hobo_front_controller/hobo_front_controller_generator.rb +4 -5
  7. data/hobo_files/plugin/generators/hobo_model_resource/hobo_model_resource_generator.rb +75 -0
  8. data/hobo_files/plugin/generators/hobo_model_resource/templates/controller.rb +7 -0
  9. data/hobo_files/plugin/generators/hobo_model_resource/templates/functional_test.rb +8 -0
  10. data/hobo_files/plugin/generators/hobo_model_resource/templates/helper.rb +2 -0
  11. data/hobo_files/plugin/generators/hobo_rapid/templates/hobo-rapid.js +30 -11
  12. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/clean/public/stylesheets/application.css +149 -92
  13. data/hobo_files/plugin/generators/hobo_rapid/templates/themes/clean/public/stylesheets/rapid-ui.css +0 -48
  14. data/hobo_files/plugin/init.rb +45 -13
  15. data/hobo_files/plugin/lib/action_view_extensions/base.rb +4 -3
  16. data/hobo_files/plugin/lib/active_record/association_proxy.rb +18 -0
  17. data/hobo_files/plugin/lib/active_record/association_reflection.rb +5 -0
  18. data/hobo_files/plugin/lib/active_record/has_many_association.rb +7 -11
  19. data/hobo_files/plugin/lib/active_record/has_many_through_association.rb +8 -0
  20. data/hobo_files/plugin/lib/extensions/test_case.rb +1 -1
  21. data/hobo_files/plugin/lib/hobo.rb +38 -60
  22. data/hobo_files/plugin/lib/hobo/authentication_support.rb +1 -1
  23. data/hobo_files/plugin/lib/hobo/bundle.rb +131 -34
  24. data/hobo_files/plugin/lib/hobo/composite_model.rb +1 -1
  25. data/hobo_files/plugin/lib/hobo/controller.rb +7 -8
  26. data/hobo_files/plugin/lib/hobo/dev_controller.rb +21 -0
  27. data/hobo_files/plugin/lib/hobo/dryml/dryml_builder.rb +14 -8
  28. data/hobo_files/plugin/lib/hobo/dryml/dryml_support_controller.rb +13 -0
  29. data/hobo_files/plugin/lib/hobo/dryml/taglib.rb +6 -7
  30. data/hobo_files/plugin/lib/hobo/dryml/template.rb +207 -73
  31. data/hobo_files/plugin/lib/hobo/dryml/template_environment.rb +67 -55
  32. data/hobo_files/plugin/lib/hobo/dryml/template_handler.rb +53 -3
  33. data/hobo_files/plugin/lib/hobo/hobo_helper.rb +75 -107
  34. data/hobo_files/plugin/lib/hobo/model.rb +236 -429
  35. data/hobo_files/plugin/lib/hobo/model_controller.rb +277 -437
  36. data/hobo_files/plugin/lib/hobo/model_router.rb +62 -29
  37. data/hobo_files/plugin/lib/hobo/rapid_helper.rb +48 -9
  38. data/hobo_files/plugin/lib/hobo/scopes.rb +98 -0
  39. data/hobo_files/plugin/lib/hobo/scopes/association_proxy_extensions.rb +31 -0
  40. data/hobo_files/plugin/lib/hobo/scopes/automatic_scopes.rb +282 -0
  41. data/hobo_files/plugin/lib/hobo/scopes/defined_scope_proxy_extender.rb +88 -0
  42. data/hobo_files/plugin/lib/hobo/scopes/scope_reflection.rb +18 -0
  43. data/hobo_files/plugin/lib/hobo/scopes/scoped_proxy.rb +59 -0
  44. data/hobo_files/plugin/lib/hobo/undefined.rb +2 -0
  45. data/hobo_files/plugin/lib/hobo/user.rb +31 -14
  46. data/hobo_files/plugin/lib/hobo/user_controller.rb +41 -27
  47. data/hobo_files/plugin/taglibs/core.dryml +9 -11
  48. data/hobo_files/plugin/taglibs/rapid.dryml +51 -108
  49. data/hobo_files/plugin/taglibs/rapid_editing.dryml +25 -25
  50. data/hobo_files/plugin/taglibs/rapid_forms.dryml +111 -79
  51. data/hobo_files/plugin/taglibs/rapid_generics.dryml +74 -0
  52. data/hobo_files/plugin/taglibs/rapid_navigation.dryml +23 -21
  53. data/hobo_files/plugin/taglibs/rapid_pages.dryml +83 -169
  54. data/hobo_files/plugin/taglibs/rapid_plus.dryml +16 -2
  55. data/hobo_files/plugin/taglibs/rapid_support.dryml +3 -3
  56. data/hobo_files/plugin/taglibs/rapid_user_pages.dryml +104 -0
  57. metadata +60 -55
  58. data/hobo_files/plugin/generators/hobo_migration/hobo_migration_generator.rb +0 -276
  59. data/hobo_files/plugin/generators/hobo_migration/templates/migration.rb +0 -9
  60. data/hobo_files/plugin/lib/active_record/table_definition.rb +0 -34
  61. data/hobo_files/plugin/lib/extensions.rb +0 -375
  62. data/hobo_files/plugin/lib/hobo/email_address.rb +0 -12
  63. data/hobo_files/plugin/lib/hobo/enum_string.rb +0 -50
  64. data/hobo_files/plugin/lib/hobo/field_declaration_dsl.rb +0 -43
  65. data/hobo_files/plugin/lib/hobo/field_spec.rb +0 -68
  66. data/hobo_files/plugin/lib/hobo/html_string.rb +0 -7
  67. data/hobo_files/plugin/lib/hobo/lazy_hash.rb +0 -40
  68. data/hobo_files/plugin/lib/hobo/markdown_string.rb +0 -11
  69. data/hobo_files/plugin/lib/hobo/migrations.rb +0 -12
  70. data/hobo_files/plugin/lib/hobo/model_queries.rb +0 -117
  71. data/hobo_files/plugin/lib/hobo/password_string.rb +0 -7
  72. data/hobo_files/plugin/lib/hobo/percentage.rb +0 -14
  73. data/hobo_files/plugin/lib/hobo/predicate_dispatch.rb +0 -78
  74. data/hobo_files/plugin/lib/hobo/proc_binding.rb +0 -32
  75. data/hobo_files/plugin/lib/hobo/text.rb +0 -3
  76. data/hobo_files/plugin/lib/hobo/textile_string.rb +0 -25
  77. data/hobo_files/plugin/lib/hobo/where_fragment.rb +0 -28
@@ -62,7 +62,7 @@ module Hobo
62
62
 
63
63
  def id
64
64
  objects = self.class.models.map {|m| instance_variable_get("@#{m.underscore}")}
65
- objects.every(:id).join("_")
65
+ objects.*.id.join("_")
66
66
  end
67
67
 
68
68
  alias_method :to_param, :id
@@ -87,12 +87,10 @@ module Hobo
87
87
  page << renderer.part_contexts_storage if renderer
88
88
  end
89
89
  end
90
-
91
-
92
- def render_tag(tag, options={}, render_options={})
93
- add_variables_to_assigns
94
- text = Hobo::Dryml.render_tag(@template, tag, options)
95
- text && render({:text => text, :layout => false }.merge(render_options))
90
+
91
+
92
+ def dryml_context
93
+ @this
96
94
  end
97
95
 
98
96
 
@@ -111,11 +109,12 @@ module Hobo
111
109
 
112
110
 
113
111
  def site_search(query)
114
- results = Hobo.find_by_search(query).select {|r| Hobo.can_view?(current_user, r, nil)}
112
+ results = Hobo.find_by_search(query).select{|r| Hobo.can_view?(current_user, r, nil)}
115
113
  if results.empty?
116
114
  render :text => "<p>Your search returned no matches.</p>"
117
115
  else
118
- render_tags(results, :card, :for_type => true)
116
+ # TODO: call one tag that renders all the search results with headings for each model
117
+ render_tags(results.map {|r|r.last}.flatten, :search_card, :for_type => true)
119
118
  end
120
119
  end
121
120
 
@@ -0,0 +1,21 @@
1
+ class Hobo::DevController < ActionController::Base
2
+
3
+ hobo_controller
4
+
5
+ before_filter :developer_modes_only
6
+
7
+ def set_current_user
8
+ model = params[:model] || Hobo::User.default_user_model
9
+ self.current_user = params[:name] ? model[params[:name]] : model.find(params[:id])
10
+ redirect_to(request.env["HTTP_REFERER"] ? :back : home_page)
11
+ end
12
+
13
+ private
14
+
15
+ def developer_modes_only
16
+ # Belt and braces. In addition to this check, the routes only get
17
+ # defined when developer_features? is true
18
+ render :text => "Permission Denied", :status => 403 unless Hobo.developer_features?
19
+ end
20
+
21
+ end
@@ -2,13 +2,17 @@ module Hobo::Dryml
2
2
 
3
3
  class DRYMLBuilder
4
4
 
5
- def initialize(template_path)
6
- @template_path = template_path
5
+ def initialize(template)
6
+ @template = template
7
7
  @build_instructions = Array.new
8
8
  @part_names = []
9
9
  end
10
10
 
11
- attr_reader :template_path
11
+ attr_reader :template
12
+
13
+ def template_path
14
+ template.template_path
15
+ end
12
16
 
13
17
 
14
18
  def set_environment(environment)
@@ -34,7 +38,7 @@ module Hobo::Dryml
34
38
 
35
39
  def add_part(name, src, line_num)
36
40
  raise DrymlException.new("duplicate part: #{name}", template_path, line_num) if name.in?(@part_names)
37
- add_build_instruction(:part, :src => src, :line_num => line_num)
41
+ add_build_instruction(:def, :src => src, :line_num => line_num)
38
42
  @part_names << name
39
43
  end
40
44
 
@@ -73,9 +77,6 @@ module Hobo::Dryml
73
77
  src = erb_process(instruction[:src])
74
78
  @environment.class_eval(src, template_path, instruction[:line_num])
75
79
 
76
- when :part
77
- @environment.class_eval(erb_process(instruction[:src]), template_path, instruction[:line_num])
78
-
79
80
  when :render_page
80
81
  method_src = render_page_source(erb_process(instruction[:src]), local_names)
81
82
  @environment.compiled_local_names = local_names
@@ -107,7 +108,12 @@ module Hobo::Dryml
107
108
  import_module(options[:module].constantize, options[:as])
108
109
  else
109
110
  template_dir = File.dirname(template_path)
110
- taglib = Taglib.get(options.merge(:template_dir => template_dir))
111
+ options = options.merge(:template_dir => template_dir)
112
+
113
+ # Pass on the current bundle, if there is one, to the sub-taglib
114
+ options[:bundle] = template.bundle.name unless template.bundle.nil? || options[:bundle] || options[:plugin]
115
+
116
+ taglib = Taglib.get(options)
111
117
  taglib.import_into(@environment, options[:as])
112
118
  end
113
119
  end
@@ -0,0 +1,13 @@
1
+ class Hobo::Dryml::DrymlSupportController < ActionController::Base
2
+
3
+ def edit_source
4
+ dryml_editor = ENV['DRYML_EDITOR']
5
+ if dryml_editor
6
+ file = File.join(RAILS_ROOT, params[:file])
7
+ command = dryml_editor.sub(":file", file).sub(":line", params[:line])
8
+ system(command)
9
+ end
10
+ render :nothing => true
11
+ end
12
+
13
+ end
@@ -14,10 +14,9 @@ module Hobo
14
14
  taglib.reload
15
15
  else
16
16
  src_file = taglib_filename(options)
17
- renames = (bundle = options[:bundle] and
18
- Bundle.bundles[bundle]._?.renames)
17
+ bundle = options[:bundle] && Bundle.bundles[options[:bundle]]
19
18
 
20
- taglib = Taglib.new(src_file, renames)
19
+ taglib = Taglib.new(src_file, bundle)
21
20
  @cache[options] = taglib
22
21
  end
23
22
  taglib
@@ -38,7 +37,7 @@ module Hobo
38
37
  elsif options[:src] =~ /\//
39
38
  "app/views"
40
39
  else
41
- options[:template_dir]
40
+ options[:template_dir].gsub(/^\//, "") # remove leading / if there is one
42
41
  end
43
42
 
44
43
  filename = "#{RAILS_ROOT}/#{base}/#{options[:src]}.dryml"
@@ -48,9 +47,9 @@ module Hobo
48
47
 
49
48
  end
50
49
 
51
- def initialize(src_file, renames)
50
+ def initialize(src_file, bundle)
52
51
  @src_file = src_file
53
- @renames = renames
52
+ @bundle = bundle
54
53
  load
55
54
  end
56
55
 
@@ -84,7 +83,7 @@ module Hobo
84
83
  end
85
84
 
86
85
  end
87
- template = Template.new(File.read(@src_file), @module, @src_file, @renames)
86
+ template = Template.new(File.read(@src_file), @module, @src_file, @bundle)
88
87
  template.compile([], [])
89
88
  @last_load_time = File.mtime(@src_file)
90
89
  end
@@ -12,6 +12,8 @@ module Hobo::Dryml
12
12
 
13
13
  CODE_ATTRIBUTE_CHAR = "&"
14
14
 
15
+ NO_METADATA_TAGS = %w(doctype if else unless repeat do with name type-name)
16
+
15
17
  SPECIAL_ATTRIBUTES = %w(param merge merge-params merge-attrs
16
18
  for-type
17
19
  if unless repeat
@@ -28,19 +30,19 @@ module Hobo::Dryml
28
30
  end
29
31
  end
30
32
 
31
- def initialize(src, environment, template_path, renames={})
33
+ def initialize(src, environment, template_path, bundle=nil)
32
34
  @src = src
33
35
  @environment = environment # a class or a module
34
36
  @template_path = template_path.sub(/^#{Regexp.escape(RAILS_ROOT)}/, "")
35
- @class_renames = renames
37
+ @bundle = bundle
36
38
 
37
- @builder = Template.build_cache[@template_path] || DRYMLBuilder.new(@template_path)
39
+ @builder = Template.build_cache[@template_path] || DRYMLBuilder.new(self)
38
40
  @builder.set_environment(environment)
39
41
 
40
42
  @last_element = nil
41
43
  end
42
44
 
43
- attr_reader :tags, :template_path, :class_renames
45
+ attr_reader :tags, :template_path, :bundle
44
46
 
45
47
  def compile(local_names=[], auto_taglibs=[])
46
48
  now = Time.now
@@ -74,6 +76,7 @@ module Hobo::Dryml
74
76
 
75
77
  def create_render_page_method
76
78
  erb_src = process_src
79
+
77
80
  @builder.add_build_instruction(:render_page, :src => erb_src, :line_num => 1)
78
81
  end
79
82
 
@@ -127,7 +130,7 @@ module Hobo::Dryml
127
130
  REXML::Comment::START + node.to_s + REXML::Comment::STOP
128
131
 
129
132
  when REXML::Text
130
- node.to_s
133
+ strip_suppressed_whiteaspace(node.to_s)
131
134
 
132
135
  when REXML::Element
133
136
  element_to_erb(node)
@@ -135,6 +138,11 @@ module Hobo::Dryml
135
138
  end
136
139
 
137
140
 
141
+ def strip_suppressed_whiteaspace(s)
142
+ s # s.gsub(/ -(\s*\n\s*)/, '<% \1 %>')
143
+ end
144
+
145
+
138
146
  def element_to_erb(el)
139
147
  dryml_exception("old-style parameter tag (<#{el.name}>)", el) if el.name.starts_with?(":")
140
148
 
@@ -194,7 +202,7 @@ module Hobo::Dryml
194
202
 
195
203
  def set_element(el)
196
204
  assigns = el.attributes.map do |name, value|
197
- dryml_exception(el, "invalid name in set") unless name =~ /^#{DRYML_NAME}(\.#{DRYML_NAME})*$/
205
+ dryml_exception("invalid name in <set>", el) unless name =~ /^#{DRYML_NAME}(\.#{DRYML_NAME})*$/
198
206
  "#{ruby_name name} = #{attribute_to_ruby(value)}; "
199
207
  end.join
200
208
  code = apply_control_attributes("begin; #{assigns}; end", el)
@@ -204,7 +212,7 @@ module Hobo::Dryml
204
212
 
205
213
  def set_scoped_element(el)
206
214
  assigns = el.attributes.map do |name, value|
207
- dryml_exception(el, "invalid name in set-scoped") unless name =~ DRYML_NAME_RX
215
+ dryml_exception("invalid name in <set-scoped>", el) unless name =~ DRYML_NAME_RX
208
216
  "scope[:#{ruby_name name}] = #{attribute_to_ruby(value)}; "
209
217
  end.join
210
218
  "<% scope.new_scope { #{assigns}#{tag_newlines(el)} %>#{children_to_erb(el)}<% } %>"
@@ -214,7 +222,7 @@ module Hobo::Dryml
214
222
  def declared_attributes(def_element)
215
223
  attrspec = def_element.attributes["attrs"]
216
224
  attr_names = attrspec ? attrspec.split(/\s*,\s*/).map{ |n| n.underscore.to_sym } : []
217
- invalids = attr_names & ([:with, :field, :this] + SPECIAL_ATTRIBUTES.every(:to_sym))
225
+ invalids = attr_names & ([:with, :field, :this] + SPECIAL_ATTRIBUTES.*.to_sym)
218
226
  dryml_exception("invalid attrs in def: #{invalids * ', '}", def_element) unless invalids.empty?
219
227
  attr_names
220
228
  end
@@ -243,11 +251,10 @@ module Hobo::Dryml
243
251
  unsafe_name = el.attributes["tag"]
244
252
  name = Hobo::Dryml.unreserve(unsafe_name)
245
253
  if (for_type = el.attributes['for'])
246
- type_name = case for_type
247
- when /^[a-z]/
254
+ type_name = if defined?(HoboFields) && for_type =~ /^[a-z]/
248
255
  # It's a symbolic type name - look up the Ruby type name
249
- Hobo.field_types[for_type].name
250
- when /^_.*_$/
256
+ HoboFields.to_class(for_type).name
257
+ elsif for_type =~ /^_.*_$/
251
258
  rename_class(for_type)
252
259
  else
253
260
  for_type
@@ -257,13 +264,8 @@ module Hobo::Dryml
257
264
  unsafe_name += suffix
258
265
  end
259
266
 
260
- # While processing this def, @def_name contains
261
- # the names of all nested defs join with '_'. It's used to
262
- # disambiguate local variables as a workaround for the broken
263
- # scope semantics of Ruby 1.8.
264
- old_def_name = @def_name
265
- @def_name = @def_name ? "#{@def_name}_#{unsafe_name}" : unsafe_name
266
-
267
+ @def_element = el
268
+
267
269
  alias_of = el.attributes['alias-of']
268
270
  extend_with = el.attributes['extend-with']
269
271
 
@@ -272,7 +274,8 @@ module Hobo::Dryml
272
274
 
273
275
 
274
276
  @builder.add_build_instruction(:alias_method,
275
- :new => ruby_name(name).to_sym, :old => ruby_name(alias_of).to_sym) if alias_of
277
+ :new => ruby_name(name).to_sym,
278
+ :old => ruby_name(Hobo::Dryml.unreserve(alias_of)).to_sym) if alias_of
276
279
 
277
280
  res = if alias_of
278
281
  "<% #{tag_newlines(el)} %>"
@@ -293,7 +296,7 @@ module Hobo::Dryml
293
296
  # keep line numbers matching up
294
297
  "<% #{"\n" * src.count("\n")} %>"
295
298
  end
296
- @def_name = old_def_name
299
+ @def_element = nil
297
300
  res
298
301
  end
299
302
 
@@ -332,13 +335,52 @@ module Hobo::Dryml
332
335
  "#{start} " +
333
336
  # reproduce any line breaks in the start-tag so that line numbers are preserved
334
337
  tag_newlines(el) + "%>" +
335
- children_to_erb(el) +
338
+ wrap_tag_method_body_with_metadata(children_to_erb(el)) +
336
339
  "<% _erbout; end"
337
340
  end
338
341
 
342
+
343
+ def wrap_source_with_metadata(content, kind, name, *args)
344
+ if (!include_source_metadata) || name.in?(NO_METADATA_TAGS)
345
+ content
346
+ else
347
+ metadata = [kind, name] + args + [@template_path]
348
+ "<!--[DRYML|#{metadata * '|'}[-->" + content + "<!--]DRYML]-->"
349
+ end
350
+ end
351
+
352
+
353
+ def wrap_tag_method_body_with_metadata(content)
354
+ name = @def_element.attributes['tag']
355
+ extend = @def_element.attributes['extend-with']
356
+ for_ = @def_element.attributes['for']
357
+ name = extend ? "#{name}-with-#{extend}" : name
358
+ name += " for #{for_}" if for_
359
+ wrap_source_with_metadata(content, "def", name, element_line_num(@def_element))
360
+ end
361
+
362
+
363
+ def wrap_tag_call_with_metadata(el, content)
364
+ name = el.expanded_name
365
+ param = el.attributes['param']
366
+
367
+ if param == "&true"
368
+ name += " param"
369
+ elsif param
370
+ name += " param='#{param}'"
371
+ end
372
+
373
+ wrap_source_with_metadata(content, "call", name, element_line_num(el))
374
+ end
375
+
339
376
 
340
- def param_content_element(el)
341
- name = el.attributes['for'] || @containing_tag_name
377
+ def param_content_element(name_or_el)
378
+ name = if name_or_el.is_a?(String)
379
+ name_or_el
380
+ else
381
+ el = name_or_el
382
+ el.attributes['for'] || @containing_tag_name
383
+ end
342
384
  local_name = param_content_local_name(name)
343
385
  "<%= #{local_name} && #{local_name}.call %>"
344
386
  end
@@ -346,10 +388,19 @@ module Hobo::Dryml
346
388
 
347
389
  def part_element(el, content)
348
390
  require_attribute(el, "part", DRYML_NAME_RX)
391
+
392
+ if contains_param?(el)
393
+ delegated_part_element(el, content)
394
+ else
395
+ simple_part_element(el, content)
396
+ end
397
+ end
398
+
399
+
400
+ def simple_part_element(el, content)
349
401
  part_name = el.attributes['part']
350
402
  dom_id = el.attributes['id'] || part_name
351
403
  part_name = ruby_name(part_name)
352
-
353
404
  part_locals = el.attributes["part-locals"]
354
405
 
355
406
  part_src = "<% def #{part_name}_part(#{part_locals._?.gsub('@', '')}) #{tag_newlines(el)}; new_context do %>" +
@@ -363,6 +414,22 @@ module Hobo::Dryml
363
414
  end
364
415
 
365
416
 
417
+ def delegated_part_element(el, content)
418
+ # TODO
419
+ end
420
+
421
+
422
+ def contains_param?(el)
423
+ # TODO
424
+ false
425
+ end
426
+
427
+
428
+ def part_delegate_tag_name(el)
429
+ "#{@def_name}_#{el.attributes['part']}__part_delegate"
430
+ end
431
+
432
+
366
433
  def get_param_name(el)
367
434
  param_name = el.attributes["param"]
368
435
 
@@ -391,8 +458,8 @@ module Hobo::Dryml
391
458
  'this_type'
392
459
  elsif t =~ /^[A-Z]/
393
460
  t
394
- elsif t =~ /^[a-z]/
395
- "Hobo.field_types[:#{t}]"
461
+ elsif t =~ /^[a-z]/ && defined? HoboFields.to_class
462
+ "Hobo.to_class(:#{t})"
396
463
  elsif is_code_attribute?(t)
397
464
  t[1..-1]
398
465
  else
@@ -431,14 +498,21 @@ module Hobo::Dryml
431
498
  elsif (call_type = polymorphic_call_type(el))
432
499
  "send(find_polymorphic_tag(:#{ruby_name name}, #{call_type}), #{attributes}, #{parameters})"
433
500
  elsif attributes == "{}" && parameters == "{}"
434
- "#{ruby_name name}.to_s"
501
+ if name =~ /^[A-Z]/
502
+ # it's a tag with a cap name - not a local
503
+ "#{ruby_name name}()"
504
+ else
505
+ # could be a tag or a local variable
506
+ "#{ruby_name name}.to_s"
507
+ end
435
508
  else
436
509
  "#{ruby_name name}(#{attributes}, #{parameters})"
437
510
  end
438
511
  end
439
512
 
440
513
  call = apply_control_attributes(call, el)
441
- maybe_make_part_call(el, "<% _output(#{call}) %>")
514
+ call = maybe_make_part_call(el, "<% _output(#{call}) %>")
515
+ wrap_tag_call_with_metadata(el, call)
442
516
  end
443
517
 
444
518
 
@@ -449,29 +523,20 @@ module Hobo::Dryml
449
523
  end
450
524
 
451
525
 
452
- def parameter_tags_hash(el)
526
+ def parameter_tags_hash(el, containing_tag_name=nil)
453
527
  call_type = nil
454
528
 
529
+ metadata_name = containing_tag_name || el.expanded_name
530
+
455
531
  param_items = el.map do |node|
456
532
  case node
457
533
  when REXML::Text
458
534
  text = node.to_s
459
- if text.blank?
460
- # include whitespace in hash literal to keep line numbers
461
- # matching
462
- text
463
- else
464
- case call_type
465
- when nil
466
- call_type = :default_param_only
467
- text
468
- when :default_param_only
469
- text
470
- when :named_params
471
- dryml_exception("mixed content and parameter tags", el)
472
- end
535
+ unless text.blank?
536
+ dryml_exception("mixed content and parameter tags", el) if call_type == :named_params
537
+ call_type = :default_param_only
473
538
  end
474
- node.to_s
539
+ text
475
540
  when REXML::Element
476
541
  e = node
477
542
  is_parameter_tag = e.parameter_tag?
@@ -487,19 +552,14 @@ module Hobo::Dryml
487
552
  end
488
553
 
489
554
  if is_parameter_tag
490
- param_name = get_param_name(e)
491
- if param_name
492
- ":#{ruby_name e.name} => merge_tag_parameter(#{param_proc(e)}, all_parameters[:#{param_name}]), "
493
- else
494
- ":#{ruby_name e.name} => #{param_proc(e)}, "
495
- end
555
+ parameter_tag_hash_item(e, metadata_name) + ", "
496
556
  end
497
557
  end
498
558
  end.join
499
559
 
500
- if call_type == :default_param_only
560
+ if call_type == :default_param_only || (el.children.empty? && el.has_end_tag?)
501
561
  with_containing_tag_name(el) do
502
- param_items = " :default => #{default_param_proc(el)}, "
562
+ param_items = " :default => #{default_param_proc(el, containing_tag_name)}, "
503
563
  end
504
564
  end
505
565
 
@@ -510,17 +570,72 @@ module Hobo::Dryml
510
570
  elsif is_code_attribute?(merge_params)
511
571
  merge_params[1..-1]
512
572
  else
513
- dryml_exception("invalid merge_params", el)
573
+ merge_param_names = merge_params.split(/\s*,\s*/).*.gsub("-", "_")
574
+ "all_parameters & #{merge_param_names.inspect}"
514
575
  end
515
576
  "{#{param_items}}.merge((#{extra_params}) || {})"
516
577
  else
517
578
  "{#{param_items}}"
518
579
  end
519
580
  end
581
+
582
+
583
+ def parameter_tag_hash_item(el, metadata_name)
584
+ if el.name =~ /^before-/
585
+ before_parameter_tag_hash_item(el, metadata_name)
586
+ elsif el.name =~ /^after-/
587
+ after_parameter_tag_hash_item(el, metadata_name)
588
+ elsif el.name =~ /^prepend-/
589
+ prepend_parameter_tag_hash_item(el, metadata_name)
590
+ elsif el.name =~ /^append-/
591
+ append_parameter_tag_hash_item(el, metadata_name)
592
+ elsif (param_name = get_param_name(el))
593
+ ":#{ruby_name el.name} => merge_tag_parameter(#{param_proc(el, metadata_name)}, all_parameters[:#{param_name}])"
594
+ else
595
+ ":#{ruby_name el.name} => #{param_proc(el, metadata_name)}"
596
+ end
597
+ end
598
+
599
+
600
+ def before_parameter_tag_hash_item(el, metadata_name)
601
+ param_name = get_param_name(el)
602
+ dryml_exception("param declaration not allowed on 'before' parameters", el) if param_name
603
+ name = el.name.sub(/^before-/, "")
604
+ content = children_to_erb(el) + "<% _output(#{param_restore_local_name(name)}.call({}, {})) %>"
605
+ ":#{ruby_name name} => #{replace_parameter_proc(el, metadata_name, content)}"
606
+ end
520
607
 
521
608
 
522
- def default_param_proc(el)
523
- "proc { |#{param_content_local_name(el.dryml_name)}| new_context { %>#{children_to_erb(el)}<% } #{tag_newlines(el)}}"
609
+ def after_parameter_tag_hash_item(el, metadata_name)
610
+ param_name = get_param_name(el)
611
+ dryml_exception("param declaration not allowed on 'after' parameters", el) if param_name
612
+ name = el.name.sub(/^after-/, "")
613
+ content = "<% _output(#{param_restore_local_name(name)}.call({}, {})) %>" + children_to_erb(el)
614
+ ":#{ruby_name name} => #{replace_parameter_proc(el, metadata_name, content)}"
615
+ end
616
+
617
+
618
+ def append_parameter_tag_hash_item(el, metadata_name)
619
+ name = el.name.sub(/^append-/, "")
620
+ ":#{ruby_name name} => proc { [{}, { :default => proc { |#{param_content_local_name(name)}| new_context { %>" +
621
+ param_content_element(name) + children_to_erb(el) +
622
+ "<% } } } ] }"
623
+ end
624
+
625
+
626
+ def prepend_parameter_tag_hash_item(el, metadata_name)
627
+ name = el.name.sub(/^prepend-/, "")
628
+ ":#{ruby_name name} => proc { [{}, { :default => proc { |#{param_content_local_name(name)}| new_context { %>" +
629
+ children_to_erb(el) + param_content_element(name) +
630
+ "<% } } } ] }"
631
+ end
632
+
633
+
634
+ def default_param_proc(el, containing_param_name=nil)
635
+ content = children_to_erb(el)
636
+ content = wrap_source_with_metadata(content, "param", containing_param_name,
637
+ element_line_num(el)) if containing_param_name
638
+ "proc { |#{param_content_local_name(el.dryml_name)}| new_context { %>#{content}<% } #{tag_newlines(el)}}"
524
639
  end
525
640
 
526
641
 
@@ -529,26 +644,39 @@ module Hobo::Dryml
529
644
  end
530
645
 
531
646
 
532
- def param_proc(el)
533
- param_name = el.dryml_name
647
+ def wrap_replace_parameter(el, name)
648
+ wrap_source_with_metadata(children_to_erb(el), "replace", name, element_line_num(el))
649
+ end
650
+
651
+
652
+ def param_proc(el, metadata_name_prefix)
653
+ metadata_name = "#{metadata_name_prefix}><#{el.name}"
654
+
534
655
  nl = tag_newlines(el)
535
656
 
536
657
  if (repl = el.attribute("replace"))
537
658
  dryml_exception("replace attribute must not have a value", el) if repl.has_rhs?
538
659
  dryml_exception("replace parameters must not have attributes", el) if el.attributes.length > 1
539
660
 
540
- "proc { |#{param_restore_local_name(param_name)}| new_context { %>#{children_to_erb(el)}<% } #{nl}}"
661
+ replace_parameter_proc(el, metadata_name)
541
662
  else
542
663
  attributes = el.attributes.map do
543
664
  |name, value| ":#{ruby_name name} => #{attribute_to_ruby(value, el)}" unless name.in?(SPECIAL_ATTRIBUTES)
544
665
  end.compact
545
666
 
546
- nested_parameters_hash = parameter_tags_hash(el)
667
+ nested_parameters_hash = parameter_tags_hash(el, metadata_name)
547
668
  "proc { [{#{attributes * ', '}}, #{nested_parameters_hash}] #{nl}}"
548
669
  end
549
670
  end
550
671
 
551
672
 
673
+ def replace_parameter_proc(el, metadata_name, content=nil)
674
+ content ||= wrap_replace_parameter(el, metadata_name)
675
+ param_name = el.dryml_name.sub(/^(before|after|append|prepend)-/, "")
676
+ "proc { |#{param_restore_local_name(param_name)}| new_context { %>#{content}<% } #{tag_newlines(el)}}"
677
+ end
678
+
679
+
552
680
  def param_content_local_name(name)
553
681
  "_#{ruby_name name}__default_content"
554
682
  end
@@ -576,7 +704,7 @@ module Hobo::Dryml
576
704
  end.compact
577
705
 
578
706
  # if there's a ':' el.name is just the part after the ':'
579
- items << ":field => \"#{el.name}\"" if el.name != el.dryml_name
707
+ items << ":field => \"#{ruby_name el.name}\"" if el.expanded_name =~ /:./
580
708
 
581
709
  items = items.join(", ")
582
710
 
@@ -657,17 +785,17 @@ module Hobo::Dryml
657
785
 
658
786
 
659
787
  def apply_control_attributes(expression, el)
660
- if_, unless_, repeat = controls = %w(if unless repeat).map {|x| el.attributes[x]}
661
- controls.compact!
788
+ controls = %w(if unless repeat).map_hash { |x| el.attributes[x] }.compact
662
789
 
663
790
  dryml_exception("You can't have multiple control attributes on the same element", el) if
664
791
  controls.length > 1
665
792
 
666
- val = controls.first
793
+ attr = controls.keys.first
794
+ val = controls.values.first
667
795
  if val.nil?
668
796
  expression
669
797
  else
670
- control = if repeat && val == "&true"
798
+ control = if !el.attribute(attr).has_rhs?
671
799
  "this"
672
800
  elsif is_code_attribute?(val)
673
801
  "#{val[1..-1]}"
@@ -676,13 +804,14 @@ module Hobo::Dryml
676
804
  end
677
805
 
678
806
  x = gensym
679
- if if_
807
+ case attr
808
+ when "if"
680
809
  "(if !(#{control}).blank?; (#{x} = #{expression}; Hobo::Dryml.last_if = true; #{x}) " +
681
810
  "else (Hobo::Dryml.last_if = false; ''); end)"
682
- elsif unless_
811
+ when "unless"
683
812
  "(if (#{control}).blank?; (#{x} = #{expression}; Hobo::Dryml.last_if = true; #{x}) " +
684
813
  "else (Hobo::Dryml.last_if = false; ''); end)"
685
- elsif repeat
814
+ when "repeat"
686
815
  "repeat_attribute(#{control}) { #{expression} }"
687
816
  end
688
817
  end
@@ -690,7 +819,7 @@ module Hobo::Dryml
690
819
 
691
820
 
692
821
  def attribute_to_ruby(*args)
693
- options = extract_options_from_args!(args)
822
+ options = args.extract_options!
694
823
  attr, el = args
695
824
 
696
825
  dryml_exception('erb scriptlet not allowed in this attribute (use #{ ... } instead)', el) if
@@ -747,7 +876,7 @@ module Hobo::Dryml
747
876
 
748
877
  def element_line_num(el)
749
878
  offset = el.source_offset
750
- line_no = @xmlsrc[0..offset].count("\n") + 1
879
+ @xmlsrc[0..offset].count("\n") + 1
751
880
  end
752
881
 
753
882
  def tag_newlines(el)
@@ -770,9 +899,14 @@ module Hobo::Dryml
770
899
  end
771
900
 
772
901
  def rename_class(name)
773
- name = name[1..-2]
774
- name = class_renames[name] while class_renames.has_key?(name)
775
- name
902
+ @bundle && name.starts_with?("_") ? @bundle.send(name) : name
903
+ end
904
+
905
+ def include_source_metadata
906
+ # disabled for now -- we're still getting broken rendering with this feature on
907
+ return false
908
+ @include_source_metadata = RAILS_ENV == "development" && !ENV['DRYML_EDITOR'].blank? if @include_source_metadata.nil?
909
+ @include_source_metadata
776
910
  end
777
911
 
778
912
  end