hobo 1.1.0 → 1.3.0.RC

Sign up to get free protection for your applications and to get access to all the features.
Files changed (278) hide show
  1. data/CHANGES.txt +238 -287
  2. data/Rakefile +14 -31
  3. data/VERSION +1 -0
  4. data/{lib/hobo → app/controllers}/dev_controller.rb +6 -6
  5. data/app/controllers/dryml_support_controller.rb +13 -0
  6. data/{rails_generators/hobo_front_controller/templates → app/views/dev}/summary.dryml +7 -10
  7. data/bin/hobo +12 -144
  8. data/config/initializers/inflections.rb +3 -0
  9. data/config/routes.rb +6 -0
  10. data/{doctest → doctests}/hobo/lifecycles.rdoctest +40 -59
  11. data/{doctest → doctests/hobo}/model.rdoctest +33 -40
  12. data/{doctest → doctests/hobo}/multi_model_forms.rdoctest +15 -19
  13. data/{doctest → doctests/hobo}/scopes.rdoctest +27 -41
  14. data/doctests/prepare_testapp.rb +8 -0
  15. data/hobo.gemspec +26 -199
  16. data/lib/generators/hobo/activation_email.rb +11 -0
  17. data/lib/generators/hobo/admin_subsite/admin_subsite_generator.rb +35 -0
  18. data/lib/generators/hobo/admin_subsite/templates/admin.css +20 -0
  19. data/lib/generators/hobo/admin_subsite/templates/admin_tag_injection.erb +10 -0
  20. data/{rails_generators/hobo_admin_site → lib/generators/hobo/admin_subsite}/templates/application.dryml +0 -0
  21. data/{rails_generators/hobo_admin_site/templates/controller.rb → lib/generators/hobo/admin_subsite/templates/controller.rb.erb} +5 -5
  22. data/lib/generators/hobo/admin_subsite/templates/users_index.dryml +7 -0
  23. data/lib/generators/hobo/assets/assets_generator.rb +18 -0
  24. data/{rails_generators/hobo → lib/generators/hobo/assets}/templates/application.css +0 -0
  25. data/lib/generators/hobo/assets/templates/application.dryml.erb +9 -0
  26. data/{rails_generators/hobo → lib/generators/hobo/assets}/templates/dryml-support.js +2 -2
  27. data/lib/generators/hobo/assets/templates/dryml_taglibs_initializer.rb +1 -0
  28. data/lib/generators/hobo/assets/templates/en_injection.yml +19 -0
  29. data/{rails_generators/hobo → lib/generators/hobo/assets}/templates/guest.rb +1 -1
  30. data/lib/generators/hobo/controller.rb +35 -0
  31. data/lib/generators/hobo/controller/controller_generator.rb +6 -0
  32. data/lib/generators/hobo/controller/templates/controller.rb.erb +7 -0
  33. data/{rails_generators/hobo_front_controller → lib/generators/hobo/front_controller}/USAGE +0 -0
  34. data/lib/generators/hobo/front_controller/front_controller_generator.rb +55 -0
  35. data/{rails_generators/hobo_front_controller/templates/controller.rb → lib/generators/hobo/front_controller/templates/controller.rb.erb} +0 -0
  36. data/{rails_generators/hobo_front_controller → lib/generators/hobo/front_controller}/templates/index.dryml +6 -12
  37. data/lib/generators/hobo/i18n/i18n_generator.rb +35 -0
  38. data/lib/generators/hobo/i18n/templates/app.de.yml +30 -0
  39. data/lib/generators/hobo/i18n/templates/app.en.yml +25 -0
  40. data/lib/generators/hobo/i18n/templates/app.es.yml +31 -0
  41. data/lib/generators/hobo/i18n/templates/app.fr.yml +26 -0
  42. data/lib/generators/hobo/i18n/templates/app.it.yml +28 -0
  43. data/lib/generators/hobo/i18n/templates/app.pt-PT.yml +25 -0
  44. data/lib/generators/hobo/i18n/templates/app.ru.yml +24 -0
  45. data/lib/generators/hobo/i18n/templates/hobo.de.yml +204 -0
  46. data/lib/generators/hobo/i18n/templates/hobo.en.yml +195 -0
  47. data/lib/generators/hobo/i18n/templates/hobo.es.yml +202 -0
  48. data/lib/generators/hobo/i18n/templates/hobo.fr.yml +195 -0
  49. data/lib/generators/hobo/i18n/templates/hobo.it.yml +202 -0
  50. data/lib/generators/hobo/i18n/templates/hobo.pt-PT.yml +196 -0
  51. data/lib/generators/hobo/i18n/templates/hobo.ru.yml +200 -0
  52. data/lib/generators/hobo/invite_only.rb +18 -0
  53. data/lib/generators/hobo/model/USAGE +19 -0
  54. data/lib/generators/hobo/model/model_generator.rb +11 -0
  55. data/{rails_generators/hobo_model/templates/model.rb → lib/generators/hobo/model/templates/model_injection.rb.erb} +0 -2
  56. data/{rails_generators/hobo_rapid → lib/generators/hobo/rapid}/USAGE +0 -0
  57. data/lib/generators/hobo/rapid/rapid_generator.rb +24 -0
  58. data/{rails_generators/hobo_rapid → lib/generators/hobo/rapid}/templates/IE7.js +0 -0
  59. data/{rails_generators/hobo_rapid → lib/generators/hobo/rapid}/templates/blank.gif +0 -0
  60. data/{rails_generators/hobo_rapid → lib/generators/hobo/rapid}/templates/hobo-rapid.css +0 -0
  61. data/{rails_generators/hobo_rapid → lib/generators/hobo/rapid}/templates/hobo-rapid.js +65 -65
  62. data/{rails_generators/hobo_rapid → lib/generators/hobo/rapid}/templates/ie7-recalc.js +21 -21
  63. data/{rails_generators/hobo_rapid → lib/generators/hobo/rapid}/templates/lowpro.js +31 -31
  64. data/{rails_generators/hobo_rapid → lib/generators/hobo/rapid}/templates/reset.css +1 -1
  65. data/lib/generators/hobo/rapid/templates/themes/clean-sidemenu/public/images/100-ACD3E6-DBE1E5-H.png +0 -0
  66. data/lib/generators/hobo/rapid/templates/themes/clean-sidemenu/public/images/100-DBE1E5-FCFEF5-H.png +0 -0
  67. data/lib/generators/hobo/rapid/templates/themes/clean-sidemenu/public/images/300-3B5F87-ACD3E6-H.png +0 -0
  68. data/{rails_generators/hobo_rapid/templates/themes/clean → lib/generators/hobo/rapid/templates/themes/clean-sidemenu}/public/images/spinner.gif +0 -0
  69. data/lib/generators/hobo/rapid/templates/themes/clean-sidemenu/public/stylesheets/clean-sidemenu.css +81 -0
  70. data/lib/generators/hobo/rapid/templates/themes/clean-sidemenu/views/clean-sidemenu.dryml +30 -0
  71. data/{rails_generators/hobo_rapid → lib/generators/hobo/rapid}/templates/themes/clean/public/images/101-3B5F87-ACD3E6.png +0 -0
  72. data/{rails_generators/hobo_rapid → lib/generators/hobo/rapid}/templates/themes/clean/public/images/30-3E547A-242E42.png +0 -0
  73. data/{rails_generators/hobo_rapid → lib/generators/hobo/rapid}/templates/themes/clean/public/images/30-DBE1E5-FCFEF5.png +0 -0
  74. data/{rails_generators/hobo_rapid → lib/generators/hobo/rapid}/templates/themes/clean/public/images/300-ACD3E6-fff.png +0 -0
  75. data/{rails_generators/hobo_rapid → lib/generators/hobo/rapid}/templates/themes/clean/public/images/50-ACD3E6-fff.png +0 -0
  76. data/{rails_generators/hobo_rapid → lib/generators/hobo/rapid}/templates/themes/clean/public/images/fieldbg.gif +0 -0
  77. data/{rails_generators/hobo_rapid → lib/generators/hobo/rapid}/templates/themes/clean/public/images/pencil.png +0 -0
  78. data/{rails_generators/hobo_rapid → lib/generators/hobo/rapid}/templates/themes/clean/public/images/small_close.png +0 -0
  79. data/lib/generators/hobo/rapid/templates/themes/clean/public/images/spinner.gif +0 -0
  80. data/{rails_generators/hobo_rapid → lib/generators/hobo/rapid}/templates/themes/clean/public/stylesheets/clean.css +16 -16
  81. data/{rails_generators/hobo_rapid → lib/generators/hobo/rapid}/templates/themes/clean/public/stylesheets/rapid-ui.css +3 -3
  82. data/{rails_generators/hobo_rapid → lib/generators/hobo/rapid}/templates/themes/clean/views/clean.dryml +1 -1
  83. data/lib/generators/hobo/resource/resource_generator.rb +23 -0
  84. data/lib/generators/hobo/routes/USAGE +9 -0
  85. data/lib/generators/hobo/routes/router.rb +115 -0
  86. data/lib/generators/hobo/routes/routes_generator.rb +40 -0
  87. data/lib/generators/hobo/routes/templates/hobo_routes.rb.erb +33 -0
  88. data/lib/generators/hobo/setup_wizard/USAGE +4 -0
  89. data/lib/generators/hobo/setup_wizard/setup_wizard_generator.rb +274 -0
  90. data/lib/generators/hobo/subsite.rb +48 -0
  91. data/lib/generators/hobo/subsite/subsite_generator.rb +16 -0
  92. data/{rails_generators/hobo_subsite → lib/generators/hobo/subsite}/templates/application.dryml +0 -0
  93. data/{rails_generators/hobo_subsite/templates/controller.rb → lib/generators/hobo/subsite/templates/controller.rb.erb} +1 -1
  94. data/lib/generators/hobo/subsite_taglib/subsite_taglib_generator.rb +16 -0
  95. data/{rails_generators/hobo_admin_site/templates/site_taglib.dryml → lib/generators/hobo/subsite_taglib/templates/taglib.dryml.erb} +9 -10
  96. data/lib/generators/hobo/taglib.rb +12 -0
  97. data/lib/generators/hobo/test_framework/test_framework_generator.rb +72 -0
  98. data/lib/generators/hobo/test_options.rb +19 -0
  99. data/{rails_generators/hobo_user_controller → lib/generators/hobo/user_controller}/templates/accept_invitation.dryml +0 -0
  100. data/lib/generators/hobo/user_controller/templates/controller.rb.erb +31 -0
  101. data/lib/generators/hobo/user_controller/user_controller_generator.rb +25 -0
  102. data/lib/generators/hobo/user_mailer/templates/activation.erb +9 -0
  103. data/{rails_generators/hobo_user_model → lib/generators/hobo/user_mailer}/templates/forgot_password.erb +2 -2
  104. data/{rails_generators/hobo_user_model → lib/generators/hobo/user_mailer}/templates/invite.erb +2 -2
  105. data/lib/generators/hobo/user_mailer/templates/mailer.rb.erb +25 -0
  106. data/lib/generators/hobo/user_mailer/user_mailer_generator.rb +33 -0
  107. data/lib/generators/hobo/user_model/USAGE +12 -0
  108. data/{rails_generators/hobo_user_model/templates/model.rb → lib/generators/hobo/user_model/templates/model_injection.rb.erb} +40 -20
  109. data/lib/generators/hobo/user_model/user_model_generator.rb +23 -0
  110. data/lib/generators/hobo/user_resource/user_resource_generator.rb +27 -0
  111. data/lib/hobo.rb +29 -129
  112. data/lib/hobo/controller.rb +37 -47
  113. data/lib/hobo/controller/authentication_support.rb +109 -0
  114. data/lib/hobo/{model_controller.rb → controller/model.rb} +71 -79
  115. data/lib/hobo/{user_controller.rb → controller/user.rb} +59 -49
  116. data/lib/hobo/engine.rb +80 -0
  117. data/lib/hobo/extensions/action_controller/hobo_methods.rb +44 -0
  118. data/lib/hobo/extensions/action_mailer/helper.rb +38 -0
  119. data/lib/{action_view_extensions/helpers → hobo/extensions/action_view}/tag_helper.rb +5 -2
  120. data/lib/hobo/extensions/action_view/translation_helper.rb +25 -0
  121. data/lib/hobo/extensions/active_model/name.rb +16 -0
  122. data/lib/hobo/extensions/active_model/translation.rb +35 -0
  123. data/lib/{active_record/association_collection.rb → hobo/extensions/active_record/associations/collection.rb} +8 -17
  124. data/lib/{active_record/association_proxy.rb → hobo/extensions/active_record/associations/proxy.rb} +6 -7
  125. data/lib/hobo/extensions/active_record/associations/reflection.rb +23 -0
  126. data/lib/hobo/extensions/active_record/associations/scope.rb +35 -0
  127. data/lib/hobo/extensions/active_record/hobo_methods.rb +11 -0
  128. data/lib/hobo/extensions/active_record/permissions.rb +159 -0
  129. data/lib/hobo/extensions/active_record/relation_with_origin.rb +28 -0
  130. data/lib/hobo/extensions/array.rb +27 -0
  131. data/lib/hobo/extensions/enumerable.rb +12 -0
  132. data/lib/hobo/extensions/i18n.rb +17 -0
  133. data/lib/hobo/{hobo_helper.rb → helper.rb} +58 -31
  134. data/lib/hobo/helper/translations.rb +54 -0
  135. data/lib/hobo/helper/translations/normalizer.rb +39 -0
  136. data/lib/hobo/model.rb +61 -95
  137. data/lib/hobo/model/accessible_associations.rb +178 -0
  138. data/lib/hobo/{find_for.rb → model/find_for.rb} +17 -17
  139. data/lib/hobo/model/guest.rb +25 -0
  140. data/lib/hobo/model/include_in_save.rb +55 -0
  141. data/lib/hobo/model/lifecycles.rb +119 -0
  142. data/lib/hobo/model/lifecycles/actions.rb +146 -0
  143. data/lib/hobo/model/lifecycles/creator.rb +74 -0
  144. data/lib/hobo/model/lifecycles/lifecycle.rb +243 -0
  145. data/lib/hobo/model/lifecycles/state.rb +22 -0
  146. data/lib/hobo/model/lifecycles/transition.rb +70 -0
  147. data/lib/hobo/model/permissions.rb +448 -0
  148. data/lib/hobo/model/scopes.rb +38 -0
  149. data/lib/hobo/model/scopes/apply_scopes.rb +21 -0
  150. data/lib/hobo/model/scopes/automatic_scopes.rb +428 -0
  151. data/lib/hobo/model/user_base.rb +184 -0
  152. data/lib/hobo/model/view_hints.rb +123 -0
  153. data/{rapid_generators → lib/hobo/rapid/generators}/rapid/cards.dryml.erb +2 -2
  154. data/{rapid_generators → lib/hobo/rapid/generators}/rapid/forms.dryml.erb +3 -3
  155. data/{rapid_generators → lib/hobo/rapid/generators}/rapid/pages.dryml.erb +38 -51
  156. data/lib/hobo/rapid/helper.rb +166 -0
  157. data/{taglibs → lib/hobo/rapid/taglibs}/rapid.dryml +6 -5
  158. data/{taglibs → lib/hobo/rapid/taglibs}/rapid_core.dryml +286 -118
  159. data/{taglibs → lib/hobo/rapid/taglibs}/rapid_document_tags.dryml +2 -2
  160. data/{taglibs → lib/hobo/rapid/taglibs}/rapid_editing.dryml +45 -45
  161. data/{taglibs → lib/hobo/rapid/taglibs}/rapid_forms.dryml +190 -158
  162. data/{taglibs → lib/hobo/rapid/taglibs}/rapid_generics.dryml +12 -9
  163. data/lib/hobo/rapid/taglibs/rapid_i18n.dryml +107 -0
  164. data/{taglibs → lib/hobo/rapid/taglibs}/rapid_lifecycles.dryml +7 -7
  165. data/{taglibs → lib/hobo/rapid/taglibs}/rapid_navigation.dryml +15 -15
  166. data/{taglibs → lib/hobo/rapid/taglibs}/rapid_pages.dryml +37 -36
  167. data/{taglibs → lib/hobo/rapid/taglibs}/rapid_plus.dryml +107 -43
  168. data/{taglibs → lib/hobo/rapid/taglibs}/rapid_summary.dryml +28 -57
  169. data/{taglibs → lib/hobo/rapid/taglibs}/rapid_support.dryml +9 -9
  170. data/{taglibs → lib/hobo/rapid/taglibs}/rapid_user_pages.dryml +41 -40
  171. data/lib/hobo/routes.rb +31 -0
  172. data/{doctest → test/doctest}/hobo/hobo_helper.rdoctest +11 -11
  173. data/test/irt/generators/admin_subsite.irt +27 -0
  174. data/test/irt/generators/assets.irt +16 -0
  175. data/test/irt/generators/controller.irt +6 -0
  176. data/test/irt/generators/front_controller.irt +30 -0
  177. data/test/irt/generators/helper.rb +31 -0
  178. data/test/irt/generators/model.irt +28 -0
  179. data/test/irt/generators/partials/_account_user_model_tests.rb +21 -0
  180. data/test/irt/generators/partials/_accounts_users_controller_tests.rb +15 -0
  181. data/test/irt/generators/partials/_default_user_model_tests.rb +21 -0
  182. data/test/irt/generators/partials/_default_users_controller_tests.rb +16 -0
  183. data/test/irt/generators/partials/_house_controller_tests.rb +15 -0
  184. data/test/irt/generators/partials/_house_model_tests.rb +18 -0
  185. data/test/irt/generators/partials/_subsite_taglib_admin.rb +4 -0
  186. data/test/irt/generators/partials/_subsite_taglib_admin_invite_only.rb +3 -0
  187. data/test/irt/generators/partials/_subsite_taglib_noopt.rb +4 -0
  188. data/test/irt/generators/partials/_subsite_taglib_variables.rb +27 -0
  189. data/test/irt/generators/partials/_user_mailer_tests.rb +19 -0
  190. data/test/irt/generators/rapid.irt +29 -0
  191. data/test/irt/generators/resource.irt +8 -0
  192. data/test/irt/generators/subsite.irt +52 -0
  193. data/test/irt/generators/subsite_taglib.irt +23 -0
  194. data/test/irt/generators/test_framework.irt +62 -0
  195. data/test/irt/generators/user_controller.irt +10 -0
  196. data/test/irt/generators/user_mailer.irt +20 -0
  197. data/test/irt/generators/user_model.irt +10 -0
  198. data/test/irt/generators/user_resource.irt +14 -0
  199. data/test/irt/readme.txt +7 -0
  200. data/test/permissions/models/models.rb +27 -24
  201. data/test/permissions/test_permissions.rb +104 -104
  202. metadata +239 -217
  203. data/lib/active_record/association_reflection.rb +0 -20
  204. data/lib/active_record/viewhints_validations_interceptor.rb +0 -9
  205. data/lib/hobo/accessible_associations.rb +0 -183
  206. data/lib/hobo/authentication_support.rb +0 -131
  207. data/lib/hobo/generator.rb +0 -26
  208. data/lib/hobo/guest.rb +0 -25
  209. data/lib/hobo/include_in_save.rb +0 -55
  210. data/lib/hobo/lifecycles.rb +0 -137
  211. data/lib/hobo/lifecycles/actions.rb +0 -142
  212. data/lib/hobo/lifecycles/creator.rb +0 -72
  213. data/lib/hobo/lifecycles/lifecycle.rb +0 -249
  214. data/lib/hobo/lifecycles/state.rb +0 -22
  215. data/lib/hobo/lifecycles/transition.rb +0 -70
  216. data/lib/hobo/model_router.rb +0 -290
  217. data/lib/hobo/permissions.rb +0 -451
  218. data/lib/hobo/permissions/associations.rb +0 -175
  219. data/lib/hobo/rapid_helper.rb +0 -157
  220. data/lib/hobo/scopes.rb +0 -43
  221. data/lib/hobo/scopes/apply_scopes.rb +0 -23
  222. data/lib/hobo/scopes/association_proxy_extensions.rb +0 -62
  223. data/lib/hobo/scopes/automatic_scopes.rb +0 -421
  224. data/lib/hobo/scopes/named_scope_extensions.rb +0 -39
  225. data/lib/hobo/tasks/rails.rb +0 -4
  226. data/lib/hobo/translations.rb +0 -93
  227. data/lib/hobo/undefined_access_error.rb +0 -5
  228. data/lib/hobo/user.rb +0 -182
  229. data/lib/hobo/view_hints.rb +0 -115
  230. data/rails/init.rb +0 -10
  231. data/rails_generators/hobo/USAGE +0 -4
  232. data/rails_generators/hobo/hobo_generator.rb +0 -53
  233. data/rails_generators/hobo/templates/application.dryml +0 -1
  234. data/rails_generators/hobo/templates/initializer.rb +0 -2
  235. data/rails_generators/hobo_admin_site/USAGE +0 -16
  236. data/rails_generators/hobo_admin_site/hobo_admin_site_generator.rb +0 -45
  237. data/rails_generators/hobo_admin_site/templates/admin.css +0 -2
  238. data/rails_generators/hobo_admin_site/templates/users_index.dryml +0 -5
  239. data/rails_generators/hobo_front_controller/hobo_front_controller_generator.rb +0 -95
  240. data/rails_generators/hobo_front_controller/templates/functional_test.rb +0 -8
  241. data/rails_generators/hobo_front_controller/templates/helper.rb +0 -2
  242. data/rails_generators/hobo_model/USAGE +0 -25
  243. data/rails_generators/hobo_model/hobo_model_generator.rb +0 -43
  244. data/rails_generators/hobo_model/templates/fixtures.yml +0 -6
  245. data/rails_generators/hobo_model/templates/hints.rb +0 -7
  246. data/rails_generators/hobo_model/templates/unit_test.rb +0 -8
  247. data/rails_generators/hobo_model_controller/USAGE +0 -30
  248. data/rails_generators/hobo_model_controller/hobo_model_controller_generator.rb +0 -46
  249. data/rails_generators/hobo_model_controller/templates/controller.rb +0 -7
  250. data/rails_generators/hobo_model_controller/templates/functional_test.rb +0 -8
  251. data/rails_generators/hobo_model_controller/templates/helper.rb +0 -2
  252. data/rails_generators/hobo_model_resource/USAGE +0 -38
  253. data/rails_generators/hobo_model_resource/hobo_model_resource_generator.rb +0 -73
  254. data/rails_generators/hobo_model_resource/templates/controller.rb +0 -7
  255. data/rails_generators/hobo_model_resource/templates/functional_test.rb +0 -8
  256. data/rails_generators/hobo_model_resource/templates/helper.rb +0 -2
  257. data/rails_generators/hobo_rapid/hobo_rapid_generator.rb +0 -94
  258. data/rails_generators/hobo_subsite/USAGE +0 -16
  259. data/rails_generators/hobo_subsite/hobo_subsite_generator.rb +0 -73
  260. data/rails_generators/hobo_subsite/templates/site_taglib.dryml +0 -13
  261. data/rails_generators/hobo_user_controller/USAGE +0 -34
  262. data/rails_generators/hobo_user_controller/hobo_user_controller_generator.rb +0 -65
  263. data/rails_generators/hobo_user_controller/templates/controller.rb +0 -29
  264. data/rails_generators/hobo_user_controller/templates/functional_test.rb +0 -8
  265. data/rails_generators/hobo_user_controller/templates/helper.rb +0 -2
  266. data/rails_generators/hobo_user_model/USAGE +0 -16
  267. data/rails_generators/hobo_user_model/hobo_user_model_generator.rb +0 -46
  268. data/rails_generators/hobo_user_model/templates/fixtures.yml +0 -19
  269. data/rails_generators/hobo_user_model/templates/mailer.rb +0 -29
  270. data/rails_generators/hobo_user_model/templates/unit_test.rb +0 -8
  271. data/script/destroy +0 -14
  272. data/script/generate +0 -14
  273. data/taglibs/rapid_translations.dryml +0 -36
  274. data/tasks/environments.rake +0 -19
  275. data/tasks/hobo_tasks.rake +0 -58
  276. data/test/generators/test_generator_helper.rb +0 -29
  277. data/test/generators/test_helper.rb +0 -1
  278. data/test/generators/test_hobo_model_controller_generator.rb +0 -56
