Mining the Historian Archives 07

Digitizing the Published Volumes

I plan to finish scanning the seven published volumes this summer so that we can continue to publish the books on CD. We have just about run out of print copies. I had expected to OCR the text, which takes a LOT longer but makes it searchable. However, if we were to OCR the Seven-Volume Index, that would be a far superior solution. We can place the Seven-Volume Index online as part of our project, and also produce it as a searchable text document.

Every entry in the Seven-Volume Index points to a specific volume and page number. I could picture re-scanning the pages in, say, 50-page chunks. If we had sufficient storage space, we could store those chunks online. Thus the Seven-Volume Index item could link to the relevant 50-page chunk.

Read more…

Be the first to comment - What do you think?  Posted by admin - June 27, 2014 at 7:34 pm

Categories: Data Design   Tags:

Mining the Historian Archives 06

Twenty-Four Shelf Feet

I have a very large number of file folders. Each file folder is labeled with a name and a Dwight number. It has proven to be completely impractical to process or even inspect each item within each file folder.

However, it might be possible to process the file folders themselves. I can envision a simple data-entry mechanism such as a spreadsheet to use as a means of inventorying the file folders.

Each file folder has a Dwight number and a name. Those are our two key lookup mechanisms.

Read more…

Be the first to comment - What do you think?  Posted by admin - at 6:58 pm

Categories: Data Design   Tags:

Mining the Historian Archives 05

Contributed GEDCOM Files

There is something which I have been trying to figure out for five years now. This project approach may well provide the solution.

The basic problem which I have been trying to figure out is, How do I fold all of this disparate information together in some coherent way so that we can publish it for our membership? It’s proven to be an impossible task. We have collected genealogical information which has not seen the light of day for more than a quarter century. Many of our information submitters never lived to see their submissions published or otherwise made available!

To be sure, our Association has been actively working with the information, curating and combining it. But it’s never been in a form to be published.

Read more…

1 comment - What do you think?  Posted by admin - at 5:58 pm

Categories: Data Design   Tags:

Mining the Historian Archives 04

Getting to the Specifics

The point of this project is to make information available in usable form. We’ll now look at each collection and see what I envision us doing with it.

Read more…

Be the first to comment - What do you think?  Posted by admin - at 4:56 pm

Categories: Data Design   Tags:

Mining the Historian Archives 03

Genealogy Databases

I personally have a number of genealogy databases, in actual database form:

  1. The John Langbehn database. John, for many years, has been typing the contents of Dwight’s History of the Strong Family into his Family Tree Maker program, a Windows-based genealogy program. Each person, in this Langbehn database, has the Dwight number typed into the “User Reference Number” field.
  2. I have previous editions of the same database from John Langbehn.
  3. I have two versions of my own research database.
  4. I have other databases contributed from related persons.

I have commercial software (TNG, The Next Generation of Genealogy Site Building) capable of importing each of the above databases into its MySQL database and display the information as web pages.

Since TNG is capable of maintaining each family tree database separately, there is no specific need to merge databases. You will recall that I have concluded it is ALWAYS a bad idea to merge groups of genealogy data. The result is a tangled mess. That in fact is why I have two versions of my own research database: I made a mess of the first to the point that I started over from scratch.

Read more…

Be the first to comment - What do you think?  Posted by admin - at 4:15 pm

Categories: Data Design   Tags:

Mining the Historian Archives 02

I don’t have a good handle on the exact project description. Instead let me describe what I am visualizing. From this, I trust, we can define what the project actually is. This post is about classification; then we’ll move on to details.

The Dwight Number

Benjamin Dwight, in 1871, published a two-volume History of the Strong Family. Each person in the books was individually numbered, with a total of about 29,000 numbered persons. Elder John Strong (1610-1699), the Progenitor of the family, is person #1. Sergeant Joseph Barnard (1641-1695), my ancestor, is person #27454.

Read more…

Be the first to comment - What do you think?  Posted by admin - at 3:28 pm

Categories: Data Design   Tags:

Mining the Historian Archives 01

For over five years, I have been trying to find a way to publish information from the Historian Archives of the Strong Family Association of America (SFAA).

The SFAA published seven volumes of genealogy in the 1980s and 1990s. I have in my basement 24 shelf-feet of upright file folders. We have been collecting information for a quarter century which has never yet seen the light of day.

One effort has been to convert this information to database form. There are a number of genealogy-database programs for Macs and PCs, with a standard data interchange format. These programs can generate genealogy books from the database. In theory, then, as new information comes in it can be added to the “master database” and new versions of our books generated.

