@@ -14,16 +14,28 @@ Included with MovingImages is a library of ruby code that makes creating the JSO
Ruby is a scripting language that comes with OS X, as well as being able to write standalone scripts ruby has an interactive environment called irb which makes it easy to try things out. To access irb just type irb in terminal. If you are completely new to ruby I would recommend the [20 minute ruby introduction](https://www.ruby-lang.org/en/documentation/quickstart/).
MILA comes with a ruby script library for interacting and giving MILA instructions. The library is wrapped in a ruby module called MovingImages. When the ruby library is a little more stable I will make the library into a ruby gem.
MILA comes with a ruby gem for interacting and giving MILA instructions. A gem is the usual way for installing a library of functionality that adds features to ruby.
To have access to MovingImage's ruby library whilst using interactive ruby (irb) you will need to change directories in terminal to the directory where you have installed the ruby scripts before type irb. This will be easier when I've created a ruby gem. for now if you've installed the scripts into the default location using the MovingImages application then in terminal typing:
#### Interactive Ruby
To start ruby's interactive environment (it is called a repl)
cd ~/scripts
irb
will start an interactive ruby session with access to the MovingImage ruby library.
and you will get a prompt that looks like:
```ruby
irb(main):001:0>
```
To have access to MovingImage's ruby library whilst using interactive ruby (irb) you just need to require the moving images ruby gem, so type in require 'moving_images':
```ruby
irb(main):001:0>require'moving_images'
```
will start an interactive ruby session with access to the MovingImages gem. You only need to "require" the "moving_images" gem once each time you start irb.
Within the MovingImages module are a collection of modules and classes. The module Smig is the one used for sending commands to MILA using smig. A few small commands that are used often in irb are included in Smig for convenience.
...
...
@@ -33,14 +45,27 @@ A convenience method to close all base objects in MovingImages is:
MovingImages::Smig.closeall_nothrow()
```
To reduce what you need to type the MovingImages:: prefix can be left out if earlier on in the script or in irb "include MovingImages" has been run. If you start irb from within the scripts folder as described above then "include MovingImages" will have already been done for you.
To reduce what you need to type the MovingImages:: prefix can be left out if earlier on in the script or in irb "include MovingImages" has been run.
```ruby
includeMovingImages
```
This can also be done for sub modules of the MovingImages module. The module CommandModule is also a useful module to include as it contains a lot of functionality, so instead you could do:
```ruby
includeMovingImages
includeMovingImages::CommandModule
```
#### Creating and Performing Commands
The Smig methods will raise an exception if the command line tool "smig" returns an error code unless the methods have nothrow in their method name. The closeall method is a bit dangerous because if a script is running that has nothing to do with who sent the closeall_nothrow message then any objects that script created will also be closed. I only use this method in interactive ruby when I've been testing things out and I know I have no other scripts running. I do not use it in complete scripts.
If you have a single command that you want performed you can just call:
```ruby
Smig.perform_command(theCommand)
Smig.perform_command(the_command)
```
where "theCommand" is the command to be run, see further down for how to create a command.
...
...
@@ -48,12 +73,12 @@ where "theCommand" is the command to be run, see further down for how to create
More commonly you will have a SmigCommands object which contains a list of commands to be performed, a SmigCommands object has a few properties for customizing how the commands should be run or how the results of the commands should be returned.
```ruby
Smig.perform_commands(theCommands)
Smig.perform_commands(the_commands)
```
You can take two different approaches to writing your ruby scripts. You can make a single command, send that command to be run using the Smig.perform_method, wait for the reply and then prepare and send the next command. This is still quick and the time taken for message passing is not large. The restriction of this approach is that you can't take advantage of asynchronous processing but you can make decisions in your ruby code based on the output from a previous command.
Originally my biggest need for feedback as to the results of commands was to get the dimensions of image files using a get property request of an image importer object, but as long as spotlight is working you can use that to get the dimensions of image files and other properties like color space and bit depth.
I have written a Spotlight module that uses Apple's spotlight technology to find image files with particular dimensions or when the files were created, the Spotlight module also provides methods for getting information about image file like their dimensions. These methods can be used when generating commands to fit the needs of images with different dimensions etc..
The second approach is to build up a list of as many commands as possible and to then run them using Smig.perform_commands. This approach can require more planning, but is slightly faster and more efficient. If you really want to maximise throughput of image processing this approach makes running commands asynchronously possible.
...
...
@@ -64,14 +89,14 @@ The CommandModule module of MovingImages wraps all the methods and classes that
Any CommandModule method that begins with "make_", makes a command object.
The above makes a command that will be sent to the receiver object which in this example will need to be an image exporter object as it is the only object type that understands the "addimage" command. The imageSource is the object that provides the image to be added to image exporter and imageIndex is optional. If the image to be added is from a image importer object and the imported image file contains more than one image and you don't want the first image you will need to specify imageIndex with the desired image index. A bitmap context object and a nsgraphics context (window) are also valid objects to be an image source.
After a command has been created, it can either be run immediately using:
After a command has been created, it can either be run immediately using the Smig class method perform_command:
```ruby
Smig.perform_command(addImageCommand)
Smig.perform_command(addimage_command)
```
or it can be added to a SmigCommands object.
...
...
@@ -79,38 +104,50 @@ or it can be added to a SmigCommands object.
To create a SmigCommands object:
```ruby
theCommands=SmigCommands.new
the_commands=SmigCommands.new
```
You can set various properties of a SmigCommands object, the following demonstrates some of these:
To add a command to a SmigCommands object you use the add_command method:
```ruby
the_commands.add_command(addimage_command)
```
The default configuration will run the commands synchronously waiting for the commands to complete before returning, it will return the result of the last command to be performed and if an error occurs running one of the commands then that command will be last command to run and the result will be the error code and message.
You use the Smig class method perform_commands to run the commands in a SmigCommands object:
```ruby
theCommands.set_run_asynchronously(true)# default is false
theCommands.set_stoponfailure(true)# default is true
The above sets up the SmigCommands object so that the commands will be run asynchronously (not concurrently, the commands will be run one after the other, but that the commands will be put into a queue to be run and then MovingImages will return and become available to be sent another list of commands). If there is a failure running any command then no more commands in that list will be run (except cleanup commands). The result of the last command to be run will be saved to a json file called "movingimagesresults.json". This way you can check when the commands were completed, you can also check the contents to see if an error occurred.
Note the plural form of the perform_commands method here. The singular version actually just creates a SmigCommands object and adds the command to it before calling perform_commands.
To add a command to be performed to a SmigCommands object you do:
You can set various properties of a SmigCommands object, the following demonstrates configuring running commands asynchronously:
There is also a add_tocleanupcommands_closeobject method. This method takes an object id which is a way for MovingImages to identify an object internally and creates a close object command and then adds that command to the list of clean up commands. The cleanup commands are a list of commands that get run when the last command to run in the main command list is finished. This provides a mechanism for ensuring all objects that are created that need to be closed will be closed whether the commands in the main list were run without failure or not.
The above sets up a SmigCommands object so that the commands will be run asynchronously (not concurrently, the commands will be run one after the other, but that the commands will be put into a queue to be run and then MovingImages will return and become available to be sent another list of commands). If there is a failure running any command then no more commands in that list will be run (except cleanup commands). The result of the last command to be run will be saved to a json file called "movingimagesresults.json". This way you can check when the commands were completed, you can also check the contents to see if an error occurred.
There is also a ```add_tocleanupcommands_closeobject``` method. This method takes an object id which is a way for MovingImages to identify an object internally and creates a close object command and then adds that command to the list of clean up commands. The cleanup commands are a list of commands that get run when the last command to run in the main command list is finished. This provides a mechanism for ensuring all objects that are created that need to be closed will be closed whether the commands in the main list were run without failure or not.
SmigCommands objects also has some convenience methods for making commands that create objects. I added these methods because I noticed I was repeatedly following the same pattern for making create object commands.
So if I want to make a create bitmap context command:
which will make the create bitmap context command, add it to the SmigCommands object list of commands, add a close object command to the cleanup command list, and returns an object id which you can use to refer to the object when sending commands to the bitmap context object. When creating those commands the bitmapObjectID is the receiver object.
...
...
@@ -118,17 +155,28 @@ which will make the create bitmap context command, add it to the SmigCommands ob
The MovingImages Framework identifies a base object from the information in a simple dictionary/json object. The SmigIDHash module provides a number of methods for creating the dictionary/json object representation of an internal MovingImages base object. When object creation and closing happens within the same list of commands the best way to refer to an object is to provide a unique name when you make the create object command. This is what a SmigCommands object does when you ask it to create an object. The uuid method of the SecureRandom module makes it easy to create unique names and this is what I use. To limit the scope within which to find an object with a particular name the object type is also specified. In this situation the SmidIDHash method is called like so:
When an object is created MovingImages gives it a unique object reference, if the command that created the object was the last to run then the result returned will be the object reference. You can create an object id using an object reference:
If you have created an object, you need to remember to close that object. If the object id has been passed to the ```add_tocleanupcommands_closeobject``` method after being created or when created with the SmigCommands convenience methods with the add to clean up commands set to true then the object will be closed for you. If not then you need to send it the close command.
To create the close command to be sent to the bitmap object: