hobo 0.8.5 → 0.8.6

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -98,12 +98,12 @@ This will use `<input/>` as the tag in each table cell instead of `<view/>`
98
98
  <thead if="&all_parameters[:thead] || fields" param>
99
99
  <tr param="field-heading-row">
100
100
  <with-field-names merge-attrs="&all_attributes & attrs_for(:with_fields)">
101
- <th param="#{scope.field_name}-heading"><%= this.member_class.view_hints.field_name(scope.field_name) %></th>
101
+ <th param="#{scope.field_name}-heading"><%= this.member_class.try.view_hints.try.field_name(scope.field_name) if scope %></th>
102
102
  </with-field-names>
103
103
  <th if="&all_parameters[:controls]" class="controls"/>
104
104
  </tr>
105
105
  </thead>
106
- <tbody>
106
+ <tbody param>
107
107
  <repeat>
108
108
  <tr param if="&can_view?"
109
109
  class="#{scope.even_odd} #{this_type.name.underscore} #{model_id_class}">
@@ -112,7 +112,7 @@ This will use `<input/>` as the tag in each table cell instead of `<view/>`
112
112
  <td param="#{this_field.to_s.sub('?', '').gsub('.', '-')}-view"><call-tag tag="&field_tag"/></td>
113
113
  </with-fields>
114
114
  <td class="controls" param="controls" if="&all_parameters[:controls]">
115
- <a param="edit-link" action="edit">Edit</a>
115
+ <a param="edit-link" action="edit" if="&can_edit?">Edit</a>
116
116
  <delete-button param/>
117
117
  </td>
118
118
  </if>
@@ -213,8 +213,9 @@ Provides a short hand way of displaying images in public/images
213
213
  -->
214
214
  <def tag="type-name" attrs="plural, lowercase, dasherize"><%=
215
215
  type ||= (this if this.is_a?(Class)) || this.try.member_class || this.class
216
-
217
- name = dasherize ? type.name.underscore.dasherize : type.name.titleize
216
+
217
+ name = type.respond_to?(:view_hints) ? type.view_hints.model_name : type.name
218
+ name = dasherize ? name.underscore.dasherize : name.titleize
218
219
  name = name.pluralize if plural
219
220
  name = name.downcase if lowercase
220
221
  name
@@ -293,7 +294,7 @@ Or a new page if the context is a class:
293
294
  * If `action="new"` then `<a>` will check that the current user has permission to create the object
294
295
  * Several useful classes are added automatically to the output `<a>`.
295
296
  -->
296
- <def tag="a" attrs="action, to, params, query-params, href, format, subsite"><%=
297
+ <def tag="a" attrs="action, to, params, query-params, href, format, subsite, force"><%=
297
298
  content = parameters.default
298
299
 
299
300
  params = self.query_params.merge(params || HashWithIndifferentAccess.new) if query_params
@@ -314,7 +315,7 @@ Or a new page if the context is a class:
314
315
  new_record.set_creator(current_user)
315
316
  href = object_url(target, "new", params._?.merge(:subsite => subsite))
316
317
 
317
- if href && can_create?(new_record)
318
+ if href && (force || can_create?(new_record))
318
319
  new_class_name = if target.respond_to?(:proxy_reflection)
319
320
  target.proxy_reflection.klass.name
320
321
  else
@@ -527,7 +528,7 @@ The label can be customised using the `label` attribute, e.g.
527
528
  <def tag="theme-stylesheet" attrs="name">
528
529
  <% name ||= Hobo.current_theme -%>
529
530
  <link href="#{base_url}/hobothemes/#{Hobo.current_theme}/stylesheets/#{name}.css"
530
- media="screen" rel="Stylesheet" type="text/css" />
531
+ media="all" rel="Stylesheet" type="text/css" merge/>
531
532
  </def>
532
533
 
533
534
  <!-- Convenience tag to help with the common situation where you need to address the current user as "you", and refer to other users by name