Read more…

Be the first to comment - What do you think?  Posted by admin - at 2:32 pm

Categories: Data Design   Tags:

Zeta Workflow 13: Sniffer Integration

What’s left?

  1. When constructing the Zeta Components Wrapper, include the Sniffer.
  2. When the wrapper starts or resumes an execution, attach the Sniffer Plugin.
  3. Add each of the Sniffer’s current*() methods to the wrapper interface and implementation.
  4. Copy and modify the sub workflow test. Change the new (copied) test to use currentInterpretation() in the slide verification. This should tell us that everything is integrated and working correctly.
  5. Add docblock comments everywhere. “make phpdoc” will tell us of anything missing.
  6. Run all unit tests (make all).
  7. Add, commit, and push the code.

Having done the above except commit the code, I find that the integration test fails in a big way.

A bit of investigation turns up the problem. We are hitting our own exception for seeing a null execution id in the sub workflow. We’ll add that to the interpreter so it’s clear what happened.

In ExecutionStateRegistry, if the id is null, we now set the id (our internal registry index) to ‘null id’. This will break the unit test which checks for null exception.

The subWorkflowTest distinguishes between the main workflow execution’s slide, and the “current” or “active” sub workflow slide. Since we’re now using the interpreter, we only want the current slide. Adjust the new sniffer sub workflow test accordingly. Now the test passes.

Everything runs cleanly!

5 comments - What do you think?  Posted by admin - March 30, 2014 at 10:12 am

Categories: Zeta Components Workflow   Tags:

Zeta Workflow 12: The Sniffer Class

Here are the Execution Sniffer features.

  • When requested by the wrapper, and given the execution, attach our plugin to the execution. attachPlugin()
  • Return the current Execution State Interpreter result. currentInterpretation()
  • Return execution state of the current Execution State object. currentIsCancelled(), currentHasEnded(), currentIsSuspended(), currentGetVariables(), currentGetVariable(), currentHasVariable(), currentGetId().

We’ll have the Laravel Service Provider wire everything together.

Test Outcomes

Here is our list of tests.

  1. getPlugin() returns the plugin object which was wired up by the service provider.
  2. getInterpreter() returns the interpreter object which was wired up by the service provider.
  3. getRegistry() returns the registry object which was wired up by the service provider.
  4. attachPlugin($execution) calls $execution->addPlugin() with our plugin as wired up by the service provider.
  5. currentInterpretation() calls $registry->getCurrentExecutionState() and returns mock $state, and calls $interpreter->interpretExecutionState() with mock $state and returns ‘whatever’, and the currentInterpretation() call returns the same ‘whatever’.
  6. currentIsCancelled(), currentHasEnded(), currentIsSuspended(), currentGetVariables(), currentGetVariable(), currentHasVariable(), currentGetId() each call $registry->getCurrentExecutionState which returns mock $state; mock $state gets the relevant method called, and that method’s return value is the return value for the current*() retrieval methods.

Service Provider

The tricky part is getting everything wired up correctly in the Service Provider. Here is the first test of getPlugin(), plus various tests ensuring we have our mocks set up correctly.

namespace StarTribune\Workflow\Tests\Sniffer;
use Mockery as m;
class snifferTest extends \TestCase {
    public $execution;
    public $registry;
    public $fixture;
    public $interpreter;
    public $plugin;

    public function setUp() {
        parent::setUp();
        $this->plugin      = m::mock('\StarTribune\Workflow\Sniffer\Plugin');
        $this->interpreter = m::mock('\StarTribune\Workflow\Sniffer\ExecutionStateInterpreter');
        $this->registry    = m::mock('\StarTribune\Workflow\Sniffer\ExecutionStateRegistry');
        $this->plugin->shouldReceive('getRegistry')->andReturn($this->registry);
        $this->execution   = m::mock('\ezcWorkflowExecution');

        \App::instance('workflow.snifferplugin',                                  $this->plugin);
        \App::instance('\StarTribune\Workflow\Sniffer\ExecutionStateInterpreter', $this->interpreter);
        \App::instance('\StarTribune\Workflow\Sniffer\ExecutionStateRegistry',    $this->registry);

        $this->fixture     = \App::make('workflow.sniffer');
    }

