hobo 0.8.5 → 0.8.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 (68) hide show
  1. data/CHANGES.txt +41 -0
  2. data/Manifest +1 -5
  3. data/Rakefile +10 -3
  4. data/bin/hobo +38 -15
  5. data/dryml_generators/rapid/cards.dryml.erb +7 -7
  6. data/dryml_generators/rapid/pages.dryml.erb +52 -24
  7. data/hobo.gemspec +42 -322
  8. data/init.rb +0 -7
  9. data/lib/active_record/association_collection.rb +9 -0
  10. data/lib/hobo.rb +13 -14
  11. data/lib/hobo/accessible_associations.rb +32 -7
  12. data/lib/hobo/authentication_support.rb +1 -1
  13. data/lib/hobo/controller.rb +5 -7
  14. data/lib/hobo/dryml.rb +9 -2
  15. data/lib/hobo/dryml/dryml_builder.rb +11 -12
  16. data/lib/hobo/dryml/dryml_doc.rb +22 -24
  17. data/lib/hobo/dryml/dryml_generator.rb +41 -4
  18. data/lib/hobo/dryml/part_context.rb +5 -3
  19. data/lib/hobo/dryml/template.rb +7 -7
  20. data/lib/hobo/dryml/template_environment.rb +11 -22
  21. data/lib/hobo/dryml/template_handler.rb +94 -25
  22. data/lib/hobo/find_for.rb +2 -2
  23. data/lib/hobo/hobo_helper.rb +21 -21
  24. data/lib/hobo/include_in_save.rb +9 -5
  25. data/lib/hobo/lifecycles/transition.rb +2 -2
  26. data/lib/hobo/model.rb +11 -61
  27. data/lib/hobo/model_controller.rb +28 -29
  28. data/lib/hobo/model_router.rb +12 -13
  29. data/lib/hobo/permissions.rb +47 -37
  30. data/lib/hobo/permissions/associations.rb +1 -1
  31. data/lib/hobo/scopes/association_proxy_extensions.rb +5 -6
  32. data/lib/hobo/scopes/automatic_scopes.rb +7 -4
  33. data/lib/hobo/tasks/rails.rb +4 -0
  34. data/lib/hobo/user.rb +0 -1
  35. data/lib/hobo/user_controller.rb +3 -1
  36. data/lib/hobo/view_hints.rb +17 -3
  37. data/rails_generators/hobo/hobo_generator.rb +1 -0
  38. data/rails_generators/hobo_front_controller/templates/functional_test.rb +1 -11
  39. data/rails_generators/hobo_front_controller/templates/index.dryml +1 -6
  40. data/rails_generators/hobo_rapid/hobo_rapid_generator.rb +1 -0
  41. data/rails_generators/hobo_rapid/templates/hobo-rapid.css +3 -2
  42. data/rails_generators/hobo_rapid/templates/hobo-rapid.js +24 -15
  43. data/rails_generators/hobo_rapid/templates/themes/clean/public/stylesheets/clean.css +17 -12
  44. data/rails_generators/hobo_rapid/templates/themes/clean/public/stylesheets/rapid-ui.css +6 -2
  45. data/rails_generators/hobo_rapid/templates/themes/clean/views/clean.dryml +2 -2
  46. data/rails_generators/hobo_user_model/templates/forgot_password.erb +2 -2
  47. data/rails_generators/hobo_user_model/templates/model.rb +2 -2
  48. data/taglibs/rapid.dryml +3 -2
  49. data/taglibs/rapid_core.dryml +21 -16
  50. data/taglibs/rapid_document_tags.dryml +1 -1
  51. data/taglibs/rapid_editing.dryml +7 -10
  52. data/taglibs/rapid_forms.dryml +115 -26
  53. data/taglibs/rapid_generics.dryml +13 -3
  54. data/taglibs/rapid_lifecycles.dryml +18 -1
  55. data/taglibs/rapid_navigation.dryml +50 -61
  56. data/taglibs/rapid_pages.dryml +103 -19
  57. data/taglibs/rapid_plus.dryml +54 -6
  58. data/taglibs/rapid_support.dryml +38 -1
  59. data/taglibs/rapid_user_pages.dryml +17 -5
  60. data/test/permissions/models/models.rb +24 -12
  61. data/test/permissions/models/test.sqlite3 +0 -0
  62. metadata +6 -15
  63. data/lib/extensions/test_case.rb +0 -129
  64. data/lib/hobo/composite_model.rb +0 -73
  65. data/lib/hobo/model_support.rb +0 -44
  66. data/tasks/fix_dryml.rake +0 -143
  67. data/tasks/generate_tag_reference.rake +0 -192
  68. data/test/dryml/complilation_test.rb +0 -261
