This is the new home of the egghelp.org community forum.
All data has been migrated (including user logins/passwords) to a new phpBB version.


For more information, see this announcement post. Click the X in the top right-corner of this box to dismiss this message.

Namespaces

Issues often discussed about Tcl scripting. Check before posting a scripting question.
Post Reply
User avatar
Sir_Fz
Revered One
Posts: 3794
Joined: Sun Apr 27, 2003 3:10 pm
Location: Lebanon
Contact:

Namespaces

Post by Sir_Fz »

Written by BarkerJr. Source: http://www.barkerjr.net/irc/eggdrop/Scr ... spaces.php.
-----------------------------------------------------------------------------------------------------

Why namespaces? If you've ever had issues with global variables or procedures in your script conflicting with other scripts, you need namespaces. Namespaces are the closest that Tcl gets to object oriented programming. They allow the programmer to place variables and procedures inside one neat named-package in Tcl so as not to conflict with anything in the global namespace.


Lets Begin!

Code: Select all

namespace eval MyScript {
  variable response "Hello World!"

  bind pub - "hi" MyScript::respond
  proc respond {nick uhost hand chan text} {
    variable response
    puthelp "PRIVMSG $nick :$response"
    return 1
  }
}
MyScript is the name of the namespace. This will be the prefix used in the name of variables and procedures if they are being called from other namespaces (such as the global). Notice that the bind specifies the full name in the procedure parameter. Another thing to note is that the variable $response resides within the namespace. That means it's not a global variable. So, inside the procedure, it is declared as a variable, rather than global. Note that variables must be declared on separate lines if you have more than one. They cannot be declared in one long line like globals.

Code: Select all

catch MyScript::uninstall
namespace eval MyScript {
  variable response "Hello World!"

  bind pub - "hi" MyScript::respond
  proc respond {nick uhost hand chan text} {
    variable response
    puthelp "PRIVMSG $nick :$response"
    return 1
  }

  bind evnt - prerehash MyScript::uninstall
  proc uninstall {args} {
    unbind pub - "hi" MyScript::respond
    unbind evnt - prerehash MyScript::uninstall
    namespace delete MyScript
  }
}
Rather than forcing users to .restart to uninstall scripts, lets uninstall on rehash. If the user still has the script in the conf, it'll be loaded automatically after the rehash. First of all, binds are not directly associated with the namespace, so they have to be unbound separatly. Then the namespace itself can be deleted. Don't forget to kill any timers that may be in your script here, too. Also, in order to also uninstall/reinstall for users that .tcl source the script, add the top line to call the uninstall procedure. The catch is there so that the bot doesn't crash if the uninstall procedure doesn't exist yet (first run).

Code: Select all

set ns "MyScript"
catch ${ns}::uninstall
namespace eval $ns {
  unset ::ns

  variable response "Hello World!"

  bind pub - "hi" [namespace current]::respond
  proc respond {nick uhost hand chan text} {
    variable response
    puthelp "PRIVMSG $nick :$response"
    return 1
  }

  bind evnt - prerehash [namespace current]::uninstall
  proc uninstall {args} {
    unbind pub - "hi" [namespace current]::respond
    unbind evnt - prerehash [namespace current]::uninstall
    namespace delete [namespace current]
  }
}
If you are worried about the the namespace possibly conflicting with another namespace, do something like this. First, set the namespace in a global variable. It's only used in the first two lines, so we unset the global variable after, so as not to clutter up the global namespace. After that, [namespace current] should be used anywhere MyScript was used. Doing this will make it practically impossible for your script to conflict with someone else's.


All Done!
-----------------------------------------------------------------------------------------------------
Edit: Replaced [set] commands with [variable] inside namespace after reading user's tip.
Last edited by Sir_Fz on Thu Jan 11, 2007 10:35 pm, edited 1 time in total.
User avatar
user
 
Posts: 1452
Joined: Tue Mar 18, 2003 9:58 pm
Location: Norway

slight error

Post by user »

To create a variable inside a namespace you have to use 'variable', not 'set'. If a variable by that name exists in a parent namespace, you'll change the existing one outside your namespace (which defeats the purpose of this whole name conflict avoiding thing :P)

Code: Select all

% set test 1; namespace eval test {set test 2}; set test
2
% set test2 1; namespace eval test2 {variable test2 2}; set test2
1
You can assign a value to scalar variables using 'variable' (like i did in test 2), but if your variable is an array, do 'variable theArray', then 'set' / 'array set' to create elements. Just make sure you use 'variable' the first time you address a variable inside your namespace if you want to make sure you're not messing with parent namespaces.
Have you ever read "The Manual"?
n
nml375
Revered One
Posts: 2860
Joined: Fri Aug 04, 2006 2:09 pm

Post by nml375 »

When dealing with namespaces, and making sure bindings work correctly, I'd create a separate namespace called eggdrop with a replacement for the bind function, utilizing the namespace code along with uplevel to capture the actual namespace. No more hazzle with namespace current..
Keep in mind that your binds will look a little messy however.

Code: Select all

namespace eval eggdrop {
#First export functions for easy access:
 namespace export bind

 proc bind {type flag mask proc} {
  ::bind $type $flag $mask [uplevel 1 [list namespace code $proc]]
 }
}
To use this, you'd simply do something like this:

Code: Select all

namespace eval myspace {
 #import the namespace along with bind, so we don't have to use the long version ::eggdrop::bind
 namespace import ::eggdrop::*

 proc myproc {nick host handle text} {
  puthelp "PRIVMSG $nick :Hello $nick! I see you from $host, know you as $handle, and recieved $text from you."
 }
 bind msg - foo myproc

}
NML_375
Post Reply