    public function testMockClass() {
        $this->assertTrue($this->plugin      instanceof \StarTribune\Workflow\Sniffer\Plugin);
        $this->assertTrue($this->interpreter instanceof \StarTribune\Workflow\Sniffer\ExecutionStateInterpreter);
        $this->assertTrue($this->registry    instanceof \StarTribune\Workflow\Sniffer\ExecutionStateRegistry);
    }

    public function testSamePlugin() {
        $actual = \App::make('workflow.snifferplugin');
        $this->assertSame($this->plugin, $actual);
    }

    public function testSameInterpreter() {
        $actual = \App::make('\StarTribune\Workflow\Sniffer\ExecutionStateInterpreter');
        $this->assertSame($this->interpreter, $actual);
    }

    public function testSameRegistry() {
        $actual = \App::make('\StarTribune\Workflow\Sniffer\ExecutionStateRegistry');
        $this->assertSame($this->registry, $actual);
    }

    public function testFixtureCorrectClass() {
        $this->assertTrue($this->fixture instanceof \StarTribune\Workflow\Sniffer\Sniffer);
    }

    public function testgetPluginReturnsPlugin() {
        $actual = $this->fixture->getPlugin();
        $this->assertSame($this->plugin, $actual);
    }
}

Lines 12-16 set up our mock objects. Inside the service provider, I needed to ensure we pass around the same registry object. You’ll see how we do that in a moment. In short, what we do, is create the registry object, pass it in to the plugin object, and then ask the plugin object to give it back to us.

Lines 18-20 register our mocks with the Laravel Inversion of Control (IoC) container.

Line 22 creates the sniffer object as our test fixture. We have to do this last, because the service provider wires together the various objects, and we need to register them on lines 18-20 to ensure that our test fixture incorporates our mock objects.

The first test, lines 25-29, ensures we got our class names correct in the mock objects. This test won’t often fail unless you mess up the initial copy/paste stuff.

The next three tests (testSamePlugin, testSameInterpreter, testSameRegistry) ensure that we get the same mock object every time we ask for the object. I wrote these tests before touching the Laravel Service Provider. That way, as I mess with things in the service provider, I can be sure this aspect did not break.

Line 46 is my usual boilerplate test ensuring the test fixture is what I think it should be.

Finally, lines 50-53 will only pass once we have the Service Provider wiring everything up correctly.

Here is the Sniffer class:

namespace StarTribune\Workflow\Sniffer;
class Sniffer {
    private $_plugin;
    private $_interpreter;
    private $_registry;

    public function __construct(\StarTribune\Workflow\Sniffer\Plugin $plugin,
                                \StarTribune\Workflow\Sniffer\ExecutionStateInterpreter $interpreter,
                                \StarTribune\Workflow\Sniffer\ExecutionStateRegistry $registry) {
        $this->_plugin = $plugin;
        $this->_interpreter = $interpreter;
        $this->_registry = $registry;
    }

    public function getPlugin() {
        return $this->_plugin;
    }
}

Here are the relevant parts of the service provider:

	public function register()
	{
        $this->registerPurgeExpiredCommand();
        $this->commands('workflow.purgeexpired');
        $this->registerCleanVersionCommand();
        $this->commands('workflow.cleanversion');
        $this->registerDeleteByNameCommand();
        $this->commands('workflow.deletebyname');
        $this->bindZetaComponentsInterface();
        $this->bindSnifferPlugin();
        $this->bindSniffer();
	}

    public function bindSnifferPlugin() {
        $this->app['workflow.snifferplugin'] = $this->app->share(function($app){
            $registry = $this->app->make('\StarTribune\Workflow\Sniffer\ExecutionStateRegistry');
            $plugin   = $this->app->make('\StarTribune\Workflow\Sniffer\Plugin', array($registry));
            return $plugin;
        });
    }

    public function bindSniffer() {
        $this->app['workflow.sniffer'] = $this->app->share(function($app){
            $plugin = $this->app->make('workflow.snifferplugin');
            $registry = $plugin->getRegistry();
            $interpreter = $this->app->make('\StarTribune\Workflow\Sniffer\ExecutionStateInterpreter');
            $implementation = new \StarTribune\Workflow\Sniffer\Sniffer($plugin, $interpreter, $registry);
            return $implementation;
        });
    }

	public function provides()
	{
		return array('workflow.purgeexpired', 'workflow.cleanversion', 'workflow.deletebyname',
                     'workflow.snifferplugin', 'workflow.sniffer');
	}

The Service Provider took some figuring out. I’ve not found clear documentation on exactly how to do this.