@@ -193,7 +193,7 @@ module Hobo::Dryml
193
193
  def set_element(el)
194
194
  assigns = el.attributes.map do |name, value|
195
195
  next if name.in?(SPECIAL_ATTRIBUTES)
196
- dryml_exception("invalid name in <set>", el) unless name =~ /^#{DRYML_NAME}(\.#{DRYML_NAME})*$/
196
+ dryml_exception("invalid name in <set> (remember to use '-' rather than '_')", el) unless name =~ /^#{DRYML_NAME}(\.#{DRYML_NAME})*$/
197
197
  "#{ruby_name name} = #{attribute_to_ruby(value)}; "
198
198
  end.join
199
199
  code = apply_control_attributes("begin; #{assigns}; end", el)
@@ -203,7 +203,7 @@ module Hobo::Dryml
203
203
 
204
204
  def set_scoped_element(el)
205
205
  variables = el.attributes.map do |name, value|
206
- dryml_exception("invalid name in <set-scoped>", el) unless name =~ DRYML_NAME_RX
206
+ dryml_exception("invalid name in <set-scoped> (remember to use '-' rather than '_')", el) unless name =~ DRYML_NAME_RX
207
207
  ":#{ruby_name name} => #{attribute_to_ruby(value)} "
208
208
  end
209
209
  "<% scope.new_scope(#{variables * ', '}) { #{tag_newlines(el)} %>#{children_to_erb(el)}<% } %>"
@@ -367,7 +367,7 @@ module Hobo::Dryml
367
367
  # reproduce any line breaks in the start-tag so that line numbers are preserved
368
368
  tag_newlines(el) + "%>" +
369
369
  wrap_tag_method_body_with_metadata(children_to_erb(el)) +
370
- "<% _erbout; end"
370
+ "<% output_buffer; end"
371
371
  end
372
372
 
373
373
 
@@ -576,7 +576,7 @@ module Hobo::Dryml
576
576
  end
577
577
 
578
578
  call = apply_control_attributes(call, el)
579
- call = maybe_make_part_call(el, "<% _output(#{call}) %>")
579
+ call = maybe_make_part_call(el, "<% concat(#{call}) %>")
580
580
  wrap_tag_call_with_metadata(el, call)
581
581
  end
582
582
 
@@ -678,7 +678,7 @@ module Hobo::Dryml
678
678
  def before_parameter_tag_hash_item(name, el, metadata_name)
679
679
  param_name = get_param_name(el)
680
680
  dryml_exception("param declaration not allowed on 'before' parameters", el) if param_name
681
- content = children_to_erb(el) + "<% _output(#{param_restore_local_name(name)}.call({}, {})) %>"
681
+ content = children_to_erb(el) + "<% concat(#{param_restore_local_name(name)}.call({}, {})) %>"
682
682
  ":#{ruby_name name}_replacement => #{replace_parameter_proc(el, metadata_name, content)}"
683
683
  end
684
684
 
@@ -686,7 +686,7 @@ module Hobo::Dryml
686
686
  def after_parameter_tag_hash_item(name, el, metadata_name)
687
687
  param_name = get_param_name(el)
688
688
  dryml_exception("param declaration not allowed on 'after' parameters", el) if param_name
689
- content = "<% _output(#{param_restore_local_name(name)}.call({}, {})) %>" + children_to_erb(el)
689
+ content = "<% concat(#{param_restore_local_name(name)}.call({}, {})) %>" + children_to_erb(el)
690
690
  ":#{ruby_name name}_replacement => #{replace_parameter_proc(el, metadata_name, content)}"
