Továbbfejlesztés
A konstruktor bővítése
Ha átnevezünk egy tagot, akkor szükség lehet a metódusokban is átnevezni. Ezt megtámogathatjuk egy-két paranccsal. Ezért máris továbbfejlesztjük az éppen elkészült OOB rendszert.
Egyszerű szöveg cseréje
A basic.oob (verzió: 1.0.1) fájlba először megírunk egy általános szövegcserélő parancsot, amely az új osztály metódusaiban dolgozik:
oob_chgText() { # input: "new text" "pattern" [method]... # change pattern to text in method descriptor of class
local i text="$1" pattern="$2"; local -a methods; shift 2
(($#==0)) && methods=( ${!dc_new_m[@]} ) || methods=( $@ ) # pick name of methods
for i in ${methods[@]}; do [[ -n ${dc_new_m[$i]} ]] && dc_new_m[$i]="${dc_new_m[$i]//$pattern/$text}"; done # change pattern to text
} # oob_chgText()A parancs első argumentuma az új szöveg, a második a cserélendő, a többi a metódusok neve. Ha nem adunk meg metódusneveket, akkor az összes metődusban cseréli a szöveget.
A teszteléshez a develop.sh fájl tartalma:
#!/usr/bin/env oob
## run
main() {
oob_setTags -d info="-gA"
oob_setTags -m sayHello='{ echo "Hello $(${self}_getName)!"; }' getName='{ echo "${self_info[name]}"; }'
oob_chgText "By" "Hello"
oob_new o_star o_star_planet4 o_star_planet4_moon1 o_star_planet4_moon2
o_star_planet4_moon1_info=( [name]=Phobos )
oob_code > $HOME/user-objects
}
: set -x; main "$@"; set +x
Tagok cseréje
Az oob_chgText parancsra alapozva elkészíthetjük az adattagok és a metódusok neveinek cseréjét is. A parancs a basic.oob (verzió: 1.0.2) fájlban:
oob_chgTag() { # input: -d|-m name pattern [method]... # change name of data or method in method descriptor of class
local name=$2 pattern=$3 sd='${self' sm='${self}'
case $1 in # select data or method
-d) shift 3; name="${sd}_$name" pattern="${sd}_$pattern";; # prepare for data
-m) shift 3; name="${sm}_$name" pattern="${sm}_$pattern";; # prepare for method
*) oob_trap "Bad option:" "$1"; return 1;;
esac # select data or method
oob_chgText "$name" "$pattern" "$@"
} # oob_chgTag()A parancs első argumentuma kiválasztja, hogy adattag vagy metódusnevet szeretnénk cserélni. A második argumentum az új név, a harmadik az, amit cserélnénk, a többi a metódusok nevei, amelyikben cserélnénk a neveket. Ha nem adunk meg metódusokat, akkor az összesben cseréli a tag neveket. A parancs a tagok nevei elé befűzi a megfelelő előtagot és így hívja meg a tenyleges cserét végző oob_chgText parancsot.
A develop.sh fájl a teszteléshez:
#!/usr/bin/env oob
## run
main() {
oob_setTags -d info="-gA"
oob_setTags -m sayHello='{ echo "Hello $(${self}_getName)!"; }' getName='{ echo "${self_info[name]}"; }'
oob_cpTags -m getTheName=getName; oob_rmTags -m getName; oob_chgTag -m getTheName getName
oob_new o_star o_star_planet4 o_star_planet4_moon1 o_star_planet4_moon2
o_star_planet4_moon1_info=( [name]=Phobos )
oob_code > $HOME/user-objects
}
: set -x; main "$@"; set +xOsztálykönyvtárak
Az objektumorientált nyelvek tartozéka a változatos funkciókhoz készült osztálykönyvtárak. Megvalósítjuk ezt is az OOB rendszerben.
Import
Az import nagyon egyszerű: megkeressük a könyvtárfájlokat és betöltjük. Az importot a basic.oob (verzió: 1.0.3) fájlba írjuk:
oob_import() { # input: OOB libraries
local i lib; for i; do # one argument
[[ -f $i.ooblib ]] && lib="$i.ooblib" || lib="$oob_HOME/$i.ooblib" # select OOB lib
source "$lib" || oob_import_error 1 "$lib"
done # one argument
} # oob_import()Minden argumentum egy-egy könyvtárfájl. A parancs az argumentumot teljes útvonalként értelmezve és az ooblib kiterjesztést hozzáadva megkeresi az osztálykönyvtárat tartalmazó fájlt. Ha nem találja, akkor azt tételezi, hogy a fájl az OOB saját könyvtárához képest relatív útvonalon van. Ezután megkísérli betölteni az osztálykönyvtárat. Ez a módszer lehetőséget ad az OOB rendszerrel adott vagy saját osztálykönyvtárak betöltésére is.
Az OOB saját könyvtárának útvonalát az oob_HOME változóban tároljuk. Ezt közvetlenül az első programsorban meg is adjuk, ezért azt módosítjuk, így a gyökérobjektum definiciója elé ennek kitöltését is beírjuk:
oob_HOME="${BASH_SOURCE%/*}" do=0 # define OOB home directory and descriptor of the root objectAz importálás végrehajtásakor előfordulhatnak hibák is. Ezekre felkészülve megírjuk az oob_import saját hibakezelőjét is:
oob_import_error() { # input: error code, other info # result: error text
local t
case $1 in 1) t="Cannot load $2";; 2) t="Only oob_import can import library";; 3) shift; t="Object names mitchmatch: $@";; esac
echo "$t" >&2; exit 1
} # oob_import_error()Export
Exportálni eddig is tudtunk az oob_code parancs használatával. Ez azonban csak egyszerűen kiírja a kódot, egy könyvtár betöltésekor azonban elvárjuk, hogy figyelmeztessen a hibákra. Az OOB rendszerben az osztályoknak nincsen nevük, csak megjegyezhetetlen időbélyegük. Emiatt az osztályok mindig egyediek, azonban a használatbavételükhöz szükséges legalább egy objektumot (prototípust) is betöltenünk. Az objektum nevek viszont ütközhetnek. Ez nem fatális probléma, megoldható úgy, hogy a korábban betöltött prototípust átnevezzük (készítünk belőle egy újat és töröljük az eredetit) és betölthetjük az újat. Ehhez azonban szükséges, hogy ütközés esetén az import figyelmeztessen. Ezt úgy érjük el, hogy néhány parancsot írunk a könyvtárfájlba az exportált kódok elé. Ehhez megírunk két segédfüggvényt a basic.oob (verzió: 1.0.4) fájlba:
oob_anyObject() { local i dobj; for i; do dobj=d$i; case ${!dobj} in ?*) return 1;; esac; done; } # error if any object exists
oob_allObjects() { local i dobj; for i; do dobj=d$i; case ${!dobj} in "") return 1;; esac; done; } # error if missing any objectAz első segédfüggvény hibával tér vissza, ha a felsorolt objektumok közül akár egy is létezik már. A második akkor, ha a felsorolt objektumok nem mindegyike létezik. Ezt a második függvényt a szimmetria kedvéért és a felhasználói szkriptekben való felhasználásra írtuk meg.
Az oob_export parancs az basic.oob (verzió: 1.0.5) fájlban:
oob_export() { # input: library file, version, text, object-pattens # create OOB library for oob objects and classes
local libFile="$1.ooblib" libVer="$2" libText="$3"; shift 3; [[ -z $1 ]] && return
oob_lss $@; [[ -z ${oob_LSS[*]} ]] && return # get names of objects
echo "#
# OOB library
# "${libFile##*/}", version $libVer
# https://oob.xport.hu
# objects and classes library for Object Oriented Bash
# $libText" > "$libFile" # create OOB library file and write its header
echo "
[[ -n \$oob_HOME && \$do == "0" ]] || { echo \"Error: Not loaded OOB! Exit\"; exit 1; }
[[ \${FUNCNAME[1]} == oob_import ]] || { oob_import_error 2; exit 1; }
oob_anyObject ${oob_LSS[@]} || oob_import_error 3 ${oob_LSS[@]}
" >> "$libFile" # write commands for check OOB environment and existing objects
mode="-odmc" oob_getObjectsAndClassesCode ${oob_LSS[@]} >> "$libFile" # code of objects and classes
} # oob_export()A parancs először összeállítja az exportálandó objektumok listáját. Ezután kiírja az osztálykönyvtár fejlécét. Ehhez a parancs első 3 argumentumában megadott információkat (a fájl neve kiterjesztés nélkül, az osztálykönyvtár verziószáma, tájékoztató szöveg) használja fel. A fejléc után következő programsorok elvégzik az importáláskor elvárt ellenőrzéseket. Az első az OOB saját könyvtárának és a gyökérobjektum ellenőrzésével megvizsgálja, hogy be van-e töltve az OOB és ha nem, akkor hibával megszakítja a program futását. A második sor ellenőrzi, hogy az oob_import hajtja végre az importálást és ha nem, a hibaágra fut és ki is lépteti a programot. A harmadik sor ellenőrzi, hogy a betöltendő objektumnevek léteznek-e és ha igen, akkor ez is a hibaágra fut. Végül a parancs kiírja a szükséges kódokat az osztálykönyvtárba. Az oob_getObjectsAndClassesCode parancsnak kívülről átadott lokális mode változó biztosítja, hogy minden komponens kiírásra kerüljön.
Az export teszteléséhez a develop.sh fájlt cseréljük:
#!/usr/bin/env oob
## run
main() {
oob_setTags -d info="-gA"
oob_setTags -m sayHello='{ echo "Hello $(${self}_getName)!"; }' getName='{ echo "${self_info[name]}"; }'
oob_new o_skyObject
oob_export "$HOME/userlib" 0.9 "Test for OOB export-import" o_@
}
: set -x; main "$@"; set +xAz import teszteléséhez a run.sh fájlt cseréljük:
#!/usr/bin/env oob ## load class oob_import "$HOME/userlib" ## run main() { oob_cp o_star=o_skyObject; o_star_info=( [name]=Sun ); o_star_sayHello; } : set -x; main "$@"; set +x
Klónozás
Az importtal betöltött objektumokban kezdeti adatokat is tárolhatunk. Ha át kell nevezni az objektumot, akkor gondoskodnunk kell ezeknek az adatoknak az átviteléről is. Ezt megkönnyítendő írunk egy új parancsot a basic.oob (verzió: 1.0.6) fájlba:
oob_clone() { # input <target object>=<object>... # result: data are same in both objects
local i dobj proto tag code; for i; do # one argument
dobj=d${i%%=*} proto=d${i#*=}; [[ $dobj == $proto ]] && continue # target and source are the same
[[ ${!dobj:-t} == ${!proto:-s} ]] || { oob_trap "Objects are not in the same class:" "$i"; continue; }
local -n dc_d=dc_${!proto}_d; for tag in ${!dc_d[@]}; do # one attribute
oob_getDeclare ${i#*=}_$tag; eval ${code/${i#*=}_$tag/${i%%=*}_$tag} # get and create attribute
done # one attribute
done # one argument
} # oob_clone()A parancs minden argumentuma <cél objektum>=<minta objektum> alakú. A parancs egymás után, egyenként dolgozza fel az argumentumokat, amikor is a két létező, azonos osztályhoz tartozó objektum között viszi át az összes adattagot.
Megírtuk az összes szükséges alapvető parancsot, amelyekkel kényelmesen kezelhetjük az objektumokat, szerepeket, osztályokat és osztálykönyvtárakat. Ahhoz, hogy ne kelljen fejben tartani a parancsokat, a következő fejezetben megírjuk az OOB súgóját is.