@@ -0,0 +1,166 @@
1
+ module Hobo
2
+ module Rapid
3
+ module Helper
4
+
5
+ def comma_split(x)
6
+ case x
7
+ when nil
8
+ []
9
+ when String
10
+ x.strip.split(/\s*,\s*/)
11
+ else
12
+ x.compact.map{|e| comma_split(e)}.flatten
13
+ end
14
+ end
15
+
16
+ def rapid_build_callbacks(options)
17
+ callbacks = {}
18
+ options.each do |callback, code|
19
+ if AJAX_CALLBACKS.include?(callback.to_sym)
20
+ name = 'on' + callback.to_s.capitalize
21
+ callbacks[name] = "function(request){#{code}}"
22
+ end
23
+ end
24
+ callbacks
25
+ end
26
+
27
+
28
+ def options_for_hobo_ajax(options)
29
+ js_options = rapid_build_callbacks(options)
30
+
31
+ js_options['asynchronous'] = false if options[:type] == :synchronous
32
+ js_options['method'] = method_option_to_s(options[:method]) if options[:method]
33
+ js_options['evalScripts'] = false if options[:script] == false
34
+ js_options['form'] = options[:form] if options[:form]
35
+ js_options['params'] = make_params_js(options[:params]) if options[:params]
36
+ js_options['resultUpdate'] = js_result_updates(options[:result_update]) if options[:result_update]
37
+ js_options['resetForm'] = options[:reset_form] if options.has_key?(:reset_form)
38
+ js_options['refocusForm'] = options[:refocus_form] if options.has_key?(:refocus_form)
39
+ js_options['spinnerNextTo'] = js_str(options[:spinner_next_to]) if options.has_key?(:spinner_next_to)
40
+ js_options['message'] = js_str(options[:message]) if options[:message]
41
+
42
+ js_options.empty? ? nil : options_for_javascript(js_options)
43
+ end
44
+
45
+
46
+ def js_updates(updates)
47
+ return '[]' unless updates
48
+ updates = [updates] unless updates.is_a? Array
49
+ updates = comma_split(updates).map do |u|
50
+ if u.to_s == "self"
51
+ "Hobo.partFor(this)"
52
+ else
53
+ js_str(u)
54
+ end
55
+ end
56
+ "[#{updates * ', '}]"
57
+ end
58
+
59
+
60
+ def js_result_updates(updates)
61
+ return '[]' unless updates
62
+ updates = [updates] unless updates.is_a? Array
63
+ pairs = comma_split(updates).map &it.split(/\s*=\s*/)
64
+ '[' + pairs.map{|p| "{id: #{js_str(p[0])}, result: #{js_str(p[1])}}"}.join(", ") + ']'
65
+ end
66
+
67
+
68
+ def ajax_updater(url_or_form, update, options={})
69
+ options ||= {}
70
+ options = options.symbolize_keys
71
+
72
+ target = if url_or_form == :post_form
73
+ target = "this"
74
+ else
75
+ js_str(url_or_form)
76
+ end
77
+ js_options = options_for_hobo_ajax(options)
78
+ args = [target, js_updates(update), js_options].compact
79
+
80
+ confirm = options.delete(:confirm)
81
+
82
+ func = "Hobo.ajaxRequest(#{args * ', '})"
83
+ if confirm
84
+ "if (confirm(#{js_str(confirm)})) { #{func} }"
85
+ else
86
+ func
87
+ end
88
+ end
89
+
90
+
91
+ def a_or_an(word)
92
+ if word =~ /^[aeiouh]/
93
+ "an #{word}"
94
+ else
95
+ "a #{word}"
96
+ end
97
+ end
98
+
99
+
100
+ def no_break(s)
101
+ s = new_context { yield } if block_given?
102
+ s.gsub(' ', ' ')
103
+ end
104
+
105
+ # returns the number of items in the collection. See LH #889
106
+ def collection_count
107
+ this.try.to_int || this.try.total_entries || (this.try.loaded? && this.try.length) || this.try.count || this.try.length
108
+ end
109
+
110
+
111
+ def in_place_editor(attributes, this=nil)
112
+ blank_message = attributes.delete(:blank_message) ||
113
+ t('hobo.in_place_editor.click_to_edit', :default => '(click to edit)')
114
+
115
+ attributes = add_classes(attributes, "in-place-edit", model_id_class(this_parent, this_field))
116
+ attributes.update(:hobo_blank_message => blank_message,
117
+ :if_blank => blank_message,
118
+ :no_wrapper => false)
119
+
120
+ edit_text = this._?.to_s
121
+ attributes.update(:hobo_edit_text => edit_text) unless edit_text.nil?
122
+
123
+ update = attributes.delete(:update)
124
+ attributes[:hobo_update] = update if update
125
+
126
+ view(attributes)
127
+ end
128
+
129
+
130
+
131
+ AJAX_CALLBACKS = [ :before, :success, :failure, :complete ]
132
+
133
+ AJAX_ATTRS = AJAX_CALLBACKS + [ :update, :type, :method,
134
+ :script, :form, :params, :confirm, :message,
135
+ :reset_form, :refocus_form, :result_update, :spinner_next_to ]
136
+
137
+
138
+ def through_collection_names(object=this)
139
+ object.class.reflections.values.select do |refl|
140
+ refl.macro == :has_many && refl.options[:through]
141
+ end.map {|x| x.options[:through]}
142
+ end
143
+
144
+
145
+ def non_through_collections(object=this)
146
+ names = object.class.reflections.values.select do |refl|
147
+ refl.macro == :has_many
148
+ end.*.name
149
+
150
+ names - through_collection_names
151
+ end
152
+
153
+ def standard_fields(model, include_timestamps=false)
154
+ fields = model.attr_order.*.to_s & model.content_columns.*.name
155
+ fields -= %w{created_at updated_at created_on updated_on deleted_at} unless include_timestamps
156
+ fields.reject! { |f| model.never_show? f }
157
+ fields
158
+ end
159
+
160
+ def current_time
161
+ Time.zone ? Time.zone.now : Time.now
162
+ end
163
+
164
+ end
165
+ end
166
+ end
@@ -1,14 +1,14 @@
1
1
  <!--
2
2
 
3
- The Rapid tag library makes web development go fast. The Rapid tag library is your friend.
3
+ The Rapid tag library makes web development go fast. The Rapid tag library is your friend.
4
4
 
5
5
  (This taglib defines no tags - it just includes all the other taglibs. Move along. Nothing to see here.)
6
6
 
7
7
  -->
8
8
 
9
- <include module="Hobo::HoboHelper"/>
10
- <include module="Hobo::RapidHelper"/>
11
- <include module="Hobo::Translations"/>
9
+ <include module="Hobo::Helper"/>
10
+ <include module="Hobo::Rapid::Helper"/>
11
+ <include module="Hobo::Helper::Translations"/>
12
12
 
13
13
  <include src="rapid_core"/>
14
14
  <include src="rapid_support"/>
@@ -21,4 +21,5 @@ The Rapid tag library makes web development go fast. The Rapid tag library is yo
21
21
  <include src="rapid_generics"/>
22
22
  <include src="rapid_lifecycles"/>
23
23
  <include src="rapid_summary"/>
24
- <include src="rapid_user_pages"/>
24
+ <include src="rapid_user_pages"/>
25
+ <include src="rapid_i18n"/>
@@ -1,23 +1,26 @@
1
+ <!-- Core Rapid tags and tags that don't belong anywhere else. -->
1
2
 
