rsence 2.0.0.10.pre → 2.0.0.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (161) hide show
  1. data/.yardopts +10 -0
  2. data/INSTALL.rdoc +52 -86
  3. data/{LICENSE → LICENSE.txt} +0 -0
  4. data/README.rdoc +52 -49
  5. data/VERSION +1 -1
  6. data/bin/rsence +4 -6
  7. data/conf/default_conf.yaml +314 -305
  8. data/conf/default_strings.yaml +70 -71
  9. data/docs/ExampleGuiPlugin.rdoc +193 -0
  10. data/{js/comm/comm/autosync/js.inc → docs/JavascriptBundles.rdoc} +0 -0
  11. data/docs/PluginBundleInfo.rdoc +173 -0
  12. data/docs/PluginBundles.rdoc +96 -0
  13. data/docs/Values.rdoc +163 -0
  14. data/js/comm/{comm/autosync → autosync}/autosync.js +1 -2
  15. data/js/comm/{comm → autosync}/js.inc +0 -0
  16. data/js/comm/{comm/comm.js → comm.js} +1 -0
  17. data/js/comm/{comm/queue/js.inc → js.inc} +0 -0
  18. data/js/comm/jsloader/jsloader.js +2 -4
  19. data/js/comm/{comm/session → queue}/js.inc +0 -0
  20. data/js/comm/{comm/queue → queue}/queue.js +1 -0
  21. data/js/comm/{comm/sessionwatcher → session}/js.inc +0 -0
  22. data/js/comm/{comm/session → session}/session.js +4 -3
  23. data/js/comm/{comm/transporter → sessionwatcher}/js.inc +0 -0
  24. data/js/comm/{comm/sessionwatcher → sessionwatcher}/sessionwatcher.js +1 -0
  25. data/js/comm/{comm/urlresponder → transporter}/js.inc +0 -0
  26. data/js/comm/{comm/transporter → transporter}/transporter.js +1 -0
  27. data/js/comm/{comm/values → urlresponder}/js.inc +0 -0
  28. data/js/comm/{comm/urlresponder → urlresponder}/urlresponder.js +1 -0
  29. data/js/comm/{reloadapp → values}/js.inc +0 -0
  30. data/js/comm/{comm/values → values}/values.js +1 -0
  31. data/js/controls/button/button.js +10 -1
  32. data/js/controls/checkbox/checkbox.js +2 -0
  33. data/js/controls/dialogs/alert_sheet/alert_sheet.js +1 -0
  34. data/js/controls/dialogs/confirm_sheet/confirm_sheet.js +1 -0
  35. data/js/controls/dialogs/sheet/sheet.js +1 -0
  36. data/js/controls/imageview/imageview.js +1 -0
  37. data/js/controls/passwordcontrol/passwordcontrol.js +1 -0
  38. data/js/controls/progress/progressbar/progressbar.js +1 -0
  39. data/js/controls/progress/progressindicator/progressindicator.js +1 -0
  40. data/js/controls/radiobutton/radiobutton.js +3 -1
  41. data/js/controls/sliders/slider/slider.js +1 -0
  42. data/js/controls/sliders/vslider/vslider.js +1 -0
  43. data/js/controls/stepper/stepper.js +1 -0
  44. data/js/controls/stringview/stringview.js +2 -2
  45. data/js/controls/tab/tab.js +7 -5
  46. data/js/controls/textarea/textarea.js +1 -0
  47. data/js/controls/textcontrol/textcontrol.js +3 -1
  48. data/js/controls/uploader/uploader.js +1 -0
  49. data/js/controls/validatorview/validatorview.js +62 -59
  50. data/js/controls/window/window.js +1 -0
  51. data/js/core/class/class.js +2 -1
  52. data/js/core/elem/elem.js +27 -27
  53. data/js/core/event/event.js +3 -874
  54. data/js/{comm/sha → core/rsence_ns}/js.inc +0 -0
  55. data/js/core/rsence_ns/rsence_ns.js +15 -0
  56. data/js/{controls/datetime → datetime}/calendar/calendar.js +1 -0
  57. data/js/{comm/values/value → datetime/calendar}/js.inc +0 -0
  58. data/js/{controls/datetime → datetime}/calendar/themes/default/calendar.css +0 -0
  59. data/js/{controls/datetime → datetime}/calendar/themes/default/calendar.html +0 -0
  60. data/js/{controls/datetime → datetime}/calendar/themes/default/calendar_arrows-ie6.gif +0 -0
  61. data/js/{controls/datetime → datetime}/calendar/themes/default/calendar_arrows.png +0 -0
  62. data/js/{controls/datetime → datetime}/datetimevalue/datetimevalue.js +1 -0
  63. data/js/{comm/values/valuematrix → datetime/datetimevalue}/js.inc +0 -0
  64. data/js/{controls/datetime/calendar → datetime/timesheet}/js.inc +0 -0
  65. data/js/{controls/datetime → datetime}/timesheet/themes/default/timesheet.css +0 -0
  66. data/js/{controls/datetime → datetime}/timesheet/themes/default/timesheet.html +0 -0
  67. data/js/{controls/datetime → datetime}/timesheet/timesheet.js +1 -0
  68. data/js/{controls/datetime/datetimevalue → datetime/timesheet_item}/js.inc +0 -0
  69. data/js/{controls/datetime → datetime}/timesheet_item/themes/default/timesheet_item.css +0 -0
  70. data/js/{controls/datetime → datetime}/timesheet_item/themes/default/timesheet_item.html +0 -0
  71. data/js/{controls/datetime → datetime}/timesheet_item/timesheet_item.js +1 -0
  72. data/js/{controls/datetime/timesheet → datetime/timesheet_item_edit}/js.inc +0 -0
  73. data/js/{controls/datetime → datetime}/timesheet_item_edit/timesheet_item_edit.js +1 -1
  74. data/js/foundation/application/application.js +1 -2
  75. data/js/foundation/control/control.js +1 -4
  76. data/js/foundation/control/controldefaults/controldefaults.js +1 -4
  77. data/js/foundation/control/dummyvalue/dummyvalue.js +1 -0
  78. data/js/foundation/control/dyncontrol/dyncontrol.js +1 -2
  79. data/js/foundation/control/eventresponder/eventresponder.js +11 -3
  80. data/js/{controls/datetime/timesheet_item → foundation/control/valuematrix}/js.inc +0 -0
  81. data/js/{comm/values → foundation/control}/valuematrix/valuematrix.js +2 -5
  82. data/js/foundation/control/valueresponder/valueresponder.js +3 -1
  83. data/js/foundation/eventmanager/eventmanager.js +884 -0
  84. data/js/{controls/datetime/timesheet_item_edit → foundation/eventmanager}/js.inc +0 -0
  85. data/js/foundation/geom/point/point.js +2 -2
  86. data/js/foundation/geom/rect/rect.js +1 -0
  87. data/js/foundation/json_renderer/json_renderer.js +2 -0
  88. data/js/foundation/system/system.js +2 -1
  89. data/js/foundation/thememanager/thememanager.js +29 -23
  90. data/js/{controls/lists/checkboxlist → foundation/value}/js.inc +0 -0
  91. data/js/{comm/values → foundation}/value/value.js +1 -0
  92. data/js/foundation/view/markupview/markupview.js +2 -1
  93. data/js/foundation/view/morphanimation/morphanimation.js +1 -0
  94. data/js/foundation/view/view.js +6 -12
  95. data/js/foundation/view/viewdefaults/viewdefaults.js +1 -0
  96. data/js/{controls/lists → lists}/checkboxlist/checkboxlist.js +1 -0
  97. data/js/{controls/lists/listitems → lists/checkboxlist}/js.inc +0 -0
  98. data/js/{controls/lists/radiobuttonlist → lists/listitems}/js.inc +0 -0
  99. data/js/{controls/lists → lists}/listitems/listitems.js +22 -3
  100. data/js/lists/radiobuttonlist/js.inc +0 -0
  101. data/js/{controls/lists → lists}/radiobuttonlist/radiobuttonlist.js +2 -12
  102. data/js/util/reloadapp/js.inc +0 -0
  103. data/js/{comm → util}/reloadapp/reloadapp.js +10 -8
  104. data/js/{comm → util}/reloadapp/themes/default/reloadapp_warning-ie6.gif +0 -0
  105. data/js/{comm → util}/reloadapp/themes/default/reloadapp_warning.png +0 -0
  106. data/js/util/sha/js.inc +0 -0
  107. data/js/{comm → util}/sha/sha.js +27 -33
  108. data/js/views/centerview/centerview.js +33 -3
  109. data/js/views/inlineview/inlineview.js +1 -0
  110. data/js/views/scrollview/scrollview.js +1 -0
  111. data/lib/conf/argv.rb +34 -22
  112. data/lib/conf/default.rb +165 -173
  113. data/lib/daemon/daemon.rb +9 -9
  114. data/lib/daemon/sigcomm.rb +4 -4
  115. data/lib/http/broker.rb +30 -27
  116. data/lib/http/rackup.rb +2 -3
  117. data/lib/http/request.rb +1 -2
  118. data/lib/http/response.rb +10 -10
  119. data/lib/plugins/dependencies.rb +2 -1
  120. data/lib/plugins/gui_plugin.rb +77 -50
  121. data/lib/plugins/guiparser.rb +26 -22
  122. data/lib/plugins/plugin.rb +207 -427
  123. data/lib/plugins/plugin_base.rb +162 -0
  124. data/lib/plugins/plugin_plugins.rb +21 -15
  125. data/lib/plugins/plugin_sqlite_db.rb +45 -20
  126. data/lib/plugins/pluginmanager.rb +10 -5
  127. data/lib/plugins/plugins.rb +65 -31
  128. data/lib/plugins/servlet.rb +74 -26
  129. data/lib/rsence.rb +32 -0
  130. data/lib/session/msg.rb +131 -96
  131. data/lib/session/sessionmanager.rb +5 -4
  132. data/lib/session/sessionstorage.rb +4 -3
  133. data/lib/transporter/transporter.rb +30 -21
  134. data/lib/util/gzstring.rb +4 -2
  135. data/lib/util/ruby19_fixes.rb +18 -0
  136. data/lib/values/hvalue.rb +139 -88
  137. data/lib/values/valuemanager.rb +60 -46
  138. data/plugins/client_pkg/client_pkg.rb +6 -5
  139. data/plugins/client_pkg/lib/client_pkg_build.rb +5 -5
  140. data/plugins/client_pkg/lib/client_pkg_serve.rb +8 -0
  141. data/plugins/index_html/index_html.rb +3 -1
  142. data/plugins/index_html/tmpl/index.html +2 -3
  143. data/plugins/main/js/main.js +84 -0
  144. data/plugins/main/main.rb +54 -27
  145. data/plugins/ticket/lib/common.rb +4 -2
  146. data/plugins/ticket/lib/favicon.rb +3 -2
  147. data/plugins/ticket/lib/file.rb +2 -2
  148. data/plugins/ticket/lib/img.rb +2 -2
  149. data/plugins/ticket/lib/objblob.rb +2 -2
  150. data/plugins/ticket/lib/rsrc.rb +2 -2
  151. data/plugins/ticket/lib/upload.rb +9 -8
  152. data/plugins/ticket/ticket.rb +141 -8
  153. data/setup/welcome/welcome.rb +1 -0
  154. metadata +76 -72
  155. data/lib/plugins/plugin_util.rb +0 -107
  156. data/plugins/index_html/tmpl/startup_index.html +0 -29
  157. data/plugins/main/js/riassence_ns.js +0 -87
  158. data/setup/legacy/info.yaml +0 -18
  159. data/setup/legacy/legacy.rb +0 -17
  160. data/setup/welcome/gui/welcome.yaml +0 -68
  161. data/setup/welcome/text/welcome.html +0 -8
