spiderfw 0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (638) hide show
  1. data/README +4 -0
  2. data/Rakefile +74 -0
  3. data/apps/cas_server/_init.rb +23 -0
  4. data/apps/cas_server/config/options.rb +9 -0
  5. data/apps/cas_server/controllers/mixins/cas_login_mixin.rb +353 -0
  6. data/apps/cas_server/lib/cas.rb +329 -0
  7. data/apps/cas_server/lib/utils.rb +30 -0
  8. data/apps/cas_server/models/login_ticket.rb +11 -0
  9. data/apps/cas_server/models/mixins/consumable.rb +10 -0
  10. data/apps/cas_server/models/proxy_granting_ticket.rb +12 -0
  11. data/apps/cas_server/models/proxy_ticket.rb +12 -0
  12. data/apps/cas_server/models/service_ticket.rb +22 -0
  13. data/apps/cas_server/models/ticket.rb +20 -0
  14. data/apps/cas_server/models/ticket_granting_ticket.rb +15 -0
  15. data/apps/cas_server/views/logout.shtml +3 -0
  16. data/apps/core/admin/_init.rb +23 -0
  17. data/apps/core/admin/admin.rb +17 -0
  18. data/apps/core/admin/controllers/admin_controller.rb +15 -0
  19. data/apps/core/admin/views/index.shtml +1 -0
  20. data/apps/core/admin/views/spider_admin.layout.shtml +23 -0
  21. data/apps/core/admin/widgets/admin_menu/admin_menu.rb +17 -0
  22. data/apps/core/auth/_init.rb +42 -0
  23. data/apps/core/auth/controllers/login_controller.rb +94 -0
  24. data/apps/core/auth/controllers/mixins/auth_helper.rb +114 -0
  25. data/apps/core/auth/controllers/mixins/http_basic_auth.rb +24 -0
  26. data/apps/core/auth/controllers/mixins/http_digest_auth.rb +26 -0
  27. data/apps/core/auth/lib/authenticable.rb +86 -0
  28. data/apps/core/auth/lib/authenticator.rb +11 -0
  29. data/apps/core/auth/lib/digest_authenticator.rb +44 -0
  30. data/apps/core/auth/lib/login_authenticator.rb +27 -0
  31. data/apps/core/auth/models/digest_user.rb +34 -0
  32. data/apps/core/auth/models/group.rb +22 -0
  33. data/apps/core/auth/models/login_user.rb +15 -0
  34. data/apps/core/auth/models/mixins/access_control.rb +71 -0
  35. data/apps/core/auth/models/mixins/authentication_tracking.rb +19 -0
  36. data/apps/core/auth/models/super_user.rb +8 -0
  37. data/apps/core/auth/models/user.rb +15 -0
  38. data/apps/core/auth/po/it/spider_auth.po +33 -0
  39. data/apps/core/auth/po/spider_auth.pot +31 -0
  40. data/apps/core/auth/views/login.shtml +25 -0
  41. data/apps/core/components/_init.rb +21 -0
  42. data/apps/core/components/po/it/spider_components.po +52 -0
  43. data/apps/core/components/po/spider_components.pot +53 -0
  44. data/apps/core/components/public/css/admin.css +73 -0
  45. data/apps/core/components/public/css/crud.css +58 -0
  46. data/apps/core/components/public/css/img/add.gif +0 -0
  47. data/apps/core/components/public/css/img/ajax-loader.gif +0 -0
  48. data/apps/core/components/public/css/img/back.gif +0 -0
  49. data/apps/core/components/public/css/img/bg_header.png +0 -0
  50. data/apps/core/components/public/css/img/body_bg.jpg +0 -0
  51. data/apps/core/components/public/css/img/gray_gradient.gif +0 -0
  52. data/apps/core/components/public/css/img/li_bg.png +0 -0
  53. data/apps/core/components/public/css/img/li_bg_active.png +0 -0
  54. data/apps/core/components/public/css/img/li_bg_hover.png +0 -0
  55. data/apps/core/components/public/css/img/logo.png +0 -0
  56. data/apps/core/components/public/css/img/menu_bg.png +0 -0
  57. data/apps/core/components/public/css/img/menu_bottom.png +0 -0
  58. data/apps/core/components/public/css/img/menu_top.png +0 -0
  59. data/apps/core/components/public/css/img/section-bottom.jpg +0 -0
  60. data/apps/core/components/public/css/list.css +7 -0
  61. data/apps/core/components/public/css/menu.css +47 -0
  62. data/apps/core/components/public/css/spider.css +9 -0
  63. data/apps/core/components/public/css/switcher.css +13 -0
  64. data/apps/core/components/public/css/table.css +99 -0
  65. data/apps/core/components/public/js/inheritance.js +71 -0
  66. data/apps/core/components/public/js/jquery/jquery-1.3.2.js +4376 -0
  67. data/apps/core/components/public/js/jquery/jquery-ui/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  68. data/apps/core/components/public/js/jquery/jquery-ui/css/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  69. data/apps/core/components/public/js/jquery/jquery-ui/css/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  70. data/apps/core/components/public/js/jquery/jquery-ui/css/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  71. data/apps/core/components/public/js/jquery/jquery-ui/css/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  72. data/apps/core/components/public/js/jquery/jquery-ui/css/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  73. data/apps/core/components/public/js/jquery/jquery-ui/css/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  74. data/apps/core/components/public/js/jquery/jquery-ui/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  75. data/apps/core/components/public/js/jquery/jquery-ui/css/smoothness/images/ui-icons_222222_256x240.png +0 -0
  76. data/apps/core/components/public/js/jquery/jquery-ui/css/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
  77. data/apps/core/components/public/js/jquery/jquery-ui/css/smoothness/images/ui-icons_454545_256x240.png +0 -0
  78. data/apps/core/components/public/js/jquery/jquery-ui/css/smoothness/images/ui-icons_888888_256x240.png +0 -0
  79. data/apps/core/components/public/js/jquery/jquery-ui/css/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
  80. data/apps/core/components/public/js/jquery/jquery-ui/css/smoothness/jquery-ui-1.7.2.custom.css +406 -0
  81. data/apps/core/components/public/js/jquery/jquery-ui/css/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
  82. data/apps/core/components/public/js/jquery/jquery-ui/css/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
  83. data/apps/core/components/public/js/jquery/jquery-ui/css/ui-lightness/images/ui-bg_flat_10_000000_40x100.png +0 -0
  84. data/apps/core/components/public/js/jquery/jquery-ui/css/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
  85. data/apps/core/components/public/js/jquery/jquery-ui/css/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
  86. data/apps/core/components/public/js/jquery/jquery-ui/css/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  87. data/apps/core/components/public/js/jquery/jquery-ui/css/ui-lightness/images/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
  88. data/apps/core/components/public/js/jquery/jquery-ui/css/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
  89. data/apps/core/components/public/js/jquery/jquery-ui/css/ui-lightness/images/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
  90. data/apps/core/components/public/js/jquery/jquery-ui/css/ui-lightness/images/ui-icons_222222_256x240.png +0 -0
  91. data/apps/core/components/public/js/jquery/jquery-ui/css/ui-lightness/images/ui-icons_228ef1_256x240.png +0 -0
  92. data/apps/core/components/public/js/jquery/jquery-ui/css/ui-lightness/images/ui-icons_ef8c08_256x240.png +0 -0
  93. data/apps/core/components/public/js/jquery/jquery-ui/css/ui-lightness/images/ui-icons_ffd27a_256x240.png +0 -0
  94. data/apps/core/components/public/js/jquery/jquery-ui/css/ui-lightness/images/ui-icons_ffffff_256x240.png +0 -0
  95. data/apps/core/components/public/js/jquery/jquery-ui/css/ui-lightness/jquery-ui-1.7.2.custom.css +406 -0
  96. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/AUTHORS.txt +30 -0
  97. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/GPL-LICENSE.txt +278 -0
  98. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/MIT-LICENSE.txt +25 -0
  99. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/external/bgiframe/ChangeLog.txt +20 -0
  100. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/external/bgiframe/META.json +32 -0
  101. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/external/bgiframe/jquery.bgiframe.js +100 -0
  102. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/external/bgiframe/jquery.bgiframe.min.js +10 -0
  103. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/external/bgiframe/jquery.bgiframe.pack.js +10 -0
  104. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/external/bgiframe/test/index.html +197 -0
  105. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/external/cookie/jquery.cookie.js +97 -0
  106. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/external/cookie/jquery.cookie.min.js +10 -0
  107. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/external/cookie/jquery.cookie.pack.js +10 -0
  108. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/external/cookie/jquery.cookie.zip +0 -0
  109. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/external/jsdiff/jsdiff.js +159 -0
  110. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/external/qunit/testrunner.js +780 -0
  111. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/external/qunit/testsuite.css +120 -0
  112. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/external/simulate/jquery.simulate.js +152 -0
  113. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/jquery-1.3.2.js +4376 -0
  114. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  115. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  116. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  117. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  118. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  119. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  120. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  121. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  122. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/images/ui-icons_222222_256x240.png +0 -0
  123. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/images/ui-icons_2e83ff_256x240.png +0 -0
  124. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/images/ui-icons_454545_256x240.png +0 -0
  125. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/images/ui-icons_888888_256x240.png +0 -0
  126. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/images/ui-icons_cd0a0a_256x240.png +0 -0
  127. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/ui.accordion.css +9 -0
  128. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/ui.all.css +2 -0
  129. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/ui.base.css +8 -0
  130. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/ui.core.css +37 -0
  131. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/ui.datepicker.css +62 -0
  132. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/ui.dialog.css +13 -0
  133. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/ui.progressbar.css +4 -0
  134. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/ui.resizable.css +13 -0
  135. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/ui.slider.css +17 -0
  136. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/ui.tabs.css +11 -0
  137. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/ui.theme.css +245 -0
  138. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
  139. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
  140. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/images/ui-bg_flat_10_000000_40x100.png +0 -0
  141. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
  142. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
  143. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  144. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/images/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
  145. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
  146. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/images/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
  147. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/images/ui-icons_222222_256x240.png +0 -0
  148. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/images/ui-icons_228ef1_256x240.png +0 -0
  149. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/images/ui-icons_ef8c08_256x240.png +0 -0
  150. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/images/ui-icons_ffd27a_256x240.png +0 -0
  151. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/images/ui-icons_ffffff_256x240.png +0 -0
  152. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/jquery-ui-1.7.2.custom.css +406 -0
  153. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/ui.accordion.css +9 -0
  154. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/ui.all.css +2 -0
  155. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/ui.base.css +8 -0
  156. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/ui.core.css +37 -0
  157. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/ui.datepicker.css +62 -0
  158. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/ui.dialog.css +13 -0
  159. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/ui.progressbar.css +4 -0
  160. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/ui.resizable.css +13 -0
  161. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/ui.slider.css +17 -0
  162. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/ui.tabs.css +11 -0
  163. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/ui.theme.css +247 -0
  164. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/effects.blind.js +49 -0
  165. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/effects.bounce.js +78 -0
  166. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/effects.clip.js +54 -0
  167. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/effects.core.js +545 -0
  168. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/effects.drop.js +50 -0
  169. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/effects.explode.js +79 -0
  170. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/effects.fold.js +56 -0
  171. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/effects.highlight.js +48 -0
  172. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/effects.pulsate.js +56 -0
  173. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/effects.scale.js +180 -0
  174. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/effects.shake.js +57 -0
  175. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/effects.slide.js +50 -0
  176. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/effects.transfer.js +45 -0
  177. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/jquery-ui-i18n.js +771 -0
  178. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-ar.js +20 -0
  179. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-bg.js +20 -0
  180. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-ca.js +19 -0
  181. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-cs.js +19 -0
  182. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-da.js +19 -0
  183. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-de.js +19 -0
  184. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-el.js +19 -0
  185. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-eo.js +19 -0
  186. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-es.js +19 -0
  187. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-fa.js +19 -0
  188. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-fi.js +19 -0
  189. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-fr.js +19 -0
  190. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-he.js +19 -0
  191. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-hr.js +19 -0
  192. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-hu.js +19 -0
  193. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-hy.js +19 -0
  194. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-id.js +19 -0
  195. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-is.js +19 -0
  196. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-it.js +19 -0
  197. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-ja.js +20 -0
  198. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-ko.js +19 -0
  199. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-lt.js +19 -0
  200. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-lv.js +19 -0
  201. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-ms.js +19 -0
  202. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-nl.js +19 -0
  203. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-no.js +19 -0
  204. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-pl.js +19 -0
  205. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-pt-BR.js +19 -0
  206. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-ro.js +22 -0
  207. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-ru.js +19 -0
  208. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-sk.js +19 -0
  209. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-sl.js +20 -0
  210. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-sq.js +19 -0
  211. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-sr-SR.js +19 -0
  212. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-sr.js +19 -0
  213. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-sv.js +19 -0
  214. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-th.js +19 -0
  215. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-tr.js +19 -0
  216. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-uk.js +25 -0
  217. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-zh-CN.js +19 -0
  218. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-zh-TW.js +19 -0
  219. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/jquery-ui-1.7.2.custom.js +9133 -0
  220. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/ui.accordion.js +477 -0
  221. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/ui.core.js +519 -0
  222. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/ui.datepicker.js +1636 -0
  223. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/ui.dialog.js +671 -0
  224. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/ui.draggable.js +766 -0
  225. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/ui.droppable.js +282 -0
  226. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/ui.progressbar.js +116 -0
  227. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/ui.resizable.js +800 -0
  228. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/ui.selectable.js +257 -0
  229. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/ui.slider.js +558 -0
  230. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/ui.sortable.js +1019 -0
  231. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/ui.tabs.js +685 -0
  232. data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/version.txt +1 -0
  233. data/apps/core/components/public/js/jquery/jquery-ui/index.html +367 -0
  234. data/apps/core/components/public/js/jquery/jquery-ui/js/jquery-1.3.2.min.js +19 -0
  235. data/apps/core/components/public/js/jquery/jquery-ui/js/jquery-ui-1.7.2.custom.min.js +298 -0
  236. data/apps/core/components/public/js/jquery/plugins/jquery-autocomplete/changelog.txt +27 -0
  237. data/apps/core/components/public/js/jquery/plugins/jquery-autocomplete/jquery.autocomplete.css +48 -0
  238. data/apps/core/components/public/js/jquery/plugins/jquery-autocomplete/jquery.autocomplete.js +808 -0
  239. data/apps/core/components/public/js/jquery/plugins/jquery-autocomplete/jquery.autocomplete.min.js +13 -0
  240. data/apps/core/components/public/js/jquery/plugins/jquery-autocomplete/jquery.autocomplete.pack.js +12 -0
  241. data/apps/core/components/public/js/jquery/plugins/jquery-autocomplete/lib/jquery.ajaxQueue.js +116 -0
  242. data/apps/core/components/public/js/jquery/plugins/jquery-autocomplete/lib/jquery.bgiframe.min.js +10 -0
  243. data/apps/core/components/public/js/jquery/plugins/jquery-autocomplete/lib/jquery.js +3558 -0
  244. data/apps/core/components/public/js/jquery/plugins/jquery-autocomplete/lib/thickbox-compressed.js +10 -0
  245. data/apps/core/components/public/js/jquery/plugins/jquery-autocomplete/lib/thickbox.css +163 -0
  246. data/apps/core/components/public/js/jquery/plugins/jquery-autocomplete/todo +166 -0
  247. data/apps/core/components/public/js/jquery/plugins/jquery.form.js +653 -0
  248. data/apps/core/components/public/js/jquery/plugins/jquery.query-2.1.6.js +224 -0
  249. data/apps/core/components/public/js/jquery/plugins/jquery.url.js +214 -0
  250. data/apps/core/components/public/js/jquery/plugins/jtree/jquery.jtree.1.0.js +187 -0
  251. data/apps/core/components/public/js/jquery/plugins/jtree/jquery.jtree.1.0.min.js +1 -0
  252. data/apps/core/components/public/js/jquery/plugins/jtree/jquery.jtree.spider.1.0.js +193 -0
  253. data/apps/core/components/public/js/jquery/plugins/treeview/changelog.txt +35 -0
  254. data/apps/core/components/public/js/jquery/plugins/treeview/images/ajax-loader.gif +0 -0
  255. data/apps/core/components/public/js/jquery/plugins/treeview/images/file.gif +0 -0
  256. data/apps/core/components/public/js/jquery/plugins/treeview/images/folder-closed.gif +0 -0
  257. data/apps/core/components/public/js/jquery/plugins/treeview/images/folder.gif +0 -0
  258. data/apps/core/components/public/js/jquery/plugins/treeview/images/minus.gif +0 -0
  259. data/apps/core/components/public/js/jquery/plugins/treeview/images/plus.gif +0 -0
  260. data/apps/core/components/public/js/jquery/plugins/treeview/images/treeview-black-line.gif +0 -0
  261. data/apps/core/components/public/js/jquery/plugins/treeview/images/treeview-black.gif +0 -0
  262. data/apps/core/components/public/js/jquery/plugins/treeview/images/treeview-default-line.gif +0 -0
  263. data/apps/core/components/public/js/jquery/plugins/treeview/images/treeview-default.gif +0 -0
  264. data/apps/core/components/public/js/jquery/plugins/treeview/images/treeview-famfamfam-line.gif +0 -0
  265. data/apps/core/components/public/js/jquery/plugins/treeview/images/treeview-famfamfam.gif +0 -0
  266. data/apps/core/components/public/js/jquery/plugins/treeview/images/treeview-gray-line.gif +0 -0
  267. data/apps/core/components/public/js/jquery/plugins/treeview/images/treeview-gray.gif +0 -0
  268. data/apps/core/components/public/js/jquery/plugins/treeview/images/treeview-red-line.gif +0 -0
  269. data/apps/core/components/public/js/jquery/plugins/treeview/images/treeview-red.gif +0 -0
  270. data/apps/core/components/public/js/jquery/plugins/treeview/jquery.treeview.async.js +110 -0
  271. data/apps/core/components/public/js/jquery/plugins/treeview/jquery.treeview.css +74 -0
  272. data/apps/core/components/public/js/jquery/plugins/treeview/jquery.treeview.edit.js +37 -0
  273. data/apps/core/components/public/js/jquery/plugins/treeview/jquery.treeview.js +256 -0
  274. data/apps/core/components/public/js/jquery/plugins/treeview/jquery.treeview.min.js +15 -0
  275. data/apps/core/components/public/js/jquery/plugins/treeview/jquery.treeview.pack.js +16 -0
  276. data/apps/core/components/public/js/jquery/plugins/treeview/jquery.treeview.sortable.js +386 -0
  277. data/apps/core/components/public/js/jquery/plugins/treeview/lib/jquery.cookie.js +92 -0
  278. data/apps/core/components/public/js/jquery/plugins/treeview/lib/jquery.js +3534 -0
  279. data/apps/core/components/public/js/jquery/plugins/treeview/todo +8 -0
  280. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/LICENSE +0 -0
  281. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/README +0 -0
  282. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/jquery.timepickr.css +34 -0
  283. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/jquery.timepickr.js +1413 -0
  284. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/jquery.timepickr.min.js +10 -0
  285. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/images/222222_256x240_icons_icons.png +0 -0
  286. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/images/2e83ff_256x240_icons_icons.png +0 -0
  287. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/images/454545_256x240_icons_icons.png +0 -0
  288. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/images/888888_256x240_icons_icons.png +0 -0
  289. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/images/aaaaaa_40x100_textures_01_flat_0.png +0 -0
  290. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/images/cccccc_40x100_textures_03_highlight_soft_75.png +0 -0
  291. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/images/cd0a0a_256x240_icons_icons.png +0 -0
  292. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/images/dadada_40x100_textures_02_glass_75.png +0 -0
  293. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/images/e6e6e6_40x100_textures_02_glass_75.png +0 -0
  294. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/images/fbf9ee_40x100_textures_02_glass_55.png +0 -0
  295. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/images/fef1ec_40x100_textures_05_inset_soft_95.png +0 -0
  296. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/images/ffffff_40x100_textures_02_glass_65.png +0 -0
  297. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/images/ffffff_40x100_textures_02_glass_75.png +0 -0
  298. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/ui.accordion.css +9 -0
  299. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/ui.all.css +4 -0
  300. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/ui.allplugins.css +7 -0
  301. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/ui.core.css +37 -0
  302. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/ui.datepicker.css +60 -0
  303. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/ui.dialog.css +13 -0
  304. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/ui.progressbar.css +4 -0
  305. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/ui.resizable.css +13 -0
  306. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/ui.slider.css +13 -0
  307. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/ui.tabs.css +9 -0
  308. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/ui.theme.css +238 -0
  309. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/ui.timepickr.css +44 -0
  310. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/ui.timepickr.js +2307 -0
  311. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/ui.timepickr.min.js +10 -0
  312. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/page/body-bg.png +0 -0
  313. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/page/btn-hover.png +0 -0
  314. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/page/btn.png +0 -0
  315. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/page/buttons.psd +0 -0
  316. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/page/index.html +326 -0
  317. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/page/jquery.anchorHandler.js +44 -0
  318. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/page/jquery.strings.js +290 -0
  319. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/page/jquery.ui.all.js +518 -0
  320. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/page/jquery.utils.js +255 -0
  321. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/page/jquery.utils.ui.min.js +26 -0
  322. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/page/logo-ff.png +0 -0
  323. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/page/logo-ie.png +0 -0
  324. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/page/logo.png +0 -0
  325. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/page/logo.psd +0 -0
  326. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/page/package.png +0 -0
  327. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/page/reset.css +48 -0
  328. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/page/script_code.png +0 -0
  329. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/page/styles.css +308 -0
  330. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/page/themeroller_ready_black_200px.gif +0 -0
  331. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/page/weather_sun.png +0 -0
  332. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/src/css/ui.dropslide.css +26 -0
  333. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/src/css/ui.timepickr.css +18 -0
  334. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/src/ui.dropslide.js +132 -0
  335. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/src/ui.timepickr.js +217 -0
  336. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/test/data/testrunner.js +1 -0
  337. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/test/index.html +34 -0
  338. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/test/otherlibs/jquery/1.2.1/jquery.js +11 -0
  339. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/test/otherlibs/jquery/1.2.3/jquery.js +11 -0
  340. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/test/qunit/testrunner.js +780 -0
  341. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/test/qunit/testsuite.css +120 -0
  342. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/test/styles.css +201 -0
  343. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/test/test.js +41 -0
  344. data/apps/core/components/public/js/jquery/plugins/ui-timepickr/test/unit/ui.timepickr.js +154 -0
  345. data/apps/core/components/public/js/list.js +234 -0
  346. data/apps/core/components/public/js/spider.js +484 -0
  347. data/apps/core/components/public/widgets/search_table.js +10 -0
  348. data/apps/core/components/public/widgets/table.js +9 -0
  349. data/apps/core/components/widgets/admin/admin.rb +75 -0
  350. data/apps/core/components/widgets/admin/admin.shtml +16 -0
  351. data/apps/core/components/widgets/confirm/confirm.rb +37 -0
  352. data/apps/core/components/widgets/confirm/confirm.shtml +9 -0
  353. data/apps/core/components/widgets/crud/crud.rb +119 -0
  354. data/apps/core/components/widgets/crud/crud.shtml +59 -0
  355. data/apps/core/components/widgets/crud/form_table/form_table.rb +13 -0
  356. data/apps/core/components/widgets/crud/form_table/form_table.shtml +3 -0
  357. data/apps/core/components/widgets/list/list.rb +294 -0
  358. data/apps/core/components/widgets/list/list.shtml +38 -0
  359. data/apps/core/components/widgets/list/plugins/exportable/exportable_list.rb +19 -0
  360. data/apps/core/components/widgets/list/plugins/exportable/exportable_list.shtml +5 -0
  361. data/apps/core/components/widgets/menu/menu.rb +20 -0
  362. data/apps/core/components/widgets/menu/menu.shtml +13 -0
  363. data/apps/core/components/widgets/search_table/search_table.rb +22 -0
  364. data/apps/core/components/widgets/search_table/search_table.shtml +11 -0
  365. data/apps/core/components/widgets/switcher/switcher.rb +68 -0
  366. data/apps/core/components/widgets/switcher/switcher.shtml +17 -0
  367. data/apps/core/components/widgets/table/table.rb +153 -0
  368. data/apps/core/components/widgets/table/table.shtml +41 -0
  369. data/apps/core/components/widgets/tabs/tabs.rb +38 -0
  370. data/apps/core/components/widgets/tabs/tabs.shtml +2 -0
  371. data/apps/core/forms/_init.rb +22 -0
  372. data/apps/core/forms/po/it/spider_forms.po +36 -0
  373. data/apps/core/forms/po/spider_forms.pot +37 -0
  374. data/apps/core/forms/public/date_time.js +23 -0
  375. data/apps/core/forms/public/form.css +81 -0
  376. data/apps/core/forms/public/form.js +19 -0
  377. data/apps/core/forms/public/input.js +30 -0
  378. data/apps/core/forms/public/search_select.js +123 -0
  379. data/apps/core/forms/public/select.js +11 -0
  380. data/apps/core/forms/tags/element_label.erb +1 -0
  381. data/apps/core/forms/tags/element_row.erb +4 -0
  382. data/apps/core/forms/tags/row.erb +4 -0
  383. data/apps/core/forms/widgets/form/form.rb +430 -0
  384. data/apps/core/forms/widgets/form/form.shtml +47 -0
  385. data/apps/core/forms/widgets/inputs/checkbox/checkbox.rb +13 -0
  386. data/apps/core/forms/widgets/inputs/checkbox/checkbox.shtml +3 -0
  387. data/apps/core/forms/widgets/inputs/date_time/date_time.rb +48 -0
  388. data/apps/core/forms/widgets/inputs/date_time/date_time.shtml +4 -0
  389. data/apps/core/forms/widgets/inputs/hidden/hidden.rb +8 -0
  390. data/apps/core/forms/widgets/inputs/hidden/hidden.shtml +3 -0
  391. data/apps/core/forms/widgets/inputs/input/input.rb +105 -0
  392. data/apps/core/forms/widgets/inputs/input/input.shtml +3 -0
  393. data/apps/core/forms/widgets/inputs/input/readonly.shtml +6 -0
  394. data/apps/core/forms/widgets/inputs/password/password.rb +24 -0
  395. data/apps/core/forms/widgets/inputs/password/password.shtml +5 -0
  396. data/apps/core/forms/widgets/inputs/search_select/search_select.rb +168 -0
  397. data/apps/core/forms/widgets/inputs/search_select/search_select.shtml +42 -0
  398. data/apps/core/forms/widgets/inputs/select/select.rb +126 -0
  399. data/apps/core/forms/widgets/inputs/select/select.shtml +8 -0
  400. data/apps/core/forms/widgets/inputs/subform/subform.rb +10 -0
  401. data/apps/core/forms/widgets/inputs/subform/subform.shtml +5 -0
  402. data/apps/core/forms/widgets/inputs/text/text.rb +9 -0
  403. data/apps/core/forms/widgets/inputs/text/text.shtml +3 -0
  404. data/apps/core/forms/widgets/inputs/text_area/text_area.rb +10 -0
  405. data/apps/core/forms/widgets/inputs/text_area/text_area.shtml +1 -0
  406. data/apps/drb_server/_init.rb +8 -0
  407. data/apps/drb_server/lib/model_server.rb +41 -0
  408. data/apps/drb_server/script/start_server.rb +19 -0
  409. data/apps/hippo/_init.rb +10 -0
  410. data/apps/hippo/models/mixins/hippo_struct.rb +210 -0
  411. data/apps/hippo/models/security_group.rb +21 -0
  412. data/apps/hippo/models/security_user.rb +29 -0
  413. data/apps/messenger/_init.rb +24 -0
  414. data/apps/messenger/config/options.rb +15 -0
  415. data/apps/messenger/config/worker.rb +3 -0
  416. data/apps/messenger/controllers/messenger_controller.rb +80 -0
  417. data/apps/messenger/controllers/mixins/messenger_controller_mixin.rb +54 -0
  418. data/apps/messenger/lib/backends/email/smtp.rb +0 -0
  419. data/apps/messenger/messenger.rb +94 -0
  420. data/apps/messenger/models/email.rb +22 -0
  421. data/apps/messenger/models/message.rb +44 -0
  422. data/apps/messenger/po/it/spider_messenger.po +77 -0
  423. data/apps/messenger/po/spider_messenger.pot +77 -0
  424. data/apps/messenger/views/index.shtml +13 -0
  425. data/apps/messenger/views/list.shtml +5 -0
  426. data/apps/messenger/views/messenger.layout.shtml +4 -0
  427. data/apps/messenger/views/queue.shtml +8 -0
  428. data/apps/messenger/views/tags/sent_messages.shtml +1 -0
  429. data/apps/soap/_init.rb +16 -0
  430. data/apps/soap/controllers/soap_controller.rb +334 -0
  431. data/apps/soap/doc/examples/example.rb +28 -0
  432. data/apps/soap/lib/soap.rb +217 -0
  433. data/apps/sso/_init.rb +16 -0
  434. data/apps/sso/config/options.rb +13 -0
  435. data/apps/sso/controllers/mixins/saml2_mixin.rb +149 -0
  436. data/apps/sso/lib/saml2/backend.rb +16 -0
  437. data/apps/sso/lib/saml2/lasso_backend.rb +88 -0
  438. data/apps/sso/lib/saml2.rb +112 -0
  439. data/apps/sso/views/saml2_post.shtml +19 -0
  440. data/apps/webdav/_init.rb +15 -0
  441. data/apps/webdav/controllers/webdav_controller.rb +862 -0
  442. data/apps/webdav/lib/locking.rb +193 -0
  443. data/apps/webdav/lib/vfs/abstract.rb +78 -0
  444. data/apps/webdav/lib/vfs/local.rb +126 -0
  445. data/apps/webdav/lib/vfs/mapped.rb +235 -0
  446. data/apps/webdav/lib/vfs/properties.rb +41 -0
  447. data/apps/worker/_init.rb +13 -0
  448. data/apps/worker/cmd.rb +65 -0
  449. data/apps/worker/config/options.rb +9 -0
  450. data/apps/worker/lib/runner.rb +43 -0
  451. data/apps/worker/lib/task.rb +9 -0
  452. data/apps/worker/models/job.rb +20 -0
  453. data/apps/worker/worker.rb +168 -0
  454. data/bin/spider +9 -0
  455. data/blueprints/app/_init.rb +10 -0
  456. data/blueprints/app/controllers/__APP___controller.rb +14 -0
  457. data/blueprints/app/views/__APP__.layout.shtml +8 -0
  458. data/blueprints/app/views/index.shtml +3 -0
  459. data/blueprints/install/config/config.yml +14 -0
  460. data/blueprints/install/init.rb +3 -0
  461. data/data/locale/it/LC_MESSAGES/spider.mo +0 -0
  462. data/data/locale/it/LC_MESSAGES/spider_auth.mo +0 -0
  463. data/data/locale/it/LC_MESSAGES/spider_components.mo +0 -0
  464. data/data/locale/it/LC_MESSAGES/spider_forms.mo +0 -0
  465. data/data/locale/it/LC_MESSAGES/spider_messenger.mo +0 -0
  466. data/lib/spiderfw/app.rb +186 -0
  467. data/lib/spiderfw/autoload.rb +12 -0
  468. data/lib/spiderfw/cache/template_cache.rb +136 -0
  469. data/lib/spiderfw/cmd/cmd.rb +72 -0
  470. data/lib/spiderfw/cmd/commands/cert.rb +427 -0
  471. data/lib/spiderfw/cmd/commands/console.rb +27 -0
  472. data/lib/spiderfw/cmd/commands/init.rb +52 -0
  473. data/lib/spiderfw/cmd/commands/model.rb +131 -0
  474. data/lib/spiderfw/cmd/commands/setup.rb +54 -0
  475. data/lib/spiderfw/cmd/commands/test.rb +61 -0
  476. data/lib/spiderfw/cmd/commands/webserver.rb +93 -0
  477. data/lib/spiderfw/config/configurable.rb +38 -0
  478. data/lib/spiderfw/config/configuration.rb +281 -0
  479. data/lib/spiderfw/config/options/spider.rb +122 -0
  480. data/lib/spiderfw/controller/app_controller.rb +14 -0
  481. data/lib/spiderfw/controller/controller.rb +321 -0
  482. data/lib/spiderfw/controller/controller_exceptions.rb +27 -0
  483. data/lib/spiderfw/controller/controller_io.rb +26 -0
  484. data/lib/spiderfw/controller/controller_mixin.rb +12 -0
  485. data/lib/spiderfw/controller/cookie.rb +20 -0
  486. data/lib/spiderfw/controller/cookies.rb +17 -0
  487. data/lib/spiderfw/controller/dispatcher.rb +269 -0
  488. data/lib/spiderfw/controller/first_responder.rb +59 -0
  489. data/lib/spiderfw/controller/formats/html.rb +90 -0
  490. data/lib/spiderfw/controller/helpers/widget_helper.rb +73 -0
  491. data/lib/spiderfw/controller/home_controller.rb +26 -0
  492. data/lib/spiderfw/controller/http_controller.rb +112 -0
  493. data/lib/spiderfw/controller/mixins/http_mixin.rb +228 -0
  494. data/lib/spiderfw/controller/mixins/static_content.rb +90 -0
  495. data/lib/spiderfw/controller/mixins/visual.rb +489 -0
  496. data/lib/spiderfw/controller/page_controller.rb +39 -0
  497. data/lib/spiderfw/controller/request.rb +48 -0
  498. data/lib/spiderfw/controller/response.rb +50 -0
  499. data/lib/spiderfw/controller/router.rb +9 -0
  500. data/lib/spiderfw/controller/scene.rb +27 -0
  501. data/lib/spiderfw/controller/session/file_session.rb +83 -0
  502. data/lib/spiderfw/controller/session/flash_hash.rb +56 -0
  503. data/lib/spiderfw/controller/session/memory_session.rb +62 -0
  504. data/lib/spiderfw/controller/session/transient_hash.rb +18 -0
  505. data/lib/spiderfw/controller/session.rb +106 -0
  506. data/lib/spiderfw/controller/spider_controller.rb +20 -0
  507. data/lib/spiderfw/create.rb +64 -0
  508. data/lib/spiderfw/env.rb +17 -0
  509. data/lib/spiderfw/exceptions.rb +2 -0
  510. data/lib/spiderfw/home.rb +26 -0
  511. data/lib/spiderfw/http/adapters/cgi.rb +62 -0
  512. data/lib/spiderfw/http/adapters/cgi_io.rb +27 -0
  513. data/lib/spiderfw/http/adapters/fcgi.rb +62 -0
  514. data/lib/spiderfw/http/adapters/mongrel.rb +207 -0
  515. data/lib/spiderfw/http/adapters/rack.rb +152 -0
  516. data/lib/spiderfw/http/adapters/thin.rb +38 -0
  517. data/lib/spiderfw/http/adapters/webrick.rb +176 -0
  518. data/lib/spiderfw/http/http.rb +297 -0
  519. data/lib/spiderfw/http/server.rb +40 -0
  520. data/lib/spiderfw/i18n/cldr.rb +114 -0
  521. data/lib/spiderfw/i18n/gettext.rb +8 -0
  522. data/lib/spiderfw/i18n/i18n.rb +120 -0
  523. data/lib/spiderfw/i18n/provider.rb +12 -0
  524. data/lib/spiderfw/i18n/rails.rb +89 -0
  525. data/lib/spiderfw/i18n/shtml_parser.rb +41 -0
  526. data/lib/spiderfw/model/active_record.rb +335 -0
  527. data/lib/spiderfw/model/base_model.rb +2068 -0
  528. data/lib/spiderfw/model/condition.rb +416 -0
  529. data/lib/spiderfw/model/data_type.rb +75 -0
  530. data/lib/spiderfw/model/datatypes/binary.rb +10 -0
  531. data/lib/spiderfw/model/datatypes/bool.rb +10 -0
  532. data/lib/spiderfw/model/datatypes/decimal.rb +22 -0
  533. data/lib/spiderfw/model/datatypes/email.rb +13 -0
  534. data/lib/spiderfw/model/datatypes/password.rb +63 -0
  535. data/lib/spiderfw/model/datatypes/serialized_object.rb +32 -0
  536. data/lib/spiderfw/model/datatypes/text.rb +10 -0
  537. data/lib/spiderfw/model/datatypes/uuid.rb +29 -0
  538. data/lib/spiderfw/model/datatypes.rb +19 -0
  539. data/lib/spiderfw/model/element.rb +250 -0
  540. data/lib/spiderfw/model/extended_models/managed.rb +43 -0
  541. data/lib/spiderfw/model/identity_mapper.rb +89 -0
  542. data/lib/spiderfw/model/inline_model.rb +37 -0
  543. data/lib/spiderfw/model/mappers/db_mapper.rb +1126 -0
  544. data/lib/spiderfw/model/mappers/hash_mapper.rb +114 -0
  545. data/lib/spiderfw/model/mappers/mapper.rb +926 -0
  546. data/lib/spiderfw/model/mappers/mappers.rb +7 -0
  547. data/lib/spiderfw/model/mappers/proxy_mapper.rb +25 -0
  548. data/lib/spiderfw/model/mappers/vfs/flat_file.rb +31 -0
  549. data/lib/spiderfw/model/mixins/converted.rb +110 -0
  550. data/lib/spiderfw/model/mixins/list.rb +135 -0
  551. data/lib/spiderfw/model/mixins/mixins.rb +7 -0
  552. data/lib/spiderfw/model/mixins/state_machine.rb +81 -0
  553. data/lib/spiderfw/model/mixins/synchronized.rb +88 -0
  554. data/lib/spiderfw/model/mixins/tree.rb +296 -0
  555. data/lib/spiderfw/model/mixins/versioned.rb +147 -0
  556. data/lib/spiderfw/model/model.rb +226 -0
  557. data/lib/spiderfw/model/model_hash.rb +50 -0
  558. data/lib/spiderfw/model/proxy_model.rb +37 -0
  559. data/lib/spiderfw/model/query.rb +147 -0
  560. data/lib/spiderfw/model/query_funcs.rb +99 -0
  561. data/lib/spiderfw/model/query_set.rb +700 -0
  562. data/lib/spiderfw/model/request.rb +80 -0
  563. data/lib/spiderfw/model/storage/base_storage.rb +110 -0
  564. data/lib/spiderfw/model/storage/db/adapters/mssql.rb +129 -0
  565. data/lib/spiderfw/model/storage/db/adapters/mysql.rb +436 -0
  566. data/lib/spiderfw/model/storage/db/adapters/oci8.rb +533 -0
  567. data/lib/spiderfw/model/storage/db/adapters/sqlite.rb +163 -0
  568. data/lib/spiderfw/model/storage/db/connectors/odbc.rb +213 -0
  569. data/lib/spiderfw/model/storage/db/db.rb +13 -0
  570. data/lib/spiderfw/model/storage/db/db_connection_pool.rb +126 -0
  571. data/lib/spiderfw/model/storage/db/db_connector.rb +9 -0
  572. data/lib/spiderfw/model/storage/db/db_schema.rb +233 -0
  573. data/lib/spiderfw/model/storage/db/db_storage.rb +872 -0
  574. data/lib/spiderfw/model/storage/db/dialects/no_total_rows.rb +18 -0
  575. data/lib/spiderfw/model/storage/db/reflector.rb +36 -0
  576. data/lib/spiderfw/model/storage/null_storage.rb +18 -0
  577. data/lib/spiderfw/model/storage/schema.rb +19 -0
  578. data/lib/spiderfw/model/storage.rb +72 -0
  579. data/lib/spiderfw/model/sync.rb +94 -0
  580. data/lib/spiderfw/model/type.rb +13 -0
  581. data/lib/spiderfw/model/unit_of_work.rb +75 -0
  582. data/lib/spiderfw/requires.rb +21 -0
  583. data/lib/spiderfw/resource.rb +18 -0
  584. data/lib/spiderfw/setup/setup_task.rb +44 -0
  585. data/lib/spiderfw/tag/tag.rb +28 -0
  586. data/lib/spiderfw/templates/blocks/attr_if.rb +32 -0
  587. data/lib/spiderfw/templates/blocks/comment.rb +38 -0
  588. data/lib/spiderfw/templates/blocks/debugger.rb +16 -0
  589. data/lib/spiderfw/templates/blocks/each.rb +51 -0
  590. data/lib/spiderfw/templates/blocks/html.rb +70 -0
  591. data/lib/spiderfw/templates/blocks/if.rb +35 -0
  592. data/lib/spiderfw/templates/blocks/pass.rb +16 -0
  593. data/lib/spiderfw/templates/blocks/render.rb +23 -0
  594. data/lib/spiderfw/templates/blocks/run.rb +30 -0
  595. data/lib/spiderfw/templates/blocks/tag.rb +21 -0
  596. data/lib/spiderfw/templates/blocks/tag_if.rb +29 -0
  597. data/lib/spiderfw/templates/blocks/text.rb +43 -0
  598. data/lib/spiderfw/templates/blocks/widget.rb +58 -0
  599. data/lib/spiderfw/templates/blocks/yield.rb +18 -0
  600. data/lib/spiderfw/templates/layout.rb +44 -0
  601. data/lib/spiderfw/templates/resources/less.rb +14 -0
  602. data/lib/spiderfw/templates/template.rb +625 -0
  603. data/lib/spiderfw/templates/template_blocks.rb +185 -0
  604. data/lib/spiderfw/utils/annotations.rb +201 -0
  605. data/lib/spiderfw/utils/fork.rb +75 -0
  606. data/lib/spiderfw/utils/hash_comparison.rb +8 -0
  607. data/lib/spiderfw/utils/inflector.rb +42 -0
  608. data/lib/spiderfw/utils/logger.rb +92 -0
  609. data/lib/spiderfw/utils/monkey/class.rb +59 -0
  610. data/lib/spiderfw/utils/monkey/date_time.rb +34 -0
  611. data/lib/spiderfw/utils/monkey/debugger.rb +25 -0
  612. data/lib/spiderfw/utils/monkey/exception.rb +23 -0
  613. data/lib/spiderfw/utils/monkey/kernel.rb +12 -0
  614. data/lib/spiderfw/utils/monkey/module.rb +12 -0
  615. data/lib/spiderfw/utils/monkey/symbol.rb +7 -0
  616. data/lib/spiderfw/utils/monkey/time.rb +17 -0
  617. data/lib/spiderfw/utils/multi_level_hash.rb +33 -0
  618. data/lib/spiderfw/utils/periodic_runner.rb +50 -0
  619. data/lib/spiderfw/utils/rails.rb +78 -0
  620. data/lib/spiderfw/utils/rails_app.rb +53 -0
  621. data/lib/spiderfw/utils/sanitizer.rb +178 -0
  622. data/lib/spiderfw/utils/setup_task.rb +35 -0
  623. data/lib/spiderfw/utils/shared_store/file_shared_store.rb +73 -0
  624. data/lib/spiderfw/utils/shared_store/memory_shared_store.rb +46 -0
  625. data/lib/spiderfw/utils/shared_store.rb +47 -0
  626. data/lib/spiderfw/utils/test_case.rb +24 -0
  627. data/lib/spiderfw/utils/thread_out.rb +24 -0
  628. data/lib/spiderfw/version.rb +3 -0
  629. data/lib/spiderfw/widget/rest_model.rb +20 -0
  630. data/lib/spiderfw/widget/widget.rb +671 -0
  631. data/lib/spiderfw/widget/widget_attributes.rb +54 -0
  632. data/lib/spiderfw/widget/widget_plugin.rb +37 -0
  633. data/lib/spiderfw.rb +509 -0
  634. data/spider.gemspec +43 -0
  635. data/views/errors/404.shtml +6 -0
  636. data/views/errors/error.layout.shtml +70 -0
  637. data/views/errors/error_generic.shtml +6 -0
  638. metadata +800 -0
