spiderfw 0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/README +4 -0
- data/Rakefile +74 -0
- data/apps/cas_server/_init.rb +23 -0
- data/apps/cas_server/config/options.rb +9 -0
- data/apps/cas_server/controllers/mixins/cas_login_mixin.rb +353 -0
- data/apps/cas_server/lib/cas.rb +329 -0
- data/apps/cas_server/lib/utils.rb +30 -0
- data/apps/cas_server/models/login_ticket.rb +11 -0
- data/apps/cas_server/models/mixins/consumable.rb +10 -0
- data/apps/cas_server/models/proxy_granting_ticket.rb +12 -0
- data/apps/cas_server/models/proxy_ticket.rb +12 -0
- data/apps/cas_server/models/service_ticket.rb +22 -0
- data/apps/cas_server/models/ticket.rb +20 -0
- data/apps/cas_server/models/ticket_granting_ticket.rb +15 -0
- data/apps/cas_server/views/logout.shtml +3 -0
- data/apps/core/admin/_init.rb +23 -0
- data/apps/core/admin/admin.rb +17 -0
- data/apps/core/admin/controllers/admin_controller.rb +15 -0
- data/apps/core/admin/views/index.shtml +1 -0
- data/apps/core/admin/views/spider_admin.layout.shtml +23 -0
- data/apps/core/admin/widgets/admin_menu/admin_menu.rb +17 -0
- data/apps/core/auth/_init.rb +42 -0
- data/apps/core/auth/controllers/login_controller.rb +94 -0
- data/apps/core/auth/controllers/mixins/auth_helper.rb +114 -0
- data/apps/core/auth/controllers/mixins/http_basic_auth.rb +24 -0
- data/apps/core/auth/controllers/mixins/http_digest_auth.rb +26 -0
- data/apps/core/auth/lib/authenticable.rb +86 -0
- data/apps/core/auth/lib/authenticator.rb +11 -0
- data/apps/core/auth/lib/digest_authenticator.rb +44 -0
- data/apps/core/auth/lib/login_authenticator.rb +27 -0
- data/apps/core/auth/models/digest_user.rb +34 -0
- data/apps/core/auth/models/group.rb +22 -0
- data/apps/core/auth/models/login_user.rb +15 -0
- data/apps/core/auth/models/mixins/access_control.rb +71 -0
- data/apps/core/auth/models/mixins/authentication_tracking.rb +19 -0
- data/apps/core/auth/models/super_user.rb +8 -0
- data/apps/core/auth/models/user.rb +15 -0
- data/apps/core/auth/po/it/spider_auth.po +33 -0
- data/apps/core/auth/po/spider_auth.pot +31 -0
- data/apps/core/auth/views/login.shtml +25 -0
- data/apps/core/components/_init.rb +21 -0
- data/apps/core/components/po/it/spider_components.po +52 -0
- data/apps/core/components/po/spider_components.pot +53 -0
- data/apps/core/components/public/css/admin.css +73 -0
- data/apps/core/components/public/css/crud.css +58 -0
- data/apps/core/components/public/css/img/add.gif +0 -0
- data/apps/core/components/public/css/img/ajax-loader.gif +0 -0
- data/apps/core/components/public/css/img/back.gif +0 -0
- data/apps/core/components/public/css/img/bg_header.png +0 -0
- data/apps/core/components/public/css/img/body_bg.jpg +0 -0
- data/apps/core/components/public/css/img/gray_gradient.gif +0 -0
- data/apps/core/components/public/css/img/li_bg.png +0 -0
- data/apps/core/components/public/css/img/li_bg_active.png +0 -0
- data/apps/core/components/public/css/img/li_bg_hover.png +0 -0
- data/apps/core/components/public/css/img/logo.png +0 -0
- data/apps/core/components/public/css/img/menu_bg.png +0 -0
- data/apps/core/components/public/css/img/menu_bottom.png +0 -0
- data/apps/core/components/public/css/img/menu_top.png +0 -0
- data/apps/core/components/public/css/img/section-bottom.jpg +0 -0
- data/apps/core/components/public/css/list.css +7 -0
- data/apps/core/components/public/css/menu.css +47 -0
- data/apps/core/components/public/css/spider.css +9 -0
- data/apps/core/components/public/css/switcher.css +13 -0
- data/apps/core/components/public/css/table.css +99 -0
- data/apps/core/components/public/js/inheritance.js +71 -0
- data/apps/core/components/public/js/jquery/jquery-1.3.2.js +4376 -0
- data/apps/core/components/public/js/jquery/jquery-ui/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/css/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/css/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/css/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/css/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/css/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/css/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/css/smoothness/images/ui-icons_222222_256x240.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/css/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/css/smoothness/images/ui-icons_454545_256x240.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/css/smoothness/images/ui-icons_888888_256x240.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/css/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/css/smoothness/jquery-ui-1.7.2.custom.css +406 -0
- data/apps/core/components/public/js/jquery/jquery-ui/css/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/css/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/css/ui-lightness/images/ui-bg_flat_10_000000_40x100.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/css/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/css/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/css/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/css/ui-lightness/images/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/css/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/css/ui-lightness/images/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/css/ui-lightness/images/ui-icons_222222_256x240.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/css/ui-lightness/images/ui-icons_228ef1_256x240.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/css/ui-lightness/images/ui-icons_ef8c08_256x240.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/css/ui-lightness/images/ui-icons_ffd27a_256x240.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/css/ui-lightness/images/ui-icons_ffffff_256x240.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/css/ui-lightness/jquery-ui-1.7.2.custom.css +406 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/AUTHORS.txt +30 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/GPL-LICENSE.txt +278 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/MIT-LICENSE.txt +25 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/external/bgiframe/ChangeLog.txt +20 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/external/bgiframe/META.json +32 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/external/bgiframe/jquery.bgiframe.js +100 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/external/bgiframe/jquery.bgiframe.min.js +10 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/external/bgiframe/jquery.bgiframe.pack.js +10 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/external/bgiframe/test/index.html +197 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/external/cookie/jquery.cookie.js +97 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/external/cookie/jquery.cookie.min.js +10 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/external/cookie/jquery.cookie.pack.js +10 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/external/cookie/jquery.cookie.zip +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/external/jsdiff/jsdiff.js +159 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/external/qunit/testrunner.js +780 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/external/qunit/testsuite.css +120 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/external/simulate/jquery.simulate.js +152 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/jquery-1.3.2.js +4376 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/images/ui-icons_222222_256x240.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/images/ui-icons_2e83ff_256x240.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/images/ui-icons_454545_256x240.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/images/ui-icons_888888_256x240.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/ui.accordion.css +9 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/ui.all.css +2 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/ui.base.css +8 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/ui.core.css +37 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/ui.datepicker.css +62 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/ui.dialog.css +13 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/ui.progressbar.css +4 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/ui.resizable.css +13 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/ui.slider.css +17 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/ui.tabs.css +11 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/base/ui.theme.css +245 -0
- 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
- 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
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/images/ui-bg_flat_10_000000_40x100.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- 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
- 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
- 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
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/images/ui-icons_222222_256x240.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/images/ui-icons_228ef1_256x240.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/images/ui-icons_ef8c08_256x240.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/images/ui-icons_ffd27a_256x240.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/images/ui-icons_ffffff_256x240.png +0 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/jquery-ui-1.7.2.custom.css +406 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/ui.accordion.css +9 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/ui.all.css +2 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/ui.base.css +8 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/ui.core.css +37 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/ui.datepicker.css +62 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/ui.dialog.css +13 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/ui.progressbar.css +4 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/ui.resizable.css +13 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/ui.slider.css +17 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/ui.tabs.css +11 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/themes/ui-lightness/ui.theme.css +247 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/effects.blind.js +49 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/effects.bounce.js +78 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/effects.clip.js +54 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/effects.core.js +545 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/effects.drop.js +50 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/effects.explode.js +79 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/effects.fold.js +56 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/effects.highlight.js +48 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/effects.pulsate.js +56 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/effects.scale.js +180 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/effects.shake.js +57 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/effects.slide.js +50 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/effects.transfer.js +45 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/jquery-ui-i18n.js +771 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-ar.js +20 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-bg.js +20 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-ca.js +19 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-cs.js +19 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-da.js +19 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-de.js +19 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-el.js +19 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-eo.js +19 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-es.js +19 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-fa.js +19 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-fi.js +19 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-fr.js +19 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-he.js +19 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-hr.js +19 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-hu.js +19 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-hy.js +19 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-id.js +19 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-is.js +19 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-it.js +19 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-ja.js +20 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-ko.js +19 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-lt.js +19 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-lv.js +19 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-ms.js +19 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-nl.js +19 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-no.js +19 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-pl.js +19 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-pt-BR.js +19 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-ro.js +22 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-ru.js +19 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-sk.js +19 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-sl.js +20 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-sq.js +19 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-sr-SR.js +19 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-sr.js +19 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-sv.js +19 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-th.js +19 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-tr.js +19 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-uk.js +25 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-zh-CN.js +19 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/i18n/ui.datepicker-zh-TW.js +19 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/jquery-ui-1.7.2.custom.js +9133 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/ui.accordion.js +477 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/ui.core.js +519 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/ui.datepicker.js +1636 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/ui.dialog.js +671 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/ui.draggable.js +766 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/ui.droppable.js +282 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/ui.progressbar.js +116 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/ui.resizable.js +800 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/ui.selectable.js +257 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/ui.slider.js +558 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/ui.sortable.js +1019 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/ui/ui.tabs.js +685 -0
- data/apps/core/components/public/js/jquery/jquery-ui/development-bundle/version.txt +1 -0
- data/apps/core/components/public/js/jquery/jquery-ui/index.html +367 -0
- data/apps/core/components/public/js/jquery/jquery-ui/js/jquery-1.3.2.min.js +19 -0
- data/apps/core/components/public/js/jquery/jquery-ui/js/jquery-ui-1.7.2.custom.min.js +298 -0
- data/apps/core/components/public/js/jquery/plugins/jquery-autocomplete/changelog.txt +27 -0
- data/apps/core/components/public/js/jquery/plugins/jquery-autocomplete/jquery.autocomplete.css +48 -0
- data/apps/core/components/public/js/jquery/plugins/jquery-autocomplete/jquery.autocomplete.js +808 -0
- data/apps/core/components/public/js/jquery/plugins/jquery-autocomplete/jquery.autocomplete.min.js +13 -0
- data/apps/core/components/public/js/jquery/plugins/jquery-autocomplete/jquery.autocomplete.pack.js +12 -0
- data/apps/core/components/public/js/jquery/plugins/jquery-autocomplete/lib/jquery.ajaxQueue.js +116 -0
- data/apps/core/components/public/js/jquery/plugins/jquery-autocomplete/lib/jquery.bgiframe.min.js +10 -0
- data/apps/core/components/public/js/jquery/plugins/jquery-autocomplete/lib/jquery.js +3558 -0
- data/apps/core/components/public/js/jquery/plugins/jquery-autocomplete/lib/thickbox-compressed.js +10 -0
- data/apps/core/components/public/js/jquery/plugins/jquery-autocomplete/lib/thickbox.css +163 -0
- data/apps/core/components/public/js/jquery/plugins/jquery-autocomplete/todo +166 -0
- data/apps/core/components/public/js/jquery/plugins/jquery.form.js +653 -0
- data/apps/core/components/public/js/jquery/plugins/jquery.query-2.1.6.js +224 -0
- data/apps/core/components/public/js/jquery/plugins/jquery.url.js +214 -0
- data/apps/core/components/public/js/jquery/plugins/jtree/jquery.jtree.1.0.js +187 -0
- data/apps/core/components/public/js/jquery/plugins/jtree/jquery.jtree.1.0.min.js +1 -0
- data/apps/core/components/public/js/jquery/plugins/jtree/jquery.jtree.spider.1.0.js +193 -0
- data/apps/core/components/public/js/jquery/plugins/treeview/changelog.txt +35 -0
- data/apps/core/components/public/js/jquery/plugins/treeview/images/ajax-loader.gif +0 -0
- data/apps/core/components/public/js/jquery/plugins/treeview/images/file.gif +0 -0
- data/apps/core/components/public/js/jquery/plugins/treeview/images/folder-closed.gif +0 -0
- data/apps/core/components/public/js/jquery/plugins/treeview/images/folder.gif +0 -0
- data/apps/core/components/public/js/jquery/plugins/treeview/images/minus.gif +0 -0
- data/apps/core/components/public/js/jquery/plugins/treeview/images/plus.gif +0 -0
- data/apps/core/components/public/js/jquery/plugins/treeview/images/treeview-black-line.gif +0 -0
- data/apps/core/components/public/js/jquery/plugins/treeview/images/treeview-black.gif +0 -0
- data/apps/core/components/public/js/jquery/plugins/treeview/images/treeview-default-line.gif +0 -0
- data/apps/core/components/public/js/jquery/plugins/treeview/images/treeview-default.gif +0 -0
- data/apps/core/components/public/js/jquery/plugins/treeview/images/treeview-famfamfam-line.gif +0 -0
- data/apps/core/components/public/js/jquery/plugins/treeview/images/treeview-famfamfam.gif +0 -0
- data/apps/core/components/public/js/jquery/plugins/treeview/images/treeview-gray-line.gif +0 -0
- data/apps/core/components/public/js/jquery/plugins/treeview/images/treeview-gray.gif +0 -0
- data/apps/core/components/public/js/jquery/plugins/treeview/images/treeview-red-line.gif +0 -0
- data/apps/core/components/public/js/jquery/plugins/treeview/images/treeview-red.gif +0 -0
- data/apps/core/components/public/js/jquery/plugins/treeview/jquery.treeview.async.js +110 -0
- data/apps/core/components/public/js/jquery/plugins/treeview/jquery.treeview.css +74 -0
- data/apps/core/components/public/js/jquery/plugins/treeview/jquery.treeview.edit.js +37 -0
- data/apps/core/components/public/js/jquery/plugins/treeview/jquery.treeview.js +256 -0
- data/apps/core/components/public/js/jquery/plugins/treeview/jquery.treeview.min.js +15 -0
- data/apps/core/components/public/js/jquery/plugins/treeview/jquery.treeview.pack.js +16 -0
- data/apps/core/components/public/js/jquery/plugins/treeview/jquery.treeview.sortable.js +386 -0
- data/apps/core/components/public/js/jquery/plugins/treeview/lib/jquery.cookie.js +92 -0
- data/apps/core/components/public/js/jquery/plugins/treeview/lib/jquery.js +3534 -0
- data/apps/core/components/public/js/jquery/plugins/treeview/todo +8 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/LICENSE +0 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/README +0 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/jquery.timepickr.css +34 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/jquery.timepickr.js +1413 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/jquery.timepickr.min.js +10 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/images/222222_256x240_icons_icons.png +0 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/images/2e83ff_256x240_icons_icons.png +0 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/images/454545_256x240_icons_icons.png +0 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/images/888888_256x240_icons_icons.png +0 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/images/aaaaaa_40x100_textures_01_flat_0.png +0 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/images/cccccc_40x100_textures_03_highlight_soft_75.png +0 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/images/cd0a0a_256x240_icons_icons.png +0 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/images/dadada_40x100_textures_02_glass_75.png +0 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/images/e6e6e6_40x100_textures_02_glass_75.png +0 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/images/fbf9ee_40x100_textures_02_glass_55.png +0 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/images/fef1ec_40x100_textures_05_inset_soft_95.png +0 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/images/ffffff_40x100_textures_02_glass_65.png +0 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/images/ffffff_40x100_textures_02_glass_75.png +0 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/ui.accordion.css +9 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/ui.all.css +4 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/ui.allplugins.css +7 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/ui.core.css +37 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/ui.datepicker.css +60 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/ui.dialog.css +13 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/ui.progressbar.css +4 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/ui.resizable.css +13 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/ui.slider.css +13 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/ui.tabs.css +9 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/ui.theme.css +238 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/themes/default/ui.timepickr.css +44 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/ui.timepickr.js +2307 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/dist/ui.timepickr.min.js +10 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/page/body-bg.png +0 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/page/btn-hover.png +0 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/page/btn.png +0 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/page/buttons.psd +0 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/page/index.html +326 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/page/jquery.anchorHandler.js +44 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/page/jquery.strings.js +290 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/page/jquery.ui.all.js +518 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/page/jquery.utils.js +255 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/page/jquery.utils.ui.min.js +26 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/page/logo-ff.png +0 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/page/logo-ie.png +0 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/page/logo.png +0 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/page/logo.psd +0 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/page/package.png +0 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/page/reset.css +48 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/page/script_code.png +0 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/page/styles.css +308 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/page/themeroller_ready_black_200px.gif +0 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/page/weather_sun.png +0 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/src/css/ui.dropslide.css +26 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/src/css/ui.timepickr.css +18 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/src/ui.dropslide.js +132 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/src/ui.timepickr.js +217 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/test/data/testrunner.js +1 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/test/index.html +34 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/test/otherlibs/jquery/1.2.1/jquery.js +11 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/test/otherlibs/jquery/1.2.3/jquery.js +11 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/test/qunit/testrunner.js +780 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/test/qunit/testsuite.css +120 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/test/styles.css +201 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/test/test.js +41 -0
- data/apps/core/components/public/js/jquery/plugins/ui-timepickr/test/unit/ui.timepickr.js +154 -0
- data/apps/core/components/public/js/list.js +234 -0
- data/apps/core/components/public/js/spider.js +484 -0
- data/apps/core/components/public/widgets/search_table.js +10 -0
- data/apps/core/components/public/widgets/table.js +9 -0
- data/apps/core/components/widgets/admin/admin.rb +75 -0
- data/apps/core/components/widgets/admin/admin.shtml +16 -0
- data/apps/core/components/widgets/confirm/confirm.rb +37 -0
- data/apps/core/components/widgets/confirm/confirm.shtml +9 -0
- data/apps/core/components/widgets/crud/crud.rb +119 -0
- data/apps/core/components/widgets/crud/crud.shtml +59 -0
- data/apps/core/components/widgets/crud/form_table/form_table.rb +13 -0
- data/apps/core/components/widgets/crud/form_table/form_table.shtml +3 -0
- data/apps/core/components/widgets/list/list.rb +294 -0
- data/apps/core/components/widgets/list/list.shtml +38 -0
- data/apps/core/components/widgets/list/plugins/exportable/exportable_list.rb +19 -0
- data/apps/core/components/widgets/list/plugins/exportable/exportable_list.shtml +5 -0
- data/apps/core/components/widgets/menu/menu.rb +20 -0
- data/apps/core/components/widgets/menu/menu.shtml +13 -0
- data/apps/core/components/widgets/search_table/search_table.rb +22 -0
- data/apps/core/components/widgets/search_table/search_table.shtml +11 -0
- data/apps/core/components/widgets/switcher/switcher.rb +68 -0
- data/apps/core/components/widgets/switcher/switcher.shtml +17 -0
- data/apps/core/components/widgets/table/table.rb +153 -0
- data/apps/core/components/widgets/table/table.shtml +41 -0
- data/apps/core/components/widgets/tabs/tabs.rb +38 -0
- data/apps/core/components/widgets/tabs/tabs.shtml +2 -0
- data/apps/core/forms/_init.rb +22 -0
- data/apps/core/forms/po/it/spider_forms.po +36 -0
- data/apps/core/forms/po/spider_forms.pot +37 -0
- data/apps/core/forms/public/date_time.js +23 -0
- data/apps/core/forms/public/form.css +81 -0
- data/apps/core/forms/public/form.js +19 -0
- data/apps/core/forms/public/input.js +30 -0
- data/apps/core/forms/public/search_select.js +123 -0
- data/apps/core/forms/public/select.js +11 -0
- data/apps/core/forms/tags/element_label.erb +1 -0
- data/apps/core/forms/tags/element_row.erb +4 -0
- data/apps/core/forms/tags/row.erb +4 -0
- data/apps/core/forms/widgets/form/form.rb +430 -0
- data/apps/core/forms/widgets/form/form.shtml +47 -0
- data/apps/core/forms/widgets/inputs/checkbox/checkbox.rb +13 -0
- data/apps/core/forms/widgets/inputs/checkbox/checkbox.shtml +3 -0
- data/apps/core/forms/widgets/inputs/date_time/date_time.rb +48 -0
- data/apps/core/forms/widgets/inputs/date_time/date_time.shtml +4 -0
- data/apps/core/forms/widgets/inputs/hidden/hidden.rb +8 -0
- data/apps/core/forms/widgets/inputs/hidden/hidden.shtml +3 -0
- data/apps/core/forms/widgets/inputs/input/input.rb +105 -0
- data/apps/core/forms/widgets/inputs/input/input.shtml +3 -0
- data/apps/core/forms/widgets/inputs/input/readonly.shtml +6 -0
- data/apps/core/forms/widgets/inputs/password/password.rb +24 -0
- data/apps/core/forms/widgets/inputs/password/password.shtml +5 -0
- data/apps/core/forms/widgets/inputs/search_select/search_select.rb +168 -0
- data/apps/core/forms/widgets/inputs/search_select/search_select.shtml +42 -0
- data/apps/core/forms/widgets/inputs/select/select.rb +126 -0
- data/apps/core/forms/widgets/inputs/select/select.shtml +8 -0
- data/apps/core/forms/widgets/inputs/subform/subform.rb +10 -0
- data/apps/core/forms/widgets/inputs/subform/subform.shtml +5 -0
- data/apps/core/forms/widgets/inputs/text/text.rb +9 -0
- data/apps/core/forms/widgets/inputs/text/text.shtml +3 -0
- data/apps/core/forms/widgets/inputs/text_area/text_area.rb +10 -0
- data/apps/core/forms/widgets/inputs/text_area/text_area.shtml +1 -0
- data/apps/drb_server/_init.rb +8 -0
- data/apps/drb_server/lib/model_server.rb +41 -0
- data/apps/drb_server/script/start_server.rb +19 -0
- data/apps/hippo/_init.rb +10 -0
- data/apps/hippo/models/mixins/hippo_struct.rb +210 -0
- data/apps/hippo/models/security_group.rb +21 -0
- data/apps/hippo/models/security_user.rb +29 -0
- data/apps/messenger/_init.rb +24 -0
- data/apps/messenger/config/options.rb +15 -0
- data/apps/messenger/config/worker.rb +3 -0
- data/apps/messenger/controllers/messenger_controller.rb +80 -0
- data/apps/messenger/controllers/mixins/messenger_controller_mixin.rb +54 -0
- data/apps/messenger/lib/backends/email/smtp.rb +0 -0
- data/apps/messenger/messenger.rb +94 -0
- data/apps/messenger/models/email.rb +22 -0
- data/apps/messenger/models/message.rb +44 -0
- data/apps/messenger/po/it/spider_messenger.po +77 -0
- data/apps/messenger/po/spider_messenger.pot +77 -0
- data/apps/messenger/views/index.shtml +13 -0
- data/apps/messenger/views/list.shtml +5 -0
- data/apps/messenger/views/messenger.layout.shtml +4 -0
- data/apps/messenger/views/queue.shtml +8 -0
- data/apps/messenger/views/tags/sent_messages.shtml +1 -0
- data/apps/soap/_init.rb +16 -0
- data/apps/soap/controllers/soap_controller.rb +334 -0
- data/apps/soap/doc/examples/example.rb +28 -0
- data/apps/soap/lib/soap.rb +217 -0
- data/apps/sso/_init.rb +16 -0
- data/apps/sso/config/options.rb +13 -0
- data/apps/sso/controllers/mixins/saml2_mixin.rb +149 -0
- data/apps/sso/lib/saml2/backend.rb +16 -0
- data/apps/sso/lib/saml2/lasso_backend.rb +88 -0
- data/apps/sso/lib/saml2.rb +112 -0
- data/apps/sso/views/saml2_post.shtml +19 -0
- data/apps/webdav/_init.rb +15 -0
- data/apps/webdav/controllers/webdav_controller.rb +862 -0
- data/apps/webdav/lib/locking.rb +193 -0
- data/apps/webdav/lib/vfs/abstract.rb +78 -0
- data/apps/webdav/lib/vfs/local.rb +126 -0
- data/apps/webdav/lib/vfs/mapped.rb +235 -0
- data/apps/webdav/lib/vfs/properties.rb +41 -0
- data/apps/worker/_init.rb +13 -0
- data/apps/worker/cmd.rb +65 -0
- data/apps/worker/config/options.rb +9 -0
- data/apps/worker/lib/runner.rb +43 -0
- data/apps/worker/lib/task.rb +9 -0
- data/apps/worker/models/job.rb +20 -0
- data/apps/worker/worker.rb +168 -0
- data/bin/spider +9 -0
- data/blueprints/app/_init.rb +10 -0
- data/blueprints/app/controllers/__APP___controller.rb +14 -0
- data/blueprints/app/views/__APP__.layout.shtml +8 -0
- data/blueprints/app/views/index.shtml +3 -0
- data/blueprints/install/config/config.yml +14 -0
- data/blueprints/install/init.rb +3 -0
- data/data/locale/it/LC_MESSAGES/spider.mo +0 -0
- data/data/locale/it/LC_MESSAGES/spider_auth.mo +0 -0
- data/data/locale/it/LC_MESSAGES/spider_components.mo +0 -0
- data/data/locale/it/LC_MESSAGES/spider_forms.mo +0 -0
- data/data/locale/it/LC_MESSAGES/spider_messenger.mo +0 -0
- data/lib/spiderfw/app.rb +186 -0
- data/lib/spiderfw/autoload.rb +12 -0
- data/lib/spiderfw/cache/template_cache.rb +136 -0
- data/lib/spiderfw/cmd/cmd.rb +72 -0
- data/lib/spiderfw/cmd/commands/cert.rb +427 -0
- data/lib/spiderfw/cmd/commands/console.rb +27 -0
- data/lib/spiderfw/cmd/commands/init.rb +52 -0
- data/lib/spiderfw/cmd/commands/model.rb +131 -0
- data/lib/spiderfw/cmd/commands/setup.rb +54 -0
- data/lib/spiderfw/cmd/commands/test.rb +61 -0
- data/lib/spiderfw/cmd/commands/webserver.rb +93 -0
- data/lib/spiderfw/config/configurable.rb +38 -0
- data/lib/spiderfw/config/configuration.rb +281 -0
- data/lib/spiderfw/config/options/spider.rb +122 -0
- data/lib/spiderfw/controller/app_controller.rb +14 -0
- data/lib/spiderfw/controller/controller.rb +321 -0
- data/lib/spiderfw/controller/controller_exceptions.rb +27 -0
- data/lib/spiderfw/controller/controller_io.rb +26 -0
- data/lib/spiderfw/controller/controller_mixin.rb +12 -0
- data/lib/spiderfw/controller/cookie.rb +20 -0
- data/lib/spiderfw/controller/cookies.rb +17 -0
- data/lib/spiderfw/controller/dispatcher.rb +269 -0
- data/lib/spiderfw/controller/first_responder.rb +59 -0
- data/lib/spiderfw/controller/formats/html.rb +90 -0
- data/lib/spiderfw/controller/helpers/widget_helper.rb +73 -0
- data/lib/spiderfw/controller/home_controller.rb +26 -0
- data/lib/spiderfw/controller/http_controller.rb +112 -0
- data/lib/spiderfw/controller/mixins/http_mixin.rb +228 -0
- data/lib/spiderfw/controller/mixins/static_content.rb +90 -0
- data/lib/spiderfw/controller/mixins/visual.rb +489 -0
- data/lib/spiderfw/controller/page_controller.rb +39 -0
- data/lib/spiderfw/controller/request.rb +48 -0
- data/lib/spiderfw/controller/response.rb +50 -0
- data/lib/spiderfw/controller/router.rb +9 -0
- data/lib/spiderfw/controller/scene.rb +27 -0
- data/lib/spiderfw/controller/session/file_session.rb +83 -0
- data/lib/spiderfw/controller/session/flash_hash.rb +56 -0
- data/lib/spiderfw/controller/session/memory_session.rb +62 -0
- data/lib/spiderfw/controller/session/transient_hash.rb +18 -0
- data/lib/spiderfw/controller/session.rb +106 -0
- data/lib/spiderfw/controller/spider_controller.rb +20 -0
- data/lib/spiderfw/create.rb +64 -0
- data/lib/spiderfw/env.rb +17 -0
- data/lib/spiderfw/exceptions.rb +2 -0
- data/lib/spiderfw/home.rb +26 -0
- data/lib/spiderfw/http/adapters/cgi.rb +62 -0
- data/lib/spiderfw/http/adapters/cgi_io.rb +27 -0
- data/lib/spiderfw/http/adapters/fcgi.rb +62 -0
- data/lib/spiderfw/http/adapters/mongrel.rb +207 -0
- data/lib/spiderfw/http/adapters/rack.rb +152 -0
- data/lib/spiderfw/http/adapters/thin.rb +38 -0
- data/lib/spiderfw/http/adapters/webrick.rb +176 -0
- data/lib/spiderfw/http/http.rb +297 -0
- data/lib/spiderfw/http/server.rb +40 -0
- data/lib/spiderfw/i18n/cldr.rb +114 -0
- data/lib/spiderfw/i18n/gettext.rb +8 -0
- data/lib/spiderfw/i18n/i18n.rb +120 -0
- data/lib/spiderfw/i18n/provider.rb +12 -0
- data/lib/spiderfw/i18n/rails.rb +89 -0
- data/lib/spiderfw/i18n/shtml_parser.rb +41 -0
- data/lib/spiderfw/model/active_record.rb +335 -0
- data/lib/spiderfw/model/base_model.rb +2068 -0
- data/lib/spiderfw/model/condition.rb +416 -0
- data/lib/spiderfw/model/data_type.rb +75 -0
- data/lib/spiderfw/model/datatypes/binary.rb +10 -0
- data/lib/spiderfw/model/datatypes/bool.rb +10 -0
- data/lib/spiderfw/model/datatypes/decimal.rb +22 -0
- data/lib/spiderfw/model/datatypes/email.rb +13 -0
- data/lib/spiderfw/model/datatypes/password.rb +63 -0
- data/lib/spiderfw/model/datatypes/serialized_object.rb +32 -0
- data/lib/spiderfw/model/datatypes/text.rb +10 -0
- data/lib/spiderfw/model/datatypes/uuid.rb +29 -0
- data/lib/spiderfw/model/datatypes.rb +19 -0
- data/lib/spiderfw/model/element.rb +250 -0
- data/lib/spiderfw/model/extended_models/managed.rb +43 -0
- data/lib/spiderfw/model/identity_mapper.rb +89 -0
- data/lib/spiderfw/model/inline_model.rb +37 -0
- data/lib/spiderfw/model/mappers/db_mapper.rb +1126 -0
- data/lib/spiderfw/model/mappers/hash_mapper.rb +114 -0
- data/lib/spiderfw/model/mappers/mapper.rb +926 -0
- data/lib/spiderfw/model/mappers/mappers.rb +7 -0
- data/lib/spiderfw/model/mappers/proxy_mapper.rb +25 -0
- data/lib/spiderfw/model/mappers/vfs/flat_file.rb +31 -0
- data/lib/spiderfw/model/mixins/converted.rb +110 -0
- data/lib/spiderfw/model/mixins/list.rb +135 -0
- data/lib/spiderfw/model/mixins/mixins.rb +7 -0
- data/lib/spiderfw/model/mixins/state_machine.rb +81 -0
- data/lib/spiderfw/model/mixins/synchronized.rb +88 -0
- data/lib/spiderfw/model/mixins/tree.rb +296 -0
- data/lib/spiderfw/model/mixins/versioned.rb +147 -0
- data/lib/spiderfw/model/model.rb +226 -0
- data/lib/spiderfw/model/model_hash.rb +50 -0
- data/lib/spiderfw/model/proxy_model.rb +37 -0
- data/lib/spiderfw/model/query.rb +147 -0
- data/lib/spiderfw/model/query_funcs.rb +99 -0
- data/lib/spiderfw/model/query_set.rb +700 -0
- data/lib/spiderfw/model/request.rb +80 -0
- data/lib/spiderfw/model/storage/base_storage.rb +110 -0
- data/lib/spiderfw/model/storage/db/adapters/mssql.rb +129 -0
- data/lib/spiderfw/model/storage/db/adapters/mysql.rb +436 -0
- data/lib/spiderfw/model/storage/db/adapters/oci8.rb +533 -0
- data/lib/spiderfw/model/storage/db/adapters/sqlite.rb +163 -0
- data/lib/spiderfw/model/storage/db/connectors/odbc.rb +213 -0
- data/lib/spiderfw/model/storage/db/db.rb +13 -0
- data/lib/spiderfw/model/storage/db/db_connection_pool.rb +126 -0
- data/lib/spiderfw/model/storage/db/db_connector.rb +9 -0
- data/lib/spiderfw/model/storage/db/db_schema.rb +233 -0
- data/lib/spiderfw/model/storage/db/db_storage.rb +872 -0
- data/lib/spiderfw/model/storage/db/dialects/no_total_rows.rb +18 -0
- data/lib/spiderfw/model/storage/db/reflector.rb +36 -0
- data/lib/spiderfw/model/storage/null_storage.rb +18 -0
- data/lib/spiderfw/model/storage/schema.rb +19 -0
- data/lib/spiderfw/model/storage.rb +72 -0
- data/lib/spiderfw/model/sync.rb +94 -0
- data/lib/spiderfw/model/type.rb +13 -0
- data/lib/spiderfw/model/unit_of_work.rb +75 -0
- data/lib/spiderfw/requires.rb +21 -0
- data/lib/spiderfw/resource.rb +18 -0
- data/lib/spiderfw/setup/setup_task.rb +44 -0
- data/lib/spiderfw/tag/tag.rb +28 -0
- data/lib/spiderfw/templates/blocks/attr_if.rb +32 -0
- data/lib/spiderfw/templates/blocks/comment.rb +38 -0
- data/lib/spiderfw/templates/blocks/debugger.rb +16 -0
- data/lib/spiderfw/templates/blocks/each.rb +51 -0
- data/lib/spiderfw/templates/blocks/html.rb +70 -0
- data/lib/spiderfw/templates/blocks/if.rb +35 -0
- data/lib/spiderfw/templates/blocks/pass.rb +16 -0
- data/lib/spiderfw/templates/blocks/render.rb +23 -0
- data/lib/spiderfw/templates/blocks/run.rb +30 -0
- data/lib/spiderfw/templates/blocks/tag.rb +21 -0
- data/lib/spiderfw/templates/blocks/tag_if.rb +29 -0
- data/lib/spiderfw/templates/blocks/text.rb +43 -0
- data/lib/spiderfw/templates/blocks/widget.rb +58 -0
- data/lib/spiderfw/templates/blocks/yield.rb +18 -0
- data/lib/spiderfw/templates/layout.rb +44 -0
- data/lib/spiderfw/templates/resources/less.rb +14 -0
- data/lib/spiderfw/templates/template.rb +625 -0
- data/lib/spiderfw/templates/template_blocks.rb +185 -0
- data/lib/spiderfw/utils/annotations.rb +201 -0
- data/lib/spiderfw/utils/fork.rb +75 -0
- data/lib/spiderfw/utils/hash_comparison.rb +8 -0
- data/lib/spiderfw/utils/inflector.rb +42 -0
- data/lib/spiderfw/utils/logger.rb +92 -0
- data/lib/spiderfw/utils/monkey/class.rb +59 -0
- data/lib/spiderfw/utils/monkey/date_time.rb +34 -0
- data/lib/spiderfw/utils/monkey/debugger.rb +25 -0
- data/lib/spiderfw/utils/monkey/exception.rb +23 -0
- data/lib/spiderfw/utils/monkey/kernel.rb +12 -0
- data/lib/spiderfw/utils/monkey/module.rb +12 -0
- data/lib/spiderfw/utils/monkey/symbol.rb +7 -0
- data/lib/spiderfw/utils/monkey/time.rb +17 -0
- data/lib/spiderfw/utils/multi_level_hash.rb +33 -0
- data/lib/spiderfw/utils/periodic_runner.rb +50 -0
- data/lib/spiderfw/utils/rails.rb +78 -0
- data/lib/spiderfw/utils/rails_app.rb +53 -0
- data/lib/spiderfw/utils/sanitizer.rb +178 -0
- data/lib/spiderfw/utils/setup_task.rb +35 -0
- data/lib/spiderfw/utils/shared_store/file_shared_store.rb +73 -0
- data/lib/spiderfw/utils/shared_store/memory_shared_store.rb +46 -0
- data/lib/spiderfw/utils/shared_store.rb +47 -0
- data/lib/spiderfw/utils/test_case.rb +24 -0
- data/lib/spiderfw/utils/thread_out.rb +24 -0
- data/lib/spiderfw/version.rb +3 -0
- data/lib/spiderfw/widget/rest_model.rb +20 -0
- data/lib/spiderfw/widget/widget.rb +671 -0
- data/lib/spiderfw/widget/widget_attributes.rb +54 -0
- data/lib/spiderfw/widget/widget_plugin.rb +37 -0
- data/lib/spiderfw.rb +509 -0
- data/spider.gemspec +43 -0
- data/views/errors/404.shtml +6 -0
- data/views/errors/error.layout.shtml +70 -0
- data/views/errors/error_generic.shtml +6 -0
- 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
|