@@ -6,27 +6,31 @@
6
6
  # with this software package. If not, contact licensing@riassence.com
7
7
  ##
8
8
 
9
- module ::RSence
9
+
10
+ module RSence
11
+
12
+
10
13
  module Plugins
11
14
 
15
+
12
16
  # This class automatically loads a YAML file from "gui" subdirectory of a plugin.
13
- # Extend your plugin from the GUIPlugin class instead of the Plugin class to make
14
- # this work automatically.
15
- # = Usage:
16
- # Initialize like this from inside a plugin method. This will load the "gui/my_gui.yaml" file.
17
- # @gui = GUIParser.new( self, 'my_gui' )
18
- # To make the client render the contents of the yaml do this:
17
+ #
18
+ # Extend your plugin from the {GUIPlugin__ GUIPlugin} class instead of the Plugin class to make this work automatically.
19
+ #
20
+ # @example Initialize like this from inside a plugin method. This will load the "gui/my_gui.yaml" file.
21
+ # gui = GUIParser.new( self, 'my_gui' )
22
+ #
23
+ # @example To make the client render the contents of the yaml do this:
19
24
  # ses = get_ses( msg )
20
25
  # params = { :values => @gui.values( ses ) }
21
- # @gui.init( msg, params )
26
+ # gui.init( msg, params )
22
27
  class GUIParser