691
691
  end
692
692
 
@@ -851,7 +851,7 @@ module Hobo::Dryml
851
851
  end
852
852
 
853
853
  output_tag = "element(:#{el.name}, #{attrs}, new_context { %>#{body}<% })"
854
- "<% _output(" + apply_control_attributes(output_tag, el) + ") %>"
854
+ "<% concat(" + apply_control_attributes(output_tag, el) + ") %>"
855
855
  end
856
856
  end
857
857
 
@@ -240,24 +240,13 @@ module Hobo::Dryml
240
240
  end
241
241
 
242
242
 
243
- def _erbout
244
- @_erb_output
245
- end
246
-
247
-
248
- def _output(s)
249
- @_erb_output.concat(s)
250
- end
251
-
252
-
253
243
  def new_context
254
- ctx = [ @_erb_output,
255
- @_this, @_this_parent, @_this_field, @_this_type,
256
- @_form_field_path ]
257
- @_erb_output = ""
244
+ ctx = [ @_this, @_this_parent, @_this_field, @_this_type,
245
+ @_form_field_path, @_form_field_paths_by_object ]
258
246
  @_this_type = nil
259
- res = yield
260
- @_erb_output, @_this, @_this_parent, @_this_field, @_this_type, @_form_field_path = ctx
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
261
250
  res.to_s
262
251
  end
263
252
 
@@ -374,7 +363,7 @@ module Hobo::Dryml
374
363
 
375
364
 
376
365
  def call_tag_parameter_with_default_content(the_tag, attributes, default_content, overriding_content_proc)
377
- if the_tag.is_a?(String, Symbol) && the_tag.to_s.in?(Hobo.static_tags)
366
+ if the_tag.is_one_of?(String, Symbol) && the_tag.to_s.in?(Hobo.static_tags)
378
367
  body = if overriding_content_proc
379
368
  new_context { overriding_content_proc.call(proc { default_content._?.call(nil) }) }
380
369
  elsif default_content
@@ -451,7 +440,7 @@ module Hobo::Dryml
451
440
 
452
441
  default_content = parameters[:default]
453
442
 
454
- if the_tag.is_a?(String, Symbol) && the_tag.to_s.in?(Hobo.static_tags)
443
+ if the_tag.is_one_of?(String, Symbol) && the_tag.to_s.in?(Hobo.static_tags)
455
444
  body = if overriding_default_content
456
445
  new_context { overriding_default_content.call(proc { default_content.call(nil) if default_content }) }
457
446
  elsif default_content
@@ -470,7 +459,7 @@ module Hobo::Dryml
470
459
  parameters = parameters.merge(:default => d)
471
460
  end
472
461
 
473
- if the_tag.is_a?(String, Symbol)
462
+ if the_tag.is_one_of?(String, Symbol)
474
463
  # It's a defined DRYML tag
475
464
  send(the_tag, attributes, parameters)
476
465
  else
@@ -508,7 +497,7 @@ module Hobo::Dryml
508
497
  params[:default] =
509
498
  if general_parameters[:default]
510
499
  proc do |default|
511
- overriding_default.call(proc { new_context { _output(general_parameters[:default].call(default)) } } )
500
+ overriding_default.call(proc { new_context { concat(general_parameters[:default].call(default)) } } )
512
501
  end
513
502
  else
514
503
  proc do |default|
@@ -598,8 +587,8 @@ module Hobo::Dryml
598
587
  else
599
588
  "<#{name}#{attr_string}>#{content}</#{name}>"
600
589
  end
601
- if block && eval("defined? _erbout", block.binding) # in erb context
602
- _output(res)
590
+ if block_called_from_erb? block
591
+ concat res
603
592
  else
604
593
  res
605
594
  end
@@ -60,24 +60,41 @@ module ActionController
60
60
  text = call_dryml_tag(tag, attributes)
61
61
  text && render({:text => text, :layout => false }.merge(options))
62
62
  end
