Created
March 26, 2014 17:42
-
-
Save jeffdeville/9789055 to your computer and use it in GitHub Desktop.
workflow - pdf of flow: http://cl.ly/image/04331c0c3900
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class CreateWorkplaceWorkflow | |
include Workflow::Driver | |
include Workflow::Notifiable | |
attr_accessor :state, :cs_project, :bsil_workplace, :account_id | |
def initialize() | |
initialize_steps(step_arr) | |
end | |
def prepare(account_id, project_params, cs_project_service, bsil_workplace_service, bsil_workplace_notification_service) | |
@account_id = account_id | |
@project_params = project_params | |
@cs_project_service = cs_project_service | |
@bsil_workplace_service = bsil_workplace_service | |
end | |
def step_arr | |
[ | |
{ create_cs_project: { | |
success: :create_bsil_workplace, | |
failure: :notify } }, | |
{ create_bsil_workplace: { | |
success: :add_workplace_notification_recipients, | |
failure: :retry_until_complete } }, | |
{ add_workplace_notification_recipients: { | |
failure: :retry_until_complete } }, | |
] | |
end | |
def create_cs_project#(project_params) | |
@cs_project = CloudStack::Project.new(project_params) | |
cs_project_service.create @cs_project | |
Workflow::Response.success | |
rescue | |
Workflow::Response.failure | |
end | |
def add_bsil_workplace#(project_params, cs_project) | |
return Workflow::Response.retry_until_complete if BSIL.is_down? | |
@bsil_workplace = BusiBee::Workplace.new(project_params) | |
update_workplace_type @cs_project, @bsil_workplace | |
@bsil_workplace_service.create @bsil_workplace | |
Workflow::response.success | |
rescue | |
Workflow::Response.retry_until_complete | |
end | |
def add_workplace_notification_recipients#(current_user, bsil_workplace, account_id) | |
return Workflow::Response.retry_until_complete if BSIL.is_down? | |
bsil_workplace_notification_service.create(account_id, bsil_workplace.id, current_user.email) | |
Workflow::response.success | |
rescue | |
Workflow::Response.retry_until_complete | |
end | |
private | |
def update_workplace_type(cs_project, bsil_workplace) | |
if cs_project.personal? | |
bsil_workplace.cloudstack_id = current_user.accountid | |
bsil_workplace.type = "CS_ACCOUNT" | |
else | |
bsil_workplace.cloudstack_id = cs_project.id | |
bsil_workplace.type = "CS_PROJECT" | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Workflows | |
module Workflow::Driver | |
attr_accessor :steps | |
def initialize_steps(steps) | |
steps = Array(steps) | |
first_step, @steps = Workflow::Step.create_steps(steps) | |
choose_first_step first_step | |
end | |
def before_step(step) | |
method_name = "before_#{step}" | |
return self.send(method_name) if respond_to? method_name | |
true | |
end | |
def after_step(step, result) | |
method_name = "after_#{step}" | |
return self.send(method_name, result) if respond_to? method_name | |
result | |
end | |
# This shold be configurable | |
def around_step(step_name) | |
return yield | |
end | |
def execute_step(step) | |
begin | |
around_step_result = around_step(step.name) do | |
if before_step(step.name) | |
result = self.send(step.name) | |
# Can the after state cancel the flow? if so... I haven't done that yet. | |
after_step(step.name, result) | |
end | |
end | |
around_step_result | |
rescue => e | |
Rails.logger.error "#{e.message}\n#{e.backtrace.try(:join, "\n")}" | |
return Workflow::Response.failure("Uncaught exception! \n #{e.message}\n#{e.backtrace.try(:join, "\n")}") | |
end | |
end | |
def run(override_step = nil) | |
@next_step = steps[override_step] unless override_step.nil? | |
while true | |
break if @next_step.nil? | |
self.persist_state @next_step.name | |
result = execute_step @next_step | |
@next_step = determine_next_step(result) | |
end | |
end | |
# just used to verify that your workflow will handle all of its defined branches | |
def all_steps_defined? | |
undefined_steps = @steps.values.map{|s| [s.name] + s.step_map.values }.flatten.uniq. | |
find_all{|step| !(self.respond_to?(step) || step.to_s.start_with?("notify_of")) } | |
Rails.logger.error(pp undefined_steps) | |
return true if undefined_steps.empty? | |
false | |
end | |
def reload_and_continue | |
# because the properties were all serialized, they will need to be refreshed. | |
wakeup | |
choose_first_step | |
run | |
end | |
def roofy_the_workflow(response, run_at=nil) | |
delay_until(response, run_at) | |
Workflow::Response.stop | |
end | |
private | |
# There are a few 'special' response statuses, and they behave like this: | |
# :success - If success is returned, and there is nothing to go on to, the process will simply stop. | |
# Assumption is that we are at the end of the flow. Will probably want to hard code this to :stop at some point. | |
# :no_matter_what - This response has to be by itself. It's there so that come hell or high water, the next step following this | |
# one is executed. It's used when you have several things that should all run, but you don't want them in | |
# the same method because they are separate concerns. | |
def determine_next_step(response) | |
@current_step = @next_step | |
next_step_symbol = case | |
when @current_step[:no_matter_what] | |
@current_step[:no_matter_what] | |
when response.status == :stop then nil | |
when response.status == :sleep then delay_until(response, response.message) | |
when @current_step[response.status] then @current_step[response.status] | |
when @current_step[response.status].nil? && (response.success? || response.failure?) then nil | |
else | |
msg = "#{@current_step.name} returned: #{response.status}, but we can't find that outcome's step" | |
p msg | |
raise ArgumentError, msg | |
end | |
next_step_symbol ? @steps[next_step_symbol] : nil | |
end | |
# Todo: remove this steps_array - it's here because once @steps is made, I don't | |
# know which step was defined first because ruby 1.8 is pissy like that. | |
def choose_first_step(first_step=nil) | |
last_run_step = self.state #load_state | |
@next_step = last_run_step ? @steps[last_run_step] : first_step | |
end | |
def delay_until(response, run_at=nil) | |
step_after_awakening = @next_step[response.status] | |
self.persist_state(step_after_awakening) | |
if run_at | |
# require 'pry'; binding.pry | |
options = {:run_at => run_at} | |
delay(options).reload_and_continue | |
else | |
delay.reload_and_continue | |
end | |
nil | |
end | |
def delay(options = {}) | |
self | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Workflow::Step | |
include Comparable | |
attr_accessor :name, :step_map | |
def initialize(step) | |
if step.is_a? Hash | |
@name = step.keys.first | |
@step_map = step[@name] | |
# Verify biz rules | |
if @step_map[:no_matter_what] && @step_map.length > 1 | |
raise ArgumentError, "When specifying a no_matter_what step, only that step can be referenced" | |
end | |
else | |
@name = step | |
@step_map = {} | |
end | |
end | |
def [](result) | |
@step_map[result] | |
end | |
def to_s | |
name | |
end | |
def self.create_steps(steps_array) | |
steps = Array(steps_array).map{|s| Workflow::Step.new(s) } | |
first_step = steps.first | |
steps = steps.inject({}){|acc, step| acc[step.name] = step; acc; } | |
return first_step, steps | |
end | |
def <=>(other) | |
case other | |
when Symbol | |
@name == other ? 0 : -1 | |
when Workflow::Step | |
@name == other.name ? 0 : -1 | |
else | |
-1 | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment