hobo 1.3.0.pre31 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. data/CHANGES-1.1.txt +5253 -0
  2. data/CHANGES.txt +255 -5095
  3. data/VERSION +1 -1
  4. data/hobo.gemspec +1 -2
  5. data/lib/generators/hobo/admin_subsite/USAGE +25 -0
  6. data/lib/generators/hobo/admin_subsite/admin_subsite_generator.rb +2 -1
  7. data/lib/generators/hobo/assets/USAGE +5 -0
  8. data/lib/generators/hobo/assets/templates/application.dryml.erb +1 -1
  9. data/lib/generators/hobo/controller/USAGE +3 -0
  10. data/lib/generators/hobo/i18n/USAGE +3 -0
  11. data/lib/generators/hobo/i18n/templates/app.fr.yml +26 -0
  12. data/lib/generators/hobo/i18n/templates/app.nb.yml +25 -0
  13. data/lib/generators/hobo/i18n/templates/hobo.de.yml +1 -0
  14. data/lib/generators/hobo/i18n/templates/hobo.en.yml +3 -2
  15. data/lib/generators/hobo/i18n/templates/hobo.es.yml +1 -0
  16. data/lib/generators/hobo/i18n/templates/hobo.fr.yml +195 -0
  17. data/lib/generators/hobo/i18n/templates/hobo.it.yml +1 -0
  18. data/lib/generators/hobo/i18n/templates/hobo.nb.yml +198 -0
  19. data/lib/generators/hobo/i18n/templates/hobo.pt-PT.yml +1 -0
  20. data/lib/generators/hobo/i18n/templates/hobo.ru.yml +1 -0
  21. data/lib/generators/hobo/model/USAGE +2 -2
  22. data/lib/generators/hobo/rapid/templates/hobo-rapid.js +15 -30
  23. data/lib/generators/hobo/rapid/templates/themes/clean/public/stylesheets/clean.css +1 -0
  24. data/lib/generators/hobo/resource/USAGE +39 -0
  25. data/lib/generators/hobo/routes/router.rb +2 -2
  26. data/lib/generators/hobo/setup_wizard/setup_wizard_generator.rb +23 -9
  27. data/lib/generators/hobo/subsite.rb +13 -2
  28. data/lib/generators/hobo/subsite/USAGE +24 -0
  29. data/lib/generators/hobo/subsite_taglib/USAGE +4 -0
  30. data/lib/generators/hobo/subsite_taglib/templates/taglib.dryml.erb +1 -1
  31. data/lib/generators/hobo/test_framework/USAGE +2 -0
  32. data/lib/generators/hobo/user_controller/USAGE +3 -0
  33. data/lib/generators/hobo/user_controller/templates/controller.rb.erb +3 -0
  34. data/lib/generators/hobo/user_mailer/USAGE +2 -0
  35. data/lib/generators/hobo/user_model/USAGE +2 -9
  36. data/lib/generators/hobo/user_resource/USAGE +10 -0
  37. data/lib/hobo.rb +1 -1
  38. data/lib/hobo/controller/authentication_support.rb +0 -22
  39. data/lib/hobo/controller/model.rb +15 -13
  40. data/lib/hobo/controller/{user.rb → user_base.rb} +43 -32
  41. data/lib/hobo/extensions/action_controller/hobo_methods.rb +25 -1
  42. data/lib/hobo/extensions/active_record/associations/collection.rb +12 -3
  43. data/lib/hobo/extensions/active_record/associations/reflection.rb +1 -1
  44. data/lib/hobo/extensions/active_record/relation_with_origin.rb +4 -0
  45. data/lib/hobo/helper.rb +6 -1
  46. data/lib/hobo/helper/translations.rb +1 -1
  47. data/lib/hobo/model.rb +55 -19
  48. data/lib/hobo/model/lifecycles.rb +3 -3
  49. data/lib/hobo/model/lifecycles/lifecycle.rb +7 -3
  50. data/lib/hobo/model/permissions.rb +1 -0
  51. data/lib/hobo/model/scopes/automatic_scopes.rb +0 -2
  52. data/lib/hobo/model/view_hints.rb +1 -0
  53. data/lib/hobo/rapid/generators/rapid/forms.dryml.erb +2 -1
  54. data/lib/hobo/rapid/generators/rapid/pages.dryml.erb +10 -11
  55. data/lib/hobo/rapid/helper.rb +4 -3
  56. data/lib/hobo/rapid/taglibs/rapid_core.dryml +92 -67
  57. data/lib/hobo/rapid/taglibs/rapid_editing.dryml +35 -15
  58. data/lib/hobo/rapid/taglibs/rapid_forms.dryml +46 -22
  59. data/lib/hobo/rapid/taglibs/rapid_i18n.dryml +103 -37
  60. data/lib/hobo/rapid/taglibs/rapid_lifecycles.dryml +3 -1
  61. data/lib/hobo/rapid/taglibs/rapid_pages.dryml +3 -3
  62. data/lib/hobo/rapid/taglibs/rapid_plus.dryml +49 -45
  63. data/test/irt/generators/partials/_subsite_taglib_variables.rb +1 -1
  64. metadata +38 -33
