spiderfw 0.5

Sign up to get free protection for your applications and to get access to all the features.
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