23
-
24
- include ::RSence
25
-
28
+
29
+ # include ::RSence
30
+
26
31
  # Use this method to send the client all commands required to construct the GUI Tree using JSONRenderer.
27
- # = Parameters
28
- # +msg+:: The +Message+ instance +msg+ used all over the place.
29
- # +params+:: An hash containing all parameters referred from the YAML file.
32
+ # @param [Message] msg The +Message+ instance +msg+ used all over the place.
33
+ # @param [Hash] params Containing all parameters referred from the YAML file, see: {GUIPlugin__#gui_params GUIPlugin#gui_params}
30
34
  def init( msg, params )
31
35
  gui_data = YAML.load( @yaml_src )
32
36
  parse_gui( gui_data, params )
@@ -44,24 +48,24 @@ module ::RSence
44
48
  json_data = JSON.dump( gui_data )
45
49
  msg.reply( "JSONRenderer.nu(#{json_data});", true )
46
50
  end
47
-
51
+
48
52
  # Use this method to extract all the value id's of the +ses+ hash.
49
53
  def values( ses )
50
54
  ids = {}
51
55
  ses.each do | key, value |
52
56
  if value.class == HValue
53
- ids[ key ] = value.val_id
57
+ ids[ key ] = value.value_id
54
58
  end
55
59
  end
56
60
  return ids
57
61
  end
58
-
62
+
59
63
  private
60
-
64
+
61
65
  def json_fun( value )
62
66
  JSON.parse( "[#{value}]" ).first
63
67
  end
64
-
68
+
65
69
  # Parses the gui data using params. Called from +init+.
66
70
  def parse_gui( gui_data, params )
67
71
  data_class = gui_data.class
@@ -86,7 +90,7 @@ module ::RSence
86
90
  end
87
91
  return gui_data
88
92
  end
89
-
93
+
90
94
  # Searches the params hash for parameters whenever encountered a Symbol in the YAML.
91
95
  def get_params( params_path, params )
92
96
  item = params_path.shift
@@ -104,7 +108,7 @@ module ::RSence
104
108
  end
105
109
  return ''
106
110
  end
107
-
111
+
108
112
  # Loads the YAML file.
109
113
  # = Parameters
110
114
  # +parent+:: The Plugin instance called from, use +self+ when constructing in a Plugin method.
@@ -113,7 +117,7 @@ module ::RSence
113
117
  @parent = parent
114
118
  @yaml_src = yaml_src
115
119
  end
116
-
120
+
117
121
  end
118
122
  end
119
123
  end
@@ -6,314 +6,92 @@
6
6
  # with this software package. If not, contact licensing@riassence.com
7
7
  ##
8
8
 
9
- module ::RSence
9
+
10
+ module RSence
10
11
  module Plugins
11
12
 
