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,184 @@
1
+ require 'digest/sha1'
2
+
3
+ module Hobo
4
+ module Model
5
+ module UserBase
6
+
7
+ @user_models = []
8
+
9
+ def self.default_user_model
10
+ @user_models.first._?.constantize
11
+ end
12
+
13
+ AUTHENTICATION_FIELDS = [:salt, :crypted_password, :remember_token, :remember_token_expires_at]
14
+
15
+ # Extend the base class with AuthenticatedUser functionality
16
+ # This includes:
17
+ # - plaintext password during login and encrypted password in the database
18
+ # - plaintext password validation
19
+ # - login token for rembering a login during multiple browser sessions
20
+ def self.included(base)
21
+ @user_models << base.name
22
+
23
+ base.extend(ClassMethods)
24
+
25
+ base.class_eval do
26
+
27
+ fields do
28
+ crypted_password :string, :limit => 40
29
+ salt :string, :limit => 40
30
+ remember_token :string
31
+ remember_token_expires_at :datetime
32
+ end
33
+
34
+ validates_confirmation_of :password, :if => :new_password_required?
35
+ validate :validate_password
36
+ validate :validate_current_password_when_changing_password
37
+
38
+ # Virtual attributes for setting and changing the password
39
+ # note that :password_confirmation= is also defined by
40
+ # validates_confirmation_of, so this line must follow any
41
+ # validates_confirmation_of statements.
42
+ # https://hobo.lighthouseapp.com/projects/8324-hobo/tickets/530
43
+ attr_accessor :current_password, :password, :password_confirmation, :type => :password
44
+
45
+ before_save :encrypt_password
46
+ after_save :stash_current_password
47
+
48
+ never_show *AUTHENTICATION_FIELDS
49
+
50
+ attr_protected *AUTHENTICATION_FIELDS
51
+
52
+
53
+ end
54
+ end
55
+
56
+ # Additional classmethods for authentication
57
+ module ClassMethods
58
+
59
+
60
+ def login_attribute=(attr, validate=true)
61
+ @login_attribute = attr = attr.to_sym
62
+ unless attr == :login
63
+ alias_attribute(:login, attr)
64
+ declare_attr_type(:login, attr_type(attr)) if table_exists? # this breaks if the table doesn't exist
65
+ end
66
+
67
+ if validate
68
+ validates_length_of attr, :within => 3..100
69
+ validates_uniqueness_of attr, :case_sensitive => false
70
+ end
71
+ end
72
+
73
+ inheriting_cattr_reader :login_attribute
74
+
75
+
76
+ # Authenticates a user by their login name and unencrypted password. Returns the user or nil.
77
+ def authenticate(login, password)
78
+ u = where("#{@login_attribute} = ?", login).first # need to get the salt
79
+
80
+ if u && u.authenticated?(password)
81
+ if u.respond_to?(:last_login_at) || u.respond_to?(:login_count)
82
+ u.last_login_at = Time.now if u.respond_to?(:last_login_at)
83
+ u.login_count = (u.login_count.to_i + 1) if u.respond_to?(:login_count)
84
+ u.save
85
+ end
86
+ u
87
+ else
88
+ nil
89
+ end
90
+ end
91
+
92
+ # Encrypts some data with the salt.
93
+ def encrypt(password, salt)
94
+ Digest::SHA1.hexdigest("--#{salt}--#{password}--")
95
+ end
96
+
97
+ end
98
+
99
+ def account_active?
100
+ lifecycle.active_state?
101
+ end
102
+
103
+ # Encrypts the password with the user salt
104
+ def encrypt(password)
105
+ self.class.encrypt(password, salt)
106
+ end
107
+
108
+ # Check if the encrypted passwords match
109
+ def authenticated?(password)
110
+ crypted_password == encrypt(password)
111
+ end
112
+
113
+ # Do we still need to remember the login token, or has it expired?
114
+ def remember_token?
115
+ remember_token_expires_at && Time.now.utc < remember_token_expires_at
116
+ end
117
+
118
+ # These create and unset the fields required for remembering users between browser closes
119
+ def remember_me
120
+ self.remember_token_expires_at = 2.weeks.from_now.utc
121
+ self.remember_token = encrypt("#{login}--#{remember_token_expires_at}")
122
+ save(:validate => false)
123
+ end
124
+
125
+ # Expire the login token, resulting in a forced login next time.
126
+ def forget_me
127
+ self.remember_token_expires_at = nil
128
+ self.remember_token = nil
129
+ save(:validate => :false)
130
+ end
131
+
132
+ def guest?
133
+ false
134
+ end
135
+
136
+ def signed_up?
137
+ true
138
+ end
139
+
140
+ protected
141
+ # Before filter that encrypts the password before having it stored in the database.
142
+ def encrypt_password
143
+ return if password.blank?
144
+ self.salt = Digest::SHA1.hexdigest("--#{Time.now.to_s}--#{login}--") if salt.blank?
145
+ self.crypted_password = encrypt(password)
146
+ end
147
+
148
+ # after filter that sets current_password so we can pass
149
+ # validate_current_password_when_changing_password if you save
150
+ # again. See
151
+ # https://hobo.lighthouseapp.com/projects/8324-hobo/tickets/590
152
+ def stash_current_password
153
+ @current_password ||= password
154
+ end
155
+
156
+ def changing_password?
157
+ !new_record? && !lifecycle_changing_password? &&
158
+ (current_password.present? || password.present? || password_confirmation.present?)
159
+ end
160
+
161
+
162
+ def lifecycle_changing_password?
163
+ self.class.has_lifecycle? && lifecycle.active_step && :password.in?(lifecycle.active_step.parameters)
164
+ end
165
+
166
+ # Is a new password (and confirmation) required? (i.e. signing up or changing password)
167
+ def new_password_required?
168
+ (new_record? && password) || lifecycle_changing_password? || changing_password?
169
+ end
170
+
171
+ # Validation of the plaintext password. Override this function to change your validation.
172
+ def validate_password
173
+ errors.add(:password, I18n.t("hobo.messages.validate_password", :default => "must be at least 6 characters long and must not consist solely of lowercase letters")) \
174
+ if new_password_required? && (password.nil? || password.length<6 || /^[[:lower:]]*$/.match(password))
175
+ end
176
+
177
+ def validate_current_password_when_changing_password
178
+ errors.add :current_password, I18n.t("hobo.messages.current_password_is_not_correct", :default => "is not correct") \
179
+ if changing_password? && !authenticated?(current_password)
180
+ end
181
+
182
+ end
183
+ end
184
+ end
@@ -0,0 +1,123 @@
1
+ module Hobo
2
+ module Model
3
+ class ViewHints
4
+
5
+ class << self
6
+
7
+ # allows delayed set of children in order to avoid circular references
8
+ # triggered by declaring children in the model
9
+ def children(*args)
10
+ if args.empty? # reader
11
+ if @children_args.nil? # value already set
12
+ @children ||= []
13
+ else # set
14
+ child_model = model.reflect_on_association(@children_args.first).klass
15
+ if child_model.view_hints.parent.nil? and !child_model.view_hints.parent_defined
16
+ parent = model.reverse_reflection(@children_args.first)
17
+ child_model.view_hints.parent(parent.name, :undefined => true) if parent
18
+ end
19
+ @children = @children_args
20
+ @children_args = nil
21
+ @children
22
+ end
23
+ else # writer (only stores the args for delayed setting)
24
+ @children_args = args
25
+ end
26
+ end
27
+
28
+ def inline_booleans(*args)
29
+ if args.empty? # reader
30
+ if @inline_booleans_args.nil?
31
+ @inline_booleans ||= []
32
+ else
33
+ @inline_booleans = if @inline_booleans_args.first == true
34
+ model.columns.select { |c| c.type == :boolean }.*.name
35
+ else
36
+ @inline_booleans_args.*.to_s
37
+ end
38
+ @inline_booleans_args = nil
39
+ @inline_booleans
40
+ end
41
+ else # writer
42
+ @inline_booleans_args = args
43
+ end
44
+ end
45
+
46
+ def parent(*args)
47
+ if args.empty? #reader
48
+ @parent
49
+ else # writer
50
+ options = args.extract_options!
51
+ parent_defined(true) unless options[:undefined]
52
+ @parent = args.first
53
+ end
54
+ end
55
+
56
+ def parent_defined(arg=nil)
57
+ if arg.nil?
58
+ @parent_defined
59
+ else
60
+ @parent_defined = arg
61
+ end
62
+ end
63
+
64
+ def paginate?(arg=nil)
65
+ if arg.nil?
66
+ @paginate ||= !sortable?
67
+ else
68
+ @paginate = arg
69
+ end
70
+ end
71
+
72
+ def sortable?(arg=nil)
73
+ if arg.nil?
74
+ @sortable ||= defined?(ActiveRecord::Acts::List::InstanceMethods) &&
75
+ model < ActiveRecord::Acts::List::InstanceMethods &&
76
+ model.table_exists? &&
77
+ model.new.try.scope_condition == "1 = 1"
78
+ else
79
+ @sortable = arg
80
+ end
81
+ end
82
+
83
+ def _name
84
+ @_name ||= name.sub(/Hints$/, '')
85
+ end
86
+
87
+ def model
88
+ @model ||= _name.constantize
89
+ end
90
+
91
+ def primary_children
92
+ children.first
93
+ end
94
+
95
+ def secondary_children
96
+ children.rest
97
+ end
98
+
99
+
100
+ ##### LEGACY METHODS TO REMOVE #####
101
+
102
+ def model_name(*)
103
+ raise NotImplementedError, "ViewHints.model_name is no longer supported, please use model.model_name.human and set a the activerecord.models.<model_name> key in a locale file"
104
+ end
105
+
106
+ def model_name_plural(*)
107
+ raise NotImplementedError, "ViewHints.model_name_plural is no longer supported, please use model.model_name.human(:count => n) and set a the activerecord.models.<model_name> key in a locale file"
108
+ end
109
+
110
+ def field_name(*)
111
+ raise NotImplementedError, "ViewHints.field_name is no longer supported, please use model..human_attribute_name and set a the activerecord.attributes.<model_name>.<field_name> key in a locale file"
112
+ end
113
+
114
+ def field_names(*)
115
+ raise NotImplementedError, "ViewHints.field_names is no longer supported, please set the activerecord.attributes.<model_name>.<field_name> keys in a locale file"
116
+ end
117
+
118
+ end
119
+
120
+ end
121
+
122
+ end
123
+ end
@@ -19,7 +19,7 @@ has_body = (!show_link && description_attribute) || creator_link || creator_a
19
19
 
