rsence 2.0.0.0.pre
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/INSTALL.rdoc +295 -0
- data/LICENSE +622 -0
- data/README.rdoc +24 -0
- data/VERSION +1 -0
- data/bin/build_client.rb +254 -0
- data/bin/help +6 -0
- data/bin/launch.rb +120 -0
- data/bin/rdoc.sh +2 -0
- data/bin/restart +6 -0
- data/bin/rsence +120 -0
- data/bin/run +6 -0
- data/bin/run.rb +6 -0
- data/bin/save +6 -0
- data/bin/start +6 -0
- data/bin/status +6 -0
- data/bin/stop +6 -0
- data/conf/config.ru +5 -0
- data/conf/default_conf.yaml +337 -0
- data/conf/default_strings.yaml +77 -0
- data/conf/local_conf.yaml +14 -0
- data/conf/unicorn.conf +78 -0
- data/js/comm/comm/autosync/autosync.js +18 -0
- data/js/comm/comm/autosync/js.inc +0 -0
- data/js/comm/comm/comm.js +195 -0
- data/js/comm/comm/js.inc +0 -0
- data/js/comm/comm/queue/js.inc +0 -0
- data/js/comm/comm/queue/queue.js +183 -0
- data/js/comm/comm/session/js.inc +0 -0
- data/js/comm/comm/session/session.js +51 -0
- data/js/comm/comm/sessionwatcher/js.inc +0 -0
- data/js/comm/comm/sessionwatcher/sessionwatcher.js +43 -0
- data/js/comm/comm/transporter/js.inc +0 -0
- data/js/comm/comm/transporter/transporter.js +257 -0
- data/js/comm/comm/urlresponder/js.inc +0 -0
- data/js/comm/comm/urlresponder/urlresponder.js +148 -0
- data/js/comm/comm/values/js.inc +0 -0
- data/js/comm/comm/values/values.js +432 -0
- data/js/comm/jsloader/js.inc +0 -0
- data/js/comm/jsloader/jsloader.js +114 -0
- data/js/comm/reloadapp/js.inc +0 -0
- data/js/comm/reloadapp/reloadapp.js +150 -0
- data/js/comm/reloadapp/themes/default/reloadapp_warning-ie6.gif +0 -0
- data/js/comm/reloadapp/themes/default/reloadapp_warning.png +0 -0
- data/js/comm/sha/js.inc +0 -0
- data/js/comm/sha/sha.js +432 -0
- data/js/comm/values/value/js.inc +0 -0
- data/js/comm/values/value/value.js +182 -0
- data/js/comm/values/valuematrix/js.inc +0 -0
- data/js/comm/values/valuematrix/valuematrix.js +138 -0
- data/js/controls/button/button.js +57 -0
- data/js/controls/button/js.inc +0 -0
- data/js/controls/button/themes/bright/button.css +89 -0
- data/js/controls/button/themes/bright/button.html +7 -0
- data/js/controls/button/themes/bright/button_parts1-ie6.gif +0 -0
- data/js/controls/button/themes/bright/button_parts1.png +0 -0
- data/js/controls/button/themes/default/button.css +89 -0
- data/js/controls/button/themes/default/button.html +7 -0
- data/js/controls/button/themes/default/button_parts1-ie6.gif +0 -0
- data/js/controls/button/themes/default/button_parts1.png +0 -0
- data/js/controls/checkbox/checkbox.js +47 -0
- data/js/controls/checkbox/js.inc +0 -0
- data/js/controls/checkbox/themes/default/checkbox.css +69 -0
- data/js/controls/checkbox/themes/default/checkbox.html +5 -0
- data/js/controls/checkbox/themes/default/checkbox_parts1-ie6.gif +0 -0
- data/js/controls/checkbox/themes/default/checkbox_parts1.png +0 -0
- data/js/controls/datetime/calendar/calendar.js +197 -0
- data/js/controls/datetime/calendar/js.inc +0 -0
- data/js/controls/datetime/calendar/themes/default/calendar.css +108 -0
- data/js/controls/datetime/calendar/themes/default/calendar.html +9 -0
- data/js/controls/datetime/calendar/themes/default/calendar_arrows-ie6.gif +0 -0
- data/js/controls/datetime/calendar/themes/default/calendar_arrows.png +0 -0
- data/js/controls/datetime/datetimevalue/datetimevalue.js +246 -0
- data/js/controls/datetime/datetimevalue/js.inc +0 -0
- data/js/controls/datetime/timesheet/js.inc +0 -0
- data/js/controls/datetime/timesheet/themes/default/timesheet.css +30 -0
- data/js/controls/datetime/timesheet/themes/default/timesheet.html +2 -0
- data/js/controls/datetime/timesheet/timesheet.js +182 -0
- data/js/controls/datetime/timesheet_item/js.inc +0 -0
- data/js/controls/datetime/timesheet_item/themes/default/timesheet_item.css +42 -0
- data/js/controls/datetime/timesheet_item/themes/default/timesheet_item.html +8 -0
- data/js/controls/datetime/timesheet_item/timesheet_item.js +247 -0
- data/js/controls/datetime/timesheet_item_edit/js.inc +0 -0
- data/js/controls/datetime/timesheet_item_edit/timesheet_item_edit.js +274 -0
- data/js/controls/dialogs/alert_sheet/alert_sheet.js +62 -0
- data/js/controls/dialogs/alert_sheet/js.inc +0 -0
- data/js/controls/dialogs/confirm_sheet/confirm_sheet.js +36 -0
- data/js/controls/dialogs/confirm_sheet/js.inc +0 -0
- data/js/controls/dialogs/sheet/js.inc +0 -0
- data/js/controls/dialogs/sheet/sheet.js +83 -0
- data/js/controls/dialogs/sheet/themes/default/sheet.css +64 -0
- data/js/controls/dialogs/sheet/themes/default/sheet.html +14 -0
- data/js/controls/dialogs/sheet/themes/default/sheet_bg-ie6.gif +0 -0
- data/js/controls/dialogs/sheet/themes/default/sheet_bg.png +0 -0
- data/js/controls/dialogs/sheet/themes/default/sheet_dim-ie6.gif +0 -0
- data/js/controls/dialogs/sheet/themes/default/sheet_dim.png +0 -0
- data/js/controls/dialogs/sheet/themes/default/sheet_parts1-ie6.gif +0 -0
- data/js/controls/dialogs/sheet/themes/default/sheet_parts1.png +0 -0
- data/js/controls/dialogs/sheet/themes/default/sheet_parts2-ie6.gif +0 -0
- data/js/controls/dialogs/sheet/themes/default/sheet_parts2.png +0 -0
- data/js/controls/dialogs/sheet/themes/default/sheet_warning-ie6.gif +0 -0
- data/js/controls/dialogs/sheet/themes/default/sheet_warning.png +0 -0
- data/js/controls/imageview/imageview.js +108 -0
- data/js/controls/imageview/js.inc +0 -0
- data/js/controls/imageview/themes/default/blank.gif +0 -0
- data/js/controls/lists/checkboxlist/checkboxlist.js +170 -0
- data/js/controls/lists/checkboxlist/js.inc +0 -0
- data/js/controls/lists/listitems/js.inc +0 -0
- data/js/controls/lists/listitems/listitems.js +65 -0
- data/js/controls/lists/radiobuttonlist/js.inc +0 -0
- data/js/controls/lists/radiobuttonlist/radiobuttonlist.js +126 -0
- data/js/controls/passwordcontrol/js.inc +0 -0
- data/js/controls/passwordcontrol/passwordcontrol.js +22 -0
- data/js/controls/passwordcontrol/themes/default/passwordcontrol.css +0 -0
- data/js/controls/passwordcontrol/themes/default/passwordcontrol.html +18 -0
- data/js/controls/progress/progressbar/js.inc +0 -0
- data/js/controls/progress/progressbar/progressbar.js +36 -0
- data/js/controls/progress/progressbar/themes/default/progressbar.css +16 -0
- data/js/controls/progress/progressbar/themes/default/progressbar.html +2 -0
- data/js/controls/progress/progressindicator/js.inc +0 -0
- data/js/controls/progress/progressindicator/progressindicator.js +43 -0
- data/js/controls/radiobutton/js.inc +0 -0
- data/js/controls/radiobutton/radiobutton.js +41 -0
- data/js/controls/radiobutton/themes/default/radiobutton.css +69 -0
- data/js/controls/radiobutton/themes/default/radiobutton.html +5 -0
- data/js/controls/radiobutton/themes/default/radiobutton_parts1-ie6.gif +0 -0
- data/js/controls/radiobutton/themes/default/radiobutton_parts1.png +0 -0
- data/js/controls/sliders/slider/js.inc +0 -0
- data/js/controls/sliders/slider/slider.js +356 -0
- data/js/controls/sliders/slider/themes/default/hslider_tracks-ie6.gif +0 -0
- data/js/controls/sliders/slider/themes/default/hslider_tracks.png +0 -0
- data/js/controls/sliders/slider/themes/default/slider.css +108 -0
- data/js/controls/sliders/slider/themes/default/slider.html +5 -0
- data/js/controls/sliders/slider/themes/default/slider_thumbs-ie6.gif +0 -0
- data/js/controls/sliders/slider/themes/default/slider_thumbs.png +0 -0
- data/js/controls/sliders/vslider/js.inc +0 -0
- data/js/controls/sliders/vslider/themes/default/vslider.css +52 -0
- data/js/controls/sliders/vslider/themes/default/vslider.html +5 -0
- data/js/controls/sliders/vslider/themes/default/vslider_tracks-ie6.gif +0 -0
- data/js/controls/sliders/vslider/themes/default/vslider_tracks.png +0 -0
- data/js/controls/sliders/vslider/vslider.js +40 -0
- data/js/controls/stepper/js.inc +0 -0
- data/js/controls/stepper/stepper.js +212 -0
- data/js/controls/stepper/themes/default/stepper-ie6.gif +0 -0
- data/js/controls/stepper/themes/default/stepper.css +14 -0
- data/js/controls/stepper/themes/default/stepper.html +2 -0
- data/js/controls/stepper/themes/default/stepper.png +0 -0
- data/js/controls/stringview/js.inc +0 -0
- data/js/controls/stringview/stringview.js +49 -0
- data/js/controls/stringview/themes/default/stringview.css +8 -0
- data/js/controls/stringview/themes/default/stringview.html +1 -0
- data/js/controls/tab/js.inc +0 -0
- data/js/controls/tab/tab.js +276 -0
- data/js/controls/tab/themes/bright/tab.css +76 -0
- data/js/controls/tab/themes/bright/tab.html +6 -0
- data/js/controls/tab/themes/bright/tab_bg_color-ie6.gif +0 -0
- data/js/controls/tab/themes/bright/tab_bg_color.png +0 -0
- data/js/controls/tab/themes/bright/tab_border_pattern-ie6.gif +0 -0
- data/js/controls/tab/themes/bright/tab_border_pattern.png +0 -0
- data/js/controls/tab/themes/bright/tab_parts1-ie6.gif +0 -0
- data/js/controls/tab/themes/bright/tab_parts1.png +0 -0
- data/js/controls/tab/themes/default/tab.css +77 -0
- data/js/controls/tab/themes/default/tab.html +6 -0
- data/js/controls/tab/themes/default/tab_bg_color-ie6.gif +0 -0
- data/js/controls/tab/themes/default/tab_bg_color.png +0 -0
- data/js/controls/tab/themes/default/tab_border_pattern-ie6.gif +0 -0
- data/js/controls/tab/themes/default/tab_border_pattern.png +0 -0
- data/js/controls/tab/themes/default/tab_parts1-ie6.gif +0 -0
- data/js/controls/tab/themes/default/tab_parts1.png +0 -0
- data/js/controls/textarea/js.inc +0 -0
- data/js/controls/textarea/textarea.js +23 -0
- data/js/controls/textarea/themes/default/textarea.css +21 -0
- data/js/controls/textarea/themes/default/textarea.html +18 -0
- data/js/controls/textcontrol/js.inc +0 -0
- data/js/controls/textcontrol/textcontrol.js +372 -0
- data/js/controls/textcontrol/themes/default/textcontrol.css +107 -0
- data/js/controls/textcontrol/themes/default/textcontrol.html +18 -0
- data/js/controls/textcontrol/themes/default/textcontrol_parts1-ie6.gif +0 -0
- data/js/controls/textcontrol/themes/default/textcontrol_parts1.png +0 -0
- data/js/controls/textcontrol/themes/default/textcontrol_parts2-ie6.gif +0 -0
- data/js/controls/textcontrol/themes/default/textcontrol_parts2.png +0 -0
- data/js/controls/textcontrol/themes/default/textcontrol_parts3-ie6.gif +0 -0
- data/js/controls/textcontrol/themes/default/textcontrol_parts3.png +0 -0
- data/js/controls/uploader/js.inc +0 -0
- data/js/controls/uploader/themes/default/upload_progress.gif +0 -0
- data/js/controls/uploader/themes/default/uploader.css +108 -0
- data/js/controls/uploader/themes/default/uploader.html +27 -0
- data/js/controls/uploader/uploader.js +153 -0
- data/js/controls/validatorview/js.inc +0 -0
- data/js/controls/validatorview/themes/default/validator-ie6.gif +0 -0
- data/js/controls/validatorview/themes/default/validator.png +0 -0
- data/js/controls/validatorview/themes/default/validatorview.css +0 -0
- data/js/controls/validatorview/themes/default/validatorview.html +0 -0
- data/js/controls/validatorview/validatorview.js +55 -0
- data/js/controls/window/js.inc +0 -0
- data/js/controls/window/themes/default/window.css +219 -0
- data/js/controls/window/themes/default/window.html +17 -0
- data/js/controls/window/themes/default/window_bg_active-ie6.gif +0 -0
- data/js/controls/window/themes/default/window_bg_active.png +0 -0
- data/js/controls/window/themes/default/window_bg_inactive-ie6.gif +0 -0
- data/js/controls/window/themes/default/window_bg_inactive.png +0 -0
- data/js/controls/window/themes/default/window_buttons-ie6.gif +0 -0
- data/js/controls/window/themes/default/window_buttons.png +0 -0
- data/js/controls/window/themes/default/window_parts1-ie6.gif +0 -0
- data/js/controls/window/themes/default/window_parts1.png +0 -0
- data/js/controls/window/themes/default/window_parts2-ie6.gif +0 -0
- data/js/controls/window/themes/default/window_parts2.png +0 -0
- data/js/controls/window/window.js +284 -0
- data/js/core/class/class.js +317 -0
- data/js/core/class/js.inc +0 -0
- data/js/core/elem/elem.js +1376 -0
- data/js/core/elem/js.inc +0 -0
- data/js/core/event/event.js +1021 -0
- data/js/core/event/js.inc +0 -0
- data/js/core/iefix/ie_css_element.htc +5 -0
- data/js/core/iefix/ie_css_style.htc +5 -0
- data/js/core/iefix/iefix.js +359 -0
- data/js/core/iefix/js.inc +0 -0
- data/js/debugg/debugg.js +43 -0
- data/js/debugg/js.inc +0 -0
- data/js/foundation/application/application.js +209 -0
- data/js/foundation/application/js.inc +0 -0
- data/js/foundation/control/control.js +342 -0
- data/js/foundation/control/controldefaults/controldefaults.js +59 -0
- data/js/foundation/control/controldefaults/js.inc +0 -0
- data/js/foundation/control/dummyvalue/dummyvalue.js +50 -0
- data/js/foundation/control/dummyvalue/js.inc +0 -0
- data/js/foundation/control/dyncontrol/dyncontrol.js +494 -0
- data/js/foundation/control/dyncontrol/js.inc +0 -0
- data/js/foundation/control/dyncontrol/themes/default/dyncontrol.css +0 -0
- data/js/foundation/control/dyncontrol/themes/default/dyncontrol.html +0 -0
- data/js/foundation/control/eventresponder/eventresponder.js +713 -0
- data/js/foundation/control/eventresponder/js.inc +0 -0
- data/js/foundation/control/js.inc +0 -0
- data/js/foundation/control/valueresponder/js.inc +0 -0
- data/js/foundation/control/valueresponder/valueresponder.js +77 -0
- data/js/foundation/geom/point/js.inc +0 -0
- data/js/foundation/geom/point/point.js +202 -0
- data/js/foundation/geom/rect/js.inc +0 -0
- data/js/foundation/geom/rect/rect.js +610 -0
- data/js/foundation/json_renderer/js.inc +0 -0
- data/js/foundation/json_renderer/json_renderer.js +231 -0
- data/js/foundation/system/js.inc +0 -0
- data/js/foundation/system/system.js +369 -0
- data/js/foundation/thememanager/js.inc +0 -0
- data/js/foundation/thememanager/thememanager.js +387 -0
- data/js/foundation/view/js.inc +0 -0
- data/js/foundation/view/markupview/js.inc +0 -0
- data/js/foundation/view/markupview/markupview.js +113 -0
- data/js/foundation/view/morphanimation/js.inc +0 -0
- data/js/foundation/view/morphanimation/morphanimation.js +236 -0
- data/js/foundation/view/view.js +1804 -0
- data/js/foundation/view/viewdefaults/js.inc +0 -0
- data/js/foundation/view/viewdefaults/viewdefaults.js +25 -0
- data/js/views/centerview/centerview.js +45 -0
- data/js/views/centerview/js.inc +0 -0
- data/js/views/inlineview/inlineview.js +14 -0
- data/js/views/inlineview/js.inc +0 -0
- data/js/views/scrollview/js.inc +0 -0
- data/js/views/scrollview/scrollview.js +39 -0
- data/lib/conf/default.rb +220 -0
- data/lib/conf/wizard.rb +303 -0
- data/lib/daemon/daemon.rb +293 -0
- data/lib/http/broker.rb +102 -0
- data/lib/http/rackup.rb +88 -0
- data/lib/http/request.rb +69 -0
- data/lib/http/response.rb +63 -0
- data/lib/plugins/gui_plugin.rb +129 -0
- data/lib/plugins/guiparser.rb +114 -0
- data/lib/plugins/plugin.rb +652 -0
- data/lib/plugins/plugin_plugins.rb +47 -0
- data/lib/plugins/plugin_sqlite_db.rb +72 -0
- data/lib/plugins/plugin_util.rb +96 -0
- data/lib/plugins/pluginmanager.rb +517 -0
- data/lib/plugins/servlet.rb +69 -0
- data/lib/session/msg.rb +291 -0
- data/lib/session/sessionmanager.rb +491 -0
- data/lib/session/sessionstorage.rb +314 -0
- data/lib/transporter/transporter.rb +254 -0
- data/lib/util/gzstring.rb +5 -0
- data/lib/values/hvalue.rb +323 -0
- data/lib/values/valuemanager.rb +152 -0
- data/plugins/client_pkg/client_pkg.rb +186 -0
- data/plugins/client_pkg/info.yaml +25 -0
- data/plugins/client_pkg/lib/client_pkg_build.rb +569 -0
- data/plugins/client_pkg/lib/client_pkg_cache.rb +50 -0
- data/plugins/client_pkg/lib/client_pkg_serve.rb +210 -0
- data/plugins/client_pkg/log/build_log +0 -0
- data/plugins/index_html/img/loading.gif +0 -0
- data/plugins/index_html/img/riassence.gif +0 -0
- data/plugins/index_html/index_html.rb +150 -0
- data/plugins/index_html/tmpl/index.html +22 -0
- data/plugins/index_html/tmpl/startup_index.html +29 -0
- data/plugins/legacy/disabled +0 -0
- data/plugins/legacy/disabled- +0 -0
- data/plugins/legacy/info.yaml +22 -0
- data/plugins/legacy/legacy.rb +15 -0
- data/plugins/main/js/riassence_ns.js +87 -0
- data/plugins/main/main.rb +234 -0
- data/plugins/main/values.yaml +8 -0
- data/plugins/ticketservices/lib/common.rb +300 -0
- data/plugins/ticketservices/lib/favicon.rb +38 -0
- data/plugins/ticketservices/lib/file.rb +58 -0
- data/plugins/ticketservices/lib/img.rb +50 -0
- data/plugins/ticketservices/lib/objblob.rb +66 -0
- data/plugins/ticketservices/lib/rsrc.rb +34 -0
- data/plugins/ticketservices/lib/upload.rb +206 -0
- data/plugins/ticketservices/ticketservices.rb +268 -0
- data/var/db/.git_include +0 -0
- data/var/log/.git_include +0 -0
- data/var/run/.git_include +0 -0
- metadata +390 -0
data/lib/http/request.rb
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#--
|
|
2
|
+
## Riassence Framework
|
|
3
|
+
# Copyright 2008 Riassence Inc.
|
|
4
|
+
# http://riassence.com/
|
|
5
|
+
#
|
|
6
|
+
# You should have received a copy of the GNU General Public License along
|
|
7
|
+
# with this software package. If not, contact licensing@riassence.com
|
|
8
|
+
##
|
|
9
|
+
#++
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
require 'rubygems'
|
|
13
|
+
require 'rack'
|
|
14
|
+
|
|
15
|
+
module RSence
|
|
16
|
+
|
|
17
|
+
class Request < Rack::Request
|
|
18
|
+
attr_reader :header, :path, :query
|
|
19
|
+
def initialize(env)
|
|
20
|
+
@header = {
|
|
21
|
+
|
|
22
|
+
}
|
|
23
|
+
super
|
|
24
|
+
env2header()
|
|
25
|
+
@path = path_info()
|
|
26
|
+
@query = params()
|
|
27
|
+
end
|
|
28
|
+
def unparsed_uri
|
|
29
|
+
return @header['request-uri']
|
|
30
|
+
end
|
|
31
|
+
def env2header
|
|
32
|
+
[ ['SERVER_NAME', 'server-name'],
|
|
33
|
+
['HTTP_USER_AGENT', 'user-agent'],
|
|
34
|
+
['HTTP_ACCEPT_ENCODING', 'accept-encoding'],
|
|
35
|
+
['PATH_INFO', 'path-info'],
|
|
36
|
+
['HTTP_HOST', 'host'],
|
|
37
|
+
['HTTP_ACCEPT_LANGUAGE', 'accept-language'],
|
|
38
|
+
['SERVER_PROTOCOL', 'server-protocol'],
|
|
39
|
+
['REQUEST_PATH', 'request-path'],
|
|
40
|
+
['HTTP_KEEP_ALIVE', 'keep-alive'],
|
|
41
|
+
['SERVER_SOFTWARE', 'server-software'],
|
|
42
|
+
['REMOTE_ADDR', 'remote-addr'],
|
|
43
|
+
['HTTP_REFERER', 'referer'],
|
|
44
|
+
['HTTP_VERSION', 'version'],
|
|
45
|
+
['HTTP_ACCEPT_CHARSET', 'accept-charset'],
|
|
46
|
+
['REQUEST_URI', 'request-uri'],
|
|
47
|
+
['SERVER_PORT', 'server-port'],
|
|
48
|
+
['QUERY_STRING', 'query-string'],
|
|
49
|
+
['HTTP_ACCEPT', 'accept'],
|
|
50
|
+
['REQUEST_METHOD', 'request-method'],
|
|
51
|
+
['HTTP_CONNECTION', 'connection'],
|
|
52
|
+
['HTTP_SOAPACTION', 'soapaction'],
|
|
53
|
+
['HTTP_FORWARDED_HOST', 'forwarded-host']
|
|
54
|
+
].each do |env_key,header_key|
|
|
55
|
+
if @env.has_key?(env_key)
|
|
56
|
+
@header[header_key] = @env[env_key]
|
|
57
|
+
end
|
|
58
|
+
if env_key.start_with?( 'HTTP_' )
|
|
59
|
+
x_env_key = "HTTP_X#{env_key[4..-1]}"
|
|
60
|
+
if @env.has_key?( x_env_key )
|
|
61
|
+
@header["x-#{header_key}"] = @env[ x_env_key ]
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#--
|
|
2
|
+
## Riassence Framework
|
|
3
|
+
# Copyright 2008 Riassence Inc.
|
|
4
|
+
# http://riassence.com/
|
|
5
|
+
#
|
|
6
|
+
# You should have received a copy of the GNU General Public License along
|
|
7
|
+
# with this software package. If not, contact licensing@riassence.com
|
|
8
|
+
##
|
|
9
|
+
#++
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
module RSence
|
|
13
|
+
|
|
14
|
+
class ResponseBody < Array
|
|
15
|
+
def +(body_data)
|
|
16
|
+
self.push(body_data)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
## Minimally WEBrick -compatible Response object.
|
|
21
|
+
## Implements only the methods used by the framework.
|
|
22
|
+
class Response
|
|
23
|
+
def initialize
|
|
24
|
+
@body = ResponseBody.new(1)
|
|
25
|
+
@body[0] = ''
|
|
26
|
+
@status = 200
|
|
27
|
+
@header = {
|
|
28
|
+
'Content-Type' => 'text/plain',
|
|
29
|
+
'Server' => 'Riassence Framework'
|
|
30
|
+
}
|
|
31
|
+
end
|
|
32
|
+
def body=(body_data)
|
|
33
|
+
@body = ResponseBody.new(1)
|
|
34
|
+
@body[0] = body_data
|
|
35
|
+
end
|
|
36
|
+
def body
|
|
37
|
+
@body.join
|
|
38
|
+
end
|
|
39
|
+
def content_type=(new_content_type)
|
|
40
|
+
@header['Content-Type'] = new_content_type
|
|
41
|
+
end
|
|
42
|
+
def content_type
|
|
43
|
+
@header['Content-Type']
|
|
44
|
+
end
|
|
45
|
+
def camelize( header_key )
|
|
46
|
+
header_key.capitalize.gsub(/\-([a-z])/) { '-'+$1.upcase }
|
|
47
|
+
end
|
|
48
|
+
def []=(header_key,header_val)
|
|
49
|
+
@header[camelize( header_key )] = header_val.to_s
|
|
50
|
+
end
|
|
51
|
+
def status=(new_val)
|
|
52
|
+
@status = new_val.to_i
|
|
53
|
+
end
|
|
54
|
+
def status
|
|
55
|
+
@status
|
|
56
|
+
end
|
|
57
|
+
def header
|
|
58
|
+
@header
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
end
|
|
63
|
+
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
## Riassence Framework
|
|
2
|
+
# Copyright 2009 Riassence Inc.
|
|
3
|
+
# http://riassence.com/
|
|
4
|
+
#
|
|
5
|
+
# You should have received a copy of the GNU General Public License along
|
|
6
|
+
# with this software package. If not, contact licensing@riassence.com
|
|
7
|
+
##
|
|
8
|
+
|
|
9
|
+
## The GUIPlugin extends Plugin by automatically initializing an GUIParser
|
|
10
|
+
## instance as @gui
|
|
11
|
+
## It makes the include_js method public to enable automatic dependency
|
|
12
|
+
## loading based on the dependencies item in the YAML gui declaration.
|
|
13
|
+
## It also makes the @path public.
|
|
14
|
+
## It inits the gui automatically.
|
|
15
|
+
## Extend the gui_params method to define your own params for the gui data.
|
|
16
|
+
##
|
|
17
|
+
## HValues can be defined inside values.yaml at the root directory of
|
|
18
|
+
## plugin. The HValues may be linked directly with methods on the values.yaml
|
|
19
|
+
## as well.
|
|
20
|
+
##
|
|
21
|
+
## == Values.yaml
|
|
22
|
+
## :valuename: # name of the HValue
|
|
23
|
+
## :value: 2.56 # defined value
|
|
24
|
+
## :responders: # methods responding to the value on ruby code upon change
|
|
25
|
+
## - :method: validate_convert_factor
|
|
26
|
+
##
|
|
27
|
+
##
|
|
28
|
+
##
|
|
29
|
+
class GUIPlugin < Plugin
|
|
30
|
+
|
|
31
|
+
@@default_yaml_src = %{
|
|
32
|
+
type: GUITree
|
|
33
|
+
version: 0.5
|
|
34
|
+
|
|
35
|
+
class: HApplication
|
|
36
|
+
options:
|
|
37
|
+
label: "Dummy Application"
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
# Automatically initializes an GUIParser instance as @gui
|
|
41
|
+
def init
|
|
42
|
+
super
|
|
43
|
+
yaml_src = file_read( "gui/#{@name}.yaml" )
|
|
44
|
+
yaml_src = file_read( "gui/main.yaml" ) unless yaml_src
|
|
45
|
+
yaml_src = @@default_yaml_src unless yaml_src
|
|
46
|
+
@gui = GUIParser.new( self, yaml_src )
|
|
47
|
+
@client_pkgs = false
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Extend this method to return custom params to GUIParser#init.
|
|
51
|
+
# Called from init_ui.
|
|
52
|
+
# By default assigns the session values as :values to use for
|
|
53
|
+
# valueObjId: ":values.my_value_name" in the YAML GUI file.
|
|
54
|
+
def gui_params( msg )
|
|
55
|
+
return {
|
|
56
|
+
:values => @gui.values( get_ses( msg ) )
|
|
57
|
+
}
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def install_client_pkgs
|
|
61
|
+
if @client_pkgs
|
|
62
|
+
warn "install_client_pkgs: called with @client_pkgs defined; returning"
|
|
63
|
+
return
|
|
64
|
+
end
|
|
65
|
+
@client_pkgs = yaml_read( 'client_pkgs.yaml' )
|
|
66
|
+
if @client_pkgs
|
|
67
|
+
if @client_pkgs.has_key?(:src_dirs)
|
|
68
|
+
@client_pkgs[:src_dirs].each do |src_dir|
|
|
69
|
+
src_dir = bundle_path( src_dir[2..-1] ) if src_dir.start_with?('./')
|
|
70
|
+
@plugins.client_pkg.add_src_dir( src_dir )
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
@plugins.client_pkg.add_packages( @client_pkgs[:packages ] ) if @client_pkgs.has_key?(:packages )
|
|
74
|
+
@plugins.client_pkg.add_themes( @client_pkgs[:theme_names ] ) if @client_pkgs.has_key?(:theme_names )
|
|
75
|
+
@plugins.client_pkg.add_gfx_formats( @client_pkgs[:gfx_formats ] ) if @client_pkgs.has_key?(:gfx_formats )
|
|
76
|
+
@plugins.client_pkg.add_reserved_names( @client_pkgs[:reserved_names] ) if @client_pkgs.has_key?(:reserved_names)
|
|
77
|
+
@plugins.client_pkg.rebuild_client
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def uninstall_client_pkgs
|
|
82
|
+
if not @client_pkgs
|
|
83
|
+
warn "uninstall_client_pkgs: called without @client_pkgs defined"
|
|
84
|
+
else
|
|
85
|
+
if @client_pkgs.has_key?(:src_dirs)
|
|
86
|
+
@client_pkgs[:src_dirs].each do |src_dir|
|
|
87
|
+
src_dir = bundle_path( src_dir[2..-1] ) if src_dir.start_with?('./')
|
|
88
|
+
@plugins.client_pkg.del_src_dir( src_dir )
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
@plugins.client_pkg.del_reserved_names( @client_pkgs[:reserved_names] ) if @client_pkgs.has_key?(:reserved_names)
|
|
92
|
+
@plugins.client_pkg.del_gfx_formats( @client_pkgs[:gfx_formats ] ) if @client_pkgs.has_key?(:gfx_formats )
|
|
93
|
+
@plugins.client_pkg.del_themes( @client_pkgs[:theme_names ] ) if @client_pkgs.has_key?(:theme_names )
|
|
94
|
+
@plugins.client_pkg.del_packages( @client_pkgs[:packages].keys ) if @client_pkgs.has_key?(:packages )
|
|
95
|
+
@plugins.client_pkg.rebuild_client
|
|
96
|
+
end
|
|
97
|
+
@client_pkgs = false
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def open
|
|
101
|
+
super
|
|
102
|
+
install_client_pkgs if File.exist? bundle_path( 'client_pkgs.yaml' )
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def close
|
|
106
|
+
super
|
|
107
|
+
uninstall_client_pkgs if @client_pkgs
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Sends gui specification to the main plugin
|
|
111
|
+
def spec_ui( msg )
|
|
112
|
+
# TODO
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Automatically inits the UI using GUIParser#init.
|
|
116
|
+
# Passes on the return value of gui_params.
|
|
117
|
+
def init_ui( msg ); @gui.init( msg, gui_params( msg ) ); end
|
|
118
|
+
|
|
119
|
+
# Automatically kills the UI using GUIParser#kill
|
|
120
|
+
def kill_ui( msg ); @gui.kill( msg ); end
|
|
121
|
+
|
|
122
|
+
# Makes include_js public to enable calls to it from GUIParser
|
|
123
|
+
public :include_js, :read_js_once
|
|
124
|
+
|
|
125
|
+
attr_reader :plugins
|
|
126
|
+
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
## Riassence Framework
|
|
2
|
+
# Copyright 2009 Riassence Inc.
|
|
3
|
+
# http://riassence.com/
|
|
4
|
+
#
|
|
5
|
+
# You should have received a copy of the GNU General Public License along
|
|
6
|
+
# with this software package. If not, contact licensing@riassence.com
|
|
7
|
+
##
|
|
8
|
+
|
|
9
|
+
# This class automatically loads a YAML file from "gui" subdirectory of a plugin.
|
|
10
|
+
# Extend your plugin from the GUIPlugin class instead of the Plugin class to make
|
|
11
|
+
# this work automatically.
|
|
12
|
+
# = Usage:
|
|
13
|
+
# Initialize like this from inside a plugin method. This will load the "gui/my_gui.yaml" file.
|
|
14
|
+
# @gui = GUIParser.new( self, 'my_gui' )
|
|
15
|
+
# To make the client render the contents of the yaml do this:
|
|
16
|
+
# ses = get_ses( msg )
|
|
17
|
+
# params = { :values => @gui.values( ses ) }
|
|
18
|
+
# @gui.init( msg, params )
|
|
19
|
+
class GUIParser
|
|
20
|
+
|
|
21
|
+
include ::RSence
|
|
22
|
+
|
|
23
|
+
# Use this method to send the client all commands required to construct the GUI Tree using JSONRenderer.
|
|
24
|
+
# = Parameters
|
|
25
|
+
# +msg+:: The +Message+ instance +msg+ used all over the place.
|
|
26
|
+
# +params+:: An hash containing all parameters referred from the YAML file.
|
|
27
|
+
def init( msg, params )
|
|
28
|
+
gui_data = YAML.load( @yaml_src )
|
|
29
|
+
parse_gui( gui_data, params )
|
|
30
|
+
if gui_data.has_key?('dependencies')
|
|
31
|
+
@parent.include_js( msg, gui_data['dependencies'] )
|
|
32
|
+
gui_data.delete('dependencies')
|
|
33
|
+
end
|
|
34
|
+
if gui_data.has_key?('include')
|
|
35
|
+
gui_data['include'].each do | js_file |
|
|
36
|
+
js_src = @parent.read_js_once( msg, js_file )
|
|
37
|
+
msg.reply( js_src )
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
gui_name = @parent.name
|
|
41
|
+
msg.reply( "JSONRenderer.nu(#{gui_data.to_json});", true )
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Use this method to extract all the value id's of the +ses+ hash.
|
|
45
|
+
def values( ses )
|
|
46
|
+
ids = {}
|
|
47
|
+
ses.each do | key, value |
|
|
48
|
+
if value.class == HValue
|
|
49
|
+
ids[ key ] = value.val_id
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
return ids
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def json_fun( value )
|
|
58
|
+
JSON.parse( "[#{value}]" ).first
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Parses the gui data using params. Called from +init+.
|
|
62
|
+
def parse_gui( gui_data, params )
|
|
63
|
+
data_class = gui_data.class
|
|
64
|
+
if data_class == Array
|
|
65
|
+
gui_data.each_with_index do | item, i |
|
|
66
|
+
gui_data[i] = parse_gui( item, params )
|
|
67
|
+
end
|
|
68
|
+
elsif data_class == Hash
|
|
69
|
+
gui_data.each do | key, value |
|
|
70
|
+
gui_data[key] = parse_gui( value, params )
|
|
71
|
+
end
|
|
72
|
+
elsif data_class == Symbol
|
|
73
|
+
sym_str = gui_data.to_s
|
|
74
|
+
if sym_str.include? '.'
|
|
75
|
+
sym_arr = sym_str.split('.')
|
|
76
|
+
else
|
|
77
|
+
sym_arr = [ sym_str ]
|
|
78
|
+
end
|
|
79
|
+
return get_params( sym_arr, params )
|
|
80
|
+
elsif data_class == String and gui_data.strip.start_with?('function(')
|
|
81
|
+
return @parent.plugins[:client_pkg].squeeze( "a="+json_fun( gui_data.to_json ) )[2..-1]
|
|
82
|
+
end
|
|
83
|
+
return gui_data
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Searches the params hash for parameters whenever encountered a Symbol in the YAML.
|
|
87
|
+
def get_params( params_path, params )
|
|
88
|
+
item = params_path.shift
|
|
89
|
+
if params.class == Hash
|
|
90
|
+
has_str = params.has_key?( item )
|
|
91
|
+
has_sym = params.has_key?( item.to_sym )
|
|
92
|
+
item = item.to_sym if has_sym
|
|
93
|
+
if has_str or has_sym
|
|
94
|
+
if params_path.size == 0
|
|
95
|
+
return params[item]
|
|
96
|
+
else
|
|
97
|
+
return get_params( params_path, params[ item ] )
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
return ''
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Loads the YAML file.
|
|
105
|
+
# = Parameters
|
|
106
|
+
# +parent+:: The Plugin instance called from, use +self+ when constructing in a Plugin method.
|
|
107
|
+
# +yaml_src+:: The YAML source template for the GUI
|
|
108
|
+
def initialize( parent, yaml_src )
|
|
109
|
+
@parent = parent
|
|
110
|
+
@yaml_src = yaml_src
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
end
|
|
114
|
+
|
|
@@ -0,0 +1,652 @@
|
|
|
1
|
+
## Riassence Framework
|
|
2
|
+
# Copyright 2006 Riassence Inc.
|
|
3
|
+
# http://riassence.com/
|
|
4
|
+
#
|
|
5
|
+
# You should have received a copy of the GNU General Public License along
|
|
6
|
+
# with this software package. If not, contact licensing@riassence.com
|
|
7
|
+
##
|
|
8
|
+
|
|
9
|
+
## = Abstract
|
|
10
|
+
## The Plugin class is the base class for extending server logic.
|
|
11
|
+
## A single Plugin instance serves the requests of all sessions,
|
|
12
|
+
## which makes them very cpu and memory efficient compared to systems,
|
|
13
|
+
## where the server classes are constructed and destructed for each
|
|
14
|
+
## request.
|
|
15
|
+
##
|
|
16
|
+
## Plugins are designed to be contained in a plugin directory bundle and
|
|
17
|
+
## to be loaded by the +PluginManager+, which is also responsible for
|
|
18
|
+
## delegating the events and other calls throughout the system.
|
|
19
|
+
##
|
|
20
|
+
## == Anatomy of a plugin bundle
|
|
21
|
+
## The plugin bundle contains all data needed to run the plugin. Design
|
|
22
|
+
## your plugin without any hard-coded paths, remember that it's intended
|
|
23
|
+
## to be deployed by "dropping" the whole plugin into one of the server's
|
|
24
|
+
## plugins directories.
|
|
25
|
+
##
|
|
26
|
+
## The +PluginManager+ looks for such bundles and evaluates them into an
|
|
27
|
+
## anonymous +Module+ namespace. The content of the ruby source file
|
|
28
|
+
## is then responsible for including its libraries, constructing an
|
|
29
|
+
## instance of itself and registering itself as a part of the system.
|
|
30
|
+
##
|
|
31
|
+
## It's advised to use the +GUIPlugin+ class for plugins that handle
|
|
32
|
+
## user interfaces. Usage of the +Plugin+ bundle is advised to use
|
|
33
|
+
## for plugins that provide extra functionality, value responders
|
|
34
|
+
## and other utilities that supplement the user interface.
|
|
35
|
+
##
|
|
36
|
+
## You must call the +#register+ method after the class is constructed.
|
|
37
|
+
## Otherwise, the class is not connected to the system and just discarded
|
|
38
|
+
## and then garbage collected.
|
|
39
|
+
##
|
|
40
|
+
## == Messages
|
|
41
|
+
## As a side effect of having single instances of plugins serve the requests
|
|
42
|
+
## of all sessions, the request/response/session messaging is implemented
|
|
43
|
+
## as messaging objects. These objects contain or delegate all the necessary
|
|
44
|
+
## hooks required by the complete request/response cycle.
|
|
45
|
+
##
|
|
46
|
+
## The naming convention of the +Message+ instance is +msg+ and it's
|
|
47
|
+
## given as the first parameter of methods needing it.
|
|
48
|
+
##
|
|
49
|
+
## Use +msg.ses_id+ to identify the session's serial number and +msg.user_id+
|
|
50
|
+
## to identify the user's identity.
|
|
51
|
+
##
|
|
52
|
+
## Use the +msg.session+ +Hash+ to store any persistent data
|
|
53
|
+
## associated with the user's session, preferably using the name of the
|
|
54
|
+
## plugin or its registered name as the primary key entry in the Hash.
|
|
55
|
+
## The session data is persistent; it's stored in the session database
|
|
56
|
+
## by +SessionStorage+ automatically.
|
|
57
|
+
##
|
|
58
|
+
## The +msg+ instance also provides access to the +Request+ and +Response+
|
|
59
|
+
## objects as +msg.request+ and +msg.response+, respectively.
|
|
60
|
+
##
|
|
61
|
+
## Use the +msg.run+ method to call other plugins.
|
|
62
|
+
##
|
|
63
|
+
## To append js source code to be evaluated in the client, use the +msg.reply+
|
|
64
|
+
## call. The +msg.console+ call appends messages to the browser's js console.
|
|
65
|
+
##
|
|
66
|
+
##
|
|
67
|
+
## == Session -related event methods
|
|
68
|
+
## The +#get_ses+ method returns (or creates and returns) the entry in
|
|
69
|
+
## the session based on the name your plugin is registered as. It's advised
|
|
70
|
+
## to use this call instead of manually managing +msg.session+ in most cases.
|
|
71
|
+
##
|
|
72
|
+
## The +#idle+ method is called each time a client performs a data
|
|
73
|
+
## synchronization or "idle poll" request.
|
|
74
|
+
##
|
|
75
|
+
## The +#init_ses+ method is called once in the same request a new session
|
|
76
|
+
## is created. A new session is created, when a user enters accesses the
|
|
77
|
+
## server the first time, or the first time after the previous session is
|
|
78
|
+
## expired.
|
|
79
|
+
##
|
|
80
|
+
## The +#init_ui+ method is called by the "main" plugin after the client has
|
|
81
|
+
## booted successfully. The +GUIPlugin+ class extends this method to
|
|
82
|
+
## automatically load and initialize the user interface from a data structure.
|
|
83
|
+
##
|
|
84
|
+
## The +#restore_ses+ method is called once in the same request an old
|
|
85
|
+
## session is restored. A session is restored, when the user returns to
|
|
86
|
+
## the page or reloads the page before the session is expired.
|
|
87
|
+
##
|
|
88
|
+
## === When the server is configured to restore previous sessions (default):
|
|
89
|
+
## If the user accesses the same page using the same browser (in different
|
|
90
|
+
## tabs or windows), only the most recently restored one is valid, while
|
|
91
|
+
## the previous ones are immediately invalidated.
|
|
92
|
+
## If your application is intended to support several sessions per browser,
|
|
93
|
+
## enable session cloning in the configuration file.
|
|
94
|
+
##
|
|
95
|
+
## === When the server is configured to restore and clone previous sessions:
|
|
96
|
+
## When sessions are cloned, the previous session is not invalidated and
|
|
97
|
+
## exists until timing out as a result of the web browser window being closed
|
|
98
|
+
## or client computer losing network connectivity for a certain (configurable)
|
|
99
|
+
## time frame.
|
|
100
|
+
##
|
|
101
|
+
## The +#cloned_target+ method is like +#restore_ses+, but called when
|
|
102
|
+
## the session is a clone of a previous session.
|
|
103
|
+
##
|
|
104
|
+
## The +#cloned_source+ method is called on the next request of the previous
|
|
105
|
+
## session after it has been cloned.
|
|
106
|
+
##
|
|
107
|
+
## == Server event methods
|
|
108
|
+
## Extend the +#init+ method to invoke constructor functionality that
|
|
109
|
+
## depends on the plugin to be constructed and registered as a part of
|
|
110
|
+
## the system.
|
|
111
|
+
##
|
|
112
|
+
## Extend the +#open+, +#flush+ and +#close+ methods to open, flush and close
|
|
113
|
+
## streams or other similar functionality.
|
|
114
|
+
##
|
|
115
|
+
## == Data handling
|
|
116
|
+
## The data exchange system exists to support bi-directional
|
|
117
|
+
## data synchronization between the browser and the plugin. The values
|
|
118
|
+
## are stored in the session as +HValue+ instances.
|
|
119
|
+
##
|
|
120
|
+
## Values support Hashes, Arrays, Strings, Numbers, Booleans and
|
|
121
|
+
## combinations of them. The data is automatically converted between
|
|
122
|
+
## ruby objects (server) and json objects (client).
|
|
123
|
+
##
|
|
124
|
+
## Each instance may be bound to plugin methods that are used as
|
|
125
|
+
## value change notification responders.
|
|
126
|
+
##
|
|
127
|
+
## When a method is bound to the value, the method is called as an
|
|
128
|
+
## event notification whenever the client has changed the value and
|
|
129
|
+
## synchronizes it to the server. The responders act as validators
|
|
130
|
+
## by default.
|
|
131
|
+
##
|
|
132
|
+
## Values are also bound in the client to classes implementing the
|
|
133
|
+
## HValueResponder interface, like any derivate of HControl. See the
|
|
134
|
+
## client documentation for instructions about using them.
|
|
135
|
+
##
|
|
136
|
+
## To define a value responder method, it needs to respond to exactly
|
|
137
|
+
## two parameters: the +Message+ instance +msg+ and the HValue object
|
|
138
|
+
## (in that order). The method's return value must be either +true+
|
|
139
|
+
## or +false+. When the method returns +false+, the change is discarded
|
|
140
|
+
## and the previously server-set value is sent back to the client.
|
|
141
|
+
##
|
|
142
|
+
## A minimal value responder method is defined like this:
|
|
143
|
+
##
|
|
144
|
+
## def my_value_responder( msg, my_value )
|
|
145
|
+
## return true
|
|
146
|
+
## end
|
|
147
|
+
##
|
|
148
|
+
## To access the content of the value, use the +HValue#data+ attribute.
|
|
149
|
+
##
|
|
150
|
+
## def int_between_100_and_200( msg, value )
|
|
151
|
+
## data = value.data.to_i
|
|
152
|
+
## return ( data >= 100 and data <= 200 )
|
|
153
|
+
## end
|
|
154
|
+
##
|
|
155
|
+
## To change the content of the value, use the +HValue#set+ method.
|
|
156
|
+
##
|
|
157
|
+
## def int_between_100_and_200( msg, value )
|
|
158
|
+
## data = value.data.to_i
|
|
159
|
+
## value.set( msg, 100 ) if data < 100
|
|
160
|
+
## value.set( msg, 200 ) if data > 200
|
|
161
|
+
## return true
|
|
162
|
+
## end
|
|
163
|
+
##
|
|
164
|
+
## == Defining values
|
|
165
|
+
## The simplest and recommended way of defining the values is to define
|
|
166
|
+
## the value configuration file +values.yaml+. Its configuration is then
|
|
167
|
+
## applied to sessions automatically.
|
|
168
|
+
##
|
|
169
|
+
##
|
|
170
|
+
## === Syntax reference of the contents of a +values.yaml+ file:
|
|
171
|
+
##
|
|
172
|
+
## # The name of the value (:value_name).
|
|
173
|
+
## # A hash key in the yaml syntax
|
|
174
|
+
## :value_name:
|
|
175
|
+
##
|
|
176
|
+
## # All of these keys are optional!
|
|
177
|
+
##
|
|
178
|
+
## # Default value, a string "Foo" here.
|
|
179
|
+
## # Defaults to 0
|
|
180
|
+
## :value: Foo
|
|
181
|
+
##
|
|
182
|
+
## # A plugin method to call to define the default value
|
|
183
|
+
## # instead of the one defined in :value
|
|
184
|
+
## :value_call:
|
|
185
|
+
## :plugin: plugin_name # defaults to the plugin where defined
|
|
186
|
+
##
|
|
187
|
+
## # Mandatory; name of the method to call
|
|
188
|
+
## :method: method_name
|
|
189
|
+
##
|
|
190
|
+
## # Optional, list of parameter values for the :method
|
|
191
|
+
## :args:
|
|
192
|
+
## # three parameters: 1, 'foo', 3
|
|
193
|
+
## - 1
|
|
194
|
+
## - foo
|
|
195
|
+
## - 3
|
|
196
|
+
##
|
|
197
|
+
## # When false, doesn't pass the msg as the first parameter.
|
|
198
|
+
## # Defaults to true
|
|
199
|
+
## :uses_msg: true
|
|
200
|
+
##
|
|
201
|
+
## # Restore the default, when the session is restored; defaults to false
|
|
202
|
+
## :restore_default: false
|
|
203
|
+
##
|
|
204
|
+
## # List of value responder methods to bind.
|
|
205
|
+
## :responders:
|
|
206
|
+
## -
|
|
207
|
+
## # name of plugin to call, defaults to the plugin where defined:
|
|
208
|
+
## :plugin: plugin_name
|
|
209
|
+
##
|
|
210
|
+
## # mandatory, name of the method to call
|
|
211
|
+
## :method: method_name
|
|
212
|
+
##
|
|
213
|
+
## # Another responder, this one using the same plugin where defined:
|
|
214
|
+
## - :method: another_method
|
|
215
|
+
##
|
|
216
|
+
## # Another value, this one just defining the defaults
|
|
217
|
+
## # by supplying an empty Hash:
|
|
218
|
+
## # (value: 0, default restored, no responders or calls)
|
|
219
|
+
## :value_with_defaults: {}
|
|
220
|
+
##
|
|
221
|
+
## # This value defines a Number (123) and doesn't restore
|
|
222
|
+
## # the default, when restoring the session.
|
|
223
|
+
## :one_two_three:
|
|
224
|
+
## :value: 123
|
|
225
|
+
## :restore_default: false
|
|
226
|
+
##
|
|
227
|
+
## # This value gets a random string and specifies a responder,
|
|
228
|
+
## # that ensures it's unique, if changed in the client.
|
|
229
|
+
## :random_unique_string:
|
|
230
|
+
## :value_call:
|
|
231
|
+
## :method: get_unique_random_string
|
|
232
|
+
## :uses_msg: false
|
|
233
|
+
## :responders:
|
|
234
|
+
## - :method: ensure_unique_random_string
|
|
235
|
+
##
|
|
236
|
+
## = Examples
|
|
237
|
+
## More examples are available in the repository;
|
|
238
|
+
## http://svn.rsence.org/contrib/plugins
|
|
239
|
+
## ..as well as the standard "main" plugin in the "plugins" directory.
|
|
240
|
+
##
|
|
241
|
+
##
|
|
242
|
+
## == A minimal Plugin bundle
|
|
243
|
+
## The minimal active plugin bundle (named "name_of_plugin")
|
|
244
|
+
## is defined like this:
|
|
245
|
+
##
|
|
246
|
+
## [dir] name_of_plugin
|
|
247
|
+
## |
|
|
248
|
+
## +---[file] name_of_plugin.rb
|
|
249
|
+
##
|
|
250
|
+
## This sample Plugin doesn't do anything except construct itself and
|
|
251
|
+
## respond as 'name_of_plugin'.
|
|
252
|
+
##
|
|
253
|
+
## Plugin.new.register('name_of_plugin')
|
|
254
|
+
##
|
|
255
|
+
## However, this is not very useful in itself, so you'll need to extend
|
|
256
|
+
## its functionality to do anything useful.
|
|
257
|
+
##
|
|
258
|
+
## == A simple Plugin extension
|
|
259
|
+
## This plugin logs session events to the logs/session_log file.
|
|
260
|
+
##
|
|
261
|
+
## [dir] ses_logger
|
|
262
|
+
## |
|
|
263
|
+
## +---[file] ses_logger.rb
|
|
264
|
+
## |
|
|
265
|
+
## +---[dir] logs
|
|
266
|
+
## |
|
|
267
|
+
## +---[file] session_log
|
|
268
|
+
##
|
|
269
|
+
## == Contents of "ses_logger.rb"
|
|
270
|
+
##
|
|
271
|
+
## class SessionLogger < Plugin
|
|
272
|
+
## def init
|
|
273
|
+
## super
|
|
274
|
+
## @logfile = false
|
|
275
|
+
## end
|
|
276
|
+
## def open
|
|
277
|
+
## log_path = compose_plugin_path( 'session_log', 'logs' )
|
|
278
|
+
## @logfile = File.open( log_path, 'a' )
|
|
279
|
+
## end
|
|
280
|
+
## def close
|
|
281
|
+
## @logfile.close if @logfile
|
|
282
|
+
## @logfile = false
|
|
283
|
+
## end
|
|
284
|
+
## def flush
|
|
285
|
+
## @logfile.flush if @logfile
|
|
286
|
+
## end
|
|
287
|
+
## def init_ses( msg )
|
|
288
|
+
## super
|
|
289
|
+
## @logfile.write( "#{Time.new} -- Session id #{msg.ses_id} was created.\n" )
|
|
290
|
+
## end
|
|
291
|
+
## def restore_ses( msg )
|
|
292
|
+
## super
|
|
293
|
+
## @logfile.write( "#{Time.new} -- Session id #{msg.ses_id} was restored.\n" )
|
|
294
|
+
## end
|
|
295
|
+
## def idle( msg )
|
|
296
|
+
## @logfile.write( "#{Time.new} -- Client of session id #{msg.ses_id} connected.\n" )
|
|
297
|
+
## end
|
|
298
|
+
## end
|
|
299
|
+
## SessionLogger.new.register( 'ses_logger' )
|
|
300
|
+
##
|
|
301
|
+
class Plugin
|
|
302
|
+
|
|
303
|
+
include PluginUtil
|
|
304
|
+
|
|
305
|
+
# The +names+ is a list of (usually just one) names the plugin is registered under.
|
|
306
|
+
attr_reader :name, :path, :info, :inited
|
|
307
|
+
|
|
308
|
+
# The constructor should not take any parameters. In most cases, it's better
|
|
309
|
+
# to extend the +#init+ method, because it's called after the plugin is set up.
|
|
310
|
+
def initialize
|
|
311
|
+
@inited = false
|
|
312
|
+
@info = @@bundle_info
|
|
313
|
+
@name = @@bundle_name
|
|
314
|
+
@path = @@bundle_path
|
|
315
|
+
@plugins = @@plugin_manager
|
|
316
|
+
register unless @info[:inits_self]
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
# Extend this method to do any initial tasks before other methods are called.
|
|
320
|
+
# By default init_values is called to load the +values.yaml+ configuration file.
|
|
321
|
+
def init
|
|
322
|
+
@values = init_values
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
# Extend this method to do any tasks every time the client makes a request.
|
|
326
|
+
def idle( msg )
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
# Extend this method to invoke actions, when a new session is created.
|
|
330
|
+
# By default +#init_ses_values+ is called to initialize values defined in the
|
|
331
|
+
# +values.yaml+ configuration file.
|
|
332
|
+
def init_ses( msg )
|
|
333
|
+
init_ses_values( msg )
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
# Extend this method to invoke actions, when a previous session is restored.
|
|
337
|
+
# By default +#restore_ses_values+ is called to perform actions on values as
|
|
338
|
+
# defined in the +values.yaml+ configuration file.
|
|
339
|
+
def restore_ses( msg )
|
|
340
|
+
restore_ses_values( msg )
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
# Extend this method to invoke actions, when the session
|
|
344
|
+
# is a clone of another session. It's called once, just
|
|
345
|
+
# before +#restore_ses+ is called.
|
|
346
|
+
#
|
|
347
|
+
# A session is cloned, when a user opens a another browser
|
|
348
|
+
# window or tab, while the previous session is still active.
|
|
349
|
+
#
|
|
350
|
+
# The +source_ses+ is the actual previous session object, which
|
|
351
|
+
# was used as the source of the clone.
|
|
352
|
+
def cloned_target( msg, source_session )
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
# Extend this method to invoke actions, when the session
|
|
356
|
+
# has been cloned to another session. It's called once, just
|
|
357
|
+
# before +#restore_ses+ is called on the first request after
|
|
358
|
+
# the cloning happened.
|
|
359
|
+
#
|
|
360
|
+
# A session is cloned, when a user opens a another browser
|
|
361
|
+
# window or tab, while the previous session is still active.
|
|
362
|
+
#
|
|
363
|
+
# The +target_ses+ is the actual cloned session object, which
|
|
364
|
+
# is a copy of the current session.
|
|
365
|
+
def cloned_source( msg, target_sessions )
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
# This method must be called to register the plugin instance
|
|
369
|
+
# into the system. Otherwise, it's subject to destruction
|
|
370
|
+
# and garbage collection. Use the +name+ parameter to
|
|
371
|
+
# give the (unique) name of your plugin.
|
|
372
|
+
def register( name=false )
|
|
373
|
+
if @inited
|
|
374
|
+
@plugins.register_alias( @name, name )
|
|
375
|
+
else
|
|
376
|
+
if name
|
|
377
|
+
name = name.to_s
|
|
378
|
+
else
|
|
379
|
+
name = @name
|
|
380
|
+
end
|
|
381
|
+
@plugins.register_bundle( self, name )
|
|
382
|
+
@inited = true
|
|
383
|
+
end
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
private
|
|
387
|
+
|
|
388
|
+
# This method looks looks for a file called "values.yaml"
|
|
389
|
+
# in the plugin's bundle directory
|
|
390
|
+
#.
|
|
391
|
+
# If this file is found, it loads it for initial value definitions.
|
|
392
|
+
#
|
|
393
|
+
# These definitions are accessible as the +@values+ attribute.
|
|
394
|
+
def init_values
|
|
395
|
+
values_path = compose_plugin_path( 'values.yaml' )
|
|
396
|
+
return yaml_read( values_path )
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
# Returns all the names your plugin respond to.
|
|
400
|
+
# def name
|
|
401
|
+
# return @names.first
|
|
402
|
+
# end
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
# Returns or creates a new session hash for the plugin.
|
|
406
|
+
#
|
|
407
|
+
# Uses the first name registered for the plugin and converts it to a symbol.
|
|
408
|
+
def get_ses( msg )
|
|
409
|
+
name_sym = name.to_sym
|
|
410
|
+
unless msg.session.has_key?( name_sym )
|
|
411
|
+
msg.session[ name_sym ] = {}
|
|
412
|
+
end
|
|
413
|
+
return msg.session[ name_sym ]
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
# Returns the source code of the javascript file +name+ in the 'js'
|
|
417
|
+
# subdirectory of the plugin bundle.
|
|
418
|
+
def read_js( js_name )
|
|
419
|
+
file_read( compose_plugin_path( js_name, 'js', '.js' ) )
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
# Deprecated name of +#read_js+
|
|
423
|
+
alias require_js read_js
|
|
424
|
+
|
|
425
|
+
# Like +#read_js+, but reads the file only once per session.
|
|
426
|
+
#
|
|
427
|
+
# Returns the contents of the file on the first call,
|
|
428
|
+
# an empty string on the subsequent calls.
|
|
429
|
+
#
|
|
430
|
+
# Returns false otherwise.
|
|
431
|
+
def read_js_once( msg, js_name )
|
|
432
|
+
ses = msg.session
|
|
433
|
+
if not ses.has_key?(:deps)
|
|
434
|
+
ses[:deps] = []
|
|
435
|
+
end
|
|
436
|
+
path = compose_plugin_path( js_name, 'js', '.js' )
|
|
437
|
+
unless ses[:deps].include?( path )
|
|
438
|
+
ses[:deps].push( path )
|
|
439
|
+
return file_read( path )
|
|
440
|
+
else
|
|
441
|
+
return ''
|
|
442
|
+
end
|
|
443
|
+
end
|
|
444
|
+
|
|
445
|
+
# Deprecated name of +#read_js_once+
|
|
446
|
+
alias require_js_once read_js_once
|
|
447
|
+
|
|
448
|
+
# Creates a new instance of HValue, assigns it as +value_name+ into the
|
|
449
|
+
# session and uses the +value_properties+ Hash to define the default
|
|
450
|
+
# value and value responders.
|
|
451
|
+
#
|
|
452
|
+
# This method is invoked automatically, when handling the properties
|
|
453
|
+
# of the +values.yaml+ configuration file of a new session.
|
|
454
|
+
#
|
|
455
|
+
# It's invoked by +#init_ses+ via +#init_ses_values+.
|
|
456
|
+
#
|
|
457
|
+
# Structure of +value_properties+, all top-level items are optional:
|
|
458
|
+
#
|
|
459
|
+
# {
|
|
460
|
+
# # Default value; defaults to 0
|
|
461
|
+
# :value => 'foo',
|
|
462
|
+
#
|
|
463
|
+
# # A plugin method to call to define the default value instead of the one defined in :value
|
|
464
|
+
# :value_call => {
|
|
465
|
+
# :plugin => 'plugin_name', # defaults to the plugin where defined
|
|
466
|
+
# :method => 'method_name', # mandatory; name of the method to call
|
|
467
|
+
# :args => [ 1, 'foo', 3 ], # optional, list of parameter values for the :method
|
|
468
|
+
# :uses_msg => true # defaults to true; when false, doesn't pass the msg as the first parameter
|
|
469
|
+
# },
|
|
470
|
+
#
|
|
471
|
+
# # Restore the default, when the session is restored; defaults to false
|
|
472
|
+
# :restore_default => false,
|
|
473
|
+
#
|
|
474
|
+
# # List of value responder methods to bind.
|
|
475
|
+
# :responders => [
|
|
476
|
+
# {
|
|
477
|
+
# :plugin => 'plugin_name', # defaults to the plugin where defined
|
|
478
|
+
# :method => 'method_name' # mandatory, name of the method to call
|
|
479
|
+
# },
|
|
480
|
+
# # You can supply as many responders as you like:
|
|
481
|
+
# { :plugin => 'another_plugin', :method => 'another_method' }
|
|
482
|
+
# ]
|
|
483
|
+
# }
|
|
484
|
+
#
|
|
485
|
+
def init_ses_value( msg, value_name, value_properties )
|
|
486
|
+
ses = get_ses( msg )
|
|
487
|
+
if value_properties.has_key?(:value_call)
|
|
488
|
+
default_value = init_value_call( msg, value_properties[:value_call] )
|
|
489
|
+
elsif value_properties.has_key?(:value)
|
|
490
|
+
default_value = value_properties[:value]
|
|
491
|
+
else
|
|
492
|
+
default_value = 0
|
|
493
|
+
end
|
|
494
|
+
ses[value_name] = HValue.new( msg, default_value )
|
|
495
|
+
if value_properties.has_key?(:responders)
|
|
496
|
+
value_properties[:responders].each do |responder|
|
|
497
|
+
if responder.has_key?(:plugin)
|
|
498
|
+
responder_plugin = responder[:plugin]
|
|
499
|
+
else
|
|
500
|
+
responder_plugin = @name
|
|
501
|
+
end
|
|
502
|
+
if responder.has_key?(:method)
|
|
503
|
+
ses[value_name].bind( responder_plugin, responder[:method] )
|
|
504
|
+
end
|
|
505
|
+
end
|
|
506
|
+
end
|
|
507
|
+
end
|
|
508
|
+
|
|
509
|
+
# Initializes session values, if the contents of the +values.yaml+
|
|
510
|
+
# file is defined in the bundle directory and loaded in +#init_values+.
|
|
511
|
+
def init_ses_values( msg )
|
|
512
|
+
return unless @values
|
|
513
|
+
@values.each do | value_name, value_properties |
|
|
514
|
+
init_ses_value( msg, value_name, value_properties )
|
|
515
|
+
end
|
|
516
|
+
end
|
|
517
|
+
|
|
518
|
+
# Returns a value based on the :method and :plugin members of the
|
|
519
|
+
# +value_call+ hash.
|
|
520
|
+
#
|
|
521
|
+
# The call is made via msg.run if the method is not defined in
|
|
522
|
+
# the local plugin bundle.
|
|
523
|
+
#
|
|
524
|
+
# This method is called from +#init_ses_value+.
|
|
525
|
+
#
|
|
526
|
+
# Structure of the +value_call+ Hash:
|
|
527
|
+
# { :plugin => 'plugin_name', # defaults to the plugin where defined
|
|
528
|
+
# :method => 'method_name', # mandatory; name of the method to call
|
|
529
|
+
# :args => [ 1, 'foo', 3 ], # optional, list of parameter values for the :method
|
|
530
|
+
# :uses_msg => true # defaults to true; when false, doesn't pass the msg as the first parameter
|
|
531
|
+
# }
|
|
532
|
+
def init_value_call( msg, value_call )
|
|
533
|
+
value_call_method = value_call[:method]
|
|
534
|
+
if value_call.has_key?(:plugin)
|
|
535
|
+
value_call_plugin = value_call[:plugin]
|
|
536
|
+
else
|
|
537
|
+
value_call_plugin = false
|
|
538
|
+
end
|
|
539
|
+
if value_call.has_key?(:args)
|
|
540
|
+
if value_call.has_key?(:uses_msg) and value_call[:uses_msg] != false
|
|
541
|
+
if value_call_plugin
|
|
542
|
+
return msg.run( value_call_plugin, value_call_method, msg, *value_call[:args] )
|
|
543
|
+
else
|
|
544
|
+
return self.method( value_call_method ).call( msg, *value_call[:args] )
|
|
545
|
+
end
|
|
546
|
+
else
|
|
547
|
+
if value_call_plugin
|
|
548
|
+
return msg.run( value_call_plugin, value_call_method, *value_call[:args] )
|
|
549
|
+
else
|
|
550
|
+
return self.method( value_call_method ).call( *value_call[:args] )
|
|
551
|
+
end
|
|
552
|
+
end
|
|
553
|
+
else
|
|
554
|
+
if value_call.has_key?(:uses_msg) and value_call[:uses_msg] != false
|
|
555
|
+
if value_call_plugin
|
|
556
|
+
return msg.run( value_call_plugin, value_call_method, msg )
|
|
557
|
+
else
|
|
558
|
+
return self.method( value_call_method ).call( msg )
|
|
559
|
+
end
|
|
560
|
+
else
|
|
561
|
+
if value_call_plugin
|
|
562
|
+
return msg.run( value_call_plugin, value_call_method )
|
|
563
|
+
else
|
|
564
|
+
return self.method( value_call_method ).call( )
|
|
565
|
+
end
|
|
566
|
+
end
|
|
567
|
+
end
|
|
568
|
+
end
|
|
569
|
+
|
|
570
|
+
# Restores session values to default, unless specified otherwise.
|
|
571
|
+
#
|
|
572
|
+
# Called from +#restore_ses+
|
|
573
|
+
def restore_ses_values( msg )
|
|
574
|
+
return unless @values
|
|
575
|
+
ses = get_ses( msg )
|
|
576
|
+
@values.each do | value_name, value_properties |
|
|
577
|
+
if ses.has_key?( value_name ) and ses[ value_name ].class == HValue
|
|
578
|
+
unless value_properties[:restore_default] == false
|
|
579
|
+
if value_properties.has_key?(:value_call)
|
|
580
|
+
default_value = init_value_call( msg, value_properties[:value_call] )
|
|
581
|
+
elsif value_properties.has_key?(:value)
|
|
582
|
+
default_value = value_properties[:value]
|
|
583
|
+
else
|
|
584
|
+
default_value = 0
|
|
585
|
+
end
|
|
586
|
+
ses[value_name].set( msg, default_value )
|
|
587
|
+
end
|
|
588
|
+
else
|
|
589
|
+
init_ses_value( msg, value_name, value_properties )
|
|
590
|
+
end
|
|
591
|
+
end
|
|
592
|
+
end
|
|
593
|
+
|
|
594
|
+
# Extracts +HValue+ references as javascript from the session Hash.
|
|
595
|
+
# The +ses+ parameter is used for supplying a hash with the +HValue+
|
|
596
|
+
# instances. It's optional and defaults to the current plugin node in
|
|
597
|
+
# the active session.
|
|
598
|
+
#
|
|
599
|
+
# The return value is a string representing a js object similar to
|
|
600
|
+
# the ruby Hash +ses+.
|
|
601
|
+
#
|
|
602
|
+
# Sample usage:
|
|
603
|
+
#
|
|
604
|
+
# values_js( msg, msg.session[:main] )
|
|
605
|
+
#
|
|
606
|
+
def values_js( msg, ses=false )
|
|
607
|
+
# backwards-compatible with pre-1.3 behaviour
|
|
608
|
+
ses = msg if msg.class == Hash
|
|
609
|
+
# gets the session automatically, if false
|
|
610
|
+
ses = get_ses( msg ) unless ses
|
|
611
|
+
js_references = []
|
|
612
|
+
ses.each_key do |key_name|
|
|
613
|
+
if ses[key_name].class == HValue
|
|
614
|
+
js_references.push( "#{key_name.to_s}:HVM.values['#{ses[key_name].val_id}']" )
|
|
615
|
+
end
|
|
616
|
+
end
|
|
617
|
+
return "{#{js_references.join(',')}}"
|
|
618
|
+
end
|
|
619
|
+
|
|
620
|
+
# Deprecated name of +#values_js+
|
|
621
|
+
alias extract_hvalues_from_hash values_js
|
|
622
|
+
|
|
623
|
+
# Tells the js client framework to load a list of dependency packages.
|
|
624
|
+
# It keeps track of what's loaded, so nothing library loaded twice.
|
|
625
|
+
#
|
|
626
|
+
# The +dependencies+ parameter is an Array of dependencies.
|
|
627
|
+
#
|
|
628
|
+
# Sample usage:
|
|
629
|
+
#
|
|
630
|
+
# include_js( msg, [ 'default_theme', 'controls', 'lists', 'datetime' ] )
|
|
631
|
+
#
|
|
632
|
+
def include_js( msg, dependencies=[] )
|
|
633
|
+
ses = msg.session
|
|
634
|
+
# check, if the session has a dependency array
|
|
635
|
+
if not ses.has_key?( :deps )
|
|
636
|
+
# make an array of dependencies for this session, if not already done
|
|
637
|
+
ses[:deps] = []
|
|
638
|
+
end
|
|
639
|
+
dependencies = [dependencies] if dependencies.class == String
|
|
640
|
+
# Check the required dependencies until everything is loaded.
|
|
641
|
+
dependencies.each do |dependency|
|
|
642
|
+
unless ses[:deps].include?( dependency )
|
|
643
|
+
ses[:deps].push( dependency )
|
|
644
|
+
msg.reply(%{jsLoader.load("#{dependency}");})
|
|
645
|
+
end
|
|
646
|
+
end
|
|
647
|
+
end
|
|
648
|
+
|
|
649
|
+
|
|
650
|
+
end
|
|
651
|
+
|
|
652
|
+
|