2
3
  <!-- Renders a table with one row per field, where each row contains a `<th>` with the field name, and a `<td>` with (by default)
3
4
  a `<view>` of the field.
4
-
5
+
5
6
  ### Attributes
6
7
 
7
8
  - fields: Comma separated list of field names to display. Defaults to the fields returned by the `standard_fields` helper. That is, all fields apart from IDs and timestamps.
8
-
9
+
9
10
  - force-all: All non-viewable fields will be skipped unless this attribute is given
10
-
11
+
11
12
  - skip: Comma separated list of fields to exclude
12
13
 
13
14
  - tag: The name of a tag to use inside the `<td>` to display the value. Defaults to `view`
14
-
15
+
15
16
  - no-edit: Controls the behavior of `<input>` tags when the user doesn't have edit permission for a field.
16
17
  - view: render the current value using the `<view>` tag
17
18
  - disable: render the input as normal, but add HTML's `disabled` attribute
18
19
  - skip: render nothing at all. This will omit the entire row (including the label)
19
20
  - ignore: render the input normally. That is, don't even perform the edit check.
20
21
 
22
+ - no-blanks: (boolean) Controls the behavior of `<view>` tags. The entire row (including the label) will be omitted if `this` is blank. Default false.
23
+
21
24
  ### Example