20
20
  show_title = !show_link && (name_attribute || !has_body)
21
21
 
22
- model_key = model.name.pluralize.underscore
22
+ model_key = model.to_s.underscore
23
23
  -%>
24
24
  <def tag="card" for="<%= model.name %>">
25
25
  <card class="<%= model_class %>" param="default" merge>
@@ -53,7 +53,7 @@ model_key = model.name.pluralize.underscore
53
53
  <view:<%= creator_attribute %> param="creator"/>
54
54
  <% end -%>
55
55
  <% if primary_collection -%>
56
- <ht key="<%= primary_collection.to_s.pluralize.underscore %>.collection.count" count="&this.<%= primary_collection %>.size">
56
+ <ht key="<%= model.reflect_on_association(primary_collection).klass.to_s.underscore %>.collection.count" count="&this.<%= primary_collection %>.size">
57
57
  <count:<%= primary_collection%> param/>
58
58
  </ht>
59
59
  <% end -%>
@@ -4,7 +4,7 @@ form_fields = standard_fields :belongs_to, :has_many
4
4
 
5
5
  cancel_to_show_page = linkable?(:show)
6
6
  cancel_to_index_page = !cancel_to_index_page && linkable?(:index)
7
- model_key = model.name.tableize
7
+ model_key = model.to_s.underscore
8
8
  -%>
9
9
  <def tag="form" for="<%= model.name %>">
10
10
  <form merge param="default">
@@ -37,10 +37,10 @@ model_key = model.name.tableize
37
37
  <input type="hidden" name="key" value="&this.lifecycle.provided_key" if="&this.lifecycle.provided_key"/>
38
38
  <field-list fields="<%= transition.parameters * ', ' %>" param/>
39
39
  <div param="actions">
40
- <submit label="#{ht '<%= model_key %>.actions.<%= transition.name.to_s %>', :default=>['<%= transition.name.to_s.titleize %>']}" param/><or-cancel param="cancel"/>
40
+ <submit label="#{t 'activerecord.attributes.<%= model_key %>.lifecycle.transitions.<%= transition.name.to_s %>', :default=>['<%= transition.name.to_s.titleize %>']}" param/><or-cancel param="cancel"/>
41
41
  </div>
42
42
  </form>
43
43
  </def>
44
44
  <% end -%>
45
45
 
46
- <% end # of each_model do -%>
46
+ <% end # of each_model do -%>
@@ -3,8 +3,8 @@
3
3
  <def tag="main-nav">
4
4
  <navigation class="main-nav" merge-attrs param="default">
5
5
  <nav-item href="#{base_url}/">Home</nav-item>
6
- <% models.select { |m| linkable?(m, :index) }[0..4].each do |m| -%>
7
- <nav-item with="&<%= m.name %>"><ht key="<%= m.name.tableize %>.nav_item"><%= m.view_hints.model_name_plural %></ht></nav-item>
6
+ <% models.select { |m| linkable?(m, :index) }.each do |m| -%>
7
+ <nav-item with="&<%= m.name %>"><ht key="<%= m.to_s.underscore %>.nav_item" count="100"><model-name-human count="100"/></ht></nav-item>
8
8
  <% end -%>
9
9
  </navigation>
10
10
  </def>
@@ -17,24 +17,24 @@
17
17
  <%
18
18
  new_link = linkable?(:new)
19
19
  new_form = !new_link && linkable?(model, :create, :method => :post)
20
- model_key = model.name.tableize
20
+ model_key = model.to_s.underscore
21
21
  -%>
22
22
 
23
23
  <def tag="index-page" for="<%= model.name %>">
24
- <page merge title="#{ht '<%= model_key %>.index.title', :default=>['<%= sq_escape(model_name :plural) %>'] }">
24
+ <page merge title="#{ht '<%= model_key %>.index.title', :default=>[model.model_name.human(:count=>100)] }">
25
25
  <body: class="index-page <%= model_class %>" param/>
26
26
 
27
27
  <content: param>
28
28
  <header param="content-header">
29
29
  <h2 param="heading">
30
30
  <ht key="<%= model_key %>.index.heading">
31
- <%= model_name :plural %>
31
+ <model-name-human model="&model"/>
32
32
  </ht>
33
33
  </h2>
34
34
 
35
35
  <p param="count" if>
36
- <ht key="<%= model_key %>.collection.count" count="&this.size">
37
- There <count prefix="are"/>
36
+ <ht key="<%= model_key %>.collection.count" count="&collection_count">
37
+ <count summary/>
38
38
  </ht>
39
39
  </p>
40
40
  </header>
@@ -101,14 +101,16 @@ model_key = model.name.tableize
101
101
 
102
102
  <%
103
103
  back_link = model.view_hints.parent
104
- boolean_fields = model.columns.select { |c| c.type == :boolean }.*.name - model.view_hints.inline_booleans
104
+ back_link_human_name = model.reflections[back_link] && model.reflections[back_link].klass unless back_link.blank?
105
+ back_link_human_name = back_link_human_name && back_link_human_name.model_name.human
106
+ boolean_fields = model.table_exists? ? (model.columns.select { |c| c.type == :boolean }.*.name - model.view_hints.inline_booleans) : []
105
107
  creator = model.creator_attribute
106
108
  creator_link = creator && model.reflections[creator] && linkable?(model.reflections[creator].klass, :show)
107
109
  edit_link = linkable?(:edit)
108
110
  main_content = model.primary_content_attribute
109
111
  show_fields = standard_fields(:belongs_to).*.to_s - [model.name_attribute, main_content, creator, back_link, *boolean_fields].*.to_s
110
112
 
111
- is_user_model = model < Hobo::User
113
+ is_user_model = model < Hobo::Model::UserBase
112
114
 
113
115
  collection = model.view_hints.primary_children
114
116
  if collection
@@ -137,10 +139,10 @@ end
137
139
  <% end -%>
138
140
  <header param="content-header">
139
141
  <% if back_link -%>
140
- <a:<%= back_link %> param="parent-link">&laquo; <ht key="<%= model_key %>.actions.back" to="<%= back_link %>"><name/></ht></a:<%= back_link %>>
142
+ <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 %>>
141
143
  <% end -%>
142
144
  <h2 param="heading">
143
- <ht key="<%= model_key %>.show.heading" name="&this.respond_to?(:to_s) ? this.to_s : ''">
145
+ <ht key="<%= model_key %>.show.heading" name="&this.respond_to?(:name) ? this.name : ''">
144
146
  <name/>
145
147
  </ht>
146
148
  </h2>
@@ -165,7 +167,7 @@ end
165
167
  <% end -%>
166
168
  </header>
167
169
 
168
- <section param="content-body"<%= ' with-flash-messages' if aside_collections %>>
170
+ <section param="content-body">
169
171
  <% if main_content -%>