@@ -0,0 +1,2068 @@
1
+ require 'spiderfw/model/mixins/state_machine'
2
+ require 'spiderfw/model/element'
3
+ require 'iconv'
4
+
5
+ module Spider; module Model
6
+
7
+ # The main class for interacting with data.
8
+ # When not dealing with legacy storages, subclasses should use Managed instead, which provides an id and
9
+ # other conveniences.
10
+ #
11
+ # Each BaseModel subclass defines a model; instances can be used as "data objects":
12
+ # they will interact with the Mapper loading and saving the values associated with the BaseModel's defined elements.
13
+ #
14
+ # Each element defines an instance variable, a getter and a setter. If the instance is set to #autoload,
15
+ # when a getter is first called the mapper will fetch the value from the Storage .
16
+ #
17
+ # Elements can be of one of the base types (Spider::Model.base_types), of a DataType, or other models. In the last
18
+ # case, they define a relationship between models.
19
+ #
20
+ # Basic usage:
21
+ # model Food < BaseModel
22
+ # element :name, String
23
+ # end
24
+ # model Animal < BaseModel
25
+ # element :name, String
26
+ # many :friends, Animal
27
+ # choice :favorite_food, Food
28
+ # end
29
+ #
30
+ # salmon = Food.new(:name => 'Salmon')
31
+ # salmon.save
32
+ # cat = Animal.new(:name => 'Cat', :favorite_food = salmon)
33
+ # weasel = Animal.new(:name => 'Weasel', :friends => [cat])
34
+ # weasel.save
35
+ # cat.friends << weasel
36
+ # cat.save
37
+ #
38
+ # wizzy = Animal.load(:name => 'Weasel')
39
+ # p wizzy.friends
40
+ # => 'Cat'
41
+ # p wizzy.friends[0].favorite_food
42
+ # => 'Salmon'
43
+ #
44
+ # bear = Animal.new(:name => 'Bear', :favorite_food = salmon)
45
+ # bear.save
46
+ # salmon_lovers = Animal.where{ favorite_food == salmon }
47
+ # p salmon_lovers.length
48
+ # => 2
49
+ # p salmon_lovers[0].name
50
+ # => 'Cat'
51
+
52
+
53
+ class BaseModel
54
+ include Spider::Logger
55
+ include DataTypes
56
+ include StateMachine
57
+
58
+ # The BaseModel class itself. Used when dealing with proxy objects.
59
+ attr_reader :model
60
+ # An Hash of loaded elements
61
+ attr_reader :loaded_elements
62
+ # Model instance or QuerySet containing the object
63
+ attr_accessor :_parent
64
+ # If _parent is a model instance, which element points to this one
65
+ attr_accessor :_parent_element
66
+
67
+ class <<self
68
+ # An Hash of model attributes. They can be used freely.
69
+ attr_reader :attributes
70
+ # An array of element names, in definition order.
71
+ attr_reader :elements_order
72
+ # An Hash of integrated models => corresponding integrated element name.
73
+ attr_reader :integrated_models
74
+ # An Hash of polymorphic models => polymorphic params
75
+ attr_reader :polymorphic_models
76
+ # An Array of named sequences.
77
+ attr_reader :sequences
78
+ end
79
+
80
+
81
+
82
+ # Copies this class' elements to the subclass.
83
+ def self.inherited(subclass) #:nodoc:
84
+ # FIXME: might need to clone every element
85
+ @subclasses ||= []
86
+ @subclasses << subclass
87
+ each_element do |el|
88
+ subclass.add_element(el.clone)
89
+ end
90
+ subclass.instance_variable_set("@mapper_procs_subclass", @mapper_procs_subclass.clone) if @mapper_procs_subclass
91
+ subclass.instance_variable_set("@mapper_modules", @mapper_modules.clone) if @mapper_modules
92
+ end
93
+
94
+ # Returns the parent Spider::App of the module
95
+ def self.app
96
+ return @app if @app
97
+ app = self
98
+ while (!app.include?(Spider::App))
99
+ app = app.parent_module
100
+ end
101
+ @app = app
102
+ end
103
+
104
+ # Returns an instance of the Model with #autoload set to false
105
+ def self.static(value=nil)
106
+ obj = self.new(value)
107
+ obj.autoload = false
108
+ return obj
109
+ end
110
+
111
+ #######################################
112
+ # Model definition methods #
113
+ #######################################
114
+
115
+ # Defines an element.
116
+ # Arguments are element name (a Symbol), element type, and a Hash of attributes.
117
+ #
118
+ # Type may be a Class: a base type (see Spider::Model.base_types), a DataType subclass,
119
+ # or a BaseModel subclass; or an Array or a Hash, in which case an InlineModel will be created.
120
+ #
121
+ # An Element instance will be available in Model::BaseModel.elements; getter and setter methods will be defined on
122
+ # the class.
123
+ #
124
+ # If a block is passed to this method, type will be 'extended': a custom junction association will be created,
125
+ # effectively adding elements to the type only in this model's context.
126
+ # Example:
127
+ # class Animal < BaseModel
128
+ # element :name, String
129
+ # element :friends, Animal, :multiple => true do
130
+ # element :how_much, String
131
+ # end
132
+ # end
133
+ # cat = Animal.new(:name => 'Cat')
134
+ # dog = Animal.new(:name => 'Dog')
135
+ # cat.friends << dog
136
+ # cat.friend[0].how_much = 'Not very much'
137
+ #
138
+ # Returns the created Element.
139
+ #
140
+ # Some used attributes:
141
+ # :primary_key:: (bool) The element is a primary key
142
+ # :length:: (number) Maximum length of the element (if meaningful)
143
+ # :required:: (bool) The element must always have a value
144
+ # :multiple:: (bool) defines a 1|n -> n relationship
145
+ # :label:: (string) a short description, used by the UI
146
+ # :association:: (symbol) A named association (such as :choice, :multiple_choice, etc.)
147
+ # :lazy:: (bool, array or symbol) If true, the element will be placed in the :default lazy group;
148
+ # if a symbol or an array of symbols is passed, the element will be placed in those groups.
149
+ # (see Element#lazy_groups)
150
+ # :reverse:: (symbol) The reverse element in the relationship to the other model
151
+ # :add_reverse:: (symbol) Adds an element on the other model, and sets it as the association reverse.
152
+ # :add_multiple_reverse:: (symbol) Adds a multiple element on the other model, and sets it as the association reverse.
153
+ # :element_position:: (number) inserts the element at the specified position in the elements order
154
+ # :auto:: (bool) Informative: the value is set automatically through some mechanism
155
+ # :autoincrement:: (bool) The value (which must be a Fixnum) will be autoincremented by the mapper
156
+ # :integrate:: (bool or symbol) type's elements will be available to this class
157
+ # as if they were defined here (see #integrate)
158
+ # :integrated_from:: (symbol) the name of the element from which this element is integrated
159
+ # :integrated_from_element:: (symbol) the name of the element of the child object from which this element is integrated
160
+ # :hidden:: (bool) a hint that the element shouldn't be shown by the UI
161
+ # :computed_from:: (array of symbols) the element is not mapped; its value is computed
162
+ # by the class from the given elements.
163
+ # :unmapped:: (bool) the element is not mapped.
164
+ # :check:: (a Proc, or a Regexp, or a Hash of messages => Regexp|Proc). See #check
165
+ # :through:: (a BaseModel subclass) model representing the many to many relationship.
166
+ # :read_only:: (bool) hint to the UI that the element should not be user modifiable.
167
+ # :owned:: (bool) only this model holds references to type
168
+ # :condition:: (hash or Condition) Restricts an association always adding the condition.
169
+ #
170
+ # Other attributes may be used by DataTypes (see #DataType::ClassMethods.take_attributes), and other code.
171
+ # See also Element.
172
+ def self.element(name, type, attributes={}, &proc)
173
+ name = name.to_sym
174
+ @elements ||= {}
175
+ @elements_order ||= []
176
+ if type.class == Class
177
+ default_attributes = case type.name
178
+ when 'String'
179
+ {:length => 255}
180
+ else
181
+ {}
182
+ end
183
+ else
184
+ default_attributes = {}
185
+ end
186
+ attributes = default_attributes.merge(attributes)
187
+ # if (type.class == Class && Model.base_type(type))
188
+ # type = Model.base_type(type)
189
+ # els
190
+ if (type.class == Hash)
191
+ type = create_inline_model(name, type)
192
+ attributes[:inline] = true
193
+ end
194
+ if (attributes[:integrated_from])
195
+ if (attributes[:integrated_from].class == String)
196
+ parts = attributes[:integrated_from].split('.')
197
+ attributes[:integrated_from] = @elements[parts[0].to_sym]
198
+ attributes[:integrated_from_element] = parts[1].to_sym if parts[1]
199
+ end
200
+ if (!attributes[:integrated_from_element])
201
+ attributes[:integrated_from_element] = name
202
+ end
203
+ end
204
+ if (attributes[:condition] && !attributes[:condition].is_a?(Condition))
205
+ attributes[:condition] = Condition.new(attributes[:condition])
206
+ end
207
+
208
+
209
+ orig_type = type
210
+ assoc_type = nil
211
+ if (attributes[:junction] || (attributes[:multiple] && (!attributes[:add_reverse]) && (!attributes[:has_single_reverse]) && \
212
+ # FIXME! the first check is needed when the referenced class has not been parsed yet
213
+ # but now it assumes that the reverse is not multiple if it is not defined
214
+ (attributes[:has_single_reverse] == false || !attributes[:reverse] || (!type.elements[attributes[:reverse]] || type.elements[attributes[:reverse]].multiple?))))
215
+ attributes[:anonymous_model] = true
216
+ attributes[:owned] = true unless attributes[:owned] != nil
217
+ first_model = self.first_definer(name)
218
+ assoc_type_name = Spider::Inflector.camelize(name)
219
+ create_junction = true
220
+ if (attributes[:through])
221
+ assoc_type = attributes[:through]
222
+ create_junction = false
223
+ elsif (first_model.const_defined?(assoc_type_name) )
224
+ assoc_type = first_model.const_get(assoc_type_name)
225
+ if (!assoc_type.attributes[:sub_model]) # other kind of inline model
226
+ assoc_type_name += 'Junction'
227
+ create_junction = false if (first_model.const_defined?(assoc_type_name))
228
+ else
229
+ create_junction = false
230
+ end
231
+ end
232
+ attributes[:junction] = true
233
+ attributes[:junction_id] = :id unless attributes.has_key?(:junction_id)
234
+ if (attributes[:junction_our_name])
235
+ self_name = attributes[:junction_our_name]
236
+ else
237
+ self_name = first_model.short_name.gsub('/', '_').downcase.to_sym
238
+ end
239
+ attributes[:reverse] = self_name
240
+ unless attributes[:junction_their_element]
241
+ other_name = Spider::Inflector.underscore(orig_type.short_name == self.short_name ? orig_type.name : orig_type.short_name).gsub('/', '_').downcase.to_sym
242
+ other_name = :"#{other_name}_ref" if (orig_type.elements[other_name])
243
+ attributes[:junction_their_element] = other_name
244
+ end
245
+ other_name = attributes[:junction_their_element]
246
+ if (create_junction)
247
+ assoc_type = first_model.const_set(assoc_type_name, Class.new(BaseModel))
248
+ assoc_type.attributes[:sub_model] = self
249
+ assoc_type.element(attributes[:junction_id], Fixnum, :primary_key => true, :autoincrement => true, :hidden => true)
250
+ assoc_type.element(self_name, self, :hidden => true, :reverse => name, :association => :choice) # FIXME: must check if reverse exists?
251
+ # FIXME! fix in case of clashes with existent elements
252
+ assoc_type.element(other_name, orig_type, :association => :choice)
253
+ assoc_type.integrate(other_name, :hidden => true, :no_pks => true) # FIXME: in some cases we want the integrated elements
254
+ if (proc) # to be hidden, but the integrated el instead
255
+ attributes[:extended] = true
256
+ attributes[:keep_junction] = true
257
+ assoc_type.class_eval(&proc)
258
+ end
259
+ end
260
+ attributes[:keep_junction] = true if (attributes[:through] && attributes[:keep_junction] != false)
261
+ attributes[:association_type] = assoc_type
262
+ end
263
+
264
+ @elements[name] = Element.new(name, type, attributes)
265
+
266
+ if (attributes[:add_reverse] && attributes[:add_reverse].is_a?(Symbol))
267
+ attributes[:add_reverse] = {:name => attributes[:add_reverse]}
268
+ end
269
+ if (attributes[:add_multiple_reverse] && attributes[:add_multiple_reverse].is_a?(Symbol))
270
+ attributes[:add_multiple_reverse] = {:name => attributes[:add_multiple_reverse]}
271
+ end
272
+
273
+ if (attributes[:add_reverse])
274
+ unless (orig_type.elements[attributes[:add_reverse]])
275
+ attributes[:reverse] ||= attributes[:add_reverse][:name]
276
+ rev = attributes[:add_reverse].merge(:reverse => name, :added_reverse => true,
277
+ :delete_cascade => attributes[:reverse_delete_cascade])
278
+ rev[:through] = assoc_type if assoc_type
279
+ rev_name = rev.delete(:name)
280
+ orig_type.element(rev_name, self, rev)
281
+ end
282
+ elsif (attributes[:add_multiple_reverse])
283
+ unless (orig_type.elements[attributes[:add_reverse]])
284
+ attributes[:reverse] ||= attributes[:add_multiple_reverse][:name]
285
+ rev = attributes[:add_multiple_reverse].merge(:reverse => name, :multiple => true,
286
+ :added_reverse => true, :delete_cascade => attributes[:reverse_delete_cascade])
287
+ rev[:through] = assoc_type if assoc_type
288
+ rev_name = rev.delete(:name)
289
+ orig_type.element(rev_name, self, rev)
290
+ end
291
+ end
292
+ if (attributes[:lazy] == nil)
293
+ # if attributes[:primary_key]
294
+ # attributes[:lazy] = true
295
+ # els
296
+ if (type < BaseModel && attributes[:multiple])
297
+ # FIXME: we can load eagerly single relations if we can do a join
298
+ attributes[:lazy] = true
299
+ else
300
+ attributes[:lazy_check_owner] = true if type < BaseModel
301
+ attributes[:lazy] = :default
302
+ end
303
+ end
304
+
305
+
306
+
307
+ if (attributes[:element_position])
308
+ @elements_order.insert(attributes[:element_position], name)
309
+ else
310
+ @elements_order << name
311
+ end
312
+ @primary_keys ||= []
313
+ if attributes[:primary_key] && !@primary_keys.include?(@elements[name])
314
+ @primary_keys << @elements[name]
315
+ end
316
+
317
+ # class element getter
318
+ unless respond_to?(name)
319
+ (class << self; self; end).instance_eval do
320
+ define_method("#{name}") do
321
+ @elements[name]
322
+ end
323
+ end
324
+ end
325
+
326
+ ivar = :"@#{ name }"
327
+
328
+ unless self.const_defined?(:ElementMethods)
329
+ em = self.const_set(:ElementMethods, Module.new)
330
+ include em
331
+
332
+ end
333
+ element_methods = self.const_get(:ElementMethods)
334
+
335
+ #instance variable getter
336
+ element_methods.send(:define_method, name) do
337
+ element = self.class.elements[name]
338
+ if (element.integrated?)
339
+ integrated = get(element.integrated_from.name)
340
+ return integrated.send(element.integrated_from_element) if integrated
341
+ return nil
342
+ end
343
+ if element_has_value?(name) || element_loaded?(name)
344
+ val = instance_variable_get(ivar)
345
+ val.set_parent(self, name) if val && element.model? && !val._parent # FIXME!!!
346
+ return val
347
+ end
348
+
349
+ # Spider.logger.debug("Element not loaded #{name} (i'm #{self.class} #{self.object_id})")
350
+ if autoload? && primary_keys_set?
351
+ if (autoload? == :save_mode)
352
+ mapper.load_element!(self, element)
353
+ else
354
+ mapper.load_element(self, element)
355
+ end
356
+ val = instance_variable_get(ivar)
357
+ prepare_value(name, val)
358
+ end
359
+ if (!val && element.model? && element.multiple?)
360
+ val = instance_variable_set(ivar, instantiate_element(name))
361
+ end
362
+ val.set_parent(self, name) if element.model? && val && !val._parent # FIXME!!!
363
+ return val
364
+ end
365
+
366
+ #instance_variable_setter
367
+ element_methods.send(:define_method, "#{name}=") do |val|
368
+ element = self.class.elements[name]
369
+ was_loaded = element_loaded?(element)
370
+ #@_autoload = false unless element.primary_key?
371
+ if (element.integrated?)
372
+ integrated_obj = get(element.integrated_from)
373
+ unless integrated_obj
374
+ integrated_obj = instantiate_element(element.integrated_from.name)
375
+ set(element.integrated_from, integrated_obj)
376
+ end
377
+ #integrated_obj.autoload = false
378
+ res = integrated_obj.send("#{element.integrated_from_element}=", val)
379
+ @modified_elements[name] = true unless element.primary_key?
380
+ return res
381
+ end
382
+ if (val && element.model?)
383
+ if (element.multiple?)
384
+ unless (val.is_a?(QuerySet))
385
+ qs = instantiate_element(name)
386
+ if (val.is_a?(Enumerable))
387
+ val.each do |row|
388
+ row = element.type.new(row) unless row.is_a?(BaseModel)
389
+ qs << row
390
+ end
391
+ else
392
+ qs << val
393
+ end
394
+ val = qs
395
+ end
396
+ else
397
+ val = element.model.new(val) unless val.is_a?(BaseModel)
398
+ end
399
+ end
400
+ val = prepare_child(element.name, val)
401
+ check(name, val)
402
+ notify_observers(name, val)
403
+ old_val = instance_variable_get(ivar)
404
+ @modified_elements[name] = true if !element.primary_key? && (!was_loaded || val != old_val)
405
+ instance_variable_set(ivar, val)
406
+ #extend_element(name)
407
+ end
408
+
409
+ if (attributes[:integrate])
410
+ integrate_params = attributes[:integrate].is_a?(Hash) ? attributes[:integrate] : {}
411
+ integrate(name, integrate_params)
412
+ end
413
+ if (@subclasses)
414
+ @subclasses.each do |sub|
415
+ sub.elements[name] = @elements[name].clone
416
+ sub.elements_order << name
417
+ end
418
+ end
419
+ element_defined(@elements[name])
420
+ return @elements[name]
421
+
422
+ end
423
+
424
+ def self.add_element(el)
425
+ @elements ||= {}
426
+ @elements[el.name] = el
427
+ @elements_order ||= []
428
+ @elements_order << el.name
429
+ @primary_keys ||= []
430
+ if el.attributes[:primary_key] && !@primary_keys.include?(el)
431
+ @primary_keys << el
432
+ end
433
+ end
434
+
435
+
436
+ # Removes a defined element
437
+ def self.remove_element(el)
438
+ el = el.name if el.is_a?(Element)
439
+ @elements.delete(el)
440
+ @elements_order.delete(el)
441
+ @primary_keys.delete_if{ |pk| pk.name == el}
442
+ remove_method(:"#{el}") rescue NameError
443
+ remove_method(:"#{el}=") rescue NameError
444
+ end
445
+
446
+ def self.element_defined(el)
447
+ if (@on_element_defined && @on_element_defined[el.name])
448
+ @on_element_defined[el.name].each do |proc|
449
+ proc.call(el)
450
+ end
451
+ end
452
+ end
453
+
454
+ def self.on_element_defined(el_name, &proc)
455
+ @on_element_defined ||= {}
456
+ @on_element_defined[el_name] ||= []
457
+ @on_element_defined[el_name] << proc
458
+ end
459
+
460
+ # Integrates an element: any call to the child object's elements will be passed to the child.
461
+ # The element must not be multiple.
462
+ # Example:
463
+ # class Address < BaseModel
464
+ # element :street, String
465
+ # element :area_code, String
466
+ # end
467
+ # class Person < BaseModel
468
+ # element :name, String
469
+ # element :address, Address
470
+ # integrate :address
471
+ # end
472
+ # p = Person.new(...)
473
+ # p.street == p.address.street
474
+ def self.integrate(element_name, params={})
475
+ params ||= {}
476
+ elements[element_name].attributes[:integrated_model] = true
477
+ model = elements[element_name].model
478
+ self.attributes[:integrated_models] ||= {}
479
+ self.attributes[:integrated_models][model] = element_name
480
+ params[:except] ||= []
481
+ model.each_element do |el|
482
+ next if params[:except].include?(el.name)
483
+ next if elements[el.name] unless params[:overwrite] # don't overwrite existing elements
484
+ attributes = el.attributes.clone.merge({
485
+ :integrated_from => elements[element_name],
486
+ :integrated_from_element => el.name
487
+ })
488
+ attributes[:hidden] = params[:hidden] unless (params[:hidden].nil?)
489
+ if (add_rev = attributes[:add_reverse] || attributes[:add_multiple_reverse])
490
+ attributes[:reverse] = add_rev[:name]
491
+ attributes.delete(:add_reverse)
492
+ attributes.delete(:add_multiple_reverse)
493
+ end
494
+ attributes.delete(:primary_key) unless (params[:keep_pks])
495
+ name = params[:mapping] && params[:mapping][el.name] ? params[:mapping][el.name] : el.name
496
+ element(name, el.type, attributes)
497
+ end
498
+ end
499
+
500
+ # Sets additional attributes on the element
501
+ #
502
+ # _Warning:_ for attributes which are parsed by the BaseModel during element definition,
503
+ # this will not have the desired effect; remove and redefine the element instead.
504
+ def self.element_attributes(element_name, attributes)
505
+ elements[element_name].attributes.merge!(attributes)
506
+ end
507
+
508
+ # Defines a multiple element. Equivalent to calling
509
+ # element(name, type, :multiple => true, :association => :many, ...)
510
+ def self.many(name, type, attributes={}, &proc)
511
+ attributes[:multiple] = true
512
+ attributes[:association] ||= :many
513
+ element(name, type, attributes, &proc)
514
+ end
515
+
516
+ # Defines an element with choice association. Shorthand for
517
+ # element(name, type, :association => :choice, ...)
518
+ def self.choice(name, type, attributes={}, &proc)
519
+ attributes[:association] = :choice
520
+ element(name, type, attributes, &proc)
521
+ end
522
+
523
+ # Defines a multiple element with :multiple_choice association. Shorthand for
524
+ # many(name, type, :association => :multiple_choice, ...)
525
+ def self.multiple_choice(name, type, attributes={}, &proc)
526
+ attributes[:association] = :multiple_choice
527
+ many(name, type, attributes, &proc)
528
+ end
529
+
530
+ def self.element_query(name, element_name, attributes={})
531
+ orig_element = self.elements[element_name]
532
+ set_el_query = lambda do
533
+ orig_element = self.elements[element_name]
534
+ attributes = attributes.merge(orig_element.attributes)
535
+ attributes[:unmapped] = true
536
+ attributes[:element_query] = element_name
537
+ attributes[:association] = :element_query
538
+ attributes[:lazy] = true
539
+ attributes.delete(:add_reverse)
540
+ attributes.delete(:add_multiple_reverse)
541
+ if (orig_element.attributes[:condition])
542
+ cond = orig_element.attributes[:condition].clone
543
+ cond = cond.and(attributes[:condition]) if attributes[:condition]
544
+ attributes[:condition] = cond
545
+ end
546
+ element(name, orig_element.type, attributes)
547
+ end
548
+ if (orig_element)
549
+ set_el_query.call
550
+ else
551
+ on_element_defined(element_name, &set_el_query)
552
+ end
553
+ end
554
+
555
+
556
+ # Saves the element definition and evals it when first needed, avoiding problems with classes not
557
+ # available yet when the model is defined.
558
+ # FIXME: remove?
559
+ def self.define_elements(&proc) #:nodoc:
560
+ @elements_definition = proc
561
+ end
562
+
563
+ # Creates an inline model
564
+ def self.create_inline_model(name, hash) #:nodoc:
565
+ model = self.const_set(Spider::Inflector.camelize(name), Class.new(InlineModel))
566
+ model.instance_eval do
567
+ hash.each do |key, val|
568
+ element(:id, key.class, :primary_key => true)
569
+ if (val.class == Hash)
570
+ # TODO: allow passing of multiple values like {:element1 => 'el1', :element2 => 'el2'}
571
+ else
572
+ element(:desc, val.class)
573
+ end
574
+ break
575
+ end
576
+ end
577
+ model.data = hash
578
+ return model
579
+ end
580
+
581
+ # An array of other models this class points to.
582
+ def self.submodels
583
+ elements.select{ |name, el| el.model? }.map{ |name, el| el.model }
584
+ end
585
+
586
+
587
+ def self.extend_model(model, params={}) #:nodoc:
588
+ if (model == superclass) # first undo table per class inheritance
589
+ @elements = {}
590
+ @elements_order = []
591
+ end
592
+ primary_keys.each{ |k| remove_element(k) } if (params[:replace_pks])
593
+ model.primary_keys.each{ |k| remove_element(k) }
594
+ integrated_name = params[:name]
595
+ if (!integrated_name)
596
+ integrated_name = (self.parent_module == model.parent_module) ? model.short_name : model.name
597
+ integrated_name = Spider::Inflector.underscore(integrated_name).gsub('/', '_')
598
+ end
599
+ integrated_name = integrated_name.to_sym
600
+ @extended_models ||= {}
601
+ @extended_models[model] = integrated_name
602
+ attributes = {}
603
+ attributes[:hidden] = true unless (params[:hide_integrated] == false)
604
+ attributes[:delete_cascade] = params[:delete_cascade]
605
+ integrated = element(integrated_name, model, attributes)
606
+ integrate_options = {:keep_pks => true}.merge((params[:integrate_options] || {}))
607
+ integrate(integrated_name, integrate_options)
608
+ model.elements_array.select{ |el| el.attributes[:local_pk] }.each{ |el| remove_element(el.name) }
609
+
610
+ unless (params[:no_local_pk] || !elements_array.select{ |el| el.attributes[:local_pk] }.empty?)
611
+ # FIXME: check if :id is already defined
612
+ pk_name = @elements[:id] ? :"id_#{self.short_name.downcase}" : :id
613
+ element(pk_name, Fixnum, :autoincrement => true, :local_pk => true, :hidden => true)
614
+ end
615
+ if (params[:add_polymorphic])
616
+ model.polymorphic(self, :through => integrated_name)
617
+ end
618
+ end
619
+
620
+ # Externalizes the superclass elements making the superclass an external integrated element.
621
+ # Parameters may be:
622
+ # * :name (symbol) name of the created element
623
+ # * :delete_cascade (bool) delete cascade the superclass instance
624
+ # * :no_local_pk (bool) do not define an id for this class
625
+ # * :add_polymorphic (bool) notify the superclass that it is extended, making polymorphic queries possible
626
+ def self.class_table_inheritance(params={})
627
+ self.extend_model(superclass, params)
628
+ end
629
+
630
+ # Makes the class use the superclass storage
631
+ def self.inherit_storage
632
+ self.attributes[:inherit_storage] = true
633
+ (class << self; self; end).instance_eval do
634
+ define_method(:storage) do
635
+ superclass.storage
636
+ end
637
+ end
638
+ end
639
+
640
+ # Sets a fixed condition.
641
+ def self.condition(condition)
642
+ self.attributes[:condition] = condition
643
+ end
644
+
645
+ #
646
+ #--
647
+ # TODO: document me
648
+ def self.group(name, &proc) #:nodoc:
649
+ proxy = Class.new(ProxyModel).proxy(name.to_s+'_', self)
650
+ proxy.instance_eval(&proc)
651
+ proxy.each_element do |el|
652
+ element(name.to_s+'_'+el.name.to_s, el.type, el.attributes.clone)
653
+ end
654
+ define_method(name) do
655
+ @proxies ||= {}
656
+ return @proxies[name] ||= proxy.new
657
+ end
658
+
659
+ end
660
+
661
+ # Add a subclass, allowing polymorphic queries on it.
662
+ def self.polymorphic(model, options)
663
+ through = options[:through] || Spider::Inflector.underscore(self.name).gsub('/', '_')
664
+ through = through.to_sym
665
+ @polymorphic_models ||= {}
666
+ @polymorphic_models[model] = {:through => through}
667
+ end
668
+
669
+ # Sets or gets class attributes (a Hash).
670
+ # If given a hash of attributes, will merge them with class attributes.
671
+ # Model attributes are generally empty, and can be used by apps.
672
+ def self.attributes(val=nil)
673
+ @attributes ||= {}
674
+ if (val)
675
+ @attributes.merge!(val)
676
+ end
677
+ @attributes
678
+ end
679
+
680
+ # Sets a model attribute. See #self.attributes
681
+ def self.attribute(name, value)
682
+ @attributes ||= {}
683
+ @attributes[name] = value
684
+ end
685
+
686
+ # Adds a sequence to the model.
687
+ def self.sequence(name)
688
+ @sequences ||= []
689
+ @sequences << name
690
+ end
691
+
692
+ # Model sequences.
693
+ def self.sequences
694
+ @sequences ||= []
695
+ end
696
+
697
+ # Does nothing. This method is to keep note of elements created in other models.
698
+ def self._added_elements(&proc)
699
+ end
700
+
701
+ #####################################################
702
+ # Methods returning information about the model #
703
+ #####################################################
704
+
705
+ # Underscored local name (without namespaces)
706
+ def self.short_name
707
+ return Inflector.underscore(self.name.match(/([^:]+)$/)[1])
708
+ end
709
+
710
+ # False for BaseModel (true for Spider::Model::Managed).
711
+ def self.managed?
712
+ return false
713
+ end
714
+
715
+ # Name
716
+ def self.to_s
717
+ self.name
718
+ end
719
+
720
+ # Sets the singolar and/or the plural label for the model
721
+ # Returns the singlular label
722
+ def self.label(sing=nil, plur=nil)
723
+ @label = sing if sing
724
+ @label_plural = plur if plur
725
+ @label || self.name || ''
726
+ end
727
+
728
+ # Sets/retrieves the plural form for the label
729
+ def self.label_plural(val=nil)
730
+ @label_plural = val if (val)
731
+ @label_plural || self.name || ''
732
+ end
733
+
734
+ ########################################################
735
+ # Methods returning information about the elements #
736
+ ########################################################
737
+
738
+ # An Hash of Elements, indexed by name.
739
+ def self.elements
740
+ @elements
741
+ end
742
+
743
+ # An array of the model's Elements.
744
+ def self.elements_array
745
+ @elements_order.map{ |key| @elements[key] }
746
+ end
747
+
748
+ # Yields each element in order.
749
+ def self.each_element
750
+ return unless @elements_order
751
+ @elements_order.each do |name|
752
+ yield elements[name]
753
+ end
754
+ end
755
+
756
+ # Returns true if the model has given element name.
757
+ def self.has_element?(name)
758
+ return elements[name] ? true : false
759
+ end
760
+
761
+ # An array of elements with primary_key attribute set.
762
+ def self.primary_keys
763
+ @primary_keys
764
+ end
765
+
766
+ # Returns the model actually defining element_name; that could be the model
767
+ # itself, a superclass, or an integrated model.
768
+ def self.first_definer(element_name)
769
+ if (self.superclass.elements && self.superclass.elements[element_name])
770
+ return self.superclass.first_definer(element_name)
771
+ end
772
+ if (self.attributes[:integrated_models])
773
+ self.attributes[:integrated_models].keys.each do |mod|
774
+ return mod.first_definer(element_name) if (mod.elements[element_name])
775
+ end
776
+ end
777
+ return self
778
+ end
779
+
780
+ # Returns true if the element with given name is associated with the passed
781
+ # association.
782
+ # This method should be used instead of querying the element's association directly,
783
+ # since subclasses and mixins may extend this method to provide association equivalence.
784
+ def self.element_association?(element_name, association)
785
+ return true if elements[element_name].association = association
786
+ end
787
+
788
+ # An Hash of extended models => element name of the extended model element
789
+ def self.extended_models
790
+ @extended_models ||= {}
791
+ end
792
+
793
+ ##############################################################
794
+ # Storage, mapper and loading (Class methods) #
795
+ ##############################################################
796
+
797
+ # The given module will be mixed in any mapper used by the class.
798
+ def self.mapper_include(mod)
799
+ @mapper_modules ||= []
800
+ @mapper_modules << mod
801
+ end
802
+
803
+ # The given proc will be mixed in the mapper used by this class
804
+ # Note that the proc will be converted to a Module, so any overridden methods will still have
805
+ # access to the super method.
806
+ def self.with_mapper(*params, &proc)
807
+ # @mapper_procs ||= []
808
+ # @mapper_procs << proc
809
+ mod = Module.new(&proc)
810
+ mapper_include(mod)
811
+ end
812
+
813
+ # FIXME: remove
814
+ def self.with_mapper_subclasses(*params, &proc) #:nodoc:
815
+ @mapper_procs_subclass ||= []
816
+ @mapper_procs_subclass << proc
817
+ end
818
+
819
+ # Like #with_mapper, but will mixin the block only if the mapper matches params.
820
+ # Possible params are:
821
+ # - a String, matching the class' use_storage
822
+ def self.with_mapper_for(*params, &proc)
823
+ @mapper_procs_for ||= []
824
+ @mapper_procs_for << [params, proc]
825
+ end
826
+
827
+ # Sets the url or the name of the storage to use
828
+ def self.use_storage(name=nil)
829
+ @use_storage = name if name
830
+ @use_storage
831
+ end
832
+
833
+ # Returns the current default storage for the class
834
+ # The storage to use can be set with #use_storage
835
+ def self.storage
836
+ return @storage if @storage
837
+ if (!@use_storage && self.attributes[:sub_model])
838
+ @use_storage = self.attributes[:sub_model].use_storage
839
+ end
840
+ return @use_storage ? get_storage(@use_storage) : get_storage
841
+ end
842
+
843
+ # Returns an instancethe storage corresponding to the storage_string if it is given,
844
+ # or of the default storage otherwise.
845
+ # The storage string can be a storage url (see #Storage.get_storage), or a named storage
846
+ # defined in configuration
847
+ #--
848
+ # Mixin!
849
+ def self.get_storage(storage_string='default')
850
+ storage_regexp = /([\w\d]+?):(.+)/
851
+ if (storage_string !~ storage_regexp)
852
+ orig_string = storage_string
853
+ storage_conf = Spider.conf.get('storages')[storage_string]
854
+ storage_string = storage_conf['url'] if storage_conf
855
+ if (!storage_string || storage_string !~ storage_regexp)
856
+ raise ModelException, "No storage '#{orig_string}' found"
857
+ end
858
+ end
859
+ type, url = $1, $2
860
+ storage = Storage.get_storage(type, url)
861
+ storage.configure(storage_conf) if storage_conf
862
+ return storage
863
+ end
864
+
865
+ # Returns an instance of the default mapper for the class.
866
+ def self.mapper
867
+ @mapper ||= get_mapper(storage)
868
+ end
869
+
870
+ # Returns an instance of the mapper for the given storage
871
+ def self.get_mapper(storage)
872
+ # map_class = self.attributes[:inherit_storage] ? superclass : self
873
+ mapper = storage.get_mapper(self)
874
+ if (@mapper_modules)
875
+ @mapper_modules.each{ |mod| mapper.extend(mod) }
876
+ end
877
+ if (@mapper_procs)
878
+ @mapper_procs.each{ |proc| mapper.instance_eval(&proc) }
879
+ end
880
+ if (@mapper_procs_for)
881
+ @mapper_procs_for.each do |params, proc|
882
+ if (params.length == 1 && params[0].class == String)
883
+ mapper.instance_eval(&proc) if (self.use_storage == params[0])
884
+ end
885
+ end
886
+ end
887
+ if (@mapper_procs_subclass)
888
+ @mapper_procs_subclass.each{ |proc| mapper.instance_eval(&proc) }
889
+ end
890
+ return mapper
891
+ end
892
+
893
+ # Executes #self.where, and calls QuerySet#load on the result.
894
+ # Returns nil if the result is empty, the QuerySet otherwise
895
+ # See #self.where for parameter syntax
896
+ def self.find(*params, &proc)
897
+ qs = self.where(*params, &proc)
898
+ return qs.empty? ? nil : qs
899
+ end
900
+
901
+ # Executes #self.where, returning the first result.
902
+ # See #self.where for parameter syntax.
903
+ def self.load(*params, &proc)
904
+ return self.where(*params, &proc)[0]
905
+ end
906
+
907
+ # Returns a queryset without conditions
908
+ def self.all
909
+ return self.where
910
+ end
911
+
912
+ # Constructs a Query based on params, and returns a QuerySet
913
+ # Allowed parameters are:
914
+ # * a Query object
915
+ # * a Condition and an (optional) Request, or anything that can be parsed by Condition.new and Request.new
916
+ # If a block is provided, it is passed to Condition.parse_block.
917
+ # Examples:
918
+ # felines = Animals.where({:family => 'felines'})
919
+ # felines = Animals.where({:family => 'felines'}, [:name, :description])
920
+ # cool_animals = Animals.where{ (has_fangs == true) | (has_claws == true)}
921
+ # See also Condition#parse_block
922
+ def self.where(*params, &proc)
923
+ if (params[0] && params[0].is_a?(Query))
924
+ query = params[0]
925
+ qs = QuerySet.new(self, query)
926
+ elsif(proc)
927
+ qs = QuerySet.new(self)
928
+ qs.autoload = true
929
+ qs.where(&proc)
930
+ else
931
+ condition = Condition.and(params[0])
932
+ request = Request.new(params[1])
933
+ query = Query.new(condition, request)
934
+ qs = QuerySet.new(self, query)
935
+ end
936
+ return qs
937
+ end
938
+
939
+ # Returns the condition for a "free" text query
940
+ # Examples:
941
+ # condition = News.free_query_condition('animals')
942
+ # animal_news = News.where(condition)
943
+ def self.free_query_condition(q)
944
+ c = Condition.or
945
+ self.elements_array.each do |el|
946
+ if (el.type == String || el.type == Text)
947
+ c.set(el.name, 'ilike', '%'+q+'%')
948
+ end
949
+ end
950
+ return c
951
+ end
952
+
953
+ # Returns the number of objects in storage
954
+ def self.count(condition=nil)
955
+ mapper.count(condition)
956
+ end
957
+
958
+ # Can be defined to provide functionality to this model's querysets.
959
+ def self.extend_queryset(qs)
960
+ end
961
+
962
+ #################################################
963
+ # Instance methods #
964
+ #################################################
965
+
966
+ # The constructor may take:
967
+ # * an Hash of values, that will be set on the new instance; or
968
+ # * a BaseModel instance; its values will be set on the new instance; or
969
+ # * a single value; it will be set on the first primary key.
970
+ def initialize(values=nil)
971
+ @_autoload = true
972
+ @_has_values = false
973
+ @loaded_elements = {}
974
+ @modified_elements = {}
975
+ @value_observers = {}
976
+ @all_values_observers = []
977
+ @_extra = {}
978
+ @model = self.class
979
+ @all_values_observers << Proc.new do |element, new_value|
980
+ @_has_values = true
981
+ Spider::Model.unit_of_work.add(self) if (Spider::Model.unit_of_work)
982
+ end
983
+ if (values)
984
+ if (values.is_a? Hash)
985
+ values.each do |key, val|
986
+ set!(key, val)
987
+ end
988
+ elsif (values.is_a? BaseModel)
989
+ values.each_val do |name, val|
990
+ set(name, val) if self.class.has_element?(name)
991
+ end
992
+ elsif (values.is_a? Array)
993
+ self.class.primary_keys.each_index do |i|
994
+ set(self.class.primary_keys[i], values[i])
995
+ end
996
+ # Single unset key, single value
997
+ elsif ((empty_keys = self.class.primary_keys.select{ |key| !element_has_value?(key) }).length == 1)
998
+ set(empty_keys[0], values)
999
+ end
1000
+ end
1001
+ end
1002
+
1003
+ # Returns the instance's IdentityMapper
1004
+ def identity_mapper
1005
+ return Spider::Model.identity_mapper if Spider::Model.identity_mapper
1006
+ @identity_mapper ||= IdentityMapper.new
1007
+ end
1008
+
1009
+ # Sets the instance's IdentityMapper.
1010
+ def identity_mapper=(im)
1011
+ @identity_mapper = im
1012
+ end
1013
+
1014
+ # Returns a new instance for given element name.
1015
+ def instantiate_element(name)
1016
+ element = self.class.elements[name]
1017
+ if (element.model?)
1018
+ if (element.multiple?)
1019
+ val = QuerySet.static(element.model)
1020
+ else
1021
+ val = element.type.new
1022
+ val.autoload = autoload?
1023
+ end
1024
+ end
1025
+ return prepare_child(name, val)
1026
+ end
1027
+
1028
+ # Prepares an object that is being set as a child.
1029
+ def prepare_child(name, obj)
1030
+ return obj if obj.nil?
1031
+ element = self.class.elements[name]
1032
+ if (element.model?)
1033
+ # convert between junction and real type if needed
1034
+ if (obj.is_a?(QuerySet) && element.attributes[:junction])
1035
+ obj.no_autoload do
1036
+ if (element.attributes[:keep_junction] && obj.model == element.type)
1037
+ qs = QuerySet.new(element.model)
1038
+ obj.each{ |el_obj|
1039
+ qs << {element.reverse => self, element.attributes[:junction_their_element] => el_obj}
1040
+ }
1041
+ obj = qs
1042
+ elsif (!element.attributes[:keep_junction] && obj.model == element.model)
1043
+ qs = QuerySet.new(element.type, obj.map{ |el_obj| el_obj.get(element.attributes[:junction_their_element])})
1044
+ obj = qs
1045
+ end
1046
+ end
1047
+ end
1048
+ obj.identity_mapper = self.identity_mapper if obj.respond_to?(:identity_mapper)
1049
+ if (element.attributes[:junction] && element.attributes[:keep_junction])
1050
+ obj.append_element = element.attributes[:junction_their_element]
1051
+ end
1052
+ if (element.attributes[:set] && element.attributes[:set].is_a?(Hash))
1053
+ element.attributes[:set].each{ |k, v| obj.set(k, v) }
1054
+ obj.reset_modified_elements(*element.attributes[:set].keys)
1055
+ # FIXME: is it always ok to not set the element as modified? But otherwise sub objects
1056
+ # are always saved (and that's definitely no good)
1057
+ end
1058
+ else
1059
+ obj = prepare_value(element, obj)
1060
+ end
1061
+ return obj
1062
+ end
1063
+
1064
+ # Returns all children that can be reached from the current path.
1065
+ # Path is expressed as a dotted String.
1066
+ def all_children(path)
1067
+ children = []
1068
+ no_autoload do
1069
+ el = path.shift
1070
+ if self.class.elements[el] && element_has_value?(el) && children = get(el)
1071
+ if path.length >= 1
1072
+ children = children.all_children(path)
1073
+ end
1074
+ end
1075
+ end
1076
+ return children
1077
+ end
1078
+
1079
+ # Sets the object currently containing this one (BaseModel or QuerySet)
1080
+ def set_parent(obj, element)
1081
+ @_parent = obj
1082
+ @_parent_element = element
1083
+ end
1084
+
1085
+
1086
+ #################################################
1087
+ # Get and set #
1088
+ #################################################
1089
+
1090
+ # Returns an element.
1091
+ # The element may be a symbol, or a dotted path String.
1092
+ # Will call the associated getter.
1093
+ # cat.get('favorite_food.name')
1094
+ def get(element)
1095
+ element = element.name if (element.class == Spider::Model::Element)
1096
+ first, rest = element.to_s.split('.', 2)
1097
+ if (rest)
1098
+ return nil unless element_has_value?(first.to_sym)
1099
+ return send(first).get(rest)
1100
+ end
1101
+ return send(element)
1102
+ end
1103
+
1104
+ # Returns an element without autoloading it.
1105
+ def get_no_load(element)
1106
+ res = nil
1107
+ no_autoload do
1108
+ res = get(element)
1109
+ end
1110
+ return res
1111
+ end
1112
+
1113
+ # Sets an element.
1114
+ # The element can be a symbol, or a dotted path String.
1115
+ # Will call the associated setter.
1116
+ # cat.set('favorite_food.name', 'Salmon')
1117
+ def set(element, value, options={})
1118
+ element = element.name if (element.class == Element)
1119
+ first, rest = element.to_s.split('.', 2)
1120
+ if (rest)
1121
+ first_val = send(first)
1122
+ unless first_val
1123
+ if (options[:instantiate])
1124
+ first_val = instantiate_element(first.to_sym)
1125
+ set(first, first_val)
1126
+ else
1127
+ raise "Element #{first} is nil, can't set #{element}"
1128
+ end
1129
+ end
1130
+ return first_val.set(rest, value, options)
1131
+ end
1132
+ return send("#{element}=", value)
1133
+ end
1134
+
1135
+ # Sets an element, instantiating intermediate objects if needed
1136
+ def set!(element, value, options={})
1137
+ options[:instantiate] = true
1138
+ set(element, value, options)
1139
+ end
1140
+
1141
+ # Calls #get on element; whenever no getter responds, returns the extra data.
1142
+ # See #[]=
1143
+ def [](element)
1144
+ element = element.name if element.is_a?(Element)
1145
+ begin
1146
+ get(element)
1147
+ rescue NoMethodError
1148
+ return @_extra[element]
1149
+ end
1150
+ end
1151
+
1152
+ # If element is a model's element, calls #set.
1153
+ # Otherwise, stores the value in an "extra" hash, where it will be accessible by #[]
1154
+ def []=(element, value)
1155
+ element = element.name if element.is_a?(Element)
1156
+ if (self.class.elements[element])
1157
+ set(element, value)
1158
+ else
1159
+ @_extra[element] = value
1160
+ end
1161
+ end
1162
+
1163
+ # Sets each value of a Hash.
1164
+ def set_hash(hash)
1165
+ hash.each { |key, val| set(key, val) }
1166
+ end
1167
+
1168
+ # Prepares a value going to be set on the object. Will convert the value to the
1169
+ # appropriate type.
1170
+ def prepare_value(element, value)
1171
+ element = self.class.elements[element] unless element.is_a?(Element)
1172
+ if (element.type < Spider::DataType)
1173
+ value = element.type.from_value(value) unless value.is_a?(element.type)
1174
+ element.type.take_attributes.each do |a|
1175
+ value.attributes[a] = element.attributes[a]
1176
+ end
1177
+ elsif element.model?
1178
+ value.autoload(autoload?, true) if value && value.respond_to?(:autolad)
1179
+ else
1180
+ case element.type.name
1181
+ when 'Date', 'DateTime'
1182
+ return nil if value.is_a?(String) && value.empty?
1183
+ parsed = nil
1184
+ if (value.is_a?(String))
1185
+ begin
1186
+ parsed = element.type.strptime(value, "%Y-%m-%dT%H:%M:%S") rescue parsed = nil
1187
+ parsed ||= element.type.lparse(value, :short) rescue parsed = nil
1188
+ parsed ||= element.type.parse(value)
1189
+ rescue ArgumentError => exc
1190
+ raise FormatError.new(element, value, _("'%s' is not a valid date"))
1191
+ end
1192
+ value = parsed
1193
+ end
1194
+ when 'String'
1195
+ when 'Spider::DataTypes::Text'
1196
+ value = value.to_s
1197
+ when 'Fixnum'
1198
+ value = value.to_i
1199
+ end
1200
+ end
1201
+ value
1202
+ end
1203
+
1204
+ # Sets a value without calling the associated setter; used by the mapper.
1205
+ def set_loaded_value(element, value)
1206
+ element_name = element.is_a?(Element) ? element.name : element
1207
+ element = self.class.elements[element_name]
1208
+ if (element.integrated?)
1209
+ get(element.integrated_from).set_loaded_value(element.integrated_from_element, value)
1210
+ else
1211
+ value = prepare_child(element.name, value) if element.model?
1212
+ instance_variable_set("@#{element_name}", value)
1213
+ end
1214
+ value.loaded = true if (value.is_a?(QuerySet))
1215
+ element_loaded(element_name)
1216
+ @modified_elements[element_name] = false
1217
+ end
1218
+
1219
+ # Records that the element has been loaded.
1220
+ def element_loaded(element_name)
1221
+ element_name = element_name.name if (element_name.class == Element)
1222
+ @loaded_elements[element_name] = true
1223
+ end
1224
+
1225
+ # Returns true if the element has been loaded by the mapper.
1226
+ def element_loaded?(element)
1227
+ element = element.name if (element.class == Element)
1228
+ return @loaded_elements[element]
1229
+ end
1230
+
1231
+ # Apply element checks for given element name and value. (See also #element, :check attribute).
1232
+ # Checks may be defined by the DataType, or be given as an element attribute.
1233
+ # The check can be a Regexp, that will be checked against the value, or a Proc, which is expected to
1234
+ # return true if the check is succesful, and false otherwise.
1235
+ # Will raise a Model::FormatError when a check is not succesful.
1236
+ # If the :check attribute is an Hash, the Hash keys will be used as messages, which will be passed
1237
+ # to the FormatError.
1238
+ def check(name, val)
1239
+ element = self.class.elements[name]
1240
+ element.type.check(val) if (element.type.respond_to?(:check))
1241
+ if (checks = element.attributes[:check])
1242
+ checks = {(_("'%s' is not in the correct format") % element.label) => checks} unless checks.is_a?(Hash)
1243
+ checks.each do |msg, check|
1244
+ test = case check
1245
+ when Regexp
1246
+ val == nil || val.empty? ? true : check.match(val)
1247
+ when Proc
1248
+ check.call(val)
1249
+ end
1250
+ raise FormatError.new(element, val, msg) unless test
1251
+ end
1252
+ end
1253
+ end
1254
+
1255
+ # Converts the object to the instance of a subclass for which this model is polymorphic.
1256
+ def polymorphic_become(model)
1257
+ raise ModelException, "#{self.class} is not polymorphic for #{model}" unless self.class.polymorphic_models[model]
1258
+ obj = model.new
1259
+ el = self.class.polymorphic_models[model][:through]
1260
+ obj.set(el, self)
1261
+ obj.element_loaded(el)
1262
+ return obj
1263
+ end
1264
+
1265
+ def become(model)
1266
+ return self if self.class == model
1267
+ obj = polymorphic_become(model) rescue ModelException
1268
+ return obj
1269
+ end
1270
+
1271
+ # Converts the object to the instance of a subclass. This will instantiate the model
1272
+ # passed as an argument, and set each value of the current object on the new one.
1273
+ # No checks are made that this makes sense, so the method will fail if the "subclass" does
1274
+ # not contain all of the current model's elements.
1275
+ def subclass(model)
1276
+ obj = model.new
1277
+ elements_array.each do |el|
1278
+ obj.set(el, self.get(el)) if element_has_value?(el)
1279
+ end
1280
+ return obj
1281
+ end
1282
+
1283
+ # Returns the current autoload status
1284
+ def autoload?
1285
+ @_autoload
1286
+ end
1287
+
1288
+ # Enables or disables autoloading.
1289
+ # An autoloading object will try to load all missing elements on first access.
1290
+ # (see also Element#lazy_groups)
1291
+ def autoload=(val)
1292
+ autoload(val, false)
1293
+ end
1294
+
1295
+ # Sets autoload mode
1296
+ # The first parameter the value of autoload to be set; it can be true, false or :save_mode (see #save_mode))
1297
+ # the second bool parameter specifies if the value should be propagated on all child objects.
1298
+ def autoload(a, traverse=true) #:nodoc:
1299
+ return if @_tmp_autoload_walk
1300
+ @_tmp_autoload_walk = true
1301
+ @_autoload = a
1302
+ if (traverse)
1303
+ self.class.elements_array.select{ |el| el.model? && element_has_value?(el.name)}.each do |el|
1304
+ val = get(el)
1305
+ val.autoload = a if val.respond_to?(:autoload=)
1306
+ end
1307
+ end
1308
+ @_tmp_autoload_walk = nil
1309
+ end
1310
+
1311
+ # Disables autoload.
1312
+ # If a block is given, the current autoload setting will be restored after yielding.
1313
+ def no_autoload
1314
+ prev_autoload = autoload?
1315
+ self.autoload = false
1316
+ if block_given?
1317
+ yield
1318
+ self.autoload = prev_autoload
1319
+ end
1320
+ return prev_autoload
1321
+ end
1322
+
1323
+ # Sets autoload to :save_mode; elements will be autoloaded only one by one, so that
1324
+ # any already set data will not be overwritten
1325
+ # If a block is given, the current autoload setting will be restored after yielding.
1326
+ def save_mode
1327
+ prev_autoload = autoload?
1328
+ self.autoload = :save_mode
1329
+ if (block_given?)
1330
+ yield
1331
+ self.autoload = prev_autoload
1332
+ end
1333
+ return prev_autoload
1334
+ end
1335
+
1336
+
1337
+ ##############################################################
1338
+ # Methods for getting information about element values #
1339
+ ##############################################################
1340
+
1341
+ # Returns true if other is_a?(self.class), and has the same values for this class' primary keys.
1342
+ def ==(other)
1343
+ return false unless other
1344
+ return false unless other.is_a?(self.class)
1345
+ self.class.primary_keys.each do |k|
1346
+ return false unless get(k) == other.get(k)
1347
+ end
1348
+ return true
1349
+ end
1350
+
1351
+ ##############################################################
1352
+ # Iterators #
1353
+ ##############################################################
1354
+
1355
+ # Iterates over elements and yields name-value pairs
1356
+ def each # :yields: element_name, element_value
1357
+ self.class.elements.each do |name, el|
1358
+ yield name, get(name)
1359
+ end
1360
+ end
1361
+
1362
+ # Iterates over non-nil elements, yielding name-value pairs
1363
+ def each_val # :yields: element_name, element_value
1364
+ self.class.elements.select{ |name, el| element_has_value?(name) }.each do |name, el|
1365
+ yield name, get(name)
1366
+ end
1367
+ end
1368
+
1369
+ # Returns an array of current primary key values
1370
+ def primary_keys
1371
+ self.class.primary_keys.map{ |k|
1372
+ k.model? ? get(k).primary_keys : get(k)
1373
+ }
1374
+ end
1375
+
1376
+ # Returns an hash of primary keys names and values
1377
+ def primary_keys_hash
1378
+ h = {}
1379
+ self.class.primary_keys.each{ |k| h[k.name] = get(k) }
1380
+ h
1381
+ end
1382
+
1383
+ # Returns a string with the primary keys joined by ','
1384
+ def keys_string
1385
+ self.class.primary_keys.map{ |pk| self.get(pk) }.join(',')
1386
+ end
1387
+
1388
+
1389
+
1390
+ # Returns true if the element instance variable is set
1391
+ #--
1392
+ # FIXME: should probably try to get away without this method
1393
+ # it is the only method that relies on the mapper
1394
+ def element_has_value?(element)
1395
+ element_name = (element.is_a?(Element)) ? element.name : element
1396
+ element = self.class.elements[element_name]
1397
+ if (element.integrated?)
1398
+ return false unless obj = instance_variable_get(:"@#{element.integrated_from.name}")
1399
+ return obj.element_has_value?(element.integrated_from_element)
1400
+ end
1401
+ if (element.attributes[:computed_from])
1402
+ element.attributes[:computed_from].each{ |el| return false unless element_has_value?(el) }
1403
+ return true
1404
+ end
1405
+ ivar = instance_variable_get(:"@#{element_name}")
1406
+ return ivar == nil ? false : true
1407
+ # FIXME: is this needed?
1408
+ # if (!mapper.mapped?(element)
1409
+ # return send("#{element_name}?") if (respond_to?("#{element_name}?"))
1410
+ # return get(element) == nil ? false : true if (!mapper.mapped?(element))
1411
+ # end
1412
+ end
1413
+
1414
+ # Returns true if the element value has been modified since instantiating or loading
1415
+ def element_modified?(element)
1416
+ element = element.is_a?(Element) ? element : self.class.elements[element]
1417
+ set_mod = @modified_elements[element.name]
1418
+ return set_mod if set_mod
1419
+ if (element.integrated?)
1420
+ return false unless integrated = get_no_load(element.integrated_from)
1421
+ return integrated.element_modified?(element.integrated_from_element)
1422
+ end
1423
+ if element_has_value?(element) && (val = get(element)).respond_to?(:modified?)
1424
+ return val.modified?
1425
+ end
1426
+ return false
1427
+ end
1428
+
1429
+ # Returns true if any of elements has been modified
1430
+ def elements_modified?(*elements)
1431
+ elements.each{ |el| return true if element_modified?(el) }
1432
+ return false
1433
+ end
1434
+
1435
+ # Returns true if any element, or any child object, has been modified
1436
+ def modified?
1437
+ return true unless @modified_elements.reject{ |key, val| !val }.empty?
1438
+ self.class.elements_array.select{ |el|
1439
+ !el.model? && element_has_value?(el) && el.type.is_a?(Spider::DataType)
1440
+ }.each do |el|
1441
+ return true if get(el).modified?
1442
+ end
1443
+ return false
1444
+ end
1445
+
1446
+ def in_storage?
1447
+ return false unless primary_keys_set?
1448
+ return self.class.load(primary_keys_hash)
1449
+ end
1450
+
1451
+ # Given elements are set as modified
1452
+ def set_modified(request) #:nodoc:
1453
+ request.each do |key, val| # FIXME: go deep
1454
+ @modified_elements[key] = true
1455
+ end
1456
+ end
1457
+
1458
+ # Resets modified elements
1459
+ def reset_modified_elements(*elements) #:nodoc:
1460
+ if (elements.length > 0)
1461
+ elements.each{ |el_name| @modified_elements.delete(el_name) }
1462
+ else
1463
+ @modified_elements = {}
1464
+ end
1465
+ end
1466
+
1467
+ # Returns true if all primary keys have a value; false if some primary key
1468
+ # is not set or the model has no primary key
1469
+ def primary_keys_set?
1470
+ primary_keys = self.class.primary_keys
1471
+ return false unless primary_keys.length > 0
1472
+ primary_keys.each do |el|
1473
+ if (el.integrated?)
1474
+ return false unless (int_obj = instance_variable_get(:"@#{el.integrated_from.name}"))
1475
+ #return false unless int_obj.instance_variable_get(:"@#{el.integrated_from_element}")
1476
+ return false unless int_obj.element_has_value?(el.integrated_from_element)
1477
+ else
1478
+ return false unless self.instance_variable_get(:"@#{el.name}")
1479
+ end
1480
+ end
1481
+ return true
1482
+ end
1483
+
1484
+ # Returns true if no element has a value
1485
+ def empty?
1486
+ return @_has_values
1487
+ end
1488
+
1489
+ # Sets all values of obj on the current object, cloning them if possible
1490
+ def merge!(obj)
1491
+ obj.class.elements_array.select{ |el|
1492
+ obj.element_has_value?(el) && !el.integrated? && !el.attributes[:computed_from]
1493
+ }.each do |el|
1494
+ val = obj.get(el)
1495
+ if (!val.is_a?(BaseModel) && val.respond_to?(:clone))
1496
+ begin; val = val.clone; rescue TypeError; end;
1497
+ end
1498
+ set_loaded_value(el, val)
1499
+ end
1500
+ @loaded_elements.merge!(obj.loaded_elements)
1501
+ end
1502
+
1503
+ # Returns a deep copy of the object
1504
+ def clone
1505
+ obj = self.class.new
1506
+ obj.merge!(self)
1507
+ return obj
1508
+ end
1509
+
1510
+ # Returns a new instance with the same primary keys
1511
+ def get_new
1512
+ obj = self.class.new
1513
+ self.class.primary_keys.each{ |k| obj.set(k, self.get(k)) }
1514
+ return obj
1515
+ end
1516
+
1517
+ # Returns a new static instance with the same primary keys
1518
+ def get_new_static
1519
+ obj = self.class.static
1520
+ self.class.primary_keys.each{ |k| obj.set(k, self.get(k)) }
1521
+ return obj
1522
+ end
1523
+
1524
+ # Returns a condition based on the current primary keys
1525
+ def keys_to_condition
1526
+ c = Condition.and
1527
+ self.class.primary_keys.each do |key|
1528
+ val = get(key)
1529
+ if (key.model?)
1530
+ c[key.name] = val.keys_to_condition
1531
+ else
1532
+ c[key.name] = val
1533
+ end
1534
+ end
1535
+ return c
1536
+ end
1537
+
1538
+
1539
+ #################################################
1540
+ # Object observers methods #
1541
+ #################################################
1542
+
1543
+ # The given block will be called whenever a value is modified.
1544
+ # The block will be passed three arguments: the object, the element name, and the previous value
1545
+ # Example:
1546
+ # obj.observe_all_values do |instance, element_name, old_val|
1547
+ # puts "#{element_name} for object #{instance} has changed from #{old_val} to #{instance.get(element_name) }"
1548
+ def observe_all_values(&proc)
1549
+ @all_values_observers << proc
1550
+ end
1551
+
1552
+ def observe_element(element_name, &proc)
1553
+ @value_observers[element_name] ||= []
1554
+ @value_observers[element_name] << proc
1555
+ end
1556
+
1557
+ def self.observer_all_values(&proc)
1558
+ @all_values_observers << proc
1559
+ end
1560
+
1561
+ def self.observe_element(element_name, &proc)
1562
+ self.value_observers[element_name] ||= []
1563
+ @value_observers[element_name] << proc
1564
+ end
1565
+
1566
+ def self.value_observers
1567
+ @value_observers ||= {}
1568
+ end
1569
+
1570
+ def self.all_values_observers
1571
+ @all_values_observers ||= []
1572
+ end
1573
+
1574
+
1575
+ # Calls the observers for element_name
1576
+ def notify_observers(element_name, new_val) #:nodoc:
1577
+ (self.class.value_observers[element_name].to_a + @value_observers[element_name].to_a) \
1578
+ .each { |proc| proc.call(self, element_name, new_val) }
1579
+ (self.class.all_values_observers.to_a + @all_values_observers.to_a).each { |proc| proc.call(self, element_name, new_val) }
1580
+ end
1581
+
1582
+
1583
+
1584
+ ##############################################################
1585
+ # Storage, mapper and schema loading (instance methods) #
1586
+ ##############################################################
1587
+
1588
+ # Returns the current @storage, or instantiates the default calling Spider::BaseModel.storage
1589
+ def storage
1590
+ return @storage || self.class.storage
1591
+ end
1592
+
1593
+ # Instantiates the storage for the instance.
1594
+ # Accepts a string (url or named storage) which will be passed to Spider::BaseModel.get_storage
1595
+ # Example:
1596
+ # obj.use_storage('my_named_db')
1597
+ # obj.use_storage('db:oracle://username:password@XE')
1598
+ def use_storage(storage)
1599
+ @storage = self.class.get_storage(storage)
1600
+ @mapper = self.class.get_mapper(@storage)
1601
+ end
1602
+
1603
+ # Returns the current mapper, or instantiates a new one (base on the current storage, if set)
1604
+ def mapper
1605
+ if (@storage)
1606
+ @mapper ||= self.class.get_mapper(@storage)
1607
+ else
1608
+ @mapper ||= self.class.mapper
1609
+ end
1610
+ return @mapper
1611
+ end
1612
+
1613
+ # Sets the current mapper
1614
+ def mapper=(mapper)
1615
+ @mapper = mapper
1616
+ end
1617
+
1618
+ ##############################################################
1619
+ # Saving and loading from storage methods #
1620
+ ##############################################################
1621
+
1622
+ # Saves the object to the storage
1623
+ # (see Mapper#save)
1624
+ def save
1625
+ mapper.save(self)
1626
+ reset_modified_elements
1627
+ end
1628
+
1629
+ # Saves the object and all child objects to the storage
1630
+ # (see Mapper#save_all)
1631
+ def save_all
1632
+ mapper.save_all(self)
1633
+ end
1634
+
1635
+ # Inserts the object in the storage
1636
+ # Note: if the object is already present in the storage and unique indexes are enforced,
1637
+ # this will raise an error.
1638
+ # (See Mapper#insert).
1639
+ def insert
1640
+ mapper.insert(self)
1641
+ reset_modified_elements
1642
+ end
1643
+
1644
+ # Updates the object in the storage
1645
+ # Note: the update will silently fail if the object is not present in the storage
1646
+ # (see Mapper#update).
1647
+ def update
1648
+ mapper.update(self)
1649
+ reset_modified_elements
1650
+ end
1651
+
1652
+ # Deletes the object from the storage
1653
+ # (see Mapper#delete).
1654
+ def delete
1655
+ mapper.delete(self)
1656
+ end
1657
+
1658
+ # Loads the object from the storage
1659
+ # Acceptable arguments are:
1660
+ # * a Query object, or
1661
+ # * a Request object, or a Hash, which will be converted to a Request, or
1662
+ # * a list of elements to request
1663
+ # It will then construct a Condition with current primary keys, and call Mapper#load
1664
+ # Note that an error will be raised by the Mapper if not all primary keys are set.
1665
+ def load(*params)
1666
+ if (params[0].is_a? Query)
1667
+ query = params[0]
1668
+ else
1669
+ return false unless primary_keys_set?
1670
+ query = Query.new
1671
+ if (params[0].is_a?(Request))
1672
+ query.request = params.shift
1673
+ elsif (params[0].is_a?(Hash))
1674
+ query.request = Request.new(params.shift)
1675
+ end
1676
+
1677
+ elements = params.length > 0 ? params : self.class.elements.keys
1678
+ return true unless elements.select{ |el| !element_loaded?(el) }.length > 0
1679
+ elements.each do |name|
1680
+ query.request[name] = true
1681
+ end
1682
+ query.condition.conjunction = :and
1683
+ self.class.primary_keys.each do |key|
1684
+ query.condition[key.name] = get(key.name)
1685
+ end
1686
+ end
1687
+ #clear_values()
1688
+ return mapper.load(self, query)
1689
+ end
1690
+
1691
+ # Sets all values to nil
1692
+ def clear_values()
1693
+ self.class.elements.each_key do |element_name|
1694
+ instance_variable_set(:"@#{element_name}", nil)
1695
+ end
1696
+ end
1697
+
1698
+ def remove_association(element, object)
1699
+ mapper.delete_element_associations(self, element, object)
1700
+ end
1701
+
1702
+ ##############################################################
1703
+ # Method missing #
1704
+ ##############################################################
1705
+
1706
+ # Tries the method on integrated models
1707
+ def method_missing(method, *args) #:nodoc:
1708
+ # UNUSED
1709
+ # case method.to_s
1710
+ # when /load_by_(.+)/
1711
+ # element = $1
1712
+ # if !self.class.elements[element.to_sym].attributes[:primary_key]
1713
+ # raise ModelException, "load_by_ called for element #{element} which is not a primary key"
1714
+ # elsif self.class.primary_keys.length > 1
1715
+ # raise ModelException, "can't call #{method} because #{element} is not the only primary key"
1716
+ # end
1717
+ # query = Query.new
1718
+ # query.condition[element.to_sym] = args[0]
1719
+ # load(query)
1720
+ # else
1721
+ if (self.class.attributes[:integrated_models])
1722
+ self.class.attributes[:integrated_models].each do |model, name|
1723
+ obj = send(name)
1724
+ if (obj.respond_to?(method))
1725
+ return obj.send(method, *args)
1726
+ end
1727
+ end
1728
+ end
1729
+ super
1730
+ # end
1731
+ end
1732
+
1733
+ # Returns a descriptive string for the object.
1734
+ # By default this method returns the value of the first String element, if any; otherwise,
1735
+ # the string representation of the first element of any type.
1736
+ # Descendant classes may well provide a better representation.
1737
+ def to_s
1738
+ self.class.each_element do |el|
1739
+ if ((el.type == String || el.type == Text) && !el.primary_key?)
1740
+ v = get(el)
1741
+ return v ? v.to_s : ''
1742
+ end
1743
+ end
1744
+ el = self.class.elements_array[0]
1745
+ if element_has_value?(el)
1746
+ v = get(el)
1747
+ return v ? v.to_s : ''
1748
+ end
1749
+ return ''
1750
+ end
1751
+
1752
+ # A compact representation of the object.
1753
+ # Note: inspect will not autoload the object.
1754
+ def inspect
1755
+ self.class.name+': {' +
1756
+ self.class.elements_array.select{ |el| (element_loaded?(el) || element_has_value?(el)) && !el.hidden? } \
1757
+ .map{ |el| ":#{el.name} => #{get(el.name).to_s}"}.join(',') + '}'
1758
+ end
1759
+
1760
+ # Returns a JSON representation of the object.
1761
+ #
1762
+ # The tree will be traversed outputting all encountered objects; when an already seen object
1763
+ # is met, the primary keys will be output (as a single value if one, as an array if many) and traversing
1764
+ # will stop.
1765
+ #
1766
+ # For more fine-grained control of the output, it is better to use the #cut method and call to_json on it.
1767
+ def to_json(state=nil, &proc)
1768
+ require 'json'
1769
+ ic = Iconv.new('UTF-8//IGNORE', 'UTF-8')
1770
+ if (@tmp_json_seen && !block_given?)
1771
+ pks = self.class.primary_keys.map{ |k| get(k).to_json }
1772
+ pks = pks[0] if pks.length == 1
1773
+ return pks.to_json
1774
+ end
1775
+ @tmp_json_seen = true
1776
+ json = ""
1777
+ #Spider::Model.with_identity_mapper do |im|
1778
+ self.class.elements_array.select{ |el| el.attributes[:integrated_model] }.each do |el|
1779
+ (int = get(el)) && int.instance_variable_set("@tmp_json_seen", true)
1780
+ end
1781
+ if (block_given?)
1782
+ select_elements = Proc.new{ true }
1783
+ else
1784
+ select_elements = Proc.new{ |name, el|
1785
+ !el.hidden?
1786
+ # &&
1787
+ # #!el.attributes[:integrated_model] &&
1788
+ # (element_has_value?(el) || (el.integrated? && element_has_value?(el.integrated_from)))
1789
+ }
1790
+ end
1791
+
1792
+
1793
+ json = "{" +
1794
+ self.class.elements.select(&select_elements).map{ |name, el|
1795
+ if (block_given?)
1796
+ val = yield(self, el)
1797
+ val ? "#{name.to_json}: #{val.to_json}" : nil
1798
+ else
1799
+ val = get(name)
1800
+ if (el.type == 'text' || el.type == 'longText')
1801
+ val = ic.iconv(val + ' ')[0..-2]
1802
+ end
1803
+ val = val.to_json
1804
+ "#{name.to_json}: #{val}"
1805
+ end
1806
+ }.select{ |pair| pair}.join(',') + "}"
1807
+ @tmp_json_seen = false
1808
+ self.class.elements_array.select{ |el| el.attributes[:integrated_model] }.each do |el|
1809
+ (int = get(el)) && int.instance_variable_set("@tmp_json_seen", false)
1810
+ end
1811
+ #end
1812
+ return json
1813
+ end
1814
+
1815
+ # Returns a part of the object tree, converted to Hashes, Arrays and Strings.
1816
+ # Arguments can be:
1817
+ # * a String, followed by a list of elements; the String will be sprintf'd with element values
1818
+ # or
1819
+ # * a depth Fixnum; depth 0 means obj.to_s will be returned, depth 1 will return an hash containing the
1820
+ # object's element values converted to string, and so on
1821
+ # or
1822
+ # * a Hash, whith element names as keys, and depths, or Hashes, or Procs as values; each element
1823
+ # will be traversed up to the depth given, or recursively according to the has; or, if a Proc is given,
1824
+ # it will be called with the current object and element name as arguments
1825
+ # or
1826
+ # * a list of elements; this is equivalent to passing a hash of the elements with depth 0.
1827
+ #
1828
+ # Examples:
1829
+ # obj.inspect
1830
+ # => Zoo::Animal: {:name => Llama, :family => Camelidae, :friends => Sheep, Camel}
1831
+ # obj.cut(0)
1832
+ # => 'Llama'
1833
+ # obj.cut(:name, :friends)
1834
+ # => {:name => 'Llama', :friends => 'Sheep, Camel'}
1835
+ # obj.cut(:name => 0, :friends => 1)
1836
+ # => {:name => 'Llama', :friends => [
1837
+ # {:name => 'Sheep', :family => 'Bovidae', :friends => 'Llama'},
1838
+ # {:name => 'Camel', :family => 'Camelidae', :friens => 'Dromedary, LLama'}
1839
+ # ]}
1840
+ # obj.cut(:name => 0, :friends => {:name => 0})
1841
+ # => {:name => 'Llama', :friends => [{:name => 'Sheep'}, {:name => 'Camel'}]}
1842
+ # objs.cut(:name => 0, :friends => lambda{ |instance, element|
1843
+ # instance.get(element).name.upcase
1844
+ # })
1845
+ # => {:name => 'Llama', :friends => ['SHEEP', 'CAMEL']}
1846
+ # obj.cut("Hi, i'm a %s and my friends are %s", :name, :friends)
1847
+ # => "Hi, i'm a Llama and my friends are Sheep, Camel"
1848
+ def cut(*params, &proc)
1849
+ h = {}
1850
+ if (params[0].is_a?(String))
1851
+ return sprintf(params[0], *params[1..-1].map{ |el| get(el) })
1852
+ elsif (params[0].is_a?(Fixnum))
1853
+ p = params.shift
1854
+ if (p < 1)
1855
+ if (block_given?)
1856
+ return proc.call(self)
1857
+ else
1858
+ return self.to_s
1859
+ end
1860
+ end
1861
+ lev = p
1862
+ where = {}
1863
+ self.class.elements_array.each { |el| where[el.name] = lev-1}
1864
+ end
1865
+ if (params[0].is_a?(Hash))
1866
+ where ||= {}
1867
+ params[0].each{ |k, v| where[k.to_sym] = v}
1868
+ else
1869
+ where ||= {}
1870
+ params.each{ |p| where[p] = 0 if p.is_a?(Symbol)}
1871
+ end
1872
+ Spider::Model.with_identity_mapper do |im|
1873
+ where.keys.each do |name|
1874
+ if (where[name].is_a?(Proc))
1875
+ val = where[name].call(self, name)
1876
+ else
1877
+ el = self.class.elements[name]
1878
+ if el
1879
+ val = get(el)
1880
+ val = val.cut(where[name], &proc) if el.model? && val
1881
+ else
1882
+ raise ModelException, "Element #{name} does not exist" unless self.respond_to?(name)
1883
+ val = self.send(name)
1884
+ val = val.cut(where[name], &proc) if val.is_a?(BaseModel)
1885
+ end
1886
+ end
1887
+ h[name] = val
1888
+ end
1889
+ end
1890
+ return h
1891
+ end
1892
+
1893
+ # Returns a element_name => value Hash
1894
+ def to_hash()
1895
+ h = {}
1896
+ self.class.elements.select{ |name, el| element_loaded? el }.each do |name, el|
1897
+ h[name] = get(name)
1898
+ end
1899
+ return h
1900
+ end
1901
+
1902
+ # Returns a yaml representation of the object. Will try to autoload all elements, unless autoload is false;
1903
+ # foreign keys will be expressed as an array if multiple, as a single primary key value otherwise
1904
+ def to_yaml(params)
1905
+ require 'yaml'
1906
+ #return YAML::dump(self)
1907
+ h = {}
1908
+ def obj_pks(obj, klass)
1909
+ unless obj
1910
+ return klass.primary_keys.length > 1 ? [] : nil
1911
+ end
1912
+ pks = obj.primary_keys
1913
+ return pks[0] if pks.length == 1
1914
+ return pks
1915
+ end
1916
+ self.class.elements_array.each do |el|
1917
+ next if params[:except] && params[:except].include?(el.name)
1918
+ if (el.model?)
1919
+ obj = get(el)
1920
+ if (el.multiple?)
1921
+ h[el.name] = obj.map_array{ |o| obj_pks(o, el.model) }
1922
+ else
1923
+ h[el.name] = obj_pks(obj, el.model)
1924
+ end
1925
+ else
1926
+ h[el.name] = get(el)
1927
+ end
1928
+ end
1929
+ return YAML::dump(h)
1930
+ end
1931
+
1932
+ def self.from_yaml(yaml)
1933
+ h = YAML::load(yaml)
1934
+ obj = self.static
1935
+ h.each do |key, value|
1936
+ el = elements[key.to_sym]
1937
+ if (el.multiple?)
1938
+ el_obj = el.model.static
1939
+ el.model.primary_keys.each do |pk|
1940
+ el_obj.set(pk, value.unshift)
1941
+ end
1942
+ obj.set(el, el_obj)
1943
+ else
1944
+ obj.set(el, value)
1945
+ end
1946
+ end
1947
+ return obj
1948
+ end
1949
+
1950
+ def self.transaction
1951
+ yield
1952
+ end
1953
+
1954
+ def self.dump_element(el)
1955
+ remove_elements = []
1956
+ method = case el.attributes[:association]
1957
+ when :many
1958
+ :many
1959
+ when :choice
1960
+ :choice
1961
+ when :multiple_choice
1962
+ :multiple_choice
1963
+ when :tree
1964
+ :tree
1965
+ else
1966
+ :element
1967
+ end
1968
+ type = el.type
1969
+ attributes = el.attributes.clone
1970
+ if (method == :many || method == :multiple_choice)
1971
+ attributes.delete(:multiple)
1972
+ end
1973
+ attributes.delete(:association) if method != :element
1974
+ if (attributes[:association_type])
1975
+ attributes[:through] = attributes[:association_type] unless attributes[:anonymous_model]
1976
+ attributes.delete(:association_type)
1977
+ end
1978
+ attributes.delete(:lazy) if attributes[:lazy] == :default
1979
+ if (method == :tree)
1980
+ delete_attrs = [:queryset_module, :multiple]
1981
+ delete_attrs.each{ |a| attributes.delete(a) }
1982
+ remove_elements += [attributes[:reverse], attributes[:tree_left], attributes[:tree_right], attributes[:tree_depth]]
1983
+ type = nil
1984
+ end
1985
+ return {
1986
+ :name => el.name,
1987
+ :type => type,
1988
+ :attributes => attributes,
1989
+ :method => method,
1990
+ :remove_elements => remove_elements
1991
+ }
1992
+ end
1993
+
1994
+ def self.prepare_to_code
1995
+ modules = self.name.split('::')[0..-2]
1996
+ included = (self.included_modules - Spider::Model::BaseModel.included_modules).select do |m|
1997
+ m.name !~ /^#{Regexp.quote(self.name)}/
1998
+ end
1999
+ local_name = self.name.split('::')[-1]
2000
+ superklass = self.superclass.name
2001
+ elements = []
2002
+ remove_elements = []
2003
+ self.elements_array.each do |el|
2004
+ next if el.integrated?
2005
+ next if (el.reverse && el.model.elements[el.reverse] && \
2006
+ (el.model.elements[el.reverse].attributes[:add_reverse] || \
2007
+ el.model.elements[el.reverse].attributes[:add_multiple_reverse]))
2008
+ el_hash = dump_element(el)
2009
+ return nil unless el_hash
2010
+ elements << el_hash
2011
+ remove_elements += el_hash[:remove_elements]
2012
+ end
2013
+ elements.reject!{ |el| remove_elements.include?(el[:name]) }
2014
+ return {
2015
+ :modules => modules,
2016
+ :included => included,
2017
+ :attributes => self.attributes,
2018
+ :elements => elements,
2019
+ :local_name => local_name,
2020
+ :superclass => superklass,
2021
+ :use_storage => @use_storage,
2022
+ :additional_code => []
2023
+ }
2024
+ end
2025
+
2026
+ def self.to_code(options={})
2027
+ c = prepare_to_code
2028
+ str = ""
2029
+ indent = 0
2030
+ append = lambda do |val|
2031
+ str += " "*indent
2032
+ str += val
2033
+ str
2034
+ end
2035
+ str += c[:modules].map{ |m| "module #{m}" }.join('; ') + "\n"
2036
+ str += "\n"
2037
+ indent = 4
2038
+ append.call "class #{c[:local_name]} < #{c[:superclass]}\n"
2039
+ indent += 4
2040
+ c[:included].each do |i|
2041
+ append.call "include #{i.name}\n"
2042
+ end
2043
+ c[:attributes].each do |k, v|
2044
+ append.call "attribute :#{k}, #{v.inspect}"
2045
+ end
2046
+ str += "\n"
2047
+ c[:elements].each do |el|
2048
+ append.call("#{el[:method].to_s} #{el[:name].inspect}")
2049
+ str += ", #{el[:type]}" if el[:type]
2050
+ str += ", #{el[:attributes].inspect[1..-2]}\n" if el[:attributes] && !el[:attributes].empty?
2051
+ end
2052
+ str += "\n"
2053
+ append.call "use_storage '#{c[:use_storage]}'\n" if c[:use_storage]
2054
+ c[:additional_code].each do |block|
2055
+ block.each_line do |line|
2056
+ append.call line
2057
+ end
2058
+ str += "\n"
2059
+ end
2060
+ indent -= 4
2061
+ append.call("end\n")
2062
+ str += c[:modules].map{ "end" }.join(';')
2063
+ return str
2064
+ end
2065
+
2066
+ end
2067
+
2068
+ end; end