12
- ## = Abstract
13
- ##
14
- ## The PluginTemplate is used to create a Plugin mimic class in
15
- ## Plugins.bundle_loader using the Plugins.PluginWrapper mimic.
16
- ##
17
- ## The Plugin class is the base class for extending server logic.
18
- ## A single Plugin instance serves the requests of all sessions,
19
- ## which makes them very cpu and memory efficient compared to systems,
20
- ## where the server classes are constructed and destructed for each
21
- ## request.
22
- ##
23
- ## Plugins are designed to be contained in a plugin directory bundle and
24
- ## to be loaded by the +PluginManager+, which is also responsible for
25
- ## delegating the events and other calls throughout the system.
26
- ##
27
- ## == Anatomy of a plugin bundle
28
- ## The plugin bundle contains all data needed to run the plugin. Design
29
- ## your plugin without any hard-coded paths, remember that it's intended
30
- ## to be deployed by "dropping" the whole plugin into one of the server's
31
- ## plugins directories.
32
- ##
33
- ## The +PluginManager+ looks for such bundles and evaluates them into an
34
- ## anonymous +Module+ namespace. The content of the ruby source file
35
- ## is then responsible for including its libraries, constructing an
36
- ## instance of itself and registering itself as a part of the system.
37
- ##
38
- ## It's advised to use the +GUIPlugin+ class for plugins that handle
39
- ## user interfaces. Usage of the +Plugin+ bundle is advised to use
40
- ## for plugins that provide extra functionality, value responders
41
- ## and other utilities that supplement the user interface.
42
- ##
43
- ## You must call the +#register+ method after the class is constructed.
44
- ## Otherwise, the class is not connected to the system and just discarded
45
- ## and then garbage collected.
46
- ##
47
- ## == Messages
48
- ## As a side effect of having single instances of plugins serve the requests
49
- ## of all sessions, the request/response/session messaging is implemented
50
- ## as messaging objects. These objects contain or delegate all the necessary
51
- ## hooks required by the complete request/response cycle.
52
- ##
53
- ## The naming convention of the +Message+ instance is +msg+ and it's
54
- ## given as the first parameter of methods needing it.
55
- ##
56
- ## Use +msg.ses_id+ to identify the session's serial number and +msg.user_id+
57
- ## to identify the user's identity.
58
- ##
59
- ## Use the +msg.session+ +Hash+ to store any persistent data
60
- ## associated with the user's session, preferably using the name of the
61
- ## plugin or its registered name as the primary key entry in the Hash.
62
- ## The session data is persistent; it's stored in the session database
63
- ## by +SessionStorage+ automatically.
64
- ##
65
- ## The +msg+ instance also provides access to the +Request+ and +Response+
66
- ## objects as +msg.request+ and +msg.response+, respectively.
67
- ##
68
- ## Use the +msg.run+ method to call other plugins.
69
- ##
70
- ## To append js source code to be evaluated in the client, use the +msg.reply+
71
- ## call. The +msg.console+ call appends messages to the browser's js console.
72
- ##
73
- ##
74
- ## == Session -related event methods
75
- ## The +#get_ses+ method returns (or creates and returns) the entry in
76
- ## the session based on the name your plugin is registered as. It's advised
77
- ## to use this call instead of manually managing +msg.session+ in most cases.
78
- ##
79
- ## The +#idle+ method is called each time a client performs a data
80
- ## synchronization or "idle poll" request.
81
- ##
82
- ## The +#init_ses+ method is called once in the same request a new session
83
- ## is created. A new session is created, when a user enters accesses the
84
- ## server the first time, or the first time after the previous session is
85
- ## expired.
86
- ##
87
- ## The +#init_ui+ method is called by the "main" plugin after the client has
88
- ## booted successfully. The +GUIPlugin+ class extends this method to
89
- ## automatically load and initialize the user interface from a data structure.
90
- ##
91
- ## The +#restore_ses+ method is called once in the same request an old
92
- ## session is restored. A session is restored, when the user returns to
93
- ## the page or reloads the page before the session is expired.
94
- ##
95
- ## === When the server is configured to restore previous sessions (default):
96
- ## If the user accesses the same page using the same browser (in different
97
- ## tabs or windows), only the most recently restored one is valid, while
98
- ## the previous ones are immediately invalidated.
99
- ## If your application is intended to support several sessions per browser,
100
- ## enable session cloning in the configuration file.
101
- ##
102
- ## === When the server is configured to restore and clone previous sessions:
103
- ## When sessions are cloned, the previous session is not invalidated and
104
- ## exists until timing out as a result of the web browser window being closed
105
- ## or client computer losing network connectivity for a certain (configurable)
106
- ## time frame.
107
- ##
108
- ## The +#cloned_target+ method is like +#restore_ses+, but called when
109
- ## the session is a clone of a previous session.
110
- ##
111
- ## The +#cloned_source+ method is called on the next request of the previous
112
- ## session after it has been cloned.
113
- ##
114
- ## == Server event methods
115
- ## Extend the +#init+ method to invoke constructor functionality that
116
- ## depends on the plugin to be constructed and registered as a part of
117
- ## the system.
118
- ##
119
- ## Extend the +#open+, +#flush+ and +#close+ methods to open, flush and close
120
- ## streams or other similar functionality.
121
- ##
122
- ## == Data handling
123
- ## The data exchange system exists to support bi-directional
124
- ## data synchronization between the browser and the plugin. The values
125
- ## are stored in the session as +HValue+ instances.
126
- ##
127
- ## Values support Hashes, Arrays, Strings, Numbers, Booleans and
128
- ## combinations of them. The data is automatically converted between
129
- ## ruby objects (server) and json objects (client).
130
- ##
131
- ## Each instance may be bound to plugin methods that are used as
132
- ## value change notification responders.
133
- ##
134
- ## When a method is bound to the value, the method is called as an
135
- ## event notification whenever the client has changed the value and
136
- ## synchronizes it to the server. The responders act as validators
137
- ## by default.
138
- ##
139
- ## Values are also bound in the client to classes implementing the
140
- ## HValueResponder interface, like any derivate of HControl. See the
141
- ## client documentation for instructions about using them.
142
- ##
143
- ## To define a value responder method, it needs to respond to exactly
144
- ## two parameters: the +Message+ instance +msg+ and the HValue object
145
- ## (in that order). The method's return value must be either +true+
146
- ## or +false+. When the method returns +false+, the change is discarded
147
- ## and the previously server-set value is sent back to the client.
148
- ##
149
- ## A minimal value responder method is defined like this:
150
- ##
151
- ## def my_value_responder( msg, my_value )
152
- ## return true
153
- ## end
154
- ##
155
- ## To access the content of the value, use the +HValue#data+ attribute.
156
- ##
157
- ## def int_between_100_and_200( msg, value )
158
- ## data = value.data.to_i
159
- ## return ( data >= 100 and data <= 200 )
160
- ## end
161
- ##
162
- ## To change the content of the value, use the +HValue#set+ method.
163
- ##
164
- ## def int_between_100_and_200( msg, value )
165
- ## data = value.data.to_i
166
- ## value.set( msg, 100 ) if data < 100
167
- ## value.set( msg, 200 ) if data > 200
168
- ## return true
169
- ## end
170
- ##
171
- ## == Defining values
172
- ## The simplest and recommended way of defining the values is to define
173
- ## the value configuration file +values.yaml+. Its configuration is then
174
- ## applied to sessions automatically.
175
- ##
176
- ##
177
- ## === Syntax reference of the contents of a +values.yaml+ file:
178
- ##
179
- ## # The name of the value (:value_name).
180
- ## # A hash key in the yaml syntax
181
- ## :value_name:
182
- ##
183
- ## # All of these keys are optional!
184
- ##
185
- ## # Default value, a string "Foo" here.
186
- ## # Defaults to 0
187
- ## :value: Foo
188
- ##
189
- ## # A plugin method to call to define the default value
190
- ## # instead of the one defined in :value
191
- ## :value_call:
192
- ## :plugin: plugin_name # defaults to the plugin where defined
193
- ##
194
- ## # Mandatory; name of the method to call
195
- ## :method: method_name
196
- ##
197
- ## # Optional, list of parameter values for the :method
198
- ## :args:
199
- ## # three parameters: 1, 'foo', 3
200
- ## - 1
201
- ## - foo
202
- ## - 3
203
- ##
204
- ## # When false, doesn't pass the msg as the first parameter.
205
- ## # Defaults to true
206
- ## :uses_msg: true
207
- ##
208
- ## # Restore the default, when the session is restored; defaults to true
209
- ## :restore_default: false
210
- ##
211
- ## # List of value responder methods to bind.
212
- ## :responders:
213
- ## -
214
- ## # name of plugin to call, defaults to the plugin where defined:
215
- ## :plugin: plugin_name
216
- ##
217
- ## # mandatory, name of the method to call
218
- ## :method: method_name
219
- ##
220
- ## # Another responder, this one using the same plugin where defined:
221
- ## - :method: another_method
222
- ##
223
- ## # Another value, this one just defining the defaults
224
- ## # by supplying an empty Hash:
225
- ## # (value: 0, default restored, no responders or calls)
226
- ## :value_with_defaults: {}
227
- ##
228
- ## # This value defines a Number (123) and doesn't restore
229
- ## # the default, when restoring the session.
230
- ## :one_two_three:
231
- ## :value: 123
232
- ## :restore_default: false
233
- ##
234
- ## # This value gets a random string and specifies a responder,
235
- ## # that ensures it's unique, if changed in the client.
236
- ## :random_unique_string:
237
- ## :value_call:
238
- ## :method: get_unique_random_string
239
- ## :uses_msg: false
240
- ## :responders:
241
- ## - :method: ensure_unique_random_string
242
- ##
243
- ## = Examples
244
- ## More examples are available in the repository;
245
- ## http://svn.rsence.org/contrib/plugins
246
- ## ..as well as the standard "main" plugin in the "plugins" directory.
247
- ##
248
- ##
249
- ## == A minimal Plugin bundle
250
- ## The minimal active plugin bundle (named "name_of_plugin")
251
- ## is defined like this:
252
- ##
253
- ## [dir] name_of_plugin
254
- ## |
255
- ## +---[file] name_of_plugin.rb
256
- ##
257
- ## This sample Plugin doesn't do anything except construct itself and
258
- ## respond as 'name_of_plugin'.
259
- ##
260
- ## Plugin.new.register('name_of_plugin')
261
- ##
262
- ## However, this is not very useful in itself, so you'll need to extend
263
- ## its functionality to do anything useful.
264
- ##
265
- ## == A simple Plugin extension
266
- ## This plugin logs session events to the logs/session_log file.
267
- ##
268
- ## [dir] ses_logger
269
- ## |
270
- ## +---[file] ses_logger.rb
271
- ## |
272
- ## +---[dir] logs
273
- ## |
274
- ## +---[file] session_log
275
- ##
276
- ## == Contents of "ses_logger.rb"
277
- ##
278
- ## class SessionLogger < Plugin
279
- ## def init
280
- ## super
281
- ## @logfile = false
282
- ## end
283
- ## def open
284
- ## log_path = bundle_path( 'session_log', 'logs' )
285
- ## @logfile = File.open( log_path, 'a' )
286
- ## end
287
- ## def close
288
- ## @logfile.close if @logfile
289
- ## @logfile = false
290
- ## end
291
- ## def flush
292
- ## @logfile.flush if @logfile
293
- ## end
294
- ## def init_ses( msg )
295
- ## super
296
- ## @logfile.write( "#{Time.new} -- Session id #{msg.ses_id} was created.\n" )
297
- ## end
298
- ## def restore_ses( msg )
299
- ## super
300
- ## @logfile.write( "#{Time.new} -- Session id #{msg.ses_id} was restored.\n" )
301
- ## end
302
- ## def idle( msg )
303
- ## @logfile.write( "#{Time.new} -- Client of session id #{msg.ses_id} connected.\n" )
304
- ## end
305
- ## end
306
- ## SessionLogger.new.register( 'ses_logger' )
307
- ##
308
- class PluginTemplate
309
- include PluginUtil
13
+ # The Plugin__ is actually available as +Plugin+ from plugin bundles using the {RSence::Plugins::Plugin} class mimic method.
14
+ #
15
+ # The Plugin class is the base class for extending server logic. A single Plugin instance serves the requests of all sessions, which makes them very cpu and memory efficient compared to systems, where the server classes are constructed and destructed for each request.
16
+ #
17
+ # Plugins are designed to be contained in a plugin directory bundle and to be loaded by the main {PluginManager}, which is also responsible for delegating the events and other calls throughout the system.
18
+ #
19
+ #
20
+ # == Anatomy of a plugin bundle
21
+ #
22
+ # First read about the {file:PluginBundles Plugin Bundles}.
23
+ #
24
+ # The plugin bundle contains all data needed to run the plugin. Design your plugin without any hard-coded paths, remember that it's intended to be installed by moving or copying the whole plugin into one of the "plugins" directories. Use {#bundle_path} to construct paths.
25
+ #
26
+ # The {PluginManager} looks for these bundles and evaluates them into an anonymous +Module+ namespace. The contents of the ruby source file is then responsible for including its libraries, constructing an instance of itself and registering itself as a part of the system.
27
+ #
28
+ # Use the {GUIPlugin__ GUIPlugin} class for plugins that handle user interfaces. Use the {Plugin__ Plugin} class for plugin bundles that provide supplemental functionality, value responders and other utilities that supplement the user interface.
29
+ #
30
+ # = Extension hooks for server events
31
+ # These methods are provided as the basic server event hooks:
32
+ # * {#init +#init+} -- Use instead of +initialize+
33
+ # * {#open +#open+} -- Extend to open objects
34
+ # * {#flush +#flush+} -- Extend to write the state and to flush buffers
35
+ # * {#close +#close+} -- Extend to close objects
36
+ #
37
+ # = Extension hooks for session events
38
+ # * {#idle +#idle+} -- Extend to implement logic for each client data synchronization and "idle poll" request.
39
+ # * {#init_ses +#init_ses+} -- Extend to implement logic when a new session is created. A new session is created, when a user enters accesses the server the first time, or the first time after the previous session expired.
40
+ # * {#restore_ses +#restore_ses+} -- Extend to implement logic when an old session is restored. A session is restored, when the user returns to the page or reloads the page before the session is expired.
41
+ #
42
+ # == Extension hooks for session events, If the server is configured to restore and clone previous sessions (default):
43
+ # When sessions are cloned, the previous session is not invalidated and exists until timing out as a result of the web browser window being closed or client computer losing network connectivity for a certain (configurable) time frame.
44
+ # * {#cloned_target +#cloned_target+} -- Extend to implement logic in the request when session is a clone of the original session.
45
+ # * {#cloned_source +#cloned_source+} -- Extend to implement logic in the next request of the original session after it has been cloned.
46
+ #
47
+ # == If the server is configured to not clone sessions:
48
+ # When the user accesses the same page using the same browser (in different tabs or windows), only the most recently restored one is valid, while the previous ones are immediately invalidated. This is a more secure mode of operation, but has several drawback to usability, so it's not enabled by default.
49
+ #
50
+ # = Utility methods
51
+ # These are general utility methods not intended to be extended.
52
+ # * {#get_ses +#get_ses+} Returns the bundle-specific session Hash.
53
+ # * {#file_read +#file_read+} Use to read files
54
+ # * {#yaml_read +#yaml_read+} Use to read yaml data
55
+ # * {#file_write +#file_write+} Use to write files
56
+ # * {#bundle_path +#bundle_path+} Use for plugin bundle -specific paths
57
+ # * {#httime +#httime+} Use for HTTP date/time
58
+ #
59
+ # = Client-support
60
+ # These methods are intended for server-client interaction. Namely, commanding the client.
61
+ # * {#read_js +#read_js+} Returns a javascript source file.
62
+ # * {#read_js_once +#read_js_once+} Returns a javascript source file once per session.
63
+ # * {#values_js +#values_js+} Returns a javascript source snippet containing references to values.
64
+ # * {#include_js +#include_js+} Tells the client to load a library package.
65
+ #
66
+ # = See also
67
+ # * {file:PluginBundles Plugin Bundles} -- General information about the plugin bundle system
68
+ # * {file:Values Values} -- General information about the value exchange system
69
+ # * {file:PluginBundleInfo Plugin Bundle +info.yaml+ files} -- General information about the meta-information files.
70
+ # * {PluginBase PluginBase} -- The PluginBase module
71
+ # * {Servlet__ Servlet} -- The Servlet base class
72
+ # * {GUIPlugin__ GUIPlugin} -- The GUIPlugin base class
73
+ class Plugin__
74
+
75
+
76
+ include PluginBase
77
+
78
+ # @private Class type identifier for the PluginManager.
79
+ # @return [:Plugin]
310
80
  def self.bundle_type; :Plugin; end