@@ -562,14 +563,7 @@ The context should be a user object. If `this == current_user` the "you" form is
562
563
  <else><do param="default"><%= n = name; n.ends_with?('s') ? "#{n}'" : "#{n}'s" %></do></else>
563
564
  </def>
564
565
 
565
- <!-- Renders "a book" or "an orange" according the the word passed in the attribute `word`
566
-
567
- ### Usage
568
-
569
- To render either "Please select a recipe" or "Please select an event", according to the type of the `this`:
570
-
571
- <a-or-an word="&type_name"/>
572
-
566
+ <!-- Deprecated. It's harder than you think to do this (e.g. an umbrealla, an user)
573
567
  -->
574
568
  <def tag="a-or-an" attrs="word"><%=
575
569
  (word =~ /^[aeiou]/i ? "an " : "a ") + word
@@ -584,3 +578,14 @@ To render either "Please select a recipe" or "Please select an event", according
584
578
 
585
579
  <!-- Renders a collection of string joined with ", ", or some other string passed in the `join` attribute -->
586
580
  <def tag="comma-list" attrs="join"><%= this.join(join || ", ") %></def>
581
+
582
+
583
+ <!-- Development mode only - a menu to change the `current_user` -->
584
+ <def tag="dev-user-changer">
585
+ <set user="&Hobo::User.default_user_model"/>
586
+ <select-menu if="&user && RAILS_ENV == 'development'"
587
+ first-option="Guest" options="&user.all(:limit => 30).*.login"
588
+ onchange="location.href = '/dev/set_current_user?login=' + this.options[this.selectedIndex].value"
589
+ selected="#{current_user.login}"
590
+ class="dev-user-changer"/>
591
+ </def>
@@ -19,7 +19,7 @@
19
19
  -->
20
20
  <def tag="section" attrs="empty, with-flash-messages">
21
21
  <set body="&parameters.default" flash="&with_flash_messages && !scope.flash_rendered"/>
22
- <div class="section #{'with-flash' if flash}" merge-attrs if="&!body.blank? || empty">
22
+ <div class="section #{'with-flash' if flash}" merge-attrs if="&!body.blank? || empty || flash">
23
23
  <flash-messages if="&flash"/>
24
24
  <%= body %>
25
25
  </div>
@@ -58,9 +58,6 @@ This area of Hobo has had less attention that the non-ajax forms of late, so it'
58
58
  <!-- Provides a simple Scriptaculous in-place-editor that uses an `<input type='text'>` -->
59
59
  <def tag="editor" for="float"><%= in_place_editor attributes %></def>
60
60
 
61
- <!-- Provides a simple Scriptaculous in-place-editor that uses an `<input type='text'>` -->
62
- <def tag="editor" for="big_integer"><%= in_place_editor attributes %></def>
63
-
64
61
  <!-- Raises an error - passwords cannot be edited in place -->
65
62
  <def tag="editor" for="password"><% raise HoboError, "passwords cannot be edited in place" %></def>
66
63
 
@@ -88,14 +85,14 @@ This area of Hobo has had less attention that the non-ajax forms of late, so it'
88
85
  NOTE: yes that's *DOM ID's* not part-names. A common source of confusion because by default the part name and DOM ID are the same.
89
86
 
90
87
  -->
91
- <def tag="select-one-editor" attrs="include-none, blank-message, sort, update"><%
88
+ <def tag="select-one-editor" attrs="include-none, blank-message, sort, update, options"><%
92
89
  raise HoboError.new("Not allowed to edit") unless can_edit?
93
90
  blank_message ||= "(No #{this_type.name.to_s.titleize})"
94
- select_options = this_field_reflection.klass.find(:all).select {|x| can_view?(x)}.map {|x|
91
+ options ||= this_field_reflection.klass.find(:all).select {|x| can_view?(x)}.map {|x|
95
92
  [ name(:with => x, :no_wrapper => true), x.id ]
96
93
  }
97
- select_options = select_options.sort if sort
98
- select_options.insert(0, [blank_message, ""]) if this.nil? || include_none
94
+ options = options.sort if sort
95
+ options.insert(0, [blank_message, ""]) if this.nil? || include_none
99
96
  f = ajax_updater(object_url(this_parent, :method => :put),
100
97
  update,
101
98
  :method => "put",
@@ -104,7 +101,7 @@ This area of Hobo has had less attention that the non-ajax forms of late, so it'
104
101
  } })
