lib/plugins/plugin.rb in rsence-2.0.0.6.pre vs lib/plugins/plugin.rb in rsence-2.0.0.7.pre
- old
+ new
@@ -4,304 +4,309 @@
#
# You should have received a copy of the GNU General Public License along
# with this software package. If not, contact licensing@riassence.com
##
-## = Abstract
-## 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.
-##
-## Plugins are designed to be contained in a plugin directory bundle and
-## to be loaded by the +PluginManager+, which is also responsible for
-## delegating the events and other calls throughout the system.
-##
-## == Anatomy of a plugin bundle
-## 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 deployed by "dropping" the whole plugin into one of the server's
-## plugins directories.
-##
-## The +PluginManager+ looks for such bundles and evaluates them into an
-## anonymous +Module+ namespace. The content 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.
-##
-## It's advised to use the +GUIPlugin+ class for plugins that handle
-## user interfaces. Usage of the +Plugin+ bundle is advised to use
-## for plugins that provide extra functionality, value responders
-## and other utilities that supplement the user interface.
-##
-## You must call the +#register+ method after the class is constructed.
-## Otherwise, the class is not connected to the system and just discarded
-## and then garbage collected.
-##
-## == Messages
-## As a side effect of having single instances of plugins serve the requests
-## of all sessions, the request/response/session messaging is implemented
-## as messaging objects. These objects contain or delegate all the necessary
-## hooks required by the complete request/response cycle.
-##
-## The naming convention of the +Message+ instance is +msg+ and it's
-## given as the first parameter of methods needing it.
-##
-## Use +msg.ses_id+ to identify the session's serial number and +msg.user_id+
-## to identify the user's identity.
-##
-## Use the +msg.session+ +Hash+ to store any persistent data
-## associated with the user's session, preferably using the name of the
-## plugin or its registered name as the primary key entry in the Hash.
-## The session data is persistent; it's stored in the session database
-## by +SessionStorage+ automatically.
-##
-## The +msg+ instance also provides access to the +Request+ and +Response+
-## objects as +msg.request+ and +msg.response+, respectively.
-##
-## Use the +msg.run+ method to call other plugins.
-##
-## To append js source code to be evaluated in the client, use the +msg.reply+
-## call. The +msg.console+ call appends messages to the browser's js console.
-##
-##
-## == Session -related event methods
-## The +#get_ses+ 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 +msg.session+ in most cases.
-##
-## The +#idle+ method is called each time a client performs a data
-## synchronization or "idle poll" request.
-##
-## The +#init_ses+ method is called once in the same request 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 is
-## expired.
-##
-## The +#init_ui+ method is called by the "main" plugin after the client has
-## booted successfully. The +GUIPlugin+ class extends this method to
-## automatically load and initialize the user interface from a data structure.
-##
-## The +#restore_ses+ method is called once in the same request 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.
-##
-## === When the server is configured to restore previous sessions (default):
-## If 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.
-## If your application is intended to support several sessions per browser,
-## enable session cloning in the configuration file.
-##
-## === When the server is configured to restore and clone previous sessions:
-## 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.
-##
-## The +#cloned_target+ method is like +#restore_ses+, but called when
-## the session is a clone of a previous session.
-##
-## The +#cloned_source+ method is called on the next request of the previous
-## session after it has been cloned.
-##
-## == Server event methods
-## Extend the +#init+ method to invoke constructor functionality that
-## depends on the plugin to be constructed and registered as a part of
-## the system.
-##
-## Extend the +#open+, +#flush+ and +#close+ methods to open, flush and close
-## streams or other similar functionality.
-##
-## == Data handling
-## The data exchange system exists to support bi-directional
-## data synchronization between the browser and the plugin. The values
-## are stored in the session as +HValue+ instances.
-##
-## Values support Hashes, Arrays, Strings, Numbers, Booleans and
-## combinations of them. The data is automatically converted between
-## ruby objects (server) and json objects (client).
-##
-## Each instance may be bound to plugin methods that are used as
-## value change notification responders.
-##
-## When a method is bound to the value, the method is called as an
-## event notification whenever the client has changed the value and
-## synchronizes it to the server. The responders act as validators
-## by default.
-##
-## Values are also bound in the client to classes implementing the
-## HValueResponder interface, like any derivate of HControl. See the
-## client documentation for instructions about using them.
-##
-## To define a value responder method, it needs to respond to exactly
-## two parameters: the +Message+ instance +msg+ and the HValue object
-## (in that order). The method's return value must be either +true+
-## or +false+. When the method returns +false+, the change is discarded
-## and the previously server-set value is sent back to the client.
-##
-## A minimal value responder method is defined like this:
-##
-## def my_value_responder( msg, my_value )
-## return true
-## end
-##
-## To access the content of the value, use the +HValue#data+ attribute.
-##
-## def int_between_100_and_200( msg, value )
-## data = value.data.to_i
-## return ( data >= 100 and data <= 200 )
-## end
-##
-## To change the content of the value, use the +HValue#set+ method.
-##
-## def int_between_100_and_200( msg, value )
-## data = value.data.to_i
-## value.set( msg, 100 ) if data < 100
-## value.set( msg, 200 ) if data > 200
-## return true
-## end
-##
-## == Defining values
-## The simplest and recommended way of defining the values is to define
-## the value configuration file +values.yaml+. Its configuration is then
-## applied to sessions automatically.
-##
-##
-## === Syntax reference of the contents of a +values.yaml+ file:
-##
-## # The name of the value (:value_name).
-## # A hash key in the yaml syntax
-## :value_name:
-##
-## # All of these keys are optional!
-##
-## # Default value, a string "Foo" here.
-## # Defaults to 0
-## :value: Foo
-##
-## # A plugin method to call to define the default value
-## # instead of the one defined in :value
-## :value_call:
-## :plugin: plugin_name # defaults to the plugin where defined
-##
-## # Mandatory; name of the method to call
-## :method: method_name
-##
-## # Optional, list of parameter values for the :method
-## :args:
-## # three parameters: 1, 'foo', 3
-## - 1
-## - foo
-## - 3
-##
-## # When false, doesn't pass the msg as the first parameter.
-## # Defaults to true
-## :uses_msg: true
-##
-## # Restore the default, when the session is restored; defaults to false
-## :restore_default: false
-##
-## # List of value responder methods to bind.
-## :responders:
-## -
-## # name of plugin to call, defaults to the plugin where defined:
-## :plugin: plugin_name
-##
-## # mandatory, name of the method to call
-## :method: method_name
-##
-## # Another responder, this one using the same plugin where defined:
-## - :method: another_method
-##
-## # Another value, this one just defining the defaults
-## # by supplying an empty Hash:
-## # (value: 0, default restored, no responders or calls)
-## :value_with_defaults: {}
-##
-## # This value defines a Number (123) and doesn't restore
-## # the default, when restoring the session.
-## :one_two_three:
-## :value: 123
-## :restore_default: false
-##
-## # This value gets a random string and specifies a responder,
-## # that ensures it's unique, if changed in the client.
-## :random_unique_string:
-## :value_call:
-## :method: get_unique_random_string
-## :uses_msg: false
-## :responders:
-## - :method: ensure_unique_random_string
-##
-## = Examples
-## More examples are available in the repository;
-## http://svn.rsence.org/contrib/plugins
-## ..as well as the standard "main" plugin in the "plugins" directory.
-##
-##
-## == A minimal Plugin bundle
-## The minimal active plugin bundle (named "name_of_plugin")
-## is defined like this:
-##
-## [dir] name_of_plugin
-## |
-## +---[file] name_of_plugin.rb
-##
-## This sample Plugin doesn't do anything except construct itself and
-## respond as 'name_of_plugin'.
-##
-## Plugin.new.register('name_of_plugin')
-##
-## However, this is not very useful in itself, so you'll need to extend
-## its functionality to do anything useful.
-##
-## == A simple Plugin extension
-## This plugin logs session events to the logs/session_log file.
-##
-## [dir] ses_logger
-## |
-## +---[file] ses_logger.rb
-## |
-## +---[dir] logs
-## |
-## +---[file] session_log
-##
-## == Contents of "ses_logger.rb"
-##
-## class SessionLogger < Plugin
-## def init
-## super
-## @logfile = false
-## end
-## def open
-## log_path = compose_plugin_path( 'session_log', 'logs' )
-## @logfile = File.open( log_path, 'a' )
-## end
-## def close
-## @logfile.close if @logfile
-## @logfile = false
-## end
-## def flush
-## @logfile.flush if @logfile
-## end
-## def init_ses( msg )
-## super
-## @logfile.write( "#{Time.new} -- Session id #{msg.ses_id} was created.\n" )
-## end
-## def restore_ses( msg )
-## super
-## @logfile.write( "#{Time.new} -- Session id #{msg.ses_id} was restored.\n" )
-## end
-## def idle( msg )
-## @logfile.write( "#{Time.new} -- Client of session id #{msg.ses_id} connected.\n" )
-## end
-## end
-## SessionLogger.new.register( 'ses_logger' )
-##
module ::RSence
module Plugins
+
+ ## = Abstract
+ ##
+ ## The PluginTemplate is used to create a Plugin mimic class in
+ ## Plugins.bundle_loader using the Plugins.PluginWrapper mimic.
+ ##
+ ## 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.
+ ##
+ ## Plugins are designed to be contained in a plugin directory bundle and
+ ## to be loaded by the +PluginManager+, which is also responsible for
+ ## delegating the events and other calls throughout the system.
+ ##
+ ## == Anatomy of a plugin bundle
+ ## 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 deployed by "dropping" the whole plugin into one of the server's
+ ## plugins directories.
+ ##
+ ## The +PluginManager+ looks for such bundles and evaluates them into an
+ ## anonymous +Module+ namespace. The content 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.
+ ##
+ ## It's advised to use the +GUIPlugin+ class for plugins that handle
+ ## user interfaces. Usage of the +Plugin+ bundle is advised to use
+ ## for plugins that provide extra functionality, value responders
+ ## and other utilities that supplement the user interface.
+ ##
+ ## You must call the +#register+ method after the class is constructed.
+ ## Otherwise, the class is not connected to the system and just discarded
+ ## and then garbage collected.
+ ##
+ ## == Messages
+ ## As a side effect of having single instances of plugins serve the requests
+ ## of all sessions, the request/response/session messaging is implemented
+ ## as messaging objects. These objects contain or delegate all the necessary
+ ## hooks required by the complete request/response cycle.
+ ##
+ ## The naming convention of the +Message+ instance is +msg+ and it's
+ ## given as the first parameter of methods needing it.
+ ##
+ ## Use +msg.ses_id+ to identify the session's serial number and +msg.user_id+
+ ## to identify the user's identity.
+ ##
+ ## Use the +msg.session+ +Hash+ to store any persistent data
+ ## associated with the user's session, preferably using the name of the
+ ## plugin or its registered name as the primary key entry in the Hash.
+ ## The session data is persistent; it's stored in the session database
+ ## by +SessionStorage+ automatically.
+ ##
+ ## The +msg+ instance also provides access to the +Request+ and +Response+
+ ## objects as +msg.request+ and +msg.response+, respectively.
+ ##
+ ## Use the +msg.run+ method to call other plugins.
+ ##
+ ## To append js source code to be evaluated in the client, use the +msg.reply+
+ ## call. The +msg.console+ call appends messages to the browser's js console.
+ ##
+ ##
+ ## == Session -related event methods
+ ## The +#get_ses+ 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 +msg.session+ in most cases.
+ ##
+ ## The +#idle+ method is called each time a client performs a data
+ ## synchronization or "idle poll" request.
+ ##
+ ## The +#init_ses+ method is called once in the same request 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 is
+ ## expired.
+ ##
+ ## The +#init_ui+ method is called by the "main" plugin after the client has
+ ## booted successfully. The +GUIPlugin+ class extends this method to
+ ## automatically load and initialize the user interface from a data structure.
+ ##
+ ## The +#restore_ses+ method is called once in the same request 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.
+ ##
+ ## === When the server is configured to restore previous sessions (default):
+ ## If 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.
+ ## If your application is intended to support several sessions per browser,
+ ## enable session cloning in the configuration file.
+ ##
+ ## === When the server is configured to restore and clone previous sessions:
+ ## 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.
+ ##
+ ## The +#cloned_target+ method is like +#restore_ses+, but called when
+ ## the session is a clone of a previous session.
+ ##
+ ## The +#cloned_source+ method is called on the next request of the previous
+ ## session after it has been cloned.
+ ##
+ ## == Server event methods
+ ## Extend the +#init+ method to invoke constructor functionality that
+ ## depends on the plugin to be constructed and registered as a part of
+ ## the system.
+ ##
+ ## Extend the +#open+, +#flush+ and +#close+ methods to open, flush and close
+ ## streams or other similar functionality.
+ ##
+ ## == Data handling
+ ## The data exchange system exists to support bi-directional
+ ## data synchronization between the browser and the plugin. The values
+ ## are stored in the session as +HValue+ instances.
+ ##
+ ## Values support Hashes, Arrays, Strings, Numbers, Booleans and
+ ## combinations of them. The data is automatically converted between
+ ## ruby objects (server) and json objects (client).
+ ##
+ ## Each instance may be bound to plugin methods that are used as
+ ## value change notification responders.
+ ##
+ ## When a method is bound to the value, the method is called as an
+ ## event notification whenever the client has changed the value and
+ ## synchronizes it to the server. The responders act as validators
+ ## by default.
+ ##
+ ## Values are also bound in the client to classes implementing the
+ ## HValueResponder interface, like any derivate of HControl. See the
+ ## client documentation for instructions about using them.
+ ##
+ ## To define a value responder method, it needs to respond to exactly
+ ## two parameters: the +Message+ instance +msg+ and the HValue object
+ ## (in that order). The method's return value must be either +true+
+ ## or +false+. When the method returns +false+, the change is discarded
+ ## and the previously server-set value is sent back to the client.
+ ##
+ ## A minimal value responder method is defined like this:
+ ##
+ ## def my_value_responder( msg, my_value )
+ ## return true
+ ## end
+ ##
+ ## To access the content of the value, use the +HValue#data+ attribute.
+ ##
+ ## def int_between_100_and_200( msg, value )
+ ## data = value.data.to_i
+ ## return ( data >= 100 and data <= 200 )
+ ## end
+ ##
+ ## To change the content of the value, use the +HValue#set+ method.
+ ##
+ ## def int_between_100_and_200( msg, value )
+ ## data = value.data.to_i
+ ## value.set( msg, 100 ) if data < 100
+ ## value.set( msg, 200 ) if data > 200
+ ## return true
+ ## end
+ ##
+ ## == Defining values
+ ## The simplest and recommended way of defining the values is to define
+ ## the value configuration file +values.yaml+. Its configuration is then
+ ## applied to sessions automatically.
+ ##
+ ##
+ ## === Syntax reference of the contents of a +values.yaml+ file:
+ ##
+ ## # The name of the value (:value_name).
+ ## # A hash key in the yaml syntax
+ ## :value_name:
+ ##
+ ## # All of these keys are optional!
+ ##
+ ## # Default value, a string "Foo" here.
+ ## # Defaults to 0
+ ## :value: Foo
+ ##
+ ## # A plugin method to call to define the default value
+ ## # instead of the one defined in :value
+ ## :value_call:
+ ## :plugin: plugin_name # defaults to the plugin where defined
+ ##
+ ## # Mandatory; name of the method to call
+ ## :method: method_name
+ ##
+ ## # Optional, list of parameter values for the :method
+ ## :args:
+ ## # three parameters: 1, 'foo', 3
+ ## - 1
+ ## - foo
+ ## - 3
+ ##
+ ## # When false, doesn't pass the msg as the first parameter.
+ ## # Defaults to true
+ ## :uses_msg: true
+ ##
+ ## # Restore the default, when the session is restored; defaults to false
+ ## :restore_default: false
+ ##
+ ## # List of value responder methods to bind.
+ ## :responders:
+ ## -
+ ## # name of plugin to call, defaults to the plugin where defined:
+ ## :plugin: plugin_name
+ ##
+ ## # mandatory, name of the method to call
+ ## :method: method_name
+ ##
+ ## # Another responder, this one using the same plugin where defined:
+ ## - :method: another_method
+ ##
+ ## # Another value, this one just defining the defaults
+ ## # by supplying an empty Hash:
+ ## # (value: 0, default restored, no responders or calls)
+ ## :value_with_defaults: {}
+ ##
+ ## # This value defines a Number (123) and doesn't restore
+ ## # the default, when restoring the session.
+ ## :one_two_three:
+ ## :value: 123
+ ## :restore_default: false
+ ##
+ ## # This value gets a random string and specifies a responder,
+ ## # that ensures it's unique, if changed in the client.
+ ## :random_unique_string:
+ ## :value_call:
+ ## :method: get_unique_random_string
+ ## :uses_msg: false
+ ## :responders:
+ ## - :method: ensure_unique_random_string
+ ##
+ ## = Examples
+ ## More examples are available in the repository;
+ ## http://svn.rsence.org/contrib/plugins
+ ## ..as well as the standard "main" plugin in the "plugins" directory.
+ ##
+ ##
+ ## == A minimal Plugin bundle
+ ## The minimal active plugin bundle (named "name_of_plugin")
+ ## is defined like this:
+ ##
+ ## [dir] name_of_plugin
+ ## |
+ ## +---[file] name_of_plugin.rb
+ ##
+ ## This sample Plugin doesn't do anything except construct itself and
+ ## respond as 'name_of_plugin'.
+ ##
+ ## Plugin.new.register('name_of_plugin')
+ ##
+ ## However, this is not very useful in itself, so you'll need to extend
+ ## its functionality to do anything useful.
+ ##
+ ## == A simple Plugin extension
+ ## This plugin logs session events to the logs/session_log file.
+ ##
+ ## [dir] ses_logger
+ ## |
+ ## +---[file] ses_logger.rb
+ ## |
+ ## +---[dir] logs
+ ## |
+ ## +---[file] session_log
+ ##
+ ## == Contents of "ses_logger.rb"
+ ##
+ ## class SessionLogger < Plugin
+ ## def init
+ ## super
+ ## @logfile = false
+ ## end
+ ## def open
+ ## log_path = bundle_path( 'session_log', 'logs' )
+ ## @logfile = File.open( log_path, 'a' )
+ ## end
+ ## def close
+ ## @logfile.close if @logfile
+ ## @logfile = false
+ ## end
+ ## def flush
+ ## @logfile.flush if @logfile
+ ## end
+ ## def init_ses( msg )
+ ## super
+ ## @logfile.write( "#{Time.new} -- Session id #{msg.ses_id} was created.\n" )
+ ## end
+ ## def restore_ses( msg )
+ ## super
+ ## @logfile.write( "#{Time.new} -- Session id #{msg.ses_id} was restored.\n" )
+ ## end
+ ## def idle( msg )
+ ## @logfile.write( "#{Time.new} -- Client of session id #{msg.ses_id} connected.\n" )
+ ## end
+ ## end
+ ## SessionLogger.new.register( 'ses_logger' )
+ ##
class PluginTemplate
include PluginUtil
def self.bundle_type; :Plugin; end
# The +names+ is a list of (usually just one) names the plugin is registered under.
@@ -392,11 +397,11 @@
#.
# If this file is found, it loads it for initial value definitions.
#
# These definitions are accessible as the +@values+ attribute.
def init_values
- values_path = compose_plugin_path( 'values.yaml' )
+ values_path = bundle_path( 'values.yaml' )
return yaml_read( values_path )
end
# Returns all the names your plugin respond to.
# def name
@@ -416,11 +421,11 @@
end
# Returns the source code of the javascript file +name+ in the 'js'
# subdirectory of the plugin bundle.
def read_js( js_name )
- file_read( compose_plugin_path( js_name, 'js', '.js' ) )
+ file_read( bundle_path( js_name, 'js', '.js' ) )
end
# Deprecated name of +#read_js+
alias require_js read_js
@@ -433,10 +438,10 @@
def read_js_once( msg, js_name )
ses = msg.session
if not ses.has_key?(:deps)
ses[:deps] = []
end
- path = compose_plugin_path( js_name, 'js', '.js' )
+ path = bundle_path( js_name, 'js', '.js' )
unless ses[:deps].include?( path )
ses[:deps].push( path )
return file_read( path )
else
return ''