311
81
 
312
- # The +names+ is a list of (usually just one) names the plugin is registered under.
313
- attr_reader :name, :path, :info, :inited
82
+ # @return [Symbol] The name of the plugin bundle
83
+ attr_reader :name
84
+
85
+ # @return [String] The absolute path of the plugin bundle.
86
+ attr_reader :path
87
+
88
+ # @return [Hash] The {file:PluginBundleInfo meta-information} of the plugin bundle.
89
+ attr_reader :info
90
+
91
+ # @private State of the plugin.
92
+ attr_reader :inited
314
93
 
315
- # The constructor should not take any parameters. In most cases, it's better
316
- # to extend the +#init+ method, because it's called after the plugin is set up.
94
+ # @private The constructor should not be accessed from anywhere else than the PluginManager, which does it automatically.
317
95
  def initialize( bundle_name, bundle_info, bundle_path, plugin_manager )
318
96
  @inited = false
319
97
  @info = bundle_info
@@ -324,58 +102,69 @@ module ::RSence
324
102
  end
325
103
 
326
104
  # Extend this method to do any initial tasks before other methods are called.
327
- # By default init_values is called to load the +values.yaml+ configuration file.
105
+ #
106
+ # By default {#init_values} is called to load the {file:Values +values.yaml+} configuration file.
107
+ #
108
+ # @return [nil]
109
+ #
110
+ # @see PluginBase#init
328
111
  def init