105
102
  %>
106
103
  <select onchange="#{f}" merge-attrs>
107
- <%= options_for_select(select_options, this ? this.id : "") %>
104
+ <%= options_for_select(options, this ? this.id : "") %>
108
105
  </select>
109
106
  </def>
110
107
 
@@ -212,10 +209,10 @@ This area of Hobo has had less attention that the non-ajax forms of late, so it'
212
209
  -->
213
210
  <def tag="integer-select-editor" attrs="options, min, max, update, nil-option, message">
214
211
  <% options ||= (min.to_i..max.to_i).to_a %>
215
- <select class="number-editor-bhv #{model_id_class} #{'update:' + comma_split(update).join(':') unless update.blank?}"
212
+ <select class="integer editor #{'update:' + comma_split(update).join(':') unless update.blank?} #{model_id_class(this_parent, this_field)}"
216
213
  merge-attrs="&message ? attributes.merge(:hobo_message => message) : attributes">
217
214
  <if test="&this.nil?"><option value=""><%= nil_option || "Choose a value" %></option></if>
218
- <%= options_for_select(options.*.to_s, this) %>
215
+ <%= options_for_select(options.*.to_s, this.to_s) %>
219
216
  </select>
220
217
  </def>
221
218
 
@@ -1,5 +1,7 @@
1
1
  <!-- Rapid Forms provides various tags that make it quick and easy to produce working new or edit forms.
2
2
 
3
+ ### Overview
4
+
3
5
  The main tags are:
4
6
 
5
7
  - `<form>`, which acts like the dumb HTML tag if you provide the `action` attribute, and picks up various Rapid smarts
@@ -169,7 +171,7 @@ AJAX based submission can be enabled by simply adding an `update` attribute. e.g
169
171
  b
170
172
  end
171
173
 
172
- auth_token = if method.nil? || method == 'get' || request_forgery_protection_token.nil?
174
+ auth_token = if method.nil? || method == 'get' || !protect_against_forgery?
173
175
  ''
174
176
  else
