Creating a new class

When programming a new class, one should only worry about the signal processing and state update. In order to free the programmer from most of rubyk’s internal class/object handling, the interface has been made as simple as possible.

This article will drive you through the creation of a simple class that adds value from the second inlet to the values received in the first inlet.

Session example using the object we want to create (to start a session just type “rubyk” at the terminal prompt) :

adding two numbers

> a = Add()
> a.adder(5)
> a.d # debug mode
> a.b(2) # b is a shortcut for 'bang'
[a:1] 7.00

[a:1] indicates that outlet “1” of object “a” has sent the following signal (7.00, a floating point number).

adding a number to a list

> a = Add()
> a.adder(4)
> a.d # debug mode
> a.b(2,3,10)
[a:1] <Matrix [  6.00  7.00  14.00 ], 1x3>

Let’s start by writing the test file.

write tests to define behavior

The test file should be named after the new class and should lie in rubyk/test/objects. For this new class called “Add”, we will create the test file rubyk/test/objects/Add_test.h

The header should include the test helper and declare a new test class named after our object “AddTest”.

#include "test_helper.h" 

class AddTest : public CxxTest::TestSuite, public ParseTest
{
public:

The class should inherit from CxxTest::TestSuite (C++ testing infrastructure) and ParseTest (command line simulation).

testing initial/default conditions

Test methods should start with test_.

void test_initial_conditions( void ) 
{
  setup("t=Add()\nt=>p");
  assert_print("t.b(1)","1.00");
  assert_print("t.b(2,3,10)","<Matrix [  2.00  3.00  10.00 ], 1x3>");
}

The setup method creates testing conditions. assert_print compares the command output with a result. You can omit the trailing end of line.

Note how we use the predefined Print object named “p”.

testing numbers

void test_numbers( void ) 
{
  setup("t=Add()\nt.adder(4)\nt=>p");
  assert_print("t.b(1)","5.00");
  assert_print("t.b(2,3,10)","<Matrix [  6.00  7.00  14.00 ], 1x3>");
}

testing matrices

void test_matrix( void ) 
{
  setup("t=Add()\nt.adder(1,1,2)\nt=>p");
  assert_print("t.b(1,2,3)", "<Matrix [  2.00  3.00  5.00 ], 1x3>");
  parse("b=Buffer(2)\nb.b(1,2,3)\nb=>t");
  assert_print("b.b(4,5,6)\n","<Matrix [  2.00  3.00  5.00  5.00  6.00  8.00 ], 2x3>");
}

errors

[TODO]

C++ class

Processing (life) is usually given through the first inlet called bang. Methods corresponding to other inlets can be triggered but they should only store received signals.

init function

An object is initialized in the following way:

  1. object creation with new and no parameters, adoption by parent
  2. set name
  3. methods creation from prototype
  4. inlets creation
  5. outlets creation
  6. call of object’s init method (should return gNilValue on success)
  7. if everything is ok, call methods corresponding to parameters
  8. call of object’s start method (should return gNilValue on success)

Typically, init is used to prepare the object so it can receive method calls and start launches operations based on the parameters set by these calls. For example a “socket” object might want to wait for “port” method to be called before creating the network port.