Skip to content

Commit

Permalink
Workflows can provide paths of execution.
Browse files Browse the repository at this point in the history
  • Loading branch information
jeffnyman committed Apr 3, 2015
1 parent 1082678 commit 5e2dc8f
Show file tree
Hide file tree
Showing 5 changed files with 188 additions and 0 deletions.
1 change: 1 addition & 0 deletions lib/symbiont.rb
Expand Up @@ -17,6 +17,7 @@
require 'symbiont/data_reader'
require 'symbiont/data_setter'
require 'symbiont/data_builder'
require 'symbiont/workflows'

module Symbiont
# @param caller [Class] the class including the framework
Expand Down
2 changes: 2 additions & 0 deletions lib/symbiont/errors.rb
Expand Up @@ -3,5 +3,7 @@ module Errors
class NoUrlForDefinition < StandardError; end
class NoUrlMatchForDefinition < StandardError; end
class NoTitleForDefinition < StandardError; end
class WorkflowPathNotFound < StandardError; end
class WorkflowActionNotFound < StandardError; end
end
end
89 changes: 89 additions & 0 deletions lib/symbiont/workflows.rb
@@ -0,0 +1,89 @@
module Symbiont
module WorkflowPaths
def paths
@paths
end

def paths=(path)
@paths = path
end

def path_data
@path_data
end

def path_data=(data)
@path_data = data
end
end

module Workflow
def self.included(caller)
Symbiont.trace("#{caller.class} #{caller} is using workflows.")
caller.extend WorkflowPaths
@def_caller = caller
end

def self.def_caller
@def_caller
end

# Provides a workflow for a given workflow path, using a specific
# definition that is part of that workflow path.
#
# @param definition [Object] definition object using the workflow
# @param path_name [Hash] the name of the path to be used
# @param block [Proc] a block to be executed as part of the workflow
# @return [Object] the definition being interacted with
def workflow_for(definition, path_name = {:using => :default}, &block)
path_name[:using] = :default unless path_name[:using]
path_workflow = workflow_path_for(path_name)

workflow_goal = work_item_index_for(path_workflow, definition) - 1

if workflow_goal == -1
return on(definition, &block)
else
workflow_start = path_name[:from] ? path_workflow.find_index { |item| item[0] == path_name[:from] } : 0
perform_workflow(path_workflow[workflow_start..workflow_goal])
end

on(definition, &block)
end

private

def perform_workflow(definitions)
definitions.each do |definition, action, *args|
active = on(definition)

raise Symbiont::Errors::WorkflowActionNotFound,
"Workflow action '#{action}' not defined on #{definition}." unless active.respond_to? action

active.send action unless args
active.send action, *args if args
end
end

def workflow_path_for(path_name)
# Since I'm dealing with an array that contains a hash that, in turn,
# contains an array of arrays, I need to make sure that I index into
# the array before keying into the hash. That's the purpose of the [0].
path = Workflow.def_caller.paths[path_name[:using]]

raise Symbiont::Errors::WorkflowPathNotFound,
"Workflow path '#{path_name[:using].to_s}' not found." unless path

if Workflow.def_caller.path_data
file_to_load = Workflow.def_caller.path_data[path_name[:using]]
DataBuilder.load "#{file_to_load.to_s}.yml" if file_to_load
end

path
end

def work_item_index_for(path_workflow, definition)
path_workflow.find_index { |item| item[0] == definition }
end
end
end
Empty file added spec/fixtures/test.yml
Empty file.
96 changes: 96 additions & 0 deletions spec/symbiont/workflows_spec.rb
@@ -0,0 +1,96 @@
require 'spec_helper'

class TestPage01
end

class TestPage02
end

class TestPage03
end

class TestWorkflow
include Symbiont::Workflow

def on(caller)
caller.new
end
end

describe Symbiont::Workflow do
before(:each) do
@test_workflow = TestWorkflow.new
end

it 'should store the paths' do
paths = %w(a b c)
TestWorkflow.paths = {:default => paths}
expect(TestWorkflow.paths[:default]).to eq paths
end

it 'should store path data' do
TestWorkflow.path_data = {:default => :test}
expect(TestWorkflow.path_data).to eq({:default => :test})
end

it 'should fail when it does not find a proper path' do
TestWorkflow.paths = {:default => ['a'], :testing => ['b']}
expect { @test_workflow.workflow_for(TestPage02, :using => :no_path )}.to raise_error
end

it 'should navigate to a page calling the default methods (multiple paths)' do
pages = [[TestPage01, :a_method], [TestPage02, :b_method]]
TestWorkflow.paths = {:default => pages}
mock_page = double('mock_page')

allow(TestPage01).to receive(:new).and_return(mock_page)
allow(mock_page).to receive(:a_method)

expect(@test_workflow.workflow_for(TestPage02).class).to eq TestPage02
end

it 'should navigate to a page calling the default methods (only one path)' do
pages = [[TestPage01, :a_method]]
TestWorkflow.paths = {:default => pages}
mock_page = double('mock_page')

allow(TestPage01).to receive(:new).and_return(mock_page)
allow(mock_page).to receive(:a_method)

expect(@test_workflow.workflow_for(TestPage01).class).to eq RSpec::Mocks::Double
end

it 'should load the DataBuilder file when specified' do
pages = [[TestPage01, :a_method], [TestPage02, :b_method]]
TestWorkflow.paths = {:default => pages}
TestWorkflow.path_data = {:default => :test}
mock_page = double('mock_page')

allow(TestPage01).to receive(:new).and_return(mock_page)
allow(mock_page).to receive(:a_method)
allow(Symbiont::DataBuilder).to receive(:load).with('test.yml')

expect(@test_workflow.workflow_for(TestPage02).class).to eq TestPage02
end

it 'should pass parameters to methods when navigating' do
pages = [[TestPage01, :a_method, 'test'], [TestPage02, :b_method]]
TestWorkflow.paths = {:default => pages}
mock_page = double('mock_page')

allow(TestPage01).to receive(:new).and_return(mock_page)
allow(mock_page).to receive(:a_method).with('test')

expect(@test_workflow.workflow_for(TestPage02).class).to eq TestPage02
end

it 'should fail when no default method is specified' do
TestWorkflow.paths = {
:default => [[TestPage01, :a_method], [TestPage02, :b_method]]
}
mock_page = double('mock_page')
allow(TestPage01).to receive(:new).and_return(mock_page)
allow(mock_page).to receive(:respond_to?).with(:a_method).and_return(false)
expect { @test_workflow.workflow_for(TestPage02) }.to raise_error
end
end

0 comments on commit 5e2dc8f

Please sign in to comment.