Skip to content

Commit 8239a27

Browse files
committed
Merge pull request #4 from ash-shell/br.package-imports
Br.package imports
2 parents 964232a + cbf4212 commit 8239a27

File tree

3 files changed

+181
-31
lines changed

3 files changed

+181
-31
lines changed

README.md

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,64 +2,78 @@
22

33
Obj is an [Ash](https://github.com/ash-shell/ash) module that adds object support to Bash.
44

5+
> If you want to first get excited about this before reading the entire README, jump to [this section](#class-example).
6+
57
# Getting started
68

7-
## Ash Users
9+
Obj is part of the Ash core, so you can just start using it in your Ash modules.
810

9-
Obj is part of the Ash core, so you can immediately start using it within your Ash modules.
11+
For an example of an ash module that uses objects, [look here](https://github.com/BrandonRomano/ash-obj-examples).
1012

11-
Simply place your Classes in a directory named `classes` in the root of your modules directory, and start using them!
13+
# Features + Usage
1214

13-
For an example of an ash module that uses objects, [look here](https://github.com/BrandonRomano/ash-obj-examples).
15+
## Imports
1416

15-
## Non Ash Users
17+
Before we can start using any objects, we must first import them.
1618

17-
Even if you're not an Ash user, this module can be used as a library.
19+
If the objects you want to use are in your current module, they are already imported for you.
1820

19-
Just include the `obj.sh` library file in your script, and also point to where you're going to be keeping your classes.
21+
If the objects you want to use are outside of your current module, you must import them. You can import an external module with `Obj__import`:
2022

21-
The start of your script will look something like this:
23+
```bash
24+
Obj__import "$package_to_import" "$package_alias"
25+
```
26+
27+
`$package_to_import` is the modules package (as specified in its `ash_config.yaml` file), while `$package_alias` is the alias in which we will refer to the newly imported package.
28+
29+
If I have `https://github.com/BrandonRomano/ash-obj-examples` installed and would like to import its classes in a different module, I would this following line to my module:
2230

2331
```bash
24-
. lib/obj.sh # Importing Obj library
25-
Obj__classes_directory="./classes" # Setting classes directory
32+
Obj__import "github.com/BrandonRomano/ash-obj-examples" "objex"
2633
```
2734

28-
# Features + Usage
35+
> External modules must first be installed before importing them. See [apm](https://github.com/ash-shell/apm).
2936
3037
## Creating Classes
3138

3239
Before getting into any details, as you would expect classes are the definition of what an object is.
3340

34-
Classes must be placed in the directory defined to hold classes (for Ash users, this is in a directory named `classes` in the root of your module). Classes also must be named as their classname with `.sh` as their extension. By convention, you should use [PascalCase](http://c2.com/cgi/wiki?PascalCase) for class naming as many modern programming languages use.
41+
Classes must be placed in the directory named `classes` in the root of a module. Classes also must be named as their classname with `.sh` as their extension. By convention, you should use [PascalCase](http://c2.com/cgi/wiki?PascalCase) for class naming as many modern programming languages use.
3542

3643
> For example, if I were to create a class that would define what a Person object is, I would name the file `Person.sh`.
3744
45+
### Class Example
46+
3847
I'll explain the different components of what a class is below, but here is what a class looks like. This would be in a file named Person.sh in our classes diretory:
3948

40-
> For a fully commented version of this class, look [here](https://github.com/ash-shell/obj-examples/blob/master/classes/Person.sh)
49+
> For a fully commented version of this class, look [here](https://github.com/BrandonRomano/ash-obj-examples/blob/master/classes/Person.sh)
4150
4251
```bash
4352
#!/bin/bash
4453
# This is a simple class that represents a Person.
4554

55+
# Public member variables
4656
Person__id=""
4757
Person__name=""
4858
Person__age=""
4959

60+
# Private member variable
5061
Person_birthdays_count=0
5162

63+
# Constructor
5264
Person__construct(){
5365
Person__id="$1"
5466
Person__name="$2"
5567
Person__age="$3"
5668
}
5769

70+
# Public method
5871
Person__make_older(){
5972
Person__age=$((Person__age+1))
6073
Person_update_birthday_count
6174
}
6275

76+
# Private method
6377
Person_update_birthday_count(){
6478
Person_birthdays_count=$((Person_birthdays_count+1))
6579
echo "Happy Birthday $Person__name!"
@@ -101,7 +115,7 @@ A private method is a function denoted by the Class name followed by a single un
101115
102116
### Constructors
103117

104-
Constructors are magically named funcitons that get called when an object is initialized. Constructors can take an arbitrary amount of parameters, and have all of the same powers as a public method (as it technically is a public method).
118+
Constructors are magically named funcitons that get called when an object is initialized. Constructors can take an arbitrary amount of parameters, and have all of the same powers as a public method (as it technically is just a public method).
105119

106120
A constructor is a function denoted by the class name followed by `__construct`.
107121

@@ -134,6 +148,21 @@ Obj__init $brandon 1 "Brandon" 23
134148

135149
Now I have a reference to `$brandon`, which is a pointer to a initialized object representing myself.
136150

151+
##### From an External Package
152+
153+
If I wanted to create an object from an external package that I've imported, we must specify the package alias in `Obj__alloc`, in `alias.ClassName` format:
154+
155+
```bash
156+
# Importing
157+
Obj__import "github.com/BrandonRomano/ash-obj-examples" "objex"
158+
159+
# Creating the Object
160+
brandon=$(Obj__alloc "objex.Person") # Note the `objex.` before the class name
161+
Obj__init $brandon 1 "Brandon" 23
162+
```
163+
164+
> When you're creating an object from the current context, you could actually say `this.ClassName` - Although, it isn't necessisary because if you don't specify a package, `Obj__alloc` by default assumes you mean `this`.
165+
137166
##### A quick Obj__init Gotcha
138167

139168
It's worth nothing that if you use `Obj__init` within a subshell, the object will only be initialized within the scope of that subshell. Your best bet is to not wrap this call in a subshell unless you really know what you're doing. However, we are allowed to pass objects into subshells after they have been initialized. This follows the same rules as variables in a subshell:
@@ -151,7 +180,6 @@ If I were to run `Obj__dump $brandon`, immediately after initializing the object
151180
| age=23
152181
| id=3
153182
| name='Brandon Romano'
154-
=====================================
155183
```
156184

157185
As you can see, `Obj__dump` prints out all of the public variables in an object.
@@ -183,7 +211,6 @@ An `Obj__dump` on our `$brandon` object would now yield:
183211
| age=23
184212
| id=3
185213
| name='Brandon Romano'
186-
=====================================
187214
```
188215

189216
#### Accessing Member Variables (Obj__get)
@@ -211,7 +238,7 @@ Brandon Romano
211238

212239
### Calling Public Methods
213240

214-
Inside of a class, methods are called with the full name of the function (e.g. `Person__make_older`).
241+
Inside of a class, all methods are called with the full name of the function (e.g. `Person__make_older`).
215242

216243
Outside of the class, this is different, and public methods must be called via `Obj__call`.
217244

lib/import.sh

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
#!/bin/bash
2+
3+
# Alias for current context
4+
Obj__THIS="this"
5+
6+
# Imports map
7+
Obj_imports=( )
8+
9+
################################################################
10+
# Imports a package
11+
#
12+
# @param $1: The module to import
13+
# @param $2: The alias of the module
14+
################################################################
15+
Obj__import() {
16+
# Params
17+
local import_module="$1"
18+
local module_alias="$2"
19+
20+
# Checking if package path exists
21+
local package_path=$(Ash__find_module_directory "$import_module" 0)
22+
if [[ "$package_path" = "" ]]; then
23+
Logger__error "Cannot import $import_module, no module with that package is installed"
24+
exit
25+
fi
26+
27+
# Verifying there is an alias
28+
if [[ "$module_alias" = "" ]]; then
29+
Logger__error "Cannot import without an alias"
30+
exit
31+
fi
32+
33+
# Checking if the alias already exists
34+
local import=""
35+
local pos=0
36+
for import in "${Obj_imports[@]}" ; do
37+
local key=${import%%:*}
38+
local value=${import#*:}
39+
if [[ "$key" = "$module_alias" ]]; then
40+
# Replace it
41+
Obj_imports[$pos]="$module_alias:$import_module"
42+
return
43+
fi
44+
pos=$((pos+1))
45+
done
46+
47+
# Appending to imports
48+
Obj_imports+=("$module_alias:$import_module")
49+
}
50+
51+
52+
################################################################
53+
# Gets the import directory from the alias
54+
#
55+
# @param $1: The alias of an import
56+
# @returns: The package of the module
57+
################################################################
58+
Obj_get_imported_package() {
59+
local import=""
60+
for import in "${Obj_imports[@]}" ; do
61+
local key=${import%%:*}
62+
local value=${import#*:}
63+
if [[ "$key" = "$1" ]]; then
64+
echo "$value"
65+
return
66+
fi
67+
done
68+
}
69+
70+
################################################################
71+
# Gets the import directory from the alias
72+
#
73+
# @param $1: The alias of an import
74+
# @returns: The directory of the module
75+
################################################################
76+
Obj_get_imported_directory() {
77+
local package="$(Obj_get_imported_package $1)"
78+
echo "$(Ash__find_module_directory "$package" 0)"
79+
}

lib/obj.sh

Lines changed: 58 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,23 @@
11
#!/bin/bash
22

3-
# The directory we are pulling the classes from.
4-
# This will need to bet set!
5-
Obj__classes_directory=""
6-
73
################################################################
84
# Allocates an object pointer. Must call `Obj__init` on this
95
# pointer after this is called.
106
#
11-
# @param $1: The name of the class to instantiate
7+
# @param $1: The class to instantiate, following the format:
8+
# alias.ClassName
129
# @returns: A pointer to the object
1310
################################################################
1411
Obj__alloc(){
15-
echo "$1_$(Obj_generate_uuid)"
12+
local class="$1"
13+
14+
# Setting 'this' package if no package was specified
15+
if [[ ! "$class" =~ .*\..* ]]; then
16+
class="$Obj__THIS.$class"
17+
fi
18+
19+
class=${class//\./_}
20+
echo "$class""_$(Obj_generate_uuid)"
1621
}
1722

1823
################################################################
@@ -28,17 +33,33 @@ Obj__init(){
2833
IFS='_' read -ra segment <<< "$1"
2934
for part in "${segment[@]}"; do
3035
if [[ "$position" -eq 1 ]]; then
31-
local class="$part"
36+
local package_alias="$part"
3237
elif [[ "$position" -eq 2 ]]; then
38+
local class="$part"
39+
elif [[ "$position" -eq 3 ]]; then
3340
local uuid="$part"
3441
fi
3542
position=$((position+1))
3643
done
3744

45+
# Getting the class directory for the package
46+
local class_directory="$(Obj_get_imported_directory "$package_alias")/$Ash__module_classes_folder"
47+
if [[ "$class_directory" = "" ]]; then
48+
Logger__error "Cannot create an object with the alias of $package_alias, as it has not been imported"
49+
exit
50+
fi
51+
52+
# Verifying file exists
53+
local class_file="$class_directory/$class.sh"
54+
if [[ ! -f "$class_file" ]]; then
55+
Logger__error "There is no file named $class.sh in the aliased package"
56+
exit
57+
fi
58+
3859
# Creating unique variable / method names
3960
local to_find="$class"_
40-
local to_replace="$class"_"$uuid"_
41-
eval "$(cat "$Obj__classes_directory/$class.sh" | sed -e "s:$to_find:$to_replace:g")"
61+
local to_replace="$package_alias"_"$class"_"$uuid"_
62+
eval "$(cat "$class_directory/$class.sh" | sed -e "s:$to_find:$to_replace:g")"
4263

4364
# Calling the constructor
4465
Obj__call $1 construct "${@:2}"
@@ -65,6 +86,10 @@ Obj__get(){
6586
################################################################
6687
Obj__set(){
6788
variable="$1__$2"
89+
if [ -z ${!variable+x} ]; then
90+
Logger__error "Cannot set the variable '$2' to the object, it is not defined in its class"
91+
exit
92+
fi
6893
eval $variable="\"$3\""
6994
}
7095

@@ -76,7 +101,27 @@ Obj__set(){
76101
# @param ${@:3} Any additional parameters to the method
77102
################################################################
78103
Obj__call(){
104+
# Params
105+
local pointer="$1"
106+
local method_name="$2"
107+
108+
# Getting package alias
109+
IFS='_' read -ra segment <<< "$pointer"
110+
for part in "${segment[@]}"; do
111+
local package_alias="$part"
112+
break
113+
done
114+
115+
# Swapping current context so we can self-reference in any method calls
116+
local old_context=$(Obj_get_imported_package "$Obj__THIS")
117+
local new_context=$(Obj_get_imported_package "$package_alias")
118+
Obj__import "$new_context" "$Obj__THIS"
119+
120+
# Calling method
79121
"$1__$2" "${@:3}"
122+
123+
# Resetting context
124+
Obj__import "$old_context" "$Obj__THIS"
80125
}
81126

82127
################################################################
@@ -87,7 +132,6 @@ Obj__call(){
87132
Obj__dump(){
88133
echo "====== $1 ======"
89134
(set -o posix ; set) | grep ^$1__ | sed -e "s:$1__::g" | sed -e "s:^:| :g"
90-
echo "====================================="
91135
}
92136

93137
##################################
@@ -103,13 +147,13 @@ Obj_generate_uuid(){
103147

104148
while [ "$count" -le $UUID_LENGTH ]
105149
do
106-
random_number=$RANDOM
150+
local random_number=$RANDOM
107151
let "random_number %= 16"
108-
hexval=$(Obj_map_hex "$random_number")
109-
uuid="$uuid$hexval"
152+
local hexval=$(Obj_map_hex "$random_number")
153+
local uuid="$uuid$hexval"
110154
let "count += 1"
111155
done
112-
echo $uuid
156+
echo "$uuid"
113157
}
114158

115159
##################################

0 commit comments

Comments
 (0)