@@ -12,7 +12,7 @@
12
12
 
13
13
 
14
14
  <% each_controller do -%>
15
-
15
+ <% next unless @controller < Hobo::Controller::Model %>
16
16
  <!-- ====== <%= model.name %> Pages ====== -->
17
17
  <%
18
18
  new_link = linkable?(:new)
@@ -124,11 +124,10 @@ end
124
124
 
125
125
  unless model.view_hints.secondary_children.empty?
126
126
  aside_collections = model.view_hints.secondary_children.map { |c| [model.reflections[c], model.reverse_reflection(c)] }
127
- aside_collections.reject! { |refl, reverse| reverse.nil? }
128
127
  end
129
128
  -%>
130
129
  <def tag="show-page" for="<%= model.name %>">
131
- <page merge title="#{ht '<%=model_key %>.show.title', :default=>['<%=sq_escape model_name %>'] }">
130
+ <page merge title="#{ht '<%=model_key %>.show.title', :default=>['<%=sq_escape model_name %>'], :name => name(:no_wrapper => true) }">
132
131
 
133
132
  <body: class="show-page <%= model_class %>" param/>
134
133
 
@@ -142,8 +141,8 @@ end
142
141
  <a:<%= back_link %> param="parent-link">&laquo; <ht key="<%= model_key %>.actions.back_to_parent" parent="<%= back_link_human_name %>" name="&this">Back to <name/></ht></a:<%= back_link %>>
143
142
  <% end -%>
144
143
  <h2 param="heading">
145
- <ht key="<%= model_key %>.show.heading" name="&this.respond_to?(:name) ? this.name : ''">
146
- <name/>
144
+ <ht key="<%= model_key %>.show.heading" name="#{name(:no_wrapper => true)}">
145
+ <%= model_name %> <name/>
147
146
  </ht>
148
147
  </h2>
149
148
  <% if boolean_fields -%>
@@ -160,7 +159,7 @@ end
160
159
  <% if edit_link -%>
161
160
 
162
161
  <a action="edit" if="&can_edit?" param="edit-link">
163
- <ht key="<%= model_key %>.actions.edit" name="&this.respond_to?(:name) ? this.name : ''">
162
+ <ht key="<%= model_key %>.actions.edit" name="#{name(:no_wrapper => true)}">
164
163
  Edit <%= model_name %>
165
164
  </ht>
166
165
  </a>
@@ -169,7 +168,7 @@ end
169
168
 
170
169
  <section param="content-body">
171
170
  <% if main_content -%>
172
- <view:<%= main_content %> param="description"/>
171
+ <view:<%= main_content %> param="description" />
173
172
  <% end -%>
174
173
  <% if show_fields.any? -%>
175
174
  <field-list fields="<%= show_fields * ', ' %>" param/>
@@ -216,7 +215,7 @@ end
216
215
 
217
216
  <aside param>
218
217
  <% for refl, reverse_refl in aside_collections -%>
219
- <% if linkable?(refl.klass, :"index_for_#{reverse_refl.name}") -%>
218
+ <% if reverse_refl && linkable?(refl.klass, :"index_for_#{reverse_refl.name}") -%>
220
219
  <collection-preview:<%= refl.name %>.recent param="<%= refl.name %>-preview">