170
172
  <view:<%= main_content %> param="description"/>
171
173
  <% end -%>
@@ -175,8 +177,8 @@ end
175
177
  <% if collection -%>
176
178
  <section param="collection-section">
177
179
  <h3 param="collection-heading">
178
- <ht key="<%= collection.to_s.tableize %>.collection.heading.<%= (is_user_model ? 'your':'other') %>" >
179
- <%= '<Your/>' if is_user_model %><%= collection.to_s.titleize %>
180
+ <ht key="<%= collection_class.to_s.underscore %>.collection.heading" count="&this.<%= collection.to_s %>.count" >
181
+ <human-collection-name collection="<%= collection %>" your/>
180
182
  </ht>
181
183
  </h3>
182
184
 
@@ -188,21 +190,21 @@ end
188
190
  <% if add_link -%>
189
191
 
190
192
  <a:<%= collection %> action="new" if="&can_create?(@<%= model.name.underscore %>.<%= collection %>)" param="new-link">
191
- <ht key="<%= collection.to_s.pluralize %>.actions.new">
192
- New <%= collection.to_s.singularize.titleize %>
193
+ <ht key="<%= collection_class.to_s.underscore %>.actions.new" count="1">
194
+ New <%= collection_class.to_s.titleize %>
193
195
  </ht>
194
196
  </a:<%= collection %>>
195
197
  <% elsif add_form -%>
196
198
 
197
199
  <section param="add-to-collection" if="&can_create?(@<%= model.name.underscore %>.<%= collection %>)">
198
200
  <h3 param="add-form-heading">
199
- <ht key="<%= collection.to_s.pluralize %>.collection.add_form_heading">
200
- Add <%= a_or_an collection.to_s.singularize.titleize %>
201
+ <ht key="<%= collection_class.to_s.underscore %>.collection.add_form_heading" count="1">
202
+ Add <%= a_or_an collection_class.to_s.titleize %>
201
203
  </ht>
202
204
  </h3>
203
205
  <form with="&@<%= collection_class.name.underscore %> || new_for_current_user(@<%= model.name.underscore %>.<%= collection %>)" owner="<%= owner %>" without-cancel param>
204
206
  <field-list: skip="<%= owner %>"/>
205
- <submit: label="#{ht '<%= sq_escape collection.to_s.pluralize %>.actions.add', :default=>['Add'] }"/>
207
+ <submit: label="#{ht '<%= sq_escape collection_class.to_s.underscore %>.actions.add', :default=>['Add'] }"/>
206
208
  </form>
207
209
  </section>
208
210
  <% end -%>
@@ -215,23 +217,14 @@ end
215
217
  <aside param>
216
218
  <% for refl, reverse_refl in aside_collections -%>
217
219
  <% if linkable?(refl.klass, :"index_for_#{reverse_refl.name}") -%>
218
- <preview-with-more:<%= refl.name %>.recent param="<%= refl.name %>-preview">
219
- <% if is_user_model -%>
220
- <heading:>
221
- <ht key="hobo.undefined">
222
- <Your with="&@<%= model.name.underscore %>"/> <%= refl.name.to_s.titleize %>
223
- </ht>
224
- </heading:>
225
- <% end -%>
226
- <more:>
227
- <ht key="hobo.actions.more">more</ht>
228
- </more:>
229
- </preview-with-more:<%= refl.name %>.recent>
220
+ <collection-preview:<%= refl.name %>.recent param="<%= refl.name %>-preview">
221
+ <heading-content:><human-collection-name with="&this.origin" collection="<%= refl.name %>" your/></heading-content:>
222
+ </collection-preview:<%= refl.name %>.recent>
230
223
  <% else -%>
231
224
  <section param="<%= refl.name %>-collection-section">
232
225
  <h3 param="<%= refl.name %>-collection-heading">
233
- <ht key="<%= refl.name.to_s.pluralize %>.collection.heading.other" >
234
- <%= refl.name.to_s.titleize %>
226
+ <ht key="<%= refl.klass.to_s.underscore %>.collection.heading" count="&this.<%= refl.name.to_s %>.count" >
227
+ <human-collection-name collection="<%= refl.name %>" your/>
235
228
  </ht>
236
229
  </h3>
237
230
  <collection:<%= refl.name %> param="<%= refl.name %>-collection"/>
@@ -259,7 +252,7 @@ name_attribute = model.name_attribute
259
252
  <section param="content-header">