The Sniffer has three dependencies: plugin, interpreter, registry. However, we also pass registry as a dependency to plugin.

If you’re passing a dependency into the constructor, the way to do that is in the Service Provider.

That means we need to create the plugin in the service provider (it has a dependency to pass in), and we need to create the sniffer in the service provider (it has three dependencies to pass in). That means we have two separate closures, one which creates the plugin and one which creates the sniffer.

The tricky part is that both the plugin and the sniffer need to receive the SAME registry object.

The way I solved the problem is to create the plugin, injecting the registry, and then allow the plugin to disgorge that same registry object.

On lines 14-20 we create the registry and plugin. The plugin now contains a reference to the registry.

On lines 22-30 we create the sniffer.

Line 24 creates the plugin using the previous closure (lines 14-19). Line 15 registered the closure as ‘workflow.snifferplugin’, and now on line 24 we ask for ‘workflow.snifferplugin’.

Next, we take advantage of the fact that the plugin also created the registry. On line 25 we ask for that already-constructed object.

Line 26 instantiates the interpreter object.

Now, on line 27, we create the sniffer. We pass in the plugin, interpreter, and registry objects. We now know that both the plugin and the sniffer reference the SAME registry object, which was the whole point of the exercise.

Meanwhile, we have the already-seen unit tests which validate that everything took place as we just described.

With the tricky part done, the rest of our tests should be quite straightforward. I’ll work back and forth between test case and production code. Having done so, here is the test class followed by the production Sniffer class.

In developing the addPlugin test, I checked the Zeta Workflow source code and discovered something:

    /**
     * Adds a plugin to this execution.
     *
     * @param ezcWorkflowExecutionPlugin $plugin
     * @return bool true when the plugin was added, false otherwise.
     */
    public function addPlugin( ezcWorkflowExecutionPlugin $plugin )
    {
        $pluginClass = get_class( $plugin );

        if ( !isset( $this->plugins[$pluginClass] ) )
        {
            $this->plugins[$pluginClass] = $plugin;

            return true;
        }
        else
        {
            return false;
        }
    }

We can add some test cases:

  1. Adding a plugin class the first time returns true.
  2. Adding the same plugin class a second time returns false.

The test class:

class snifferTest extends \TestCase {
    public $execution;
    public $registry;
    public $fixture;
    public $interpreter;
    public $plugin;
    public $state;

    public function setUp() {
        parent::setUp();
        $this->plugin      = m::mock('\StarTribune\Workflow\Sniffer\Plugin');
        $this->interpreter = m::mock('\StarTribune\Workflow\Sniffer\ExecutionStateInterpreter');
        $this->registry    = m::mock('\StarTribune\Workflow\Sniffer\ExecutionStateRegistry');
        $this->plugin->shouldReceive('getRegistry')->andReturn($this->registry);
        $this->state       = m::mock('\StarTribune\Workflow\Sniffer\ExecutionState');
        $this->execution   = m::mock('\ezcWorkflowExecution');

        \App::instance('workflow.snifferplugin',                                  $this->plugin);
        \App::instance('\StarTribune\Workflow\Sniffer\ExecutionStateInterpreter', $this->interpreter);
        \App::instance('\StarTribune\Workflow\Sniffer\ExecutionStateRegistry',    $this->registry);

        $this->fixture     = \App::make('workflow.sniffer');
    }

    public function testMockClass() {
        $this->assertTrue($this->plugin      instanceof \StarTribune\Workflow\Sniffer\Plugin);
        $this->assertTrue($this->interpreter instanceof \StarTribune\Workflow\Sniffer\ExecutionStateInterpreter);
        $this->assertTrue($this->registry    instanceof \StarTribune\Workflow\Sniffer\ExecutionStateRegistry);
    }

    public function testSamePlugin() {
        $actual = \App::make('workflow.snifferplugin');
        $this->assertSame($this->plugin, $actual);
    }

    public function testSameInterpreter() {
        $actual = \App::make('\StarTribune\Workflow\Sniffer\ExecutionStateInterpreter');
        $this->assertSame($this->interpreter, $actual);
    }

    public function testSameRegistry() {
        $actual = \App::make('\StarTribune\Workflow\Sniffer\ExecutionStateRegistry');
        $this->assertSame($this->registry, $actual);
    }

    public function testFixtureCorrectClass() {
        $this->assertTrue($this->fixture instanceof \StarTribune\Workflow\Sniffer\Sniffer);
    }