63
-
64
-
63
+
65
64
  # DRYML fallback tags -- monkey patch this method to attempt to render a tag if there's no template
66
- def render_for_file_with_dryml(template_path, status = nil, layout = nil, locals = {})
67
- render_for_file_without_dryml(template_path, status, layout, locals)
68
- rescue ActionView::MissingTemplate => ex
69
- # Try to use a DRYML <page> tag instead
70
- tag_name = @dryml_fallback_tag || "#{File.basename(template_path).dasherize}-page"
71
-
72
- text = call_dryml_tag(tag_name)
73
- if text
74
- render_for_text text, status
65
+ def render_for_file_with_dryml(template, status = nil, layout = nil, locals = {})
66
+ # in rails 2.2, "template" is actually "template_path"
67
+
68
+ # if we're passed a MissingTemplateWrapper, see if there's a
69
+ # dryml tag that will render the page
70
+ if template.respond_to? :original_template_path
71
+ # this is the Rails 2.3 path
72
+ tag_name = @dryml_fallback_tag || "#{File.basename(template.original_template_path).dasherize}-page"
73
+
74
+ text = call_dryml_tag(tag_name)
75
+ if text
76
+ return render_for_text text, status
77
+ else
78
+ template.raise_wrapped_exception
79
+ end
75
80
  else
76
- raise ex
81
+ begin
82
+ result = render_for_file_without_dryml(template, status, layout, locals)
83
+ rescue ActionView::MissingTemplate => ex
84
+ # this is the Rails 2.2 path
85
+ tag_name = @dryml_fallback_tag || "#{File.basename(template).dasherize}-page"
86
+
87
+ text = call_dryml_tag(tag_name)
88
+ if text
89
+ return render_for_text text, status
90
+ else
91
+ raise ex
92
+ end
93
+ end
77
94
  end
78
95
  end
79
96
  alias_method_chain :render_for_file, :dryml
80
-
97
+
81
98
  end
82
99
  end
83
100
 
@@ -96,22 +113,74 @@ class ActionView::Template
96
113
  # from trying to compile our template. DRYML templates are each compiled as a class, not just a method,
97
114
  # so the support for compiling templates that Rails provides is innadequate.
98
115
  def render_dryml(view, local_assigns = {})
99
- stack = view.instance_variable_get(:@_render_stack)
100
- stack.push(self)
116
+ if view.instance_variable_defined?(:@_render_stack)
117
+ # Rails 2.2
118
+ stack = view.instance_variable_get(:@_render_stack)
119
+ stack.push(self)
120
+
121
+ # This is only used for TestResponse to set rendered_template
122
+ unless is_a?(ActionView::InlineTemplate) || view.instance_variable_get(:@_first_render)
123
+ view.instance_variable_set(:@_first_render, self)
124
+ end
101
125
 
102
- # This is only used for TestResponse to set rendered_template
103
- unless is_a?(ActionView::InlineTemplate) || view.instance_variable_get(:@_first_render)
104
- view.instance_variable_set(:@_first_render, self)
126
+ view.send(:_evaluate_assigns_and_ivars)
127
+ view.send(:_set_controller_content_type, mime_type) if respond_to?(:mime_type)
128
+
129
+ result = Hobo::Dryml::TemplateHandler.new.render_for_rails22(self, view, local_assigns)
130
+
131
+ stack.pop
132
+ result
133
+ else
134
+ # Rails 2.3
135
+ compile(local_assigns)
136
+
137
+ view.with_template self do
138
+ view.send(:_evaluate_assigns_and_ivars)
139
+ view.send(:_set_controller_content_type, mime_type) if respond_to?(:mime_type)
140
+
141
+ Hobo::Dryml::TemplateHandler.new.render_for_rails22(self, view, local_assigns)
142
+ end
105
143
  end
144
+ end
145
+
146
+ end
106
147
 
107
- view.send(:_evaluate_assigns_and_ivars)
108
- view.send(:_set_controller_content_type, mime_type) if respond_to?(:mime_type)
148
+ # this is only used in Rails 2.3
149
+ class MissingTemplateWrapper
150
+ attr_reader :original_template_path
151
+
152
+ def initialize(exception, path)
153
+ @exception = exception
154
+ @original_template_path = path
155
+ end
109
156
 
