hobo 0.7.2 → 0.7.3

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 (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