    public function testgetPluginReturnsPlugin() {
        $actual = $this->fixture->getPlugin();
        $this->assertSame($this->plugin, $actual);
    }

    public function testgetInterpreterReturnsInterpreter() {
        $actual = $this->fixture->getInterpreter();
        $this->assertSame($this->interpreter, $actual);
    }

    public function testgetRegistryReturnsRegistry() {
        $actual = $this->fixture->getRegistry();
        $this->assertSame($this->registry, $actual);
    }

    public function testattachPlugin() {
        $this->execution->shouldReceive('addPlugin')
            ->once()
            ->with($this->plugin)
            ->andReturn(true);
        $result = $this->fixture->attachPlugin($this->execution);
        $this->assertTrue(true === $result);
    }

    public function testreattachPlugin() {
        $this->execution->shouldReceive('addPlugin')
            ->times(2)
            ->with($this->plugin)
            ->andReturn(true, false);
        $result1 = $this->fixture->attachPlugin($this->execution);
        $this->assertTrue(true === $result1);
        $result2 = $this->fixture->attachPlugin($this->execution);
        $this->assertTrue(false === $result2);
    }

    public function testcurrentInterpretation() {
        $expected = 'whatever';
        $this->registry->shouldReceive('getCurrentExecutionState')
            ->andReturn($this->state);
        $this->interpreter->shouldReceive('interpretExecutionState')
            ->with($this->state)
            ->andReturn($expected);
        $actual = $this->fixture->currentInterpretation();
        $this->assertEquals($expected, $actual);
    }

    /**
     * @dataProvider dataCurrent
     */
    public function testcurrentIsCancelled($function1, $function2) {
        $expected = 'return_'.$function1;
        $this->registry->shouldReceive('getCurrentExecutionState')
            ->andReturn($this->state);
        $this->state->shouldReceive($function1)
            ->once()
            ->andReturn($expected);
        $actual = $this->fixture->$function2();
        $this->assertEquals($expected, $actual);
    }

    public function dataCurrent() {
        $data = array();
        $data[] = array('isCancelled', 'currentIsCancelled');
        $data[] = array('hasEnded', 'currentHasEnded');
        $data[] = array('isSuspended', 'currentIsSuspended');
        $data[] = array('getId', 'currentGetId');
        $data[] = array('getVariables', 'currentGetVariables');

        return $data;
    }

    public function testGetVariable() {
        $expected = 'theValue';
        $key = 'theName';
        $this->registry->shouldReceive('getCurrentExecutionState')
            ->andReturn($this->state);
        $this->state->shouldReceive('getVariable')
            ->with($key)
            ->once()
            ->andReturn($expected);
        $actual = $this->fixture->currentGetVariable($key);
        $this->assertEquals($expected, $actual);
    }

    public function testHasVariable() {
        $expected = false;
        $key = 'theName';
        $this->registry->shouldReceive('getCurrentExecutionState')
            ->andReturn($this->state);
        $this->state->shouldReceive('hasVariable')
            ->with($key)
            ->once()
            ->andReturn($expected);
        $actual = $this->fixture->currentHasVariable($key);
        $this->assertTrue($expected === $actual);
    }
}

The production Sniffer class:

namespace StarTribune\Workflow\Sniffer;
class Sniffer {
    private $_plugin;
    private $_interpreter;
    private $_registry;

    public function __construct(\StarTribune\Workflow\Sniffer\Plugin $plugin,
                                \StarTribune\Workflow\Sniffer\ExecutionStateInterpreter $interpreter,
                                \StarTribune\Workflow\Sniffer\ExecutionStateRegistry $registry) {
        $this->_plugin = $plugin;
        $this->_interpreter = $interpreter;
        $this->_registry = $registry;
    }

    public function getPlugin() {
        return $this->_plugin;
    }

    public function getInterpreter() {
        return $this->_interpreter;
    }

    public function getRegistry() {
        return $this->_registry;
    }

    public function attachPlugin(\ezcWorkflowExecution $execution) {
        return $execution->addPlugin($this->getPlugin());
    }

    public function currentInterpretation() {
        $state = $this->getRegistry()->getCurrentExecutionState();
        return $this->getInterpreter()->interpretExecutionState($state);
    }

    public function currentIsCancelled() {
        return $this->getCurrent('isCancelled');
    }

    public function currentHasEnded() {
        return $this->getCurrent('hasEnded');
    }

    public function currentIsSuspended() {
        return $this->getCurrent('isSuspended');
    }

