For real this time, "using" directive, static classes, proper examples, declared objects are nom recommanded to be accessed using the $ prefix like all other variables.
This commit is contained in:
parent
d4a7290707
commit
13e6b1a3d3
2 changed files with 173 additions and 42 deletions
138
README.md
138
README.md
|
@ -7,57 +7,113 @@ This idea was inspired from https://stackoverflow.com/a/40981277 in order to be
|
||||||
- .shn: **SH**ell **N**amespace
|
- .shn: **SH**ell **N**amespace
|
||||||
- .shc: **SH**ell **C**lass
|
- .shc: **SH**ell **C**lass
|
||||||
|
|
||||||
## Example syntax:
|
## Syntax:
|
||||||
|
|
||||||
|
|
||||||
|
To declare objects, there exists 3 scripts.
|
||||||
|
|
||||||
|
- The main script in which we'll use our object (suggested extension: `.sh`)
|
||||||
|
- The namespace declaration script (suggested extension: `.shn`)
|
||||||
|
- The class declaration script (suggested extension: `.shc`)
|
||||||
|
|
||||||
|
The full example is available in the "example" directory.
|
||||||
|
|
||||||
### Declaring objects.
|
### Declaring objects.
|
||||||
|
|
||||||
`script.sh`:
|
An object has a type name, properties and functions.
|
||||||
This is the main script which will use our objects.
|
To declare a property, you can use the `property` function. For example, for an class named `Object`, you can declare a property name using:
|
||||||
|
```bash
|
||||||
|
property Object.name
|
||||||
|
```
|
||||||
|
**NOTE**: Bash doesn't have a typing system, so you cannot set property types.
|
||||||
|
|
||||||
|
Class functions are declared the same way you would in bash, except it uses a prefix with object type. For example:
|
||||||
|
```bash
|
||||||
|
Object.print() {
|
||||||
|
echo "Example OOP from $(this.name)!"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
As you can see here, you can access properties of the object using the `this` keyword in a function call.
|
||||||
|
Similarly, you can set properties using a `=` and value argument. For example:
|
||||||
|
```bash
|
||||||
|
this.name = "New name"
|
||||||
|
```
|
||||||
|
|
||||||
|
Objects can also have constructors which will be called at the creation of the object with arguments provided at the creation.
|
||||||
|
They are simply a function with the name `constructor`. They aren't mandatory for any object.
|
||||||
|
|
||||||
|
### Creating a namespace.
|
||||||
|
While you can import objects directly in the global namespace, it's recommanded to use a separate namespace file.
|
||||||
|
|
||||||
|
When you've created your namespace file, you can specify the name of the namespace using the `namespace` keyword:
|
||||||
|
```bash
|
||||||
|
namespace Example
|
||||||
|
```
|
||||||
|
You can then declare object classes using the `class` directive by specifying it's name and associated script file. For example:
|
||||||
|
```bash
|
||||||
|
class Object "Object.shc"
|
||||||
|
```
|
||||||
|
|
||||||
|
All objects created under this class will be accessible with namespace as prefix (here our Object class would be accessible under `Example.Object`).
|
||||||
|
|
||||||
|
Similarly, static classes can be declared using the `static_class` keyword.
|
||||||
|
```bash
|
||||||
|
static_class Static "Static.shc"
|
||||||
|
```
|
||||||
|
|
||||||
|
**NOTE**: Static classes can't have properties. As all static classes are global, you can declare global variables directly.
|
||||||
|
|
||||||
|
### Using objects.
|
||||||
|
Now that we've created our namespace, we will want to use it and our objects in our script.
|
||||||
|
First things first, we'll want to import the library `oop.sh`. Depending on where it's located, you will want to use a global variable indicating it's location.
|
||||||
```bash
|
```bash
|
||||||
. $OOP_ROOT/oop.sh # Import library.
|
. $OOP_ROOT/oop.sh # Import library.
|
||||||
|
|
||||||
importNamespace Example.shn
|
|
||||||
|
|
||||||
Example.Object t1 "Test"
|
|
||||||
Example.Object t2 "Example"
|
|
||||||
|
|
||||||
t1.print
|
|
||||||
t2.print
|
|
||||||
|
|
||||||
t1.name = "New name"
|
|
||||||
|
|
||||||
t1.print
|
|
||||||
```
|
```
|
||||||
|
|
||||||
`Example.shn`:
|
After that, we'll want to import our namespace file with all it's classes prefixed in the namespace name.
|
||||||
This file declares our namespace and all the objects within it.
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Namespace declaration.
|
importNamespace "Example.shn"
|
||||||
namespace Example
|
|
||||||
# If namespace is set to null (no argument), then the object will be declared globally.
|
|
||||||
# Otherwise, the object will be declared within the namespace.
|
|
||||||
|
|
||||||
# Object declaration, from class name to file name.
|
|
||||||
class Object "Object.shc"
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
`Object.shc`:
|
After that you can declare the object using the following syntax: `<ObjectType> <variableName> [constructor arguments...]`. For example:
|
||||||
This file will contain the object code.
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Property declaration
|
Example.Object obj1 "Test"
|
||||||
property Object.name
|
|
||||||
|
|
||||||
# Optional constructor.
|
|
||||||
Object.constructor() {
|
|
||||||
Object.name = $1
|
|
||||||
}
|
|
||||||
|
|
||||||
# Example function
|
|
||||||
Object.print() {
|
|
||||||
echo "Example OOP from $($this.name)!"
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can then call it's functions.
|
||||||
|
```bash
|
||||||
|
$obj1.print
|
||||||
|
obj1.print
|
||||||
|
```
|
||||||
|
**NOTE**: The $ is not mandatory, but is recommanded for clarity.
|
||||||
|
|
||||||
|
... or access and edit it's properties.
|
||||||
|
```bash
|
||||||
|
name=$(obj1.name)
|
||||||
|
obj1.name = "New name"
|
||||||
|
```
|
||||||
|
|
||||||
|
You can store objects in variables as a string. For example, you can have have objects as class arguments, function returs or arrays of objects like this:
|
||||||
|
```bash
|
||||||
|
Example.Object obj1 "First Object"
|
||||||
|
Example.Object obj2 "Second Object"
|
||||||
|
objs=($obj1 $obj2)
|
||||||
|
${objs[0]}.print
|
||||||
|
${objs[1]}.print
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also access the static classes by using their class type directly. For example:
|
||||||
|
```bash
|
||||||
|
Example.Static.print "Example text"
|
||||||
|
```
|
||||||
|
|
||||||
|
If you find that using the namespace everytime is a bit cumbersome, you can use the `using` keyword to alias all classes of a namespace into the global namespace.
|
||||||
|
Example usage:
|
||||||
|
```bash
|
||||||
|
using Example
|
||||||
|
|
||||||
|
Object usingObj "New"
|
||||||
|
|
||||||
|
$usingObj.print
|
||||||
|
```
|
||||||
|
**NOTE**: When `using` a namespace which contains static classes, please note that the static class file will be re-imported.
|
||||||
|
|
75
oop.sh
75
oop.sh
|
@ -3,9 +3,16 @@
|
||||||
# This file contains all functions required to create a namespace.
|
# This file contains all functions required to create a namespace.
|
||||||
# Internal variables are marked with a beginning underscore, like in most other languages.
|
# Internal variables are marked with a beginning underscore, like in most other languages.
|
||||||
# Signatures are a list of arguments.. Those within <> are mandatory, the ones within [] are optional.
|
# Signatures are a list of arguments.. Those within <> are mandatory, the ones within [] are optional.
|
||||||
|
|
||||||
|
# Namespace related variables.
|
||||||
_namespace=""
|
_namespace=""
|
||||||
_namespacePath=$(realpath $(dirname ${BASH_SOURCE[0]}))
|
_namespacePath=$(realpath $(dirname ${BASH_SOURCE[0]}))
|
||||||
|
|
||||||
|
# This dictionnary saves all classes for each namespace so they can be retreived and aliased.
|
||||||
|
declare -Ag _namespacesClasses
|
||||||
|
# This dictionnary links all files for static namespaces.
|
||||||
|
declare -Ag _namespacesStaticClasses
|
||||||
|
|
||||||
# Namespace declaration.
|
# Namespace declaration.
|
||||||
# Signature: ([string namespaceName])
|
# Signature: ([string namespaceName])
|
||||||
namespace() {
|
namespace() {
|
||||||
|
@ -13,6 +20,10 @@ namespace() {
|
||||||
}
|
}
|
||||||
|
|
||||||
# Imports a namespace into the current shell.
|
# Imports a namespace into the current shell.
|
||||||
|
# It saves the path of the file so that relative paths can be
|
||||||
|
# properly resolved.
|
||||||
|
# For example, if the object Object exists within namespace Example, it
|
||||||
|
# will be accessible with "Example.Object".
|
||||||
# Signature: (<string namespaceFile>)
|
# Signature: (<string namespaceFile>)
|
||||||
importNamespace() {
|
importNamespace() {
|
||||||
namespaceFile=$1
|
namespaceFile=$1
|
||||||
|
@ -21,6 +32,33 @@ importNamespace() {
|
||||||
. $namespaceFile
|
. $namespaceFile
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Aliases the classes in global namespace.
|
||||||
|
# For example, if the object Object exists within namespace Example, it
|
||||||
|
# will be accessible with "Example.Object" and "Object".
|
||||||
|
# Signature: (<string namespaceName>)
|
||||||
|
using() {
|
||||||
|
namespaceName=$1
|
||||||
|
# Import static classes
|
||||||
|
if [ "${_namespacesStaticClasses[$namespaceName]}" != "" ]; then
|
||||||
|
oldNamespace=_namespace
|
||||||
|
namespace # Reset namespace
|
||||||
|
# Split all
|
||||||
|
staticClasses=$(echo "${_namespacesStaticClasses[$namespaceName]}" | tr ";" "\n")
|
||||||
|
for cl in $staticClasses; do
|
||||||
|
parts=$(echo "$cl" | tr ":" "\n")
|
||||||
|
static_class ${parts[0]} ${parts[1]}
|
||||||
|
done
|
||||||
|
namespace $oldNamespace
|
||||||
|
fi
|
||||||
|
# Import static classes
|
||||||
|
if [ "${_namespacesClasses[$namespaceName]}" != "" ]; then
|
||||||
|
classes=$(echo "${_namespacesClasses[$namespaceName]}" | tr ";" "\n")
|
||||||
|
for type in $classes; do
|
||||||
|
eval "$type() { $namespaceName.$type \$@; }"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# Creates an object instance.
|
# Creates an object instance.
|
||||||
# Signature: (<string type>, <string associatedFile>, <string variableName>, [string[] constructorArguments])
|
# Signature: (<string type>, <string associatedFile>, <string variableName>, [string[] constructorArguments])
|
||||||
_createObject() {
|
_createObject() {
|
||||||
|
@ -37,6 +75,8 @@ _createObject() {
|
||||||
. <(sed s/this\\./$varName./g <(sed s/$type\\./$varName./g $associatedFile))
|
. <(sed s/this\\./$varName./g <(sed s/$type\\./$varName./g $associatedFile))
|
||||||
# Call the constructor
|
# Call the constructor
|
||||||
eval "$varName.constructor $constructorArguments"
|
eval "$varName.constructor $constructorArguments"
|
||||||
|
# alias the "varName" variable to itself, so that it can be used and transmitted in other variables (e.g: $varName.name would alias to varName.name)
|
||||||
|
eval "$varName='$varName'"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Object creation.
|
# Object creation.
|
||||||
|
@ -55,6 +95,41 @@ class() {
|
||||||
fi
|
fi
|
||||||
# Declares a new function for object initialisation.
|
# Declares a new function for object initialisation.
|
||||||
eval "$objFullName() { _createObject $type $associatedFile \$@; }"
|
eval "$objFullName() { _createObject $type $associatedFile \$@; }"
|
||||||
|
# Save the class in the dictionnary for reference.
|
||||||
|
if [ "$_namespace" != "" ]; then
|
||||||
|
if [ "${_namespacesClasses[$_namespace]}" == "" ]; then
|
||||||
|
_namespacesClasses[$_namespace]=$type
|
||||||
|
else
|
||||||
|
_namespacesClasses[$_namespace]="${_namespacesClasses[$_namespace]};$type"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Static class creation
|
||||||
|
# Signature: (<string type>, <string associatedFile>)
|
||||||
|
static_class() {
|
||||||
|
type=$1 # Type of the object as declared within the file.
|
||||||
|
associatedFile=$2
|
||||||
|
objFullName=$type # Type of the object referenced elsewhere
|
||||||
|
|
||||||
|
if [ "$_namespace" != "" ]; then
|
||||||
|
objFullName="${_namespace}.$type"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ${associatedFile::1} != "/" ]; then # Relative path, we save only the absolute path
|
||||||
|
associatedFile="$_namespacePath/$associatedFile"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Imports the file and replace all "<Type>." with the variable name.
|
||||||
|
. <(sed s/this\\./$objFullName./g <(sed s/$type\\./$objFullName./g $associatedFile))
|
||||||
|
# Save the class in the dictionnary for reference.
|
||||||
|
if [ "$_namespace" != "" ]; then
|
||||||
|
if [ "${_namespacesStaticClasses[$_namespace]}" == "" ]; then
|
||||||
|
_namespacesStaticClasses[$_namespace]="$type:$associatedFile"
|
||||||
|
else
|
||||||
|
_namespacesStaticClasses[$_namespace]="${_namespacesStaticClasses[$_namespace]};$type:$associatedFile"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Associated function for properties
|
# Associated function for properties
|
||||||
|
|
Loading…
Reference in a new issue