329
112
  @values = init_values
330
113
  end
331
114
 
332
115
  # Extend this method to do any tasks every time the client makes a request.
116
+ #
117
+ # @param [Message] msg The message is supplied by the system.
118
+ #
119
+ # @return [nil]
333
120
  def idle( msg )
334
121
  end
335
122
 
336
123
  # Extend this method to invoke actions, when a new session is created.
337
- # By default +#init_ses_values+ is called to initialize values defined in the
338
- # +values.yaml+ configuration file.
124
+ #
125
+ # By default {#init_ses_values} is called to initialize values defined in the {file:Values +values.yaml+} configuration file.
126
+ #
127
+ # @param [Message] msg The message is supplied by the system.
128
+ #
129
+ # @return [nil]
339
130
  def init_ses( msg )
340
131
  init_ses_values( msg )
341
132
  end
342
133
 
343
134
  # Extend this method to invoke actions, when a previous session is restored.
344
- # By default +#restore_ses_values+ is called to perform actions on values as
345
- # defined in the +values.yaml+ configuration file.
135
+ #
136
+ # By default +#restore_ses_values+ is called to perform actions on values as defined in the {file:Values +values.yaml+} configuration file.
137
+ #
138
+ # @param [Message] msg The message is supplied by the system.
139
+ #
140
+ # @return [nil]
346
141
  def restore_ses( msg )
347
142
  restore_ses_values( msg )