221
220
  <heading-content:><human-collection-name with="&this.origin" collection="<%= refl.name %>" your/></heading-content:>
222
221
  </collection-preview:<%= refl.name %>.recent>
@@ -244,14 +243,14 @@ end
244
243
  name_attribute = model.name_attribute
245
244
  -%>
246
245
  <def tag="edit-page" for="<%= model.name %>">
247
- <page merge title="#{ht '<%= model_key %>.edit.title', :default=>['Edit <%= sq_escape model_name %>'] }">
246
+ <page merge title="#{ht '<%= model_key %>.edit.title', :default=>['Edit <%= sq_escape model_name %>'], :name => name(:no_wrapper => true) }">
248
247
 
249
248
  <body: class="edit-page <%= model_class %>" param/>
250
249
 
251
250
  <content:>
252
251
  <section param="content-header">
253
252
  <h2 param="heading">
254
- <ht key="<%= model_key %>.edit.heading" name="&this.respond_to?(:name) ? this.name : ''">
253
+ <ht key="<%= model_key %>.edit.heading" name="#{name(:no_wrapper => true)}">
255
254
  Edit <%= model_name %>
256
255
  </ht>
257
256
  </h2>
@@ -314,7 +313,7 @@ new_link = :new.in?(actions)
314
313
 
315
314
  <section param="content-body">
316
315
  <% if new_link -%>
317
- <a action="new" to="&model" param="new-link">
316
+ <a action="new" to="&@<%= owner %>.<%= collection_name %>" param="new-link">
318
317
  <ht key="<%= model_key %>.actions.new" >New <%=model_name %></ht>
319
318
  </a>
320
319
 
@@ -120,15 +120,16 @@ module Hobo
120
120
  edit_text = this._?.to_s
121
121
  attributes.update(:hobo_edit_text => edit_text) unless edit_text.nil?
122
122
 
123
- update = attributes.delete(:update)
124
- attributes[:hobo_update] = update if update
123
+ if update = attributes.delete(:update)
124
+ attributes = add_classes(attributes, update_elements_class(update))
125
+ end
125
126
 
126
127
  view(attributes)
127
128
  end
128
129
 
129
130
 
130
131
 
131
- AJAX_CALLBACKS = [ :before, :success, :failure, :complete ]
132
+ AJAX_CALLBACKS = [ :success, :failure, :complete ]
132
133
 