110
- result = Hobo::Dryml::TemplateHandler.new.render_for_rails22(self, view, local_assigns)
157
+ def method_missing(*args)
158
+ raise @exception
159
+ end
111
160
 
112
- stack.pop
113
- result
161
+ def render
162
+ raise @exception
114
163
  end
164
+ end
115
165
 
116
- end
117
-
166
+
167
+ module ActionView
168
+ class PathSet < Array
169
+ # this is only used by Rails 2.3
170
+ def find_template_with_dryml(original_template_path, format = nil, html_fallback = true)
171
+ begin
172
+ find_template_without_dryml(original_template_path, format, html_fallback)
173
+ rescue ActionView::MissingTemplate => ex
174
+ # instead of throwing the exception right away, hand back a
175
+ # time bomb instead. It'll blow if mishandled...
176
+ return MissingTemplateWrapper.new(ex, original_template_path)
177
+ end
178
+ end
179
+
180
+ if instance_methods.include? "find_template"
181
+ # only rails 2.3 has this function
182
+ alias_method_chain :find_template, :dryml
183
+ end
184
+ end
185
+ end
186
+
@@ -33,13 +33,13 @@ module Hobo
33
33
  # result
34
34
  # end
35
35
  #}
36
-
36
+
37
37
  self.class.class_eval %{
38
38
  def #{name}
39
39
  Hobo::FindFor::Finder.new(self, '#{name}', :#{collection_name}, :#{anchor_association_name})
40
40
  end
41
41
  }
42
-
42
+
43
43
  return send(name, *args)
44
44
  end
45
45
  end
@@ -124,7 +124,7 @@ module Hobo
124
124
  elsif obj.is_a? Hobo::RawJs
125
125
  "#{name}=' + #{obj} + '"
126
126
  else
127
- v = if obj.is_a?(ActiveRecord::Base) or obj.is_a?(Array)
127
+ v = if obj.is_one_of?(ActiveRecord::Base, Array)
128
128
  "@" + typed_id(obj)
129
129
  else
130
130
  obj.to_s.gsub("'"){"\\'"}
@@ -286,7 +286,7 @@ module Hobo
286
286
  else
287
287
  object = this
288
288
  end
289
- elsif args.first.is_a?(String, Symbol)
289
+ elsif args.first.is_one_of?(String, Symbol)
290
290
  object = this
291
291
  field = args.first
292
292
  else
@@ -305,11 +305,11 @@ module Hobo
305
305
 
306
306
  @can_view_cache ||= {}
307
307
  @can_view_cache[ [object, field] ] ||=
308
- if !object.respond_to?(:viewable_by)
308
+ if !object.respond_to?(:viewable_by?)
309
309
  true
310
310
  elsif object.viewable_by?(current_user, field)
311
311
  # If possible, we also check if the current *value* of the field is viewable
312
- if field.is_a?(Symbol, String) && (v = object.send(field)) && v.respond_to?(:viewable_by?)
312
+ if field.is_one_of?(Symbol, String) && (v = object.send(field)) && v.respond_to?(:viewable_by?)
313
313
  v.viewable_by?(current_user, nil)
314
314
  else
315
315
  true
@@ -320,7 +320,6 @@ module Hobo
320
320
  end
321
321
 
322
322
 
323
-
324
323
  def select_viewable(collection=this)
325
324
  collection.select {|x| can_view?(x)}
326
325
  end
@@ -351,7 +350,7 @@ module Hobo
351
350
 
352
351
 
353
352
  def param_name_for(path)
354
- field_path = field_path.to_s.split(".") if field_path.is_a?(String, Symbol)
353
+ field_path = field_path.to_s.split(".") if field_path.is_one_of?(String, Symbol)
355
354
  attrs = path.rest.map{|part| "[#{part.to_s.sub /\?$/, ''}]"}.join
356
355
  "#{path.first}#{attrs}"
357
356
  end
@@ -382,7 +381,11 @@ module Hobo
382
381
 
383
382
  def new_for_current_user(model_or_assoc=nil)
384
383
  model_or_assoc ||= this
385
- model_or_assoc.user_new(current_user)
384
+ if model_or_assoc.respond_to?(:new_candidate)
385
+ model_or_assoc.user_new_candidate(current_user)
386
+ else
387
+ model_or_assoc.user_new(current_user)
388
+ end
386
389
  end
387
390
 
388
391
 
@@ -439,6 +442,7 @@ module Hobo
439
442
  result
440
443
  end
441
444
 
445
+
442
446
  def linkable?(*args)
443
447
  options = args.extract_options!
444
448
  target = args.empty? || args.first.is_a?(Symbol) ? this : args.shift
@@ -462,37 +466,33 @@ module Hobo
462
466
  Hobo::ModelRouter.linkable?(klass, action, options.reverse_merge(:subsite => subsite))
463
467
  end
464
468
 
469
+
465
470
  def css_data(name, *args)
466
471
  "#{name.to_s.dasherize}::#{args * '::'}"
467
472
  end
468
-
469
-
470
- # Convenience helper for the default app
473
+
471
474
 
472
- # FIXME: this should interrogate the routes to find index methods, not the models
473
- def front_models
474
- Hobo::Model.all_models.select {|m| linkable?(m) }
475
- end
476
-
475
+ # --- ViewHint Helpers --- #
477
476
 
478
- def this_field_name
479
- this_parent.class.view_hints.field_name(this_field)
477
+ def this_field_name
478
+ this_parent.class.try.view_hints.try.field_name(this_field) || this_field
480
479
  end
481
480
 
482
481
  def this_field_help
483
- this_parent.class.view_hints.field_help[this_field.to_sym]
482
+ this_parent.class.try.view_hints.try.field_help[this_field.to_sym]
484
483
  end
485
484
 
486
485
 
487
- # debugging support
486
+ # --- Debugging Helpers ---- #
488
487
 
489
488
  def abort_with(*args)
490
- raise args.map{|arg| PP.pp(arg, "")}.join("-------\n")
489
+ raise args.*.pretty_inspect.join("-------\n")
491
490
  end
492
491
 
493
492
  def log_debug(*args)
493
+ return if not logger
494
494
  logger.debug("\n### DRYML Debug ###")
495
- logger.debug(args.map {|a| PP.pp(a, "")}.join("-------\n"))
495
+ logger.debug(args.*.pretty_inspect.join("-------\n"))
496
496
  logger.debug("DRYML THIS = #{this.typed_id rescue this.inspect}")
497
497
  logger.debug("###################\n")
498
498
  args.first unless args.empty?
@@ -19,10 +19,13 @@ module Hobo
19
19
  added = false
20
20
  records.each do |record|
21
21
  # we want to call valid? on each one, but only add the error to self once
22
- unless record.valid?
23
- unless added
24
- errors.add association, "..."
25
- added = true
22
+
23
+ record.with_acting_user(acting_user) do
24
+ unless record.valid?
25
+ unless added
26
+ errors.add association, "..."
27
+ added = true
28
+ end
26
29
  end
27
30
  end
28
31
  end
@@ -35,7 +38,8 @@ module Hobo
35
38
  if included_in_save
36
39
  included_in_save.each_pair do |association, records|
37
40
  records.each do |record|
38
- record.save_without_validation # This means without transactions too
41
+ # save_without_validation means without transactions too
42
+ record.with_acting_user(acting_user) { record.save_without_validation }
39
43
  end
40
44
  end
41
45
  end
@@ -20,7 +20,7 @@ module Hobo
20
20
 
21
21
 
22
22
  def extract_attributes(attributes)
23
- update_attributes = options.fetch(:update, [])
23
+ update_attributes = options.fetch(:params, [])
24
24
  attributes & update_attributes
25
25
  end
26
26
 
@@ -50,7 +50,7 @@ module Hobo
50
50
 
51
51
 
52
52
  def parameters
53
- options[:update] || []
53
+ options[:params] || []
54
54
  end
55
55
 
56
56