hobo 1.3.0.pre31 → 1.3.0

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