22
25
 
23
26
  <field-list fields="first-name, last-name, city">
@@ -27,14 +30,14 @@
27
30
  </field-list>
28
31
 
29
32
  -->
30
- <def tag="field-list" attrs="tag, no-edit">
33
+ <def tag="field-list" attrs="tag, no-edit, no-blanks">
31
34
  <% tag ||= scope.in_form ? "input" : "view"; no_edit ||= "skip" %>
32
35
  <labelled-item-list merge-attrs="&attributes - attrs_for(:with_fields)">
33
36
  <with-fields merge-attrs="&attributes & attrs_for(:with_fields)">
34
- <% field_name = this_field_name
37
+ <% field_name = this_field_name
35
38
  input_attrs = {:no_edit => no_edit} if tag == "input"
36
39
  -%>
37
- <labelled-item unless="&tag == 'input' && no_edit == 'skip' && !can_edit?">
40
+ <labelled-item unless="&(tag == 'input' && no_edit == 'skip' && !can_edit?) || (tag == 'view' && no_blanks && this.blank?)">
38
41
  <item-label param="#{scope.field_name.to_s.sub('?', '').gsub('.', '-')}-label" unless="&field_name.blank?">
39
42
  <do param="label"><%= field_name %></do>
40
43
  </item-label>
@@ -48,13 +51,13 @@
48
51
  </def>
49
52
 
50
53
  <!-- Used to render nil values. By default renders "(Not Available)"
51
-
54
+
52
55
  ### Usage
53
56
 
54
57
  Redefine in your app to have nil values displayed differently, e.g.:
55
58
 
56
59
  <def tag="nil-view">-</def>
57
-
60
+
58
61
  -->
59
62
  <def tag="nil-view"><%= scope.nil_view || "(Not Available)" %></def>
60
63
 
@@ -107,7 +110,7 @@ This will use `<input/>` as the tag in each table cell instead of `<view/>`
107
110
  <thead if="&all_parameters[:thead] || fields" param>
108
111
  <tr param="field-heading-row">
109
112
  <with-field-names merge-attrs="&all_attributes & attrs_for(:with_fields)">
110
- <th param="#{scope.field_name}-heading"><%= (this.member_class.try.view_hints.try.field_name(scope.field_name) || scope.field_name.titleize) if scope %></th>
113
+ <th param="#{scope.field_name}-heading"><%= this.member_class.human_attribute_name(scope.field_name) if scope %></th>
111
114
  </with-field-names>
112
115
  <th if="&all_parameters[:controls]" class="controls"/>
113
116
  </tr>
@@ -121,12 +124,14 @@ This will use `<input/>` as the tag in each table cell instead of `<view/>`
121
124
  <td param="#{scope.field_name.to_s.sub('?', '').gsub('.', '-')}-view"><call-tag tag="&field_tag"/></td>
122
125
  </with-fields>
123
126
  <td class="controls" param="controls" if="&all_parameters[:controls]">
124
- <a param="edit-link" action="edit" if="&can_edit?"><ht key="hobo.action.edit">Edit</ht></a>
127
+ <a param="edit-link" action="edit" if="&can_edit?">
128
+ <t key="hobo.actions.edit_control">Edit</t>
129
+ </a>
125
130
  <delete-button param/>
126
131
  </td>
127
132
  </if>
128
133
  </tr>
129
- </repeat>
134
+ </repeat>
130
135
  </tbody>
131
136
  <tfoot if="&all_parameters[:tfoot]" param/>
132
137
  <% end %>
@@ -154,27 +159,38 @@ Provides a short hand way of displaying images in public/images
154
159
 
155
160
 
156
161
  <!-- 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. -->
157
- <def tag="hobo-rapid-javascripts"><%=
158
- res = 'var hoboParts = {};'
159
- # FIXME: This should interrogate the model-router - not the models
160
- unless Hobo::Model.all_models.empty?
161
- # Tell JS code how to pluralize names, unless they follow the simple rule
162
- names = Hobo::Model.all_models.map do |m|
163
- m = m.name.underscore
164
- "#{m}: '#{m.pluralize}'" unless m.pluralize == m + 's'
165
- end.compact
166
- res << "var pluralisations = {#{names * ', '}}; "
167
- end
168
- base = [base_url, subsite].compact.join("/")
169
- res << "urlBase = '#{base}'; hoboPagePath = '#{view_name}'"
170
- if protect_against_forgery?
171
- res << "; formAuthToken = { name: '#{request_forgery_protection_token}', value: '#{form_authenticity_token}' }"
172
- end
173
- res
174
- %></def>
162
+ <def tag="hobo-rapid-javascripts">
163
+ <script type="text/javascript" param="default"><%=
164
+ res = 'var hoboParts = {};'
165
+ # FIXME: This should interrogate the model-router - not the models
166
+ unless Hobo::Model.all_models.empty?
167
+ # Tell JS code how to pluralize names, unless they follow the simple rule
168
+ names = Hobo::Model.all_models.map do |m|
169
+ m = m.name.underscore
170
+ "#{m}: '#{m.pluralize}'" unless m.pluralize == m + 's'
171
+ end.compact
172
+ res << "var pluralisations = {#{names * ', '}}; "
173
+ end
174
+ base = [base_url, subsite].compact.join("/")
175
+ res << "urlBase = '#{base}'; hoboPagePath = '#{request.fullpath}'"
176
+ if protect_against_forgery?
177
+ res << "; formAuthToken = { name: '#{request_forgery_protection_token}', value: '#{form_authenticity_token}' }"
178
+ end
179
+ res
180
+ %></script>
181
+ </def>
182
+
183
+
184
+ <def tag="part-contexts-javascripts">
185
+ <% unless (storage = part_contexts_storage).blank? %>
186
+ <script type="text/javascript"><%=
187
+ storage
188
+ %></script>
189
+ <% end %>
190
+ </def>
175
191
 
176
192
  <!-- Renders the name of the current context using a variety of methods.
177
-
193
+
178
194
  ### Details
179
195
 
180
196
  - Equivalent to `<nil-view>` if `this` is nil
@@ -186,9 +202,9 @@ Provides a short hand way of displaying images in public/images
186
202
  ### Attributes
187
203
 
188
204
  - if-present: if given, nothing at all will be rendered for nil values (as opposed to rendering `<nil-view>`)
189
-
205
+
190
206
  -->
191
- <def tag="name" attrs="if-present"><%=
207
+ <def tag="name" attrs="if-present"><%=
192
208
  if this.nil?
193
209
  nil_view unless if_present
194
210
  else
@@ -206,44 +222,46 @@ Provides a short hand way of displaying images in public/images
206
222
 
207
223
 
208
224
  <!-- Renders a human readable version of the type of the context
209
-
225
+
210
226
  ### Details
211
227
 
212
228
  - If `this` is already a class, the name of that class is used
213
229
  - Otherwise, first `this.member_class` (for collections), then `this.class` are tried
214
230
  - By default the name is titleised and singular.
215
-
231
+
216
232
  ### Attributes
217
233
 
218
234
  - plural: pluralise the name
219
235
  - lowercase: render the name in all lower case
220
- - dasherize: render the name in lower case with dashes instead of spaces.
236
+ - dasherize: render the name in lower case with dashes instead of spaces.
221
237
 
222
238
  -->
223
239
  <def tag="type-name" attrs="plural, lowercase, dasherize"><%=
224
240
  type ||= (this if this.is_a?(Class)) || this.try.member_class || this.class
225
-
226
- name = type.respond_to?(:view_hints) ? type.view_hints.model_name : type.name
241
+
242
+ name = type.respond_to?(:model_name) ? type.model_name.human : type.name
227
243
  name = dasherize ? name.underscore.dasherize : name.titleize
228
244
  name = name.pluralize if plural
229
245
  name = name.downcase if lowercase
230
246
  name
231
247
  %></def>
232
-
248
+
233
249
 
234
250
  <!-- Renders a human readable name of a collection
235
-
251
+
252
+ Use this tag for en locale only. Use human-collection-name for i18n.
253
+
236
254
  ### Details
237
255
 
238
256
  - Uses `this.origin_attribute` as the name.
239
257
  - Falls back to `<type-name>` otherwise.