348
143
  end
349
144
 
350
- # Extend this method to invoke actions, when the session
351
- # is a clone of another session. It's called once, just
352
- # before +#restore_ses+ is called.
145
+ # Extend this method to invoke actions, when the session is a clone of another session. It's called once, just before {#restore_ses} is called.
353
146
  #
354
- # A session is cloned, when a user opens a another browser
355
- # window or tab, while the previous session is still active.
147
+ # A session is cloned, when a user opens a another browser window or tab, while the previous session is still active.
356
148
  #
357
- # The +source_ses+ is the actual previous session object, which
358
- # was used as the source of the clone.
149
+ # @param [Message] msg The message is supplied by the system.
150
+ # @param [Hash] source_session The actual previous session object, which was used as the source of the clone.
151
+ #
152
+ # @return [nil]
359
153
  def cloned_target( msg, source_session )
360
154
  end
361
155
 
362
- # Extend this method to invoke actions, when the session
363
- # has been cloned to another session. It's called once, just
364
- # before +#restore_ses+ is called on the first request after
365
- # the cloning happened.
156
+ # Extend this method to invoke actions, when the session has been cloned to another session. It's called once, just before {#restore_ses} is called on the first request after the cloning happened.
157
+ #
158
+ # A session is cloned, when a user opens a another browser window or tab, while the previous session is still active.
366
159
  #
367
- # A session is cloned, when a user opens a another browser
368
- # window or tab, while the previous session is still active.
160
+ # @param [Message] msg The message is supplied by the system.
161
+ # @param [Hash] target_session The actual cloned session object, which is a copy of the current session.
369
162
  #
370
- # The +target_ses+ is the actual cloned session object, which
371
- # is a copy of the current session.
163
+ # @return [nil]
372
164
  def cloned_source( msg, target_sessions )
373
165
  end
374
166
 
375
- # This method must be called to register the plugin instance
376
- # into the system. Otherwise, it's subject to destruction
377
- # and garbage collection. Use the +name+ parameter to
378
- # give the (unique) name of your plugin.
167
+ # @private This method must be called to register the plugin instance into the system. Otherwise, it's subject to destruction and garbage collection. Use the +name+ parameter to give the (unique) name of your plugin.
379
168
  def register( name=false )
380
169
  if @inited
381
170
  @plugins.register_alias( @name, name )
@@ -390,28 +179,13 @@ module ::RSence
390
179
  end
391
180
  end
392
181
 
393
- private
394
-
395
- # This method looks looks for a file called "values.yaml"
396
- # in the plugin's bundle directory
397
- #.
398
- # If this file is found, it loads it for initial value definitions.
399
- #
400
- # These definitions are accessible as the +@values+ attribute.
401
- def init_values
402
- values_path = bundle_path( 'values.yaml' )
403
- return yaml_read( values_path )
404
- end
405
-
406
- # Returns all the names your plugin respond to.
407
- # def name
408
- # return @names.first
409
- # end
410
-
411
-
412
- # Returns or creates a new session hash for the plugin.
182
+ # This method returns (or creates and returns) the entry in the session based on the name your plugin is registered as. It's advised to use this call instead of manually managing {Message#session msg#session} in most cases.
413
183
  #
414
184
  # Uses the first name registered for the plugin and converts it to a symbol.
185
+ #
186
+ # @param [Message] msg The message is supplied by the system.
187
+ #
188
+ # @return [Hash] Plugin-specific session hash
415
189
  def get_ses( msg )
416
190
  name_sym = name.to_sym
417
191
  unless msg.session.has_key?( name_sym )
@@ -420,21 +194,23 @@ module ::RSence
420
194
  return msg.session[ name_sym ]
421
195
  end
422
196
 
423
- # Returns the source code of the javascript file +name+ in the 'js'
424
- # subdirectory of the plugin bundle.
197
+ # Returns the source code of the javascript file +js_name+ in the 'js' subdirectory of the plugin bundle. Use it to send raw javascript command code to the client. Use {#read_js_once} for libraries.
198
+ #
199
+ # @param [String] js_name Javascript source file name without the '.js' suffix.
200
+ #
201
+ # @return [String] The source of the file.
202
+ # @return [false] Returns false, when file was not found.
425
203
  def read_js( js_name )
426
204
  file_read( bundle_path( js_name, 'js', '.js' ) )
427
205
  end
428
-
429
- # Deprecated name of +#read_js+
430
- alias require_js read_js
431
-
432
- # Like +#read_js+, but reads the file only once per session.
206
+
207
+ # Like {#read_js}, but reads the file only once per session. Use for inclusion of custom library code.
433
208
  #
434
- # Returns the contents of the file on the first call,
435
- # an empty string on the subsequent calls.
209
+ # @param [Message] msg The message is supplied by the system.
210
+ # @param [String] js_name Javascript source file name without the '.js' suffix.
436
211
  #
437
- # Returns false otherwise.
212
+ # @return [String] The source of the file on the first call in a session, or an empty string on the subsequent calls.
213
+ # @return [false] Returns false, when file was not found.
438
214
  def read_js_once( msg, js_name )
439
215
  ses = msg.session
440
216
  if not ses.has_key?(:deps)
@@ -448,13 +224,72 @@ module ::RSence
448
224
  return ''
449
225
  end
450
226
  end
451
-
452
- # Deprecated name of +#read_js_once+
453
- alias require_js_once read_js_once
454
-
455
- # Creates a new instance of HValue, assigns it as +value_name+ into the
456
- # session and uses the +value_properties+ Hash to define the default
457
- # value and value responders.
227
+
228
+ # Extracts +HValue+ references as javascript from the session Hash.
229
+ #
230
+ # @param [Message] msg The message is supplied by the system.
231
+ # @param [Hash] ses Used for supplying a Hash with the {HValue} instances. It's optional and defaults to the current plugin node in the active session.
232
+ #
233
+ # @return [String] A string representing a javascript object similar to the ruby Hash +ses+.
234
+ #
235
+ # @example
236
+ # values_js( msg, get_ses(msg) )
237
+ def values_js( msg, ses=false )
238
+ # backwards-compatible with pre-1.3 behavior
239
+ ses = msg if msg.class == Hash
240
+ # gets the session automatically, if false
241
+ ses = get_ses( msg ) unless ses
242
+ js_references = []
243
+ ses.each_key do |key_name|
244
+ if ses[key_name].class == HValue
245
+ js_references.push( "#{key_name.to_s}:HVM.values['#{ses[key_name].val_id}']" )
246
+ end
247
+ end
248
+ return "{#{js_references.join(',')}}"
249
+ end
250
+
251
+
252
+ # Tells the js client framework to load a list of pre-packaged client libraries.
253
+ #
254
+ # It keeps track of what's loaded, so nothing library loaded twice.
255
+ #
256
+ # @param [Message] msg The message is supplied by the system.
257
+ # @param [Array] dependencies A list of package names.
258
+ #
259
+ # @return [nil]
260
+ #
261
+ # @example
262
+ # include_js( msg, [ 'default_theme', 'controls' ] )
263
+ #
264
+ def include_js( msg, dependencies=[] )
265
+ ses = msg.session
266
+ # check, if the session has a dependency array
267
+ if not ses.has_key?( :deps )
268
+ # make an array of dependencies for this session, if not already done
269
+ ses[:deps] = []
270
+ end
271
+ dependencies = [dependencies] if dependencies.class == String
272
+ # Check the required dependencies until everything is loaded.
273
+ dependencies.each do |dependency|
274
+ unless ses[:deps].include?( dependency )
275
+ ses[:deps].push( dependency )
276
+ msg.reply(%{jsLoader.load("#{dependency}");})
277
+ end
278
+ end
279
+ end
280
+
281
+
282
+ # @private This method looks looks for a file called "values.yaml" in the plugin's bundle directory.
283
+ # If this file is found, it loads it for initial value definitions.
284
+ # These definitions are accessible as the +@values+ attribute.
285
+ def init_values
286
+ values_path = bundle_path( 'values.yaml' )
287
+ return yaml_read( values_path )
288
+ end
289
+
290
+ # @private Creates a new instance of HValue, assigns it as +value_name+ into the
291
+ # session and uses the +value_properties+ Hash to define the default
292
+ # value and value responders.
458
293
  #
459
294
  # This method is invoked automatically, when handling the properties
460
295
  # of the +values.yaml+ configuration file of a new session.
@@ -512,18 +347,18 @@ module ::RSence
512
347
  end
513
348
  end
514
349
  end
515
-
516
- # Initializes session values, if the contents of the +values.yaml+
517
- # file is defined in the bundle directory and loaded in +#init_values+.
350
+
351
+ # @private Initializes session values, if the contents of the +values.yaml+
352
+ # file is defined in the bundle directory and loaded in +#init_values+.
518
353
  def init_ses_values( msg )
519
354
  return unless @values
520
355
  @values.each do | value_name, value_properties |
521
356
  init_ses_value( msg, value_name, value_properties )
522
357
  end
523
358
  end
524
-
525
- # Returns a value based on the :method and :plugin members of the
526
- # +value_call+ hash.
359
+
360
+ # @private Returns a value based on the :method and :plugin members of the
361
+ # +value_call+ hash.
527
362
  #
528
363
  # The call is made via msg.run if the method is not defined in
529
364
  # the local plugin bundle.
@@ -573,8 +408,8 @@ module ::RSence
573
408
  end
574
409
  end
575
410
  end
576
-
577
- # Restores session values to default, unless specified otherwise.
411
+
412
+ # @private Restores session values to default, unless specified otherwise.
578
413
  #
579
414
  # Called from +#restore_ses+
580
415
  def restore_ses_values( msg )
@@ -597,61 +432,6 @@ module ::RSence
597
432
  end
598
433
  end
599
434
  end
600
-
601
- # Extracts +HValue+ references as javascript from the session Hash.
602
- # The +ses+ parameter is used for supplying a hash with the +HValue+
603
- # instances. It's optional and defaults to the current plugin node in
604
- # the active session.
605
- #
606
- # The return value is a string representing a js object similar to
607
- # the ruby Hash +ses+.
608
- #
609
- # Sample usage:
610
- #
611
- # values_js( msg, msg.session[:main] )
612
- #
613
- def values_js( msg, ses=false )
614
- # backwards-compatible with pre-1.3 behaviour
615
- ses = msg if msg.class == Hash
616
- # gets the session automatically, if false
617
- ses = get_ses( msg ) unless ses
618
- js_references = []
619
- ses.each_key do |key_name|
620
- if ses[key_name].class == HValue
621
- js_references.push( "#{key_name.to_s}:HVM.values['#{ses[key_name].val_id}']" )
622
- end
623
- end
624
- return "{#{js_references.join(',')}}"
625
- end
626
-
627
- # Deprecated name of +#values_js+
628
- alias extract_hvalues_from_hash values_js
629
-
630
- # Tells the js client framework to load a list of dependency packages.
631
- # It keeps track of what's loaded, so nothing library loaded twice.
632
- #
633
- # The +dependencies+ parameter is an Array of dependencies.
634
- #
635
- # Sample usage:
636
- #
637
- # include_js( msg, [ 'default_theme', 'controls', 'lists', 'datetime' ] )
638
- #
639
- def include_js( msg, dependencies=[] )
640
- ses = msg.session
641
- # check, if the session has a dependency array
642
- if not ses.has_key?( :deps )
643
- # make an array of dependencies for this session, if not already done
644
- ses[:deps] = []
645
- end
646
- dependencies = [dependencies] if dependencies.class == String
647
- # Check the required dependencies until everything is loaded.
648
- dependencies.each do |dependency|
649
- unless ses[:deps].include?( dependency )
650
- ses[:deps].push( dependency )
651
- msg.reply(%{jsLoader.load("#{dependency}");})
652
- end
653
- end
654
- end
655
435
  end
656
436
  end
657
437
  end