133
134
  AJAX_ATTRS = AJAX_CALLBACKS + [ :update, :type, :method,
134
135
  :script, :form, :params, :confirm, :message,
@@ -93,7 +93,15 @@ This will use `<input/>` as the tag in each table cell instead of `<view/>`
93
93
  <table fields="name, created_at, description">
94
94
  <controls:/>
95
95
  </table>
96
- The controls can be further customised using the "edit-link" and "delete-button" parameters or by providing completely new content for the control column, e.g.
96
+
97
+ The controls can be further customised using the "edit-link" and "delete-button" parameters:
98
+
99
+ <table fields="name, created_at, description">
100
+ <controls:/>
101
+ <delete-button: label="Nuke Me"/>
102
+ </table>
103
+
104
+ or by providing completely new content for the control column:
97
105
 
98
106
  <table fields="name, created_at, description">
99
107
  <controls:>my controls!</controls:>
@@ -124,7 +132,9 @@ This will use `<input/>` as the tag in each table cell instead of `<view/>`
124
132
  <td param="#{scope.field_name.to_s.sub('?', '').gsub('.', '-')}-view"><call-tag tag="&field_tag"/></td>
125
133
  </with-fields>
126
134
  <td class="controls" param="controls" if="&all_parameters[:controls]">
127
- <a param="edit-link" action="edit" if="&can_edit?"><t key="hobo.action.edit">Edit</t></a>
135
+ <a param="edit-link" action="edit" if="&can_edit?">
136
+ <t key="hobo.actions.edit_control">Edit</t>
137
+ </a>
128
138
  <delete-button param/>
129
139
  </td>
130
140
  </if>
@@ -157,24 +167,35 @@ Provides a short hand way of displaying images in public/images
157
167
 
158
168
 
159
169
  <!-- Renders some standard JavaScript code that various features of the Rapid library rely on. This tag would typicallu be called from your `<page>` tag. The default Rapid pages include this already. -->
160
- <def tag="hobo-rapid-javascripts"><%=
161
- res = 'var hoboParts = {};'
162
- # FIXME: This should interrogate the model-router - not the models
163
- unless Hobo::Model.all_models.empty?
164
- # Tell JS code how to pluralize names, unless they follow the simple rule
165
- names = Hobo::Model.all_models.map do |m|
166
- m = m.name.underscore
167
- "#{m}: '#{m.pluralize}'" unless m.pluralize == m + 's'
168
- end.compact
169
- res << "var pluralisations = {#{names * ', '}}; "
170
- end
171
- base = [base_url, subsite].compact.join("/")
172
- res << "urlBase = '#{base}'; hoboPagePath = '#{request.fullpath}'"
173
- if protect_against_forgery?
174
- res << "; formAuthToken = { name: '#{request_forgery_protection_token}', value: '#{form_authenticity_token}' }"
175
- end
176
- res
177
- %></def>
170
+ <def tag="hobo-rapid-javascripts">
171
+ <script type="text/javascript" param="default"><%=
172
+ res = 'var hoboParts = {};'
173
+ # FIXME: This should interrogate the model-router - not the models
174
+ unless Hobo::Model.all_models.empty?
175
+ # Tell JS code how to pluralize names, unless they follow the simple rule
176
+ names = Hobo::Model.all_models.map do |m|
177
+ m = m.name.underscore
178
+ "#{m}: '#{m.pluralize}'" unless m.pluralize == m + 's'
179
+ end.compact
180
+ res << "var pluralisations = {#{names * ', '}}; "
181
+ end
182
+ base = [base_url, subsite].compact.join("/")
183
+ res << "urlBase = '#{base}'; hoboPagePath = '#{request.fullpath}'"
184
+ if protect_against_forgery?
185
+ res << "; formAuthToken = { name: '#{request_forgery_protection_token}', value: '#{form_authenticity_token}' }"
186
+ end
187
+ res
188
+ %></script>
189
+ </def>
190
+
191
+
192
+ <def tag="part-contexts-javascripts">
193
+ <% unless (storage = part_contexts_storage).blank? %>
194
+ <script type="text/javascript"><%=
195
+ storage
196
+ %></script>
197
+ <% end %>
198
+ </def>
178
199
 
179
200
  <!-- Renders the name of the current context using a variety of methods.
180
201
 
@@ -445,7 +466,7 @@ Assuming the context is a blog post...
445
466
 
446
467
  truncate = 30 if truncate == true
447
468
  the_view = self.truncate(the_view, :length => truncate.to_i) if truncate
448
- the_view = the_view.strip
469
+ the_view = the_view.html_safe? ? the_view.strip.html_safe : the_view.strip
449
470
 
450
471
  if no_wrapper
451
472
  the_view
@@ -483,12 +504,14 @@ Assuming the context is a blog post...
483
504
  <!-- Renders localized `number_with_delimiter this`, or `format % this` if the `format` attribute is given -->
484
505
  <def tag="view" for="Numeric" attrs="format"><%= format ? format % this : number_with_delimiter(this) %></def>
485
506
 
486
- <!-- Renders `this` with HTML escaping and newlines replaced with `<br>` tags -->
507
+ <!-- If `this.html_safe?`, returns this unchanged. Otherwise renders `this` with HTML escaping and newlines replaced with `<br>` tags -->
487
508
  <def tag="view" for="string"><%=
488
- if !(this.class == String) && this.respond_to?(:to_html) # workaround for Maruku which adds String#to_html : (
509
+ if this.html_safe?
510
+ this
511
+ elsif !(this.class == String) && this.respond_to?(:to_html) # workaround for Maruku which adds String#to_html : (
489
512
  this.to_html(scope.xmldoctype)
490
513
  else
491
- h(this).gsub("\n", "<br#{scope.xmldoctype ? ' /' : ''}>")
514
+ h(this).gsub("\n", "<br#{scope.xmldoctype ? ' /' : ''}>").html_safe
492
515
  end
493
516
  %></def>
494
517
 
@@ -513,10 +536,9 @@ The label can be customised using the `label` attribute, e.g.
513
536
  <count:comments label="blog post comment"/> -> <span class="count">12 blog post comments</span>
514
537
 
515
538
  You can pass a summary attribute, which will generate a complete localized sentence. It allows 2 options:
516
- - boolean (e.g. `<count summary/>`): it will lookup the 'tags.count.default' key in the locale file.
517
- If the lookup fails, it will fallback to the english default sentences consistent with the count.
518
- - String (e.g. `<count summary="offer"/>`): it will lookup the 'tags.count.offer' key in the locale file.
519
- If the lookup fails, it will fallback to the english default sentences consistent with the count.
539
+
540
+ - boolean (e.g. `<count summary/>`): it will lookup the 'tags.count.default' key in the locale file. If the lookup fails, it will fallback to the english default sentences consistent with the count.
541
+ - String (e.g. `<count summary="offer"/>`): it will lookup the 'tags.count.offer' key in the locale file. If the lookup fails, it will fallback to the english default sentences consistent with the count.
520
542
 
521
543
  ### Examples
522
544
 
@@ -532,18 +554,22 @@ You can pass a summary attribute, which will generate a complete localized sente
532
554
  one: "Puoi scegliere solo una {{label}}"
533
555
  other: "Puoi scegliere tra {{count}} {{label}}"
534
556
 
535
- with :en locale and boolean summary (internal defaults)
557
+ with :en locale and boolean summary (internal defaults)
558
+
536
559
  <count:comments summary/> -> <span class="count">There is 1 Comment</span>
537
560
  <count:viewings summary/> -> <span class="count">There are 3 Viewings</span>
538
- (note: just add the locale english strings to use like the following examples)
539
561
 
540
- with :it locale and boolean summary (key "tags.count.default")
562
+ (note: just add the locale english strings to use like the following examples)
563
+
564
+ with :it locale and boolean summary (key "tags.count.default")
565
+
541
566
  <count:comments summary/>
542
567
  -> count => 0 -> <span class="count">Non ci sono Commenti</span>
543
568
  -> count => 1 -> <span class="count">C'è solo 1 Commento</span>
544
569
  -> count => 5 -> <span class="count">Ci sono 5 Commenti</span>
545
570
 
546
- with :it locale and summary="choice" (key "tags.count.choice")
571
+ with :it locale and summary="choice" (key "tags.count.choice")
572
+
547
573
  <count:comments summary="choice"/>
548
574
  -> count => 0 -> <span class="count">Non ci sono Commenti da scegliere</span>
549
575
  -> count => 1 -> <span class="count">Puoi scegliere solo 1 Commento</span>
@@ -596,6 +622,8 @@ You can pass a summary attribute, which will generate a complete localized sente
596
622
  end
597
623
  %></span></def>
598
624
 
625
+ <!-- Equivalent to `<you capitalize/>`-->
626
+ <def tag="You"><you merge capitalize/></def>
599
627
 
600
628
  <!-- Renders a `<link rel="Stylesheet" type="text/css">` to include the default stylesheet for the selected theme (select with `<set-theme>`). Included in the default pages.
601
629
  -->
@@ -611,9 +639,9 @@ You can pass a summary attribute, which will generate a complete localized sente
611
639
 
612
640
  The context should be a user object. If `this == current_user` the "you" form is rendered, otherwise the form with the user's name:
613
641
 
614
- - `<you have/> new mail` -> "you have new mail" or "Jim has new mail"
615
- - `<you are/> now an admin` -> "you are now an admin" or "Jim is now an admin"
616
- - `<you do/>n't want to go there` -> "you don't want to go there" or "Jim doesn't want to go there"
642
+ - `<you have/> new mail` -> "you have new mail" or "Jim has new mail"
643
+ - `<you are/> now an admin` -> "you are now an admin" or "Jim is now an admin"
644
+ - `<you do/>n't want to go there` -> "you don't want to go there" or "Jim doesn't want to go there"
617
645
 
618
646
  The tag is also localized in the namespaces "tags.you.current_user" and "tags.you.other_user".
619
647
  Each namespace can contain the legacy keys "have", "are", "do" used for the respective attributes,
@@ -636,15 +664,15 @@ that you add the keys in the correct namespaces.
636
664
  are: "{{name}} è"
637
665
  can: "{{name}} può"
638
666
 
639
- - `<you have/> un nuovo messaggio.` -> "Hai un nuovo messaggio." or "Jim ha un nuovo messaggio."
640
- - `Adesso <you are/> amministratore.` -> "Adesso sei amministratore." or "Adesso Jim è amministratore."
641
- - `<you can/> scrivere.` -> "Puoi scrivere." or "Jim può scrivere."
667
+ - `<you have/> un nuovo messaggio.` -> "Hai un nuovo messaggio." or "Jim ha un nuovo messaggio."
668
+ - `Adesso <you are/> amministratore.` -> "Adesso sei amministratore." or "Adesso Jim è amministratore."
669
+ - `<you can/> scrivere.` -> "Puoi scrivere." or "Jim può scrivere."
642
670
 
643
671
  (note: :name is added by default as an interpolable variable)
644
672
 
645
673
  ### Attributes
646
674
 
647
- - capitalize: the first letter of the resulting sentence will be capitalized
675
+ - capitalize: the first letter of the resulting sentence will be capitalized
648
676
 
649
677
 
650
678
  ### Additional Notes
@@ -666,36 +694,36 @@ The "titleize" attribute is deprecated: use "capitalize" instead.
666
694
  </if>
667
695
  <else>
668
696
  <do param="default">
669
- <%= s = t("tags.you.other_user.#{k || attributes.keys.first || 'default'}", :name=>this.name,
670
- :default=>"#{this.name} #{'has' if attributes[:have]}#{'is' if attributes[:are]}#{'does' if attributes[:do]}")
697
+ <%= s = t("tags.you.other_user.#{k || attributes.keys.first || 'default'}", :name=>name(:no_wrapper => true),
698
+ :default=>"#{name(:no_wrapper => true)} #{'has' if attributes[:have]}#{'is' if attributes[:are]}#{'does' if attributes[:do]}")
671
699
  (titleize||capitalize) ? s.sub(/^./){|c| c.upcase} : s %>
672
700
  </do>
673
701
  </else>
674
702
  </def>
675
703
 
676
- <!-- Equivalent to `<you capitalize/>`-->
677
- <def tag="You"><you merge capitalize/></def>
704
+ <!-- Equivalent to `<your ... capitalize/>`-->
705
+ <def tag="Your"><your merge capitalize/></def>
678
706
 
679
707
  <!-- Similar to `<you>`, but renders "Your" or "Fred's" or equivalent localized strings
680
708
 
681
709
  ### Attributes
682
710
 
683
- - capitalize: the first letter of the resulting sentence will be capitalized
684
- - count: used in pluralization. If omitted it will be set to 1.
685
- - key: used to lookup the translation in the locale file. It allows 3 different options:
711
+ - capitalize: the first letter of the resulting sentence will be capitalized
712
+ - count: used in pluralization. If omitted it will be set to 1.
713
+ - key: used to lookup the translation in the locale file. It allows 3 different options:
686
714
  - single key like 'message': simple translation in 'tags.your.message.current_user'
687
715
  or 'tags.your.message.other_user'
688
716
  - composite key like 'any.namespace.message': translation as for the previous case, but it will
689
- translate also the 'any.namespace.message' and will interpolate the variable <key> (in this case :message)
717
+ translate also the 'any.namespace.message' and will interpolate the variable `<key>` (in this case :message)
690
718
  in the translation
691
719
  - when key is omitted it will be set to "default" and will do the translation with that key.
692
720
  Pass other meaningful attributes to achieve a dynamic usage
693
- - any other attribute passed to the tag will be used as a variable for interpolation
721
+ - any other attribute passed to the tag will be used as a variable for interpolation
694
722
 
695
723
  Notes
696
724
 
697
- - The :name variable is added by default as an interpolable variable
698
- - If no translation is found an automatic (only english) default is generated:
725
+ - The :name variable is added by default as an interpolable variable
726
+ - If no translation is found an automatic (only english) default is generated:
699
727
  the Your/Jim's string, joined to the tag content.
700
728
  If you pass an explicit 'default' attribute you will override the automatic default.
701
729
 
@@ -718,24 +746,25 @@ Notes
718
746
  other: "Tue {{entry}}"
719
747
  other_user: "{{entry}} di {{name}}"
720
748
 
721
- - `<your key="message" count=>"&messages.count"/>`
722
- -> count => 1 -> "Tuo Messaggio" or "Messaggio di Jim"
723
- -> count => 5 -> "Tuoi Messaggi" or "Messaggi di Jim"
749
+ - `<your key="message" count=>"&messages.count"/>`:
724
750
 
725
- - `<your key="activerecord.models.entry" count=>"&this.entries.count"/>`
726
- -> count => 1 -> "Tua Entrata" or "Entrata di Jim"
727
- -> count => 5 -> "Tue Entrate" or "Entrate di Jim"
751
+ - count => 1: "Tuo Messaggio" or "Messaggio di Jim"
752
+ - count => 5: "Tuoi Messaggi" or "Messaggi di Jim"
728
753
 
729
- - `<your>Posts</your>` -> "your Posts" or "Jim's Posts"
730
- -->
754
+ - `<your key="activerecord.models.entry" count=>"&this.entries.count"/>`:
755
+
756
+ - count => 1: "Tua Entrata" or "Entrata di Jim"
757
+ - count => 5: "Tue Entrate" or "Entrate di Jim"
731
758
 
759
+ - `<your>Posts</your>`: "your Posts" or "Jim's Posts"
760
+ -->
732
761
  <def tag="your" attrs="key, capitalize"><%=
733
762
  key ||= 'default'
734
763
  # prepare symbolized attributes for merging
735
764
  attrs = {}
736
765
  attributes.each_pair{|k,v| attrs[k.to_sym] = v}
737
766
  d = "#{your_default} #{all_parameters.default}"
738
- options = {:default=>[d], :count=>(attrs[:count]||1), :name=>this.name}
767
+ options = {:default=>[d], :count=>(attrs[:count]||1), :name=>name(:no_wrapper => true)}
739
768
  your_key = key.split('.').last
740
769
  unless key.eql?(your_key) || attrs.has_key?(your_key.to_sym)
741
770
  options[your_key.to_sym] = t(key, :count=>options[:count], :default=>your_key.titleize)
@@ -750,8 +779,10 @@ Notes
750
779
  capitalize ? s.sub(/^./){|c| c.upcase} : s
751
780
  %></def>
752
781
 
753
- <!-- Equivalent to `<your ... capitalize/>`-->
754
- <def tag="Your"><your merge capitalize/></def>
782
+ <!-- Capitalised version of `<a-or-an>` -->
783
+ <def tag="A-or-An" attrs="word"><%=
784
+ (word =~ /^[aeiou]/i ? "An " : "A ") + word
785
+ %></def>
755
786
 
756
787
  <!-- Deprecated. It's harder than you think to do this (e.g. an umbrealla, an user)
757
788
  -->
@@ -760,12 +791,6 @@ Notes
760
791
  %></def>
761
792
 
762
793
 
763
- <!-- Capitalised version of `<a-or-an>` -->
764
- <def tag="A-or-An" attrs="word"><%=
765
- (word =~ /^[aeiou]/i ? "An " : "A ") + word
766
- %></def>
767
-
768
-
769
794
  <!-- Renders a collection of string joined with ", ", or some other string passed in the `join` attribute -->
770
795
  <def tag="comma-list" attrs="join"><%= this.join(join || ", ") %></def>
771
796
 
@@ -81,25 +81,45 @@ This tag does not sanitize HTML; this is provided by HtmlString before saving to
81
81
 
82
82
  ### Attributes
83
83
 
84
- - include-none: Should the menu include a "none" option (true/false). Defaults: false, or true if the association is nil at render-time.
85
-
86
- - blank-message: The text for the "none" option. Default: "(No Product)" (or whatever the model name is)
87
-
88
- - sort: Sort the options (true/false)? Default: false
89
-
90
- - update: one or more DOM ID's (comma separated string or an array) to be updated as part of the ajax call.
84
+ - `include-none` - whether to include a 'none' option (i.e. set the foreign key to null). If this value is not supplied, the default is "true" if the current value is nil; otherwise the default is "false". One implication of this is that the default may change when the form is re-rendered due to a validation failure. Setting this value explicitly is recommended.
85
+ - `blank-message` - the message for the 'none' option. Defaults to "(No `<model-name>`)", e.g. "(No Product)"
86
+ - `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` (subject to `limit`)
87
+ - `sort` - whether to sort the array of options. Defaults to no sorting.
88
+ - `limit` - if `options` is not specified, this limits the number of records. Default: 100
89
+ - `text_method` - The method to call on each record to get the text for the option. Multiple methods are supported ie "institution.name"
90
+ - `update` - one or more DOM ID's (comma separated string or an array) to be updated as part of the ajax call.
91
91
 
92
92
  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.
93
93
 
94
94
  -->
95
- <def tag="select-one-editor" attrs="include-none, blank-message, sort, update, options"><%
95
+ <def tag="select-one-editor" attrs="include-none, blank-message, options, sort, limit, text-method, update"><%
96
96
  raise Hobo::Error.new("Not allowed to edit") unless can_edit?
97
97
  blank_message ||= ht("#{this_type.name.to_s.underscore}.select_one_editor.blank_message", :count => 0, :default => "(No #{this_type.name.to_s.titleize})")
98
- options ||= this_field_reflection.klass.all.select {|x| can_view?(x)}.map {|x|
99
- [ name(:with => x, :no_wrapper => true), x.id ]
100
- }
101
- options = options.sort if sort
102
- options.insert(0, [blank_message, ""]) if this.nil? || include_none
98
+ limit ||= 100
99
+
100
+ if options.blank? || !options.first.respond_to?(:first)
101
+ options ||= begin
102
+ conditions = ActiveRecord::Associations::BelongsToAssociation.new(this_parent, this_field_reflection).send(:conditions)
103
+ order = this_field_reflection.klass.default_order
104
+ this_field_reflection.klass.all(:conditions => conditions, :limit => limit, :order => order).select {|x| can_view?(x)}
105
+ end
106
+
107
+ id_method = this_field_reflection.options[:primary_key] || this_field_reflection.klass.primary_key
108
+ if text_method.nil?
109
+ select_options = options.map { |x| [name(:with => x, :no_wrapper => true), x.send(id_method)] }
110
+ else
111
+ select_options = options.map do |x|
112
+ [ text_method.split(".").inject(x) { |v, method| v.send(method) },
113
+ x.send(id_method) ]
114
+ end
115
+ end
116
+ else
117
+ # handle the old style, where options could be passed an options_for_select style array
118
+ select_options = options
119
+ end
120
+ select_options = select_options.sort if sort
121
+ select_options.insert(0, [blank_message, ""]) if include_none || (this.nil? && include_none != false)
122
+
103
123
  f = ajax_updater(object_url(this_parent, :method => :put),
104
124
  update,
105
125
  :method => "put",
@@ -108,7 +128,7 @@ This tag does not sanitize HTML; this is provided by HtmlString before saving to
108
128
  } })
109
129
  %>
110
130
  <select onchange="#{f}" merge-attrs>
111
- <%= options_for_select(options, this ? this.id : "") %>
131
+ <%= options_for_select(select_options, this ? this.send(id_method) : "") %>
112
132
  </select>
113
133
  </def>
114
134
 
@@ -216,7 +236,7 @@ This tag does not sanitize HTML; this is provided by HtmlString before saving to
216
236
  -->
217
237
  <def tag="integer-select-editor" attrs="options, min, max, update, nil-option, message">
218
238
  <% options ||= (min.to_i..max.to_i).to_a %>
219
- <select class="integer editor #{'update:' + comma_split(update).join(':') unless update.blank?} #{model_id_class(this_parent, this_field)}"
239
+ <select class="integer editor #{update_elements_class(update)} #{model_id_class(this_parent, this_field)}"
220
240
  merge-attrs="&message ? attributes.merge(:hobo_message => message) : attributes">
221
241
  <if test="&this.nil?"><option value=""><%= nil_option || "Choose a value" %></option></if>
222
242
  <%= options_for_select(options.*.to_s, this.to_s) %>