OOB: Objektumok bash parancsértelmezőben

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 +x

Osztá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 object

Az 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 object

Az 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 +x

Az 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.

1 2 3 4 5 6 7 8 9
lt;