260
253
  <h2 param="heading">
261
254
  <ht key="<%= model_key %>.edit.heading" name="&this.respond_to?(:name) ? this.name : ''">
262
- Edit <type-name/>
255
+ Edit <%= model_name %>
263
256
  </ht>
264
257
  </h2>
265
258
  <delete-button label="#{ht '<%= model_key %>.actions.delete', :default=>['Remove This <%= sq_escape model_name %>']}" param/>
@@ -280,7 +273,7 @@ owner_model = model.reflections[owner].klass
280
273
  linkable_owner = linkable?(owner_model, :show)
281
274
  owner_tag = linkable_owner ? "a" : "name"
282
275
 
283
- owner_is_user = owner_model < Hobo::User
276
+ owner_is_user = owner_model < Hobo::Model::UserBase
284
277
 
285
278
  owner = model.reflections[owner].macro == :has_many ? owner.to_s.singularize : owner.to_s
286
279
  -%>
@@ -290,27 +283,21 @@ new_link = :new.in?(actions)
290
283
  -%>
291
284
  <def tag="index-for-<%= owner.dasherize %>-page" polymorphic/>
292
285
  <def tag="index-for-<%= owner.dasherize %>-page" for="<%= model.name %>">
293
- <page merge title="#{ht '<%= model_key %>.index_for_owner.title', :default=>['<%= sq_escape(model_name :plural) %> for']} #{name :with => @<%= owner %>, :no_wrapper => true}">
286
+ <page merge title="#{ht '<%= model_key %>.index_for_owner.title', :count=>100, :default=>['<%= sq_escape(model_name(:plural)) %> for']} #{name :with => @<%= owner %>, :no_wrapper => true}">
294
287
  <body: class="index-for-owner-page <%= owner.dasherize %> <%= model_class %>" param/>
295
288
  <content: param>
296
289
  <header param="content-header">
297
290
  <% if linkable_owner -%>
298
291
  <div param="back-to">
299
- <ht key="hobo.actions.back">Back to</ht> <a with="&@<%= owner %>"/>
292
+ <t key="hobo.actions.back">Back to</t> <a with="&@<%= owner %>"/>
300
293
  </div>
301
294
  <% end -%>
302
- <% if owner_is_user %>
303
- <h2 param="heading">
304
- <ht key="<%= model_key %>.index_for_owner.heading.your">
305
- <Your with="&@<%= owner %>"/> <%= model_name :plural %>
306
- </ht>
307
- </h2>
308
- <% else -%>
309
- <h2 param="heading">
310
- <ht key="<%= model_key %>.index_for_owner.heading.other">
311
- <%= model_name :plural %>
295
+ <h2 param="heading" if="&@owner">
296
+ <ht key="<%= model_key %>.index_for_owner.heading" count="&@<%= owner %>.<%= collection_name %>.count">
297
+ <human-collection-name with="&@<%= owner %>" collection="<%= collection_name %>" your/>
312
298
  </ht>
313
299
  </h2>
300
+ <% unless owner_is_user %>
314
301
  <h3 param="subheading">
315
302
  <ht key="<%= model_key %>.index_for_owner.subheading">
316
303
  For:
@@ -319,8 +306,8 @@ new_link = :new.in?(actions)
319
306
  </h3>
320
307
  <% end -%>
321
308
  <p param="count" if>
322
- <ht key="<%= model_key %>.collection.count" count="&this.size">
323
- There <count prefix="are"/>
309
+ <ht key="<%= model_key %>.collection.count" count="&collection_count">
310
+ <count summary/>
324
311
  </ht>
325
312
  </p>
326
313
  </header>
@@ -382,8 +369,8 @@ new_link = :new.in?(actions)
382
369
  <% creator_names.each do |creator| -%>
383
370
  <def tag="<%= creator.dasherize %>-page" polymorphic/>
384
371
  <def tag="<%= creator.dasherize %>-page" for="<%= model.name %>">
385
-
386
- <page title="#{ht '<%=model_key %>.<%= creator.underscore %>.title', :default=>['<%= sq_escape creator.titleize %>']}" merge>
372
+
373
+ <page title="#{ht '<%=model_key %>.<%= creator.underscore %>.title', :default=>['<%= sq_escape creator.titleize %>']}" merge>
387
374
 
388
375
  <body: class="lifecycle-start-page <%= creator.dasherize %>-page" param/>
389
376