Zsh Mailing List Archive
Messages sorted by:
Reverse Date,
Date,
Thread,
Author
Zoo: zsh oriented object
- X-seq: zsh-users 11311
- From: marc.chantreux@xxxxxxxxxxxxxxxxxx (Marc Chantreux)
- To: zsh-users@xxxxxxxxxx
- Subject: Zoo: zsh oriented object
- Date: Sun, 18 Mar 2007 17:34:52 +0100
- Mailing-list: contact zsh-users-help@xxxxxxxxxx; run by ezmlm
Hi all,
At the begining of last summer, Nikolai Weibull posts on zsh-users[1] that he uses autoload to deal with namespaces for his own libraries. Some posts later, Peter Stephenson told me about the ksh-style autoload. I dreamed about object oriented zsh: being able to write something like
use Zoo
use Ldap
# call the ldap constructor
# ( ldap/_new defined in Ldap )
new Ldap srv directory.example.com cn=admin 'giv3m3r00t'
#
host $( $srv .host )
$srv Search uid=mc
delete $srv
My dream came true: Zoo is born! Now, i'm really afraid about doing something that cannot be reliable, usable, maintenable, ... . That's why i would appreciate some feedbacks, ideas or advices.
[1] http://www.zsh.org/mla/users//2006/msg00587.html
A. 3 benefits of autoload -Uk (comparing with source)
sourcing files is traditionnaly done with the source command. You have to figure out by yourself where is your library and the fact you already sourced it.
for example:
if (( ! $+libloaded[ldap] )) {
source /long/is/the/road/to/my/libs/Ldap
libloaded[ldap]=1
}
ksh-autoload do it itself. Just be sure that:
* /long/is/the/road/to/my/libs in your fpath (perfect job for your .zshenv)
* /long/is/the/road/to/my/libs/Ldap define Ldap()
so now, you just have to type:
autoload -Uk Ldap; Ldap
add this function to your zshenv
use () {
local lib=$1
shift
autoload -Uk $lib
$lib "$@"
}
you can now write
use Ldap
autoload can also load a file that would not be directly under $fpath So the file /long/is/the/road/to/my/libs/ldif/Entry can be loaded typing
autoload ldif/Entry
So the function name is ... ldif/Entry !!! zsh can do it and it seriously looks like a namespace! Now you can define some functions ldif/entry/Get, ldif/entry/Dump or whatever in this file.
Do you noticed that i use uppercase carrefully? I decided it convention:
* files have a leading upper, directories have lower, so I can have an fpath with the files Ldif and ldif/Entry
* functions begin with upper and i try to have only verbs.
The fact is there is almost nothing to do to have a very beautifull library management: autoload does it for you.
B. about Zoo
The zoo concept is very simple: zsh can execute arrays (if the first element of the array is executable). Examples:
obj=( echo "i'm an object" )
$obj
So the new function is defined like this
new () {
typeset class=$1 id=$2 self=ZOOBJ$[ZOOCNT++]
shift 2
# self is the object itself,
# it's a global associative array
# it is exported to be acceded by subshells
# so
# echo $( $id .member )
# is valid
typeset -Ag $self
# you can access to it using eval, (P) ...
# it's not really readable so I wrote
# Zoo/Import, Zoo/Echo and Zoo/Set
eval "$id=( zoo/_object $class $self )"
export $id $self
# id is an array that is called as command
# so the command zoo is called with
# at least 2 params : the class and the
# name of the object. 3rd parameter can be
# the method.
# call the class constructor on the object
$class:l/_new $self "$@"
}
ldap/_new () {
shift
echo construct ldap with "$@"
}
zoo/_object () {
# not the real! just for demo:
typeset class=$1 object=$2
print $object is an associative array
print storing the values of $class object
}
new ldap boo
$boo
In fact zoo/_object() is a dispatcher that launches functions according to the class name. I joined a complete working example. Just copy all the files in a directory and run zsh example.
C. Conclusion
This is a basic oo implementation. Just supporting constructors, desctructors, properties and methods. I think it can be a base for a lot of features but as i said, i really appreciate to know if i'm in a wrong way.
source zshenv
use Zoo
use Ldap
# call the ldap constructor
# ( ldap/_new defined in Ldap )
new Ldap srv directory.example.com cn=admin 'giv3m3r00t'
#
host $( $srv .host )
$srv Search uid=mc
delete $srv
Ldap () {}
ldap/_new () {
typeset self=$1
zoo/Set $self host "$2" binddn "$3" passwd "$4"
}
ldap/_delete () {
typeset self=$1
echo i will delete $self
unset $self
}
ldap/Search () {
typeset self=$1 host binddn passwd
zoo/Import $self host binddn passwd
shift
echo ldapsearch -h"$host" -xD"$binddn" -w"$passwd" "$@"
}
Zoo () {}
zoo/_object () {
typeset class=$1 self=$2 cmd
(( $# )) &&cmd=$3 ||cmd=Default
case $cmd[1] {
(.) zoo/Echo $self $cmd[2,-1] ;;
(*) $class:l/$cmd $self "$@" ;;
}
}
zoo/Set () {
typeset self=$1 k v
shift
for k v {
eval "${self}[$k]"=${(qqq)v}
}
}
zoo/Import () {
typeset self="$1" property
shift
for property {
eval $property='${(q)'${self}\[$property']}'
}
key="$2" var="$3"
}
zoo/Echo () {
typeset self="$1" property="$2"
eval echo \$${self}\[$property]
# eval echo \$${self}[$property]
}
new () {
typeset class=$1 id=$2 self=ZOOBJ$[ZOOCNT++]
shift 2
# self is the object itself,
# it's a global associative array
# it is exported to be acceded by subshells
# so
# echo $( $id .member )
# is valid
typeset -Ag $self
# you can access to it using eval, (P) ...
# it's not really readable so I wrote
# Zoo/Import, Zoo/Echo and Zoo/Set
eval "$id=( zoo/_object $class $self )"
export $id $self
# id is an array that is called as command
# so the command zoo is called with
# at least 2 params : the class and the
# name of the object. 3rd parameter can be
# the method.
# call the class constructor on the object
$class:l/_new $self "$@"
}
delete () {
typeset class=$2 self=$3
shift 3
$class:l/_delete $self
unset $self
}
# the content of this file
# will normaly be a part of your
fpath+=$PWD
use () {
local lib=$1
shift
autoload -k $lib
$lib "$@"
}
Messages sorted by:
Reverse Date,
Date,
Thread,
Author