Home

Awesome

Obj

Obj is an Ash module that adds object support to Bash.

If you want to first get excited about this before reading the entire README, jump to this section.

Getting started

Obj is part of the Ash core, so you can just start using it in your Ash modules.

For an example of an ash module that uses objects, look here.

Features + Usage

Imports

Before we can start using any objects, we must first import them.

If the objects you want to use are in your current module, they are already imported for you.

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:

Obj__import "$package_to_import" "$package_alias"

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

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:

Obj__import "github.com/BrandonRomano/ash-obj-examples" "objex"

External modules must first be installed before importing them. See apm.

Creating Classes

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

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 for class naming as many modern programming languages use.

For example, if I were to create a class that would define what a Person object is, I would name the file Person.sh.

Class Example

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:

For a fully commented version of this class, look here

#!/bin/bash
# This is a simple class that represents a Person.

# Public member variables
Person__id=""
Person__name=""
Person__age=""

# Private member variable
Person_birthdays_count=0

# Constructor
Person__construct(){
    Person__id="$1"
    Person__name="$2"
    Person__age="$3"
}

# Public method
Person__make_older(){
    Person__age=$((Person__age+1))
    Person_update_birthday_count
}

# Private method
Person_update_birthday_count(){
    Person_birthdays_count=$((Person_birthdays_count+1))
    echo "Happy Birthday $Person__name!"
    echo "This is birthday #$Person_birthdays_count we have celebrated with $Person__name."; echo
}

Public Members

Public members are variables that are bound to an object, that may be both fetched and updated from outside the scope of the class.

A public member is denoted by the Class name followed by two underscores __.

Following our example above, Person__id is a public member.

Private Members

Private members are variables that are bound to an object, but may not be accessed from outside the scope of the class. These are very powerful, as they allow you to manage state within an object without exposing additional information.

A private member is denoted by the Class name followed by a single underscore _.

Following our example above, Person_birthdays_count is a private member.

Public Methods

Public methods are functions bound within an object that have access to all of the public/private members. Public methods also have access to all other public/private methods. Public methods may be called from outside of the scope of the object.

A public method is a function denoted by the Class name followed by two underscores __.

Following our example above, Person__make_older is a public method.

Private Methods

Private methods are functions bound within an object that have access to all of the public/private members. Private methods also have access to all other public/private methods. Private methods may not be called from outside of the scope of the object, and are to only be used internally.

A private method is a function denoted by the Class name followed by a single underscore _.

Following our example above, Person_update_birthday_count is a private method.

Constructors

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

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

Following our example above, Person__construct is our constructor.

Creating and Using Objects

Now that we've created this cool class, I'd imagine now we would want to use it.

Objects by Reference

It's worth noting that all objects created will be by reference (or by means of pointer).

This makes this library work extremely well with bash, as these pointers are just strings that can be dealt with in any way that bash currently supports. This allows you to throw objects into arrays, use them as parameters to functions, use them in subshells, etc, all without any additional extension to Bash.

Instantiating Objects (Obj__alloc / Obj__init)

To create an object, there are two steps. First we have to allocate a pointer for the object, then we have to initialize the object.

To allocate a pointer for an object, we must use Obj__alloc, which is passed a class name. Object alloc will return the pointer you will use to refer to the object, so hold onto this!

To initialize an object, we must use Obj__init, which is passed the object pointer. You may pass any additional parameters after this, and they will get passed along to the objects construct method.

For example, if I wanted to create a new person object that represents myself, I would do this:

brandon=$(Obj__alloc "Person")
Obj__init $brandon 1 "Brandon" 23

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

From an External Package

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:

# Importing
Obj__import "github.com/BrandonRomano/ash-obj-examples" "objex"

# Creating the Object
brandon=$(Obj__alloc "objex.Person")  # Note the `objex.` before the class name
Obj__init $brandon 1 "Brandon" 23

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.

A quick Obj__init Gotcha

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:

Variables in a subshell are not visible outside the block of code in the subshell. They are not accessible to the parent process, to the shell that launched the subshell. These are, in effect, variables local to the child process. - tldp.org

Object Dump for Debugging (Obj__dump)

It's very useful to find out the state of a current object when trying to debug a script.

If I were to run Obj__dump $brandon, immediately after initializing the object in the last section, it would output:

====== Person_4D69741E8CB76419 ======
| age=23
| id=3
| name='Brandon Romano'

As you can see, Obj__dump prints out all of the public variables in an object.

Setters + Getters

As mentioned in previous sections, public members can be accessed / updated.

Updating Member Variables (Obj__set)

To update a public member belonging to an object, you can use Obj__set.

There are three parameters to Obj__set.

The first is the object pointer itself that was returned by Obj__alloc.

The second is the name of the public member variable. This does not include the in-class ClassName__ prefix. If in your class you had a member Person__name, you would simply pass name.

The third is the new value you would like to set to that member variable.

Obj__set $brandon name "Brandon Romano"

An Obj__dump on our $brandon object would now yield:

====== Person_4D69741E8CB76419 ======
| age=23
| id=3
| name='Brandon Romano'

Accessing Member Variables (Obj__get)

To get the value of a public member belonging to an object, you can use Obj__get.

There are two parameters to Obj__get.

The first is the object pointer itself that was returned by Obj__alloc.

The second is the variable name, following the same rules as discussed in the previous section.

If I were to now run:

myname=$(Obj__get $brandon name)
echo $myname

It would output:

Brandon Romano

Calling Public Methods

Inside of a class, all methods are called with the full name of the function (e.g. Person__make_older).

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

There are two required parameters to Obj__call.

The first is the object pointer itself that was returned by Obj__alloc.

The second is the method name. This does not include the in-class ClassName__ prefix. If in your class you had a public method Person__make_older, you would simply pass make_older.

Following our example, we could call:

Obj__call $brandon make_older

If you wanted to pass any parameters to the actual function inside of the class, this is possible. You are able to pass any number of parameters to Obj__call after the second parameter, and they will be packed up as parameters to the make_older method.

Hypothetically, if make_older allowed a single parameter ($1) to specify how many years older, you could call:

Obj__call $brandon make_older 2

License

MIT