175
177
  element(:input, {:type => "hidden",
@@ -278,7 +280,7 @@ If the context is a `has_many :through` association, the polymorphic `<collectio
278
280
  if refl.options[:through]
279
281
  collection_input(attrs)
280
282
  else
281
- raise NotImplementedError, "An input for has-many associations has not been implemented yet"
283
+ input_many(attrs)
282
284
  end
283
285
  end
284
286
  else
@@ -363,7 +365,7 @@ The menus default to the current time if the current value is nil.
363
365
  ### Attributes
364
366
 
365
367
  - order: The order of the year, month and date menus. A comma separated string or an array. Default: "year, month,
366
- day, hour, minute, second"
368
+ day, hour, minute"
367
369
 
368
370
  Any other attributes are passed through to the `select_datetime` helper.
369
371
 
@@ -371,8 +373,13 @@ The menus default to the current time if the current value is nil.
371
373
 
372
374
  -->
373
375
  <def tag="input" for="datetime" attrs="order">
374
- <% order = order.nil? ? [:year, :month, :day, :hour, :minute, :second] : comma_split(order).*.to_sym -%>
375
- <%= select_datetime(this || Time.now, attributes.merge(:prefix => param_name_for_this, :order => order)) %>
376
+ <% if ! order.nil?
377
+ order = comma_split(order).*.to_sym
378
+ attributes.merge!(:order => order)
379
+ require 'ruby-debug'
380
+ debugger
381
+ end -%>
382
+ <%= select_datetime(this || Time.now, attributes.merge(:prefix => param_name_for_this)) %>
376
383
  </def>
377
384
 
378
385
  <!-- An `<input type='text'>` input. -->
@@ -390,11 +397,6 @@ The menus default to the current time if the current value is nil.
390
397
  <%= text_field_tag(name, this, attributes) %>
391
398
  </def>
392
399
 
393
- <!-- An `<input type='text'>` input. -->
394
- <def tag="input" for="big_integer" attrs="name">
395
- <%= text_field_tag(name, this, attributes) %>
396
- </def>
397
-
398
400
  <!-- A `<select>` menu containing the values of an 'enum string'.
399
401
 
400
402
  ### Attributes
@@ -405,12 +407,13 @@ The menus default to the current time if the current value is nil.
405
407
  - titleize: Set to false to have the value itself (rather than `value.titleize`) be the default label. Default: true
406
408
 
407
409
  -->
408
- <def tag="input" for="HoboFields::EnumString" attrs="labels, titleize"><%
410
+ <def tag="input" for="HoboFields::EnumString" attrs="labels, titleize, first-option, first-value"><%
409
411
  labels ||= {}
410
412
  titleize = true if titleize.nil?
411
413
  options = this_type.values.map {|v| [labels.fetch(v.to_sym, titleize ? v.titleize : v), v] }
412
414
  %>
413
415
  <select name="#{param_name_for_this}" merge-attrs>
416
+ <option value="#{first_value}" unless="&first_option.nil?"><first-option/></option>
414
417
  <%= options_for_select(options, this) %>
415
418
  </select>
416
419
  </def>
@@ -547,7 +550,7 @@ All the standard ajax attributes *except the callbacks* are supported (see the m
547
550
  %></def>
548
551
 
549
552
 
550
- <!-- Provides either an ajax or non-ajax create button that will send a RESTful "POST" to the server to create a new resource.
553
+ <!-- Provides an ajax create button that will send a RESTful "POST" to the server to create a new resource.
551
554
 
552
555
  All of the standard ajax attributes are supported (see the main taglib documention for Rapid Forms).
553
556
 
@@ -582,6 +585,21 @@ All of the standard ajax attributes are supported (see the main taglib documenti
582
585
  %></def>
583
586
 
584
587
 
588
+ <!-- A `<select>` menu from which the user can choose the target record for a `belongs_to` association.
589
+
590
+ This is the default input that Rapid uses for `belongs_to` associations. The menu is constructed using the `to_s` representation of the records.
591
+
592
+ ### Attributes
593
+
594
+ - `include-none` - whether to include a 'none' option (i.e. set the foreign key to null). Defaults to false
595
+ - `blank-message` - the message for the 'none' option. Defaults to "(No `<model-name>`)", e.g. "(No Product)"
596
+ - `options` - an array of records to include in the menu. Defaults to the all the records in the target table that match any `:conditions` declared on the `belongs_to` (limited to 100).
597
+
598
+ ### See Also
599
+
600
+ For situations where there are too many target records to practically include in a menu, `<name-one>` provides an autocompleter which would be more suitable.
601
+
602
+ -->
585
603
  <def tag="select-one" attrs="include-none, blank-message, options, sort"><%
586
604
  raise HoboError.new("Not allowed to edit #{this_field}") if !attributes[:disabled] && !can_edit?
587
605
 
@@ -589,7 +607,7 @@ All of the standard ajax attributes are supported (see the main taglib documenti
589
607
 
590
608
  options ||= begin
591
609
  conditions = ActiveRecord::Associations::BelongsToAssociation.new(this_parent, this_field_reflection).conditions
592
- this_field_reflection.klass.all(:conditions => conditions).select {|x| can_view?(x)}
610
+ this_field_reflection.klass.all(:conditions => conditions, :limit => 100).select {|x| can_view?(x)}
593
611
  end
594
612
 
595
613
  select_options = options.map { |x| [x.to_s, x.id ] }
@@ -603,6 +621,23 @@ All of the standard ajax attributes are supported (see the main taglib documenti
603
621
  </def>
604
622
 
605
623
 
624
+ <!-- An `<input type="text">` with auto-completion. Allows the user to chose the target of a `belongs_to` association
625
+ by name.
626
+
627
+ ### Attributes
628
+
629
+ - `complete-target`
630
+ - `completer`
631
+
632
+ In the simple case no attributes are needed, e.g.: `<name-one:category/>`.
633
+
634
+ The completions are provided by the server with a GET request. The `complete-target` and `completer` attributes can be used to customise the URL for the request. For example:
635
+
636
+ - If `completer` is a class, say `Product`: `/products/complete_name` (where `name` is the declared name attribute of `Product`)
637
+ - If `completer` is a record, say a `Product` with id `12`: `/products/complete_name?id=12`
638
+ - If `completer-name` is given, e.g. with `completer-name="new_product_names"`: `/products/complete_new_product_names`
639
+
640
+ -->
606
641
  <def tag="name-one" attrs="complete-target, completer"><%
607
642
  complete_target ||= this_field_reflection.klass
608
643
  completer ||= (complete_target.is_a?(Class) ? complete_target : complete_target.class).name_attribute
@@ -615,18 +650,31 @@ All of the standard ajax attributes are supported (see the main taglib documenti
615
650
  </def>
616
651
 
617
652
 
653
+ <!-- nodoc. -->
618
654
  <def tag="sti-type-input">
619
655
  <select name="#{param_name_for(form_field_path + ['type'])}">
620
656
  <%= options_for_select(this.class.send(:subclasses).map{|x| [x.name.titleize, x.name]}, this.class.name) %>
621
657
  </select>
622
658
  </def>
623
659
 
624
-
660
+
661
+ <!-- A `<select>` menu input. This tag differes from `<select-menu>` only in that it adds the correct `name` attribute for the current field, and `selected` default to `this`.
662
+
663
+ ### Attributes
664
+
665
+ - `options` - an array of options suitable to be passed to the Rails `options_for_select` helper.
666
+ - `selected` - the value (from the `options` array) that should be initially selected. Defaults to `this`
667
+ - first-option - A string to be used for an extra option in the first position. E.g. "Please choose..."
668
+ - first-value - the value to be used with the `first-option`. Typcially not used, meaning the option has a blank value.
669
+
670
+ -->
625
671
  <def tag="select-input">
626
672
  <select-menu name="#{param_name_for_this}" selected="&this" merge/>
627
673
  </def>
628
674
 
629
-
675
+
676
+ <!-- Renders a readable list of error messages following a form submission. Expects the errors to be in `this.errors`. Renders nothing if there are no errors.
677
+ -->
630
678
  <def tag="error-messages">
631
679
  <section class="error-messages" merge-attrs if="&this.errors.length > 0">
632
680
  <h2 param="heading">To proceed please correct the following:</h2>
@@ -676,6 +724,7 @@ To use this tag, the model of the items the user is chosing *must* have unique n
676
724
  </div>
677
725
  </def>
678
726
 
727
+
679
728
  <!--
680
729
  Used inside a form to specify where to redirect after successful submission. This works by inserting a hidden field called `after_submit` which is used by Hobo if present to perform a redirect after the form submission.
681
730
 
@@ -710,31 +759,68 @@ Use the `uri` option to specify a redirect location:
710
759
  </def>
711
760
 
712
761
 
762
+ <!-- A simple wrapper around the `<select>` tag and `options_for_select` helper
763
+
764
+ ### Attributes
765
+
766
+ - `options` - an array of options suitable to be passed to the Rails `options_for_select` helper.
767
+ - `selected` - the value (from the `options` array) that should be initially selected. Defaults to `this`
768
+ - first-option - A string to be used for an extra option in the first position. E.g. "Please choose..."
769
+ - first-value - the value to be used with the `first-option`. Typcially not used, meaning the option has a blank value.
770
+
771
+ -->
713
772
  <def tag="select-menu" attrs="options, selected, first-option, first-value">
714
773
  <select merge-attrs param="default">
774
+ <% selected=this if selected.nil? %>
715
775
  <option value="#{first_value}" unless="&first_option.nil?"><first-option/></option>
716
- <do param="options"><% options_for_select(options, selected.to_s) %></do>
776
+ <do param="options"><% options_for_select(options, selected) %></do>
717
777
  </select>
718
778
  </def>
719
779
 
720
780
 
721
- <def tag="check-many">
722
- <% collection = this; param_name = param_name_for_this; options ||= this.member_class.find(:all) -%>
723
- <ul class="check-many">
724
- <li repeat="&options">
725
- <input type="checkbox" name="#{param_name}[]" value="&h this.to_s" checked="&this.in?(collection)"/>
726
- <name/>
781
+ <!-- Renders a `<ul>` list of checkboxes, one for each of the potential targt in a `has_many` association. The use can check the items they wish to have associated. A typical use might be selecting categories for a blog post.
782
+ -->
783
+ <def tag="check-many" attrs="disabled"><%
784
+ collection = this
785
+ param_name = param_name_for_this
786
+ options ||= begin
787
+ conditions = ActiveRecord::Associations::BelongsToAssociation.new(this_parent, this_field_reflection).conditions
788
+ this_field_reflection.klass.all(:conditions => conditions, :limit => 100).select {|x| can_view?(x)}
789
+ end
790
+ -%>
791
+ <ul class="check-many" param="default" merge-attrs>
792
+ <input type="hidden" name="#{param_name}[]" value=""/><% # ensure all items are removed when nothing checked %>
793
+ <li repeat="&options" param>
794
+ <input type="checkbox" name="#{param_name}[]" value="@#{this.id}" checked="&this.in?(collection)" disabled="&disabled"/>
795
+ <name param/>
727
796
  </li>
728
797
  </ul>
729
798
  </def>
730
799
 
731
800
 
801
+ <!-- Renders an `<input type='hidden'>` for the `id` field of the current context -->
732
802
  <def tag="hidden-id-field">
733
803
  <if:id><input type="hidden" name="#{param_name_for_this}" value="#{this}" /></if>
734
804
  </def>
735
805
 
736
806
 
737
- <def tag="input-many">
807
+ <!-- Creates a sub-section of the form which the user can repeat using (+) and (-) buttons, in order to allow an entire `has_many` collection to be created/edited in a single form.
808
+
809
+ This tag is very different from tags like `<select-many>` and `<check-many>` in that:
810
+
811
+ - Those tags are used to *chose existing records* to include in the assocaition, while `<input-many>` is used to actually create or edit the records in the association.
812
+ - Those tags work by themselves, while `<input-many>` is just a wrapper for other input fields.
813
+
814
+ ### Example
815
+
816
+ Say you are creating a new `Category` in your online shop, and you want to create some initial products *in the same form*, you can add the following to your form:
817
+
818
+ <input-many:products><field-list fields="name, price"/></input-many>
819
+
820
+ The body of the tag will be repeated for each of the current records in the collection, or will just appear once (with blank fields) if the colleciton is empty.
821
+
822
+ -->
823
+ <def tag="input-many" polymorphic>
738
824
  <set empty="&this.empty?"/>
739
825
  <ul class="input-many #{this_field.dasherize} #{css_data :input_many_prefix, param_name_for_this}">
740
826
  <li repeat class="#{'record-with-errors' unless this.errors.empty?}">
@@ -742,12 +828,12 @@ Use the `uri` option to specify a redirect location:
742
828
  <hidden-id-field/>
743
829
  <div class="input-many-item" param="default"/>
744
830
  <div class="buttons">
745
- <button class="remove-item" unless="&this_parent.length == 1" merge-attrs="disabled">-</button>
831
+ <button class="remove-item" merge-attrs="disabled">-</button>
746
832
  <button class="add-item" if="&last_item?" merge-attrs="disabled">+</button>
747
833
  </div>
748
834
  </li>
749
835
  <li if="&empty">
750
- <fake-field-context fake-field="0" context="&this.new">
836
+ <fake-field-context fake-field="0" context="&this.try.new_candidate || this.member_class.new">
751
837
  <div class="input-many-item" param="default"/>
752
838
  </fake-field-context>
753
839
  <div class="buttons">
@@ -758,6 +844,8 @@ Use the `uri` option to specify a redirect location:
758
844
  </def>
759
845
 
760
846
 
847
+ <!-- Renders a sub-section of a form with fields for every record in a `has_many` association. This is similar to `<input-many>` except there is no ability to add and remove items (i.e. no (+) and (-) buttons).
848
+ -->
761
849
  <def tag="input-all">
762
850
  <% association_fkey = this_field_reflection.primary_key_name -%>
763
851
  <ul class="input-all #{this_field.dasherize}">
@@ -771,7 +859,8 @@ Use the `uri` option to specify a redirect location:
771
859
  </ul>
772
860
  </def>
773
861
 
774
-
862
+ <!-- Renders the common "or (Cancel)" for a form. Attributes are merged into the link (`<a>Cancel</a>`), making it easy to customise the destination of the cancel link. By default it will link to `this` or `this.class`.
863
+ -->
775
864
  <def tag="or-cancel">
776
865
  <if test="&linkable?">or <a merge-attrs>Cancel</a></if>
777
866
  <else>
@@ -1,3 +1,6 @@
1
+ <!-- Rapid Generics provides tags that provide generic renderings that can adapt to the model being renderd. At the moment this library provides cards and collections of cards. -->
2
+
3
+ <!-- A 'card' is a representation of an sub-object *within* a page, such as a comment on a blog-post, or a single product in a list of produtcs. This definition is just the very basic framework which gives the basis for the automatic cards that get generated. See `app/views/taglibs/auto/rapid/cards.dryml` for the cards that have been generated for your specific application. -->
1
4
  <def tag="card" polymorphic>
2
5
  <div class="card" param="default" merge-attrs>
3
6
  <header param/>
@@ -6,11 +9,14 @@
6
9
  </def>
7
10
 
8
11
 
12
+ <!-- A special card which is used by live-search to render the results. By default this just calls card, but you can define your own search cards with `<def tag='search-card' for="MyModel">` to customise search results for that model. -->
9
13
  <def tag="search-card" polymorphic>
10
- <card/>
14
+ <card merge/>
11
15
  </def>
12
16
 
13
17
 
18
+ <!-- Renders a message such as "No products to display". If the collection (`this`) is empty, `style="display:none"` is added. This means the message is still present and can be revealed with JavaScript if all items in the collection are removed with ajax remove buttons.
19
+ -->
14
20
  <def tag="empty-collection-message">
15
21
  <div class="empty-collection-message" style="#{'display:none' if !this.empty?}" param="default">
16
22
  No <collection-name lowercase/> to display
@@ -18,6 +24,10 @@
18
24
  </def>
19
25
 
20
26
 
27
+ <!-- Repeats the body of the tag inside a `<ul>` list with one item for each object in the collection (`this`). If no body is given, renders a `<card>` inside the `<li>`.
28
+
29
+ The `<li>` tags are automatically given a 'model ID' CSS class, which means the ajax `<remove-button>` will automatically be able to remove items from the collection. Also adds 'even' and 'odd' CSS classes.
30
+ -->
21
31
  <def tag="collection">
22
32
  <ul class="collection #{collection_name :dasherize => true}" merge-attrs unless="empty?">
23
33
  <li param="item" class="#{scope.even_odd} #{model_id_class}" repeat="&select_viewable">
@@ -27,7 +37,7 @@
27
37
  <empty-collection-message param="empty-message"/>
28
38
  </def>
29
39
 
30
-
31
- <def tag="field-names-where-true" attrs="fields"><%=
40
+ <!-- Renders a comma separated list of any fields passed in the `fields` attribute that are true (in the Ruby sense). For example, if a forum post had a boolean field `sticky`, this tag can be used to automatically label sticky posts "Sticky". Similarly, you could automatically add an "Administrator" label to the user's home page (this is seen in the default Hobo app). -->
41
+ <def tag="record-flags" attrs="fields"><%=
32
42
  comma_split(fields).select { |f| this.send(f) }.map { |f| f.titleize }.join(', ')
33
43
  %></def>