240
258
  - By default the name is titleised and plural.
241
-
259
+
242
260
  ### Attributes
243
261
 
244
262
  - singular: singularise the name
245
263
  - lowercase: render the name in all lower case
246
- - dasherize: render the name in lower case with dashes instead of spaces.
264
+ - dasherize: render the name in lower case with dashes instead of spaces.
247
265
 
248
266
  -->
249
267
  <def tag="collection-name" attrs="singular, lowercase, dasherize"><%=
@@ -284,7 +302,7 @@ An action can be provided for an alternative show page:
284
302
  <a with="&blog_post" action="edit">Edit Post</a> -> <a href="/blog_posts/1/edit">Edit Post</a>
285
303
 
286
304
  Or a new page if the context is a class:
287
-
305
+
288
306
  <a with="&BlogPost" action="new">New Blog Post</a> -> <a href="/blog_posts/new">New Blog Post</a>
289
307
 
290
308
  ### Additional Features
@@ -324,16 +342,16 @@ Or a new page if the context is a class:
324
342
  -->
325
343
  <def tag="a" attrs="action, to, params, query-params, href, format, subsite, force"><%=
326
344
  content = parameters.default
327
-
345
+
328
346
  params = self.query_params.merge(params || HashWithIndifferentAccess.new) if query_params
329
-
347
+
330
348
  if href || attributes[:name]
331
349
  # Regular link
332
350
  href += "?" + params.map { |n, v| "#{n}=#{v}" }.join('&') if !params.blank?
333
351
  element(:a, attributes.update(:href => href), content)
334
352
  else
335
353
  target = to || this
336
-
354
+
337
355
  if target.nil?
338
356
  Dryml.last_if = false
339
357
  nil_view
@@ -342,7 +360,7 @@ Or a new page if the context is a class:
342
360
  new_record = target.new
343
361
  new_record.set_creator(current_user)
344
362
  href = object_url(target, "new", params._?.merge(:subsite => subsite))
345
-
363
+
346
364
  if href && (force || can_create?(new_record))
347
365
  new_class_name = if target.respond_to?(:proxy_reflection)
348
366
  target.proxy_reflection.klass.name
@@ -362,17 +380,17 @@ Or a new page if the context is a class:
362
380
  # Link to an existing object
363
381
 
364
382
  content = name if content.blank?
365
-
366
- href = object_url(target, action, (params || {}).merge(:subsite => subsite))
383
+
384
+ href = object_url(target, action, (params || {}).merge(:subsite => subsite)) unless (action.nil? && target.try.new_record?)
367
385
  if href.nil?
368
- # This target is registered with ModelRouter as not linkable
386
+ # This target is registered with Hobo::Routes as not linkable
369
387
  content
370
388
  else
371
389
  css_class = target.try.origin_attribute || target.class.name.underscore.dasherize
372
390
  add_classes!(attributes, "#{css_class}-link")
373
-
391
+
374
392
  href.sub!(/\?|$/, ".#{format}\\0") unless format.blank?
375
-
393
+
376
394
  # Set default link text if none given
377
395
  element(:a, attributes.update(:href => href), content)
378
396
  end
@@ -416,14 +434,14 @@ Assuming the context is a blog post...
416
434
  <def tag="view" attrs="inline, block, if-blank, no-wrapper, truncate"><%=
417
435
  raise Hobo::PermissionDeniedError, "view of non-viewable field '#{this_field}' of #{this_parent.typed_id rescue this_parent}" unless
418
436
  can_view?
419
-
437
+
420
438
  res = if this.nil? && if_blank.nil?
421
439
  this_type.is_a?(Class) && this_type <= String ? "" : nil_view
422
440
  elsif (refl = this_field_reflection) && refl.macro == :has_many
423
441
  collection_view(attributes)
424
442
  else
425
443
  view_tag = find_polymorphic_tag("view")
426
-
444
+
427
445
  if view_tag == "view" # i.e. it didn't find a type specific tag
428
446
  if this.respond_to?(:to_html)
429
447
  this.to_html(scope.xmldoctype)
@@ -435,19 +453,19 @@ Assuming the context is a blog post...
435
453
 
436
454
  view_attrs = attrs_for(view_tag)
437
455
  the_view = send(view_tag, attrs & view_attrs)
438
-
456
+
439
457
  the_view = if_blank if if_blank && the_view.blank?
440
458
 
441
459
  truncate = 30 if truncate == true
442
460
  the_view = self.truncate(the_view, :length => truncate.to_i) if truncate
443
461
  the_view = the_view.strip
444
-
462
+
445
463
  if no_wrapper
446
464
  the_view
447
465
  else
448
466
  wrapper = if inline
449
467
  :span
450
- elsif block || this_type <= HoboFields::Text
468
+ elsif block || this_type <= HoboFields::Types::Text
451
469
  :div
452
470
  else
453
471
  :span
@@ -464,45 +482,38 @@ Assuming the context is a blog post...
464
482
  <def tag="collection-view" polymorphic><links-for-collection merge-attrs/></def>
465
483
 
466
484
  <!-- Renders a comma separated list of links (`<a>`), or "(none)" if the list is empty -->
467
- <def tag="links-for-collection"><%= this.empty? ? "(none)" : context_map { a }.join(", ") %></def>
485
+ <def tag="links-for-collection"><%= this.empty? ? "(none)" : context_map { a }.safe_join(", ") %></def>
468
486
 
469
- <!-- Renders `I18n.l(this, :format => :long)`, or `I18n.l(this, :format => format)` if the `format` attribute is given -->
470
- <def tag="view" for="Date" attrs="format"><%= this && (format ? I18n.l(this, :format => format) : I18n.l(this, :format => :long)) %></def>
487
+ <!-- Renders `this` in localized :default format. `format` can be a symbol representing a locale key or a standard format string (see strftime) -->
488
+ <def tag="view" for="date" attrs="format"><%= this && (format||= :default) && I18n.backend.localize(I18n.locale, this, format) %></def>
471
489
 
472
- <!-- Renders `I18n.l(this, :format => :long)`, or `I18n.l(this, :format => format)` if the `format` attribute is given -->
473
- <def tag="view" for="Time" attrs="format"><%= this && (format ? I18n.l(this, :format => format) : I18n.l(this, :format => :long)) %></def>
490
+ <!-- Renders `this` in localized :default format. `format` can be a symbol representing a Time::DATE_FORMATS or a standard format string (see strftime) -->
491
+ <def tag="view" for="time" attrs="format"><%= this && (format||= :time) && (format.is_a?(Symbol) ? this.to_s(format) : this.strftime(format) ) %></def>
474
492
 
475
- <!-- Renders `I18n.l(this, :format => :long)`, or `I18n.l(this, :format => format)` if the `format` attribute is given -->
476
- <def tag="view" for="ActiveSupport::TimeWithZone" attrs="format"><%= this && (format ? I18n.l(this, :format => format) : I18n.l(this, :format => :long)) %></def>
493
+ <!-- Renders `this` in localized :default format. `format` can be a symbol representing a locale key or a standard format string (see strftime) -->
494
+ <def tag="view" for="datetime" attrs="format"><%= this && (format||= :default) && I18n.backend.localize(I18n.locale, this, format) %></def>
477
495
 
478
- <!-- Renders `this.to_s`, or `format % this` if the `format` attribute is given -->
479
- <def tag="view" for="Numeric" attrs="format"><%= format ? format % this : this.to_s %></def>
496
+ <!-- Renders localized `number_with_delimiter this`, or `format % this` if the `format` attribute is given -->
497
+ <def tag="view" for="Numeric" attrs="format"><%= format ? format % this : number_with_delimiter(this) %></def>
480
498
 
481
499
  <!-- Renders `this` with HTML escaping and newlines replaced with `<br>` tags -->
482
- <def tag="view" for="string"><%=
500
+ <def tag="view" for="string"><%=
483
501
  if !(this.class == String) && this.respond_to?(:to_html) # workaround for Maruku which adds String#to_html : (
484
502
  this.to_html(scope.xmldoctype)
485
503
  else
486
- h(this).gsub("\n", "<br#{scope.xmldoctype ? ' /' : ''}>")
504
+ h(this).gsub("\n", "<br#{scope.xmldoctype ? ' /' : ''}>")
487
505
  end
488
506
  %></def>
489
507
 