    public function currentGetId() {
        return $this->getCurrent('getId');
    }

    public function currentGetVariables() {
        return $this->getCurrent('getVariables');
    }

    public function currentGetVariable($key) {
        return $this->getCurrentKey('getVariable', $key);
    }

    public function currentHasVariable($key) {
        return $this->getCurrentKey('hasVariable', $key);
    }

    protected function getCurrent($function) {
        $state = $this->getRegistry()->getCurrentExecutionState();
        return $state->$function();
    }

    protected function getCurrentKey($function, $key) {
        $state = $this->getRegistry()->getCurrentExecutionState();
        return $state->$function($key);
    }
}

Next up: Sniffer Integration

Be the first to comment - What do you think?  Posted by admin - at 10:11 am

Categories: Zeta Components Workflow   Tags:

Zeta Workflow 11: Sniffer Plugin

The available methods are documented here: http://ezcomponents.org/docs/api/latest/Workflow/ezcWorkflowExecutionPlugin.html. For now let’s keep this simple and only implement afterNodeExecuted().

When called, we want to do the following.

  • Notify the Execution State Registry that this execution is, as of this moment, the current execution.
  • Obtain the relevant Execution State object from the Execution State Registry.
  • Have the relevant Execution State object capture and retain this execution’s current execution state.

Our unit test, therefore, can be frightfully simple. We can pass in mocks of the execution and node, and condition mocks of the Execution State Registry and Execution State object to ensure they are called as intended.

To be sure, we may have some frightfully complex scenarios to explore, such as synchronized parallel executions with paused sub workflows. But we’ll get to those later!

Test Outcomes

Having now built and tested the Execution State Registry, it’s clear that we only need to call the registry’s setCurrentExecution() method. This means we’ll need to pass the registry object in to the plugin’s constructor.

A check of the Zeta Workflow source code http://ezcomponents.org/docs/api/latest/__filesource/fsource_Workflow—Workflow—1.4—src—interfaces—execution_plugin.php.html shows that we’ll be extending the abstract base class, and that it does not define a constructor. So we’re safe defining our constructor to include the registry object.

For our only test, we will condition a mock registry object to require that its setCurrentExecution() method is called with our mock execution object.

Here is the test.

namespace StarTribune\Workflow\Tests\Sniffer;
use Mockery as m;
class snifferPluginTest extends \TestCase {
    public $execution;
    public $registry;
    public $fixture;
    public $node;

    public function setUp() {
        parent::setUp();
        $this->registry  = m::mock('\StarTribune\Workflow\Sniffer\ExecutionStateRegistry');
        $this->execution = m::mock('\ezcWorkflowExecution');
        $this->node      = m::mock('\ezcWorkflowNode');
        $this->fixture   = new \StarTribune\Workflow\Sniffer\Plugin($this->registry);
    }

    public function testMockCorrectClass() {
        $this->assertTrue($this->registry  instanceof \StarTribune\Workflow\Sniffer\ExecutionStateRegistry);
        $this->assertTrue($this->execution instanceof \ezcWorkflowExecution);
        $this->assertTrue($this->node      instanceof \ezcWorkflowNode);
    }

    public function testFixtureCorrectClass() {
        $this->assertTrue($this->fixture instanceof \StarTribune\Workflow\Sniffer\Plugin);
    }

    public function testSetCurrentExecution() {
        $this->registry->shouldReceive('setCurrentExecution')
            ->once()
            ->with($this->execution);
        $this->fixture->afterNodeExecuted($this->execution, $this->node);
    }

}

Here is the production code. As the starting point, I copied the method docblock and signature from the abstract parent class.

namespace StarTribune\Workflow\Sniffer;

class Plugin extends \ezcWorkflowExecutionPlugin {
    protected $registry;

    public function __construct(\StarTribune\Workflow\Sniffer\ExecutionStateRegistry $registry) {
        $this->registry = $registry;
    }

    /**
     * Called after a node has been executed.
     *
     * @param ezcWorkflowExecution $execution
     * @param ezcWorkflowNode      $node
     */
    public function afterNodeExecuted( \ezcWorkflowExecution $execution, \ezcWorkflowNode $node )
    {
        $ignore = $this->registry->setCurrentExecution($execution);
    }
}

Once again we have a small class with a single responsibility.

Next up: The Sniffer Class.

Be the first to comment - What do you think?  Posted by admin - at 9:14 am

Categories: Zeta Components Workflow   Tags:

« Previous PageNext Page »