Overview | Index by: file name | procedure name | procedure call | annotation
procedures.tcl (annotations | original source)

#
#    Copyright (C) 2010 Alexandros Stergiakis <alsterg@gmail.com>
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero General Public License as
#    published by the Free Software Foundation, either version 3 of the
#    License, or (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#

#//#
# A library for managing user defined procedures within a session and across sessions.
#
# Note: Registration/unregistration of commands can have security implications.
# These procedures, however, do not enforce any policy with respect to AUTHMODE.
# For the sake of flexibility this security policies are left to be enforced via other means,
# (e.g. MikroConf command visibility).
#//#

namespace eval procedures {
    namespace export register unregister getproc getlist invoke
    namespace import ::session::user_sessions

# Register a previously declared procedure (using the 'proc' Tcl command).
# Newly registered procs will be fully usable within the session that they where defined,
# but on any other concurent session they will not become available.
# Only new sessions will have those new procs present.
# If a procedure is already registered, then it is updated. The Events Session is always
# kept in sync with what is registered/unregistered, so no need to restart that Sesison.
# We cannot do that with user sessions, because parallel user sessions might have
# defined the same name procedure with different body, and we cannot just spontaneously
# overwrite them.
#
# Note: When registering a procedure the state of the procedure at the moment of
# registration is recorded. Therefore when issuing "save" any changes that occured
# since registration will not be saved.
#
# @assume $name follows naming conventions
#
# @param sid Session Id.
# @param name The name of the procedure to register.
# @error
proc register {sid name} {
    variable Procedures
    Session $sid SLAVE

    if {$name ni [thread::send $sid [list $SLAVE invokehidden info procs]]} {
        error "procedure \"$name\" does not exist"
    }
    
    set args [thread::send $sid [list $SLAVE invokehidden info args $name]]
    set body [thread::send $sid [list $SLAVE invokehidden info body $name]]
    
    set Procedures($name) [list $args $body]

    # Sync with Event Session    
    Global EVENT
    thread::send $EVENT [list $EVENT invokehidden proc $name $args $body]
    
    # Synch with all User Sessions
    foreach s [user_sessions] {
        if {$s ne $sid} {
            Session $s SLAVE
            thread::send $s [list $SLAVE invokehidden proc $name $args $body]
        }
    }
}

# Unregister a previously registered procedure. As with "register", changes will
# only take effect in new sessions, and at once on the Events Session. 
#
# @param name the name of the procedure to unregister.
# @error
proc unregister {name} {
    variable Procedures

    if {$name ni [array names Procedures]} {
        error "procedure \"$name\" is not registered"
    }
    
    unset Procedures($name)

    # Sync with Event Session    
    Global EVENT
    thread::send $EVENT [list $EVENT invokehidden rename $name {}]
}

# Get the list of currently registered procedures.
proc getlist {} {
    variable Procedures
    return [array names Procedures]
}

# Retrieve a specific registered procedure.
#
# @param name The name of the procedure to get.
# @return A list with the followng elements of a procedure: Name Args Body
# @error
proc getproc {name} {
    variable Procedures
    
    if {$name ni [array names Procedures]} {
        error "procedure \"$name\" is not registered"
    }
    
    return $Procedures($name)
}

# Call a procedure registered in the Event interpreter.
#
# @param name Name of registered procedure.
# @param args Additional arguments to pass to the procedure.
# @return Whatever returned by the called procedure.
proc invoke {name args} {
    variable Procedures
    
    if {$name ni [array names Procedures]} {
        error "procedure \"$name\" is not registered"
    }
    
    Global EVENT
    Thread::bgsend $EVENT [list $EVENT eval $name {*}$args]
}

namespace ensemble create

} ;# End of Namespace

Overview | Index by: file name | procedure name | procedure call | annotation
File generated 2010-03-13 at 22:28.