490
- <!-- Renders 'Yes' for true and 'No' for false with localization support -->
491
- <def tag="view" for="boolean"><%= this ? ht('hobo.boolean_yes', {:default => 'Yes'}) : ht('hobo.boolean_no', {:default => 'No'}) %></def>
508
+ <!-- Renders 'Yes' for true and 'No' for false -->
509
+ <def tag="view" for="boolean"><%= this ? t('hobo.boolean_yes', :default => 'Yes') : t('hobo.boolean_no', :default => 'No') %></def>
492
510
 
493
511
  <!-- Renders a link (`<a>`) to `this` -->
494
512
  <def tag="view" for="ActiveRecord::Base"><a merge-attrs/></def>
495
513
 
496
- <!-- Renders `this.to_s` with localization support -->
497
- <def tag="view" for="HoboFields::LifecycleState">
498
- <span class='#{this.class.parent.name.downcase}-states #{this.class.parent.name.downcase}-state-#{this.to_s.downcase} state-#{this.to_s.downcase}'>
499
- <ht key='#{this.class.parent.name.tableize}.lifecycle_states.#{this.to_s.downcase}'><%= this.to_s %></ht>
500
- </span>
501
- </def>
502
-
503
514
 
504
515
  <!--
505
- A convenience tag used to output a count and a correctly pluralised label. Works with any kind of collection such as an `ActiveRecord` association or an array.
516
+ A convenience tag used to output a count summary with a correctly localized and pluralised label. Works with any kind of collection such as an `ActiveRecord` association or an array.
506
517
 
507
518
  ### Usage
508
519
 
@@ -514,12 +525,47 @@ The label can be customised using the `label` attribute, e.g.
514
525
 
515
526
  <count:comments label="blog post comment"/> -> <span class="count">12 blog post comments</span>
516
527
 
528
+ You can pass a summary attribute, which will generate a complete localized sentence. It allows 2 options:
529
+ - boolean (e.g. `<count summary/>`): it will lookup the 'tags.count.default' key in the locale file.
530
+ If the lookup fails, it will fallback to the english default sentences consistent with the count.
531
+ - String (e.g. `<count summary="offer"/>`): it will lookup the 'tags.count.offer' key in the locale file.
532
+ If the lookup fails, it will fallback to the english default sentences consistent with the count.
533
+
534
+ ### Examples
535
+
536
+ it:
537
+ tags:
538
+ count:
539
+ default:
540
+ zero: "Non ci sono {{label}}"
541
+ one: "C'è solo 1 {{label}}"
542
+ other: "Ci sono {{count}} {{label}}"
543
+ choice:
544
+ zero: "Non ci sono {{label}} da scegliere"
545
+ one: "Puoi scegliere solo una {{label}}"
546
+ other: "Puoi scegliere tra {{count}} {{label}}"
547
+
548
+ with :en locale and boolean summary (internal defaults)
549
+ <count:comments summary/> -> <span class="count">There is 1 Comment</span>
550
+ <count:viewings summary/> -> <span class="count">There are 3 Viewings</span>
551
+ (note: just add the locale english strings to use like the following examples)
552
+
553
+ with :it locale and boolean summary (key "tags.count.default")
554
+ <count:comments summary/>
555
+ -> count => 0 -> <span class="count">Non ci sono Commenti</span>
556
+ -> count => 1 -> <span class="count">C'è solo 1 Commento</span>
557
+ -> count => 5 -> <span class="count">Ci sono 5 Commenti</span>
558
+
559
+ with :it locale and summary="choice" (key "tags.count.choice")
560
+ <count:comments summary="choice"/>
561
+ -> count => 0 -> <span class="count">Non ci sono Commenti da scegliere</span>
562
+ -> count => 1 -> <span class="count">Puoi scegliere solo 1 Commento</span>
563
+ -> count => 5 -> <span class="count">Puoi scegliere tra 5 Commenti</span>
564
+
517
565
  ### Additional Notes
518
566
 
519
- * Use the `prefix` attribute to insert words before the count. If the prefix is "are" or "is" then it will be pluralised if needed:
567
+ * The `prefix` attribute is deprecated: use summary instead.
520
568
 
521
- There <count:comments prefix="are"/> -> There <span class="count">is 1 Comment</span>
522
- There <count:viewings prefix="are"/> -> There <span class="count">are 3 Viewings</span>
523
569
  * Use the `lowercase` attribute to force the generated label to be lowercase:
524
570
 
525
571
  <count:comments lowercase/> -> <span class="count">1 comment</span>
@@ -527,38 +573,44 @@ The label can be customised using the `label` attribute, e.g.
527
573
 
528
574
  <count:comments if-any/><else>There are no comments</else>
529
575
  -->
530
- <def tag="count" attrs="label, prefix, if-any, lowercase"><span class="count"><%=
576
+ <def tag="count" attrs="summary, label, if-any, lowercase, prefix"><span class="count"><%=
531
577
  raise Exception.new("asked for count of a string") if this.is_a?(String)
532
-
533
- c = this.try.to_int || this.try.total_entries || (this.try.loaded? && this.try.length) || this.try.count || this.try.length
578
+ Rails.logger.warn('The "prefix" attribute is deprecated: please, use a locale string') unless prefix.blank?
579
+
580
+ c = collection_count
534
581
 
535
- label ||= if this.is_a?(Class)
536
- this.view_hints.model_name
537
- elsif (attr = this.try.origin_attribute)
538
- this.member_class.view_hints.field_name(attr).singularize
582
+ # generated label will be pluralized
583
+ label ||= case
584
+ when this.is_a?(Class)
585
+ this.model_name.human(:count=>c)
586
+ when (attr = this.try.origin_attribute)
587
+ this_parent.class.human_attribute_name(attr, :count=>c)
539
588
  else
540
- this.member_class.view_hints.model_name
541
- end.titleize
589
+ this.member_class.model_name.human(:count=>c)
590
+ end
542
591
 
543
592
  label = label.downcase if lowercase
544
-
593
+
545
594
  Dryml.last_if = c > 0 if if_any
546
595
  if if_any && c == 0
547
596
  ""
548
597
  else
549
- main = label.blank? ? c : pluralize(c, label)
550
-
551
- if prefix.in? %w(are is)
552
- prefix = c == 1 ? "is" : "are"
598
+ if summary.blank? # old behaviour
599
+ main = label.blank? ? c : "#{c} #{label}"
600
+ if prefix.in? %w(are is)
601
+ prefix = c == 1 ? "is" : "are"
602
+ end
603
+ (prefix ? "#{prefix} #{main}" : main.to_s)
604
+ else
605
+ key = summary.kind_of?(String) ? summary : "default"
606
+ default = c == 1 ? "There is 1 #{label}" : "There are #{c} #{label}"
607
+ t "tags.count.#{key}", {:count=>c, :label=>label, :default=>default}.merge(attributes)
553
608
  end
554
-
555
- (prefix ? "#{prefix} #{main}" : main.to_s)
556
-
557
609
  end
558
610
  %></span></def>
559
611
 
560
612
 
561
- <!-- 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.
613
+ <!-- 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.
562
614
  -->
563
615
  <def tag="theme-stylesheet" attrs="name">
564
616
  <% name ||= Hobo.current_theme -%>
@@ -576,28 +628,144 @@ The context should be a user object. If `this == current_user` the "you" form is
576
628
  - `<you are/> now an admin` -> "you are now an admin" or "Jim is now an admin"
577
629
  - `<you do/>n't want to go there` -> "you don't want to go there" or "Jim doesn't want to go there"
578
630
 
631
+ The tag is also localized in the namespaces "tags.you.current_user" and "tags.you.other_user".
632
+ Each namespace can contain the legacy keys "have", "are", "do" used for the respective attributes,
633
+ and "nothing" used when no attribute is passed. But you can also use your own keys, providing
634
+ that you add the keys in the correct namespaces.
635
+
636
+ ### Examples
637
+
638
+ it:
639
+ tags:
640
+ you:
641
+ current_user:
642
+ nothing: "Tu"
643
+ have: "Hai"
644
+ are: "sei"
645
+ can: "Puoi"
646
+ other_user:
647
+ nothing: "{{name}}"
648
+ have: "{{name}} ha"
649
+ are: "{{name}} è"
650
+ can: "{{name}} può"
651
+
652
+ - `<you have/> un nuovo messaggio.` -> "Hai un nuovo messaggio." or "Jim ha un nuovo messaggio."
653
+ - `Adesso <you are/> amministratore.` -> "Adesso sei amministratore." or "Adesso Jim è amministratore."
654
+ - `<you can/> scrivere.` -> "Puoi scrivere." or "Jim può scrivere."
655
+
656
+ (note: :name is added by default as an interpolable variable)
657
+
579
658
  ### Attributes
580
659
 
581
- - titleize: render "You" instead of "you"
582
-
583
- -->
584
- <def tag="you" attrs="have, are, do, titleize"><if test="&this == current_user"><%= "#{titleize ? 'Y' : 'y'}ou#{' have' if have}#{' are' if are}#{' do' if do_}" %></if><else><do param="default"><name/><%= "#{' has' if have}#{' is' if are}#{' does' if do_}" %></do></else></def>
660
+ - capitalize: the first letter of the resulting sentence will be capitalized
585
661
 
586
- <!-- Equivalent to `<you titleize/>`. Yes it's an abuse of Ruby naming conventions, but it's so cute : ) -->
587
- <def tag="You"><you merge titleize/></def>
588
662
 
589
- <!-- Similar to `<you>`, but renders "Your" or "Fred's" -->
590
- <def tag="your">
591
- <if test="&this == current_user">your</if>
592
- <else><do param="default"><%= n = name; n.ends_with?('s') ? "#{n}'" : "#{n}'s" %></do></else>
593
- </def>
663
+ ### Additional Notes
594
664
 
595
- <!-- Capitalised versin of `<your>` -->
596
- <def tag="Your">
597
- <if test="&this == current_user">Your</if>
598
- <else><do param="default"><%= n = name; n.ends_with?('s') ? "#{n}'" : "#{n}'s" %></do></else>
665
+ The "titleize" attribute is deprecated: use "capitalize" instead.
666
+
667
+ -->
668
+ <def tag="you" attrs="titleize, capitalize">
669
+ <% Rails.logger.warn "'titleize' is a deprecated attribute of the 'you' tag. Please, use 'capitalize' instead." -%>
670
+ <% raise ArgumentError, "You can add only one attribute-key to the 'you' tag." if attributes.size > 1 -%>
671
+ <% k = case
672
+ when attributes[:have] then 'have'
673
+ when attributes[:are] then 'are'
674
+ when attributes[:do] then 'do'
675
+ end -%>
676
+ <if test="&this == current_user">
677
+ <%= s = t("tags.you.current_user.#{k || attributes.keys.first || 'default'}",:default=>"you #{k}")
678
+ (titleize||capitalize) ? s.sub(/^./){|c| c.upcase} : s %>
679
+ </if>
680
+ <else>
681
+ <do param="default">
682
+ <%= s = t("tags.you.other_user.#{k || attributes.keys.first || 'default'}", :name=>this.name,
683
+ :default=>"#{this.name} #{'has' if attributes[:have]}#{'is' if attributes[:are]}#{'does' if attributes[:do]}")
684
+ (titleize||capitalize) ? s.sub(/^./){|c| c.upcase} : s %>
685
+ </do>
686
+ </else>
599
687
  </def>
600
688
 
689
+ <!-- Equivalent to `<you capitalize/>`-->
690
+ <def tag="You"><you merge capitalize/></def>
691
+
692
+ <!-- Similar to `<you>`, but renders "Your" or "Fred's" or equivalent localized strings
693
+
694
+ ### Attributes
695
+
696
+ - capitalize: the first letter of the resulting sentence will be capitalized
697
+ - count: used in pluralization. If omitted it will be set to 1.
698
+ - key: used to lookup the translation in the locale file. It allows 3 different options:
699
+ - single key like 'message': simple translation in 'tags.your.message.current_user'
700
+ or 'tags.your.message.other_user'
701
+ - composite key like 'any.namespace.message': translation as for the previous case, but it will
702
+ translate also the 'any.namespace.message' and will interpolate the variable <key> (in this case :message)
703
+ in the translation
704
+ - when key is omitted it will be set to "default" and will do the translation with that key.
705
+ Pass other meaningful attributes to achieve a dynamic usage
706
+ - any other attribute passed to the tag will be used as a variable for interpolation
707
+
708
+ Notes
709
+
710
+ - The :name variable is added by default as an interpolable variable
711
+ - If no translation is found an automatic (only english) default is generated:
712
+ the Your/Jim's string, joined to the tag content.
713
+ If you pass an explicit 'default' attribute you will override the automatic default.
714
+
715
+
716
+ ### Examples
717
+
718
+ it:
719
+ tags:
720
+ your:
721
+ message:
722
+ current_user:
723
+ one: "Tuo Messaggio"
724
+ other: "Tuoi Messaggi"
725
+ other_user:
726
+ one: "Messaggio di {{name}}"
727
+ other: "Messaggi di {{name}
728
+ entry:
729
+ current_user:
730
+ one: "Tua {{entry}}"
731
+ other: "Tue {{entry}}"
732
+ other_user: "{{entry}} di {{name}}"
733
+
734
+ - `<your key="message" count=>"&messages.count"/>`
735
+ -> count => 1 -> "Tuo Messaggio" or "Messaggio di Jim"
736
+ -> count => 5 -> "Tuoi Messaggi" or "Messaggi di Jim"
737
+
738
+ - `<your key="activerecord.models.entry" count=>"&this.entries.count"/>`
739
+ -> count => 1 -> "Tua Entrata" or "Entrata di Jim"
740
+ -> count => 5 -> "Tue Entrate" or "Entrate di Jim"
741
+
742
+ - `<your>Posts</your>` -> "your Posts" or "Jim's Posts"
743
+ -->
744
+
745
+ <def tag="your" attrs="key, capitalize"><%=
746
+ key ||= 'default'
747
+ # prepare symbolized attributes for merging
748
+ attrs = {}
749
+ attributes.each_pair{|k,v| attrs[k.to_sym] = v}
750
+ d = "#{your_default} #{all_parameters.default}"
751
+ options = {:default=>[d], :count=>(attrs[:count]||1), :name=>this.name}
752
+ your_key = key.split('.').last
753
+ unless key.eql?(your_key) || attrs.has_key?(your_key.to_sym)
754
+ options[your_key.to_sym] = t(key, :count=>options[:count], :default=>your_key.titleize)
755
+ end
756
+ s = if this == current_user
757
+ options[:default].unshift :"tags.your.default.current_user"
758
+ t("tags.your.#{your_key}.current_user", options.merge(attrs))
759
+ else
760
+ options[:default].unshift :"tags.your.default.other_user"
761
+ t("tags.your.#{your_key}.other_user", options.merge(attrs))
762
+ end
763
+ capitalize ? s.sub(/^./){|c| c.upcase} : s
764
+ %></def>
765
+
766
+ <!-- Equivalent to `<your ... capitalize/>`-->
767
+ <def tag="Your"><your merge capitalize/></def>
768
+
601
769
  <!-- Deprecated. It's harder than you think to do this (e.g. an umbrealla, an user)
602
770
  -->
603
771
  <def tag="a-or-an" attrs="word"><%=
@@ -617,10 +785,10 @@ The context should be a user object. If `this == current_user` the "you" form is
617
785
 
618
786
  <!-- Development mode only - a menu to change the `current_user` -->
619
787
  <def tag="dev-user-changer">
620
- <set user="&Hobo::User.default_user_model"/>
621
- <select-menu if="&user && RAILS_ENV == 'development'"
622
- first-option="#{ht('hobo.dev_user_changer.guest', {:default=>'Guest'})}" options="&user.all(:limit => 30).*.login"
623
- onchange="location.href = '#{dev_support_path}/set_current_user?login=' + encodeURIComponent(this.options[this.selectedIndex].value)"
788
+ <set user="&Hobo::Model::UserBase.default_user_model"/>
789
+ <select-menu if="&user && Rails.env.development?"
790
+ first-option="#{t('hobo.dev_user_changer.guest', {:default=>'Guest'})}" options="&user.all(:limit => 30).*.login"
791
+ onchange="location.href = '#{dev_support_path :action=>:set_current_user}?login=' + encodeURIComponent(this.options[this.selectedIndex].value)"
624
792
  selected="#{current_user.login}"
625
793
  class="dev-user-changer"
626
794
  merge-attrs/>