yawl 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +37 -0
- data/Rakefile +1 -0
- data/examples/cook.rb +11 -0
- data/examples/cook_worker.rb +14 -0
- data/examples/steps/scrambled_eggs.rb +38 -0
- data/lib/yawl/config.rb +28 -0
- data/lib/yawl/log.rb +37 -0
- data/lib/yawl/process.rb +136 -0
- data/lib/yawl/process_definitions.rb +27 -0
- data/lib/yawl/step.rb +109 -0
- data/lib/yawl/step_attempt.rb +11 -0
- data/lib/yawl/steps/base.rb +136 -0
- data/lib/yawl/version.rb +3 -0
- data/lib/yawl/worker.rb +16 -0
- data/lib/yawl.rb +15 -0
- data/migrations/01_setup_tables.rb +34 -0
- data/migrations/02_setup_queue_classic.rb +14 -0
- data/yawl.gemspec +28 -0
- metadata +169 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Ricardo Chimal, Jr.
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# Yawl
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'yawl'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install yawl
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Example
|
24
|
+
|
25
|
+
```
|
26
|
+
bundle exec sequel -m migrations/ postgres://localhost/yawl_examples
|
27
|
+
bundle exec ruby examples/cook.rb
|
28
|
+
bundle exec ruby examples/cook_worker.rb
|
29
|
+
```
|
30
|
+
|
31
|
+
## Contributing
|
32
|
+
|
33
|
+
1. Fork it
|
34
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
35
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
36
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
37
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/examples/cook.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
$:.unshift(File.expand_path(File.join(File.dirname($0), "../lib")))
|
2
|
+
|
3
|
+
require "sequel"
|
4
|
+
|
5
|
+
Sequel.connect(ENV["DATABASE_URL"] || "postgres://localhost/yawl_examples")
|
6
|
+
|
7
|
+
require "yawl"
|
8
|
+
require File.dirname(File.expand_path(__FILE__)) + "/steps/scrambled_eggs"
|
9
|
+
|
10
|
+
p = Yawl::Process.create(:desired_state => "scrambled_eggs")
|
11
|
+
p.start
|
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$:.unshift(File.expand_path(File.join(File.dirname($0), "../lib")))
|
4
|
+
|
5
|
+
require "sequel"
|
6
|
+
Sequel.connect(ENV["DATABASE_URL"] || "postgres://localhost/yawl_examples")
|
7
|
+
|
8
|
+
require "yawl"
|
9
|
+
require "yawl/worker"
|
10
|
+
|
11
|
+
require File.dirname(File.expand_path(__FILE__)) + "/steps/scrambled_eggs"
|
12
|
+
|
13
|
+
$stdout.sync = true
|
14
|
+
Yawl::Worker.start
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# define a set of steps
|
2
|
+
|
3
|
+
Yawl::Steps.set :scrambled_eggs do
|
4
|
+
step :buy_eggs do
|
5
|
+
def run
|
6
|
+
puts "bought a dozen eggs at the market"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
step :put_pan_on_stove do
|
11
|
+
def run
|
12
|
+
puts "put a non-stick pan on the stove"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
step :turn_on_heat_to_medium do
|
17
|
+
def run
|
18
|
+
puts "turn on heat to medium"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
step :crack_eggs_and_scramble do
|
23
|
+
def run
|
24
|
+
puts "crack eggs on pan and scramble"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
step :serve do
|
29
|
+
def run
|
30
|
+
puts "you just got served"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Define a process definition and which set of steps should belong to the process
|
36
|
+
Yawl::ProcessDefinitions.add(:scrambled_eggs) do |process|
|
37
|
+
Yawl::Steps.realize_set_on(:scrambled_eggs, process)
|
38
|
+
end
|
data/lib/yawl/config.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
module Yawl
|
2
|
+
module Config
|
3
|
+
extend self
|
4
|
+
|
5
|
+
attr_writer :app
|
6
|
+
attr_accessor :deploy
|
7
|
+
|
8
|
+
def app
|
9
|
+
@app || "yawl"
|
10
|
+
end
|
11
|
+
|
12
|
+
def log_quiet?
|
13
|
+
env("LOG_QUIET") == "1"
|
14
|
+
end
|
15
|
+
|
16
|
+
def log_sequel?
|
17
|
+
env("LOG_SEQUEL") == "1"
|
18
|
+
end
|
19
|
+
|
20
|
+
def env(key)
|
21
|
+
ENV[key]
|
22
|
+
end
|
23
|
+
|
24
|
+
def env!(key)
|
25
|
+
ENV[key] or raise "Missing ENV[#{key}]"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/yawl/log.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require "scrolls"
|
2
|
+
require "thread"
|
3
|
+
|
4
|
+
module Yawl
|
5
|
+
module Log
|
6
|
+
extend self
|
7
|
+
|
8
|
+
def elapsed_since(t0)
|
9
|
+
"%dms" % (Time.now - t0)
|
10
|
+
end
|
11
|
+
|
12
|
+
def log_measure(data)
|
13
|
+
measure_base = "#{Config.app}.#{data[:ns]}.#{data[:fn]}"
|
14
|
+
begin
|
15
|
+
t0 = Time.now
|
16
|
+
log(data.merge(at: "start"))
|
17
|
+
log_control = OpenStruct.new(data.merge(finish_at: "finish"))
|
18
|
+
result = yield(log_control)
|
19
|
+
log(data.merge("measure##{measure_base}.#{log_control.finish_at}" => elapsed_since(t0), at: log_control.finish_at))
|
20
|
+
result
|
21
|
+
rescue => error
|
22
|
+
log(data.merge("measure##{measure_base}.exception" => elapsed_since(t0), at: "exception", "class" => error.class, message: error.message))
|
23
|
+
raise error
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def log(data, &block)
|
28
|
+
if Config.log_quiet?
|
29
|
+
block.call if block
|
30
|
+
else
|
31
|
+
params = { app: Config.app }
|
32
|
+
params[:source] = Config.deploy if Config.deploy
|
33
|
+
Scrolls.log(params.merge(data), &block)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/yawl/process.rb
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
require "sequel"
|
2
|
+
require "securerandom"
|
3
|
+
|
4
|
+
require "yawl/log"
|
5
|
+
|
6
|
+
module Yawl
|
7
|
+
class Process < Sequel::Model
|
8
|
+
plugin :validation_helpers
|
9
|
+
one_to_many :steps, :order => [:seq]
|
10
|
+
|
11
|
+
class ConcurrencyError < RuntimeError; end
|
12
|
+
class RequestIdAttributeMismatch < RuntimeError; end
|
13
|
+
|
14
|
+
def self.log(data, &block)
|
15
|
+
Log.log({ ns: "process" }.merge(data), &block)
|
16
|
+
end
|
17
|
+
|
18
|
+
def log(data, &block)
|
19
|
+
self.class.log({ process_id: id, desired_state: desired_state }.merge(data), &block)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.existing_by_request_id_and_attributes(request_id, attributes)
|
23
|
+
return nil unless request_id
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.web_only_attributes
|
27
|
+
%w[config desired_state request_id]
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.clean_attributes(attributes)
|
31
|
+
attributes.reject {|k, v| !web_only_attributes.include?(k.to_s) }
|
32
|
+
end
|
33
|
+
|
34
|
+
def before_validation
|
35
|
+
super
|
36
|
+
if new?
|
37
|
+
self.name = SecureRandom.uuid
|
38
|
+
self.created_at = Time.now
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def validate
|
43
|
+
super
|
44
|
+
validates_presence :name
|
45
|
+
validates_includes ProcessDefinitions.all_names, :desired_state
|
46
|
+
end
|
47
|
+
|
48
|
+
def start
|
49
|
+
realize_process_definition
|
50
|
+
start_first_unfinished_step_or_complete
|
51
|
+
end
|
52
|
+
|
53
|
+
def running?
|
54
|
+
!%w[completed failed].include?(state)
|
55
|
+
end
|
56
|
+
|
57
|
+
def current?
|
58
|
+
end
|
59
|
+
|
60
|
+
def start_first_unfinished_step_or_complete
|
61
|
+
unless start_first_unfinished_step
|
62
|
+
log(at: "completed")
|
63
|
+
update(:state => "completed")
|
64
|
+
end_state_reached
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def start_first_unfinished_step
|
69
|
+
if unfinished_step = unfinished_steps.first
|
70
|
+
update(:state => "executing")
|
71
|
+
unfinished_step.start
|
72
|
+
unfinished_step
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def step_finished
|
77
|
+
start_first_unfinished_step_or_complete
|
78
|
+
end
|
79
|
+
|
80
|
+
def step_failed
|
81
|
+
log(at: "failed")
|
82
|
+
update(:state => "failed")
|
83
|
+
end_state_reached
|
84
|
+
end
|
85
|
+
|
86
|
+
def next_step_seq
|
87
|
+
steps_dataset.count == 0 ? 1 : steps_dataset.last.seq + 1
|
88
|
+
end
|
89
|
+
|
90
|
+
def add_step(data = {})
|
91
|
+
data[:seq] ||= next_step_seq
|
92
|
+
super(data)
|
93
|
+
end
|
94
|
+
|
95
|
+
def unfinished_steps
|
96
|
+
steps_dataset.where("state != 'completed'")
|
97
|
+
end
|
98
|
+
|
99
|
+
def end_state_reached
|
100
|
+
end
|
101
|
+
|
102
|
+
def callback_payload
|
103
|
+
{
|
104
|
+
"process" => to_public_h.merge("url" => Urls.process(self))
|
105
|
+
}
|
106
|
+
end
|
107
|
+
|
108
|
+
def merged_config
|
109
|
+
default_config.deep_merge(config || {})
|
110
|
+
end
|
111
|
+
|
112
|
+
def default_config
|
113
|
+
{
|
114
|
+
"ion_branch" => "master",
|
115
|
+
"manifesto_source_manifest" => "production",
|
116
|
+
"manifesto_releases" => {}
|
117
|
+
}
|
118
|
+
end
|
119
|
+
|
120
|
+
def realize_process_definition
|
121
|
+
log(fn: "realize_process_definition") do
|
122
|
+
ProcessDefinitions.realize_on(desired_state, self)
|
123
|
+
save # if realizing made any changes, such as to config
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def to_public_h
|
128
|
+
{
|
129
|
+
"name" => name,
|
130
|
+
"desired_state" => desired_state,
|
131
|
+
"state" => state,
|
132
|
+
"config" => merged_config
|
133
|
+
}
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Yawl
|
2
|
+
module ProcessDefinitions
|
3
|
+
def self.[](name)
|
4
|
+
all[name.to_sym]
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.add(name, &block)
|
8
|
+
all[name.to_sym] = block
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.all
|
12
|
+
@all ||= {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.all_names
|
16
|
+
all.keys.map {|k| k.to_s }
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.realize_on(name, process)
|
20
|
+
unless definition = self[name]
|
21
|
+
raise "Can't find definition #{name}"
|
22
|
+
end
|
23
|
+
|
24
|
+
definition.call(process)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/yawl/step.rb
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
require "sequel"
|
2
|
+
require "queue_classic"
|
3
|
+
require "queue_classic-later"
|
4
|
+
|
5
|
+
require "yawl/log"
|
6
|
+
|
7
|
+
module Yawl
|
8
|
+
class Step < Sequel::Model
|
9
|
+
many_to_one :process
|
10
|
+
one_to_many :attempts, :class => "Yawl::StepAttempt", :order => :started_at
|
11
|
+
|
12
|
+
class Fatal < StandardError
|
13
|
+
def initialize
|
14
|
+
super("Fatal error in step")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class Tired < StandardError
|
19
|
+
def initialize
|
20
|
+
super("Step slept")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.log(data, &block)
|
25
|
+
Log.log({ ns: "step" }.merge(data), &block)
|
26
|
+
end
|
27
|
+
|
28
|
+
def log(data, &block)
|
29
|
+
self.class.log({ process_id: process.id, step_id: id, step_name: name }.merge(data), &block)
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.execute(id)
|
33
|
+
self[id].execute
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.restart_interrupted
|
37
|
+
where(:state => "interrupted").each do |step|
|
38
|
+
step.start
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def duration
|
43
|
+
if attempts.any?
|
44
|
+
(attempts.last.completed_at || Time.now) - attempts.first.started_at
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def start
|
49
|
+
log(fn: "start")
|
50
|
+
update(:state => "pending")
|
51
|
+
QC.enqueue("Yawl::Step.execute", id)
|
52
|
+
end
|
53
|
+
|
54
|
+
def start_in(seconds)
|
55
|
+
log(fn: "start_in", seconds: seconds)
|
56
|
+
QC.enqueue_in(seconds, "Yawl::Step.execute", id)
|
57
|
+
end
|
58
|
+
|
59
|
+
def start_after_delay
|
60
|
+
start_in(real_step.delay_between_attempts)
|
61
|
+
end
|
62
|
+
|
63
|
+
def real_step
|
64
|
+
@real_step ||= Steps.for(name, self)
|
65
|
+
end
|
66
|
+
|
67
|
+
def out_of_attempts?
|
68
|
+
attempts.count >= real_step.attempts
|
69
|
+
end
|
70
|
+
|
71
|
+
def execute
|
72
|
+
log(fn: "execute") do
|
73
|
+
begin
|
74
|
+
# TODO(dpiddy): transactions here?
|
75
|
+
update(:state => "executing")
|
76
|
+
attempt = add_attempt(:started_at => Time.now)
|
77
|
+
real_step.run_with_log
|
78
|
+
log(fn: "execute", at: "run_completed")
|
79
|
+
attempt.update(:output => real_step.output, :completed_at => Time.now)
|
80
|
+
update(:state => "completed")
|
81
|
+
process.step_finished
|
82
|
+
rescue Step::Fatal, Step::Tired, StandardError, SignalException => e
|
83
|
+
log(:fn => "execute", :at => "caught_exception", :class => e.class, :message => e.message)
|
84
|
+
attempt.update(:output => "#{real_step.output}\n\n---\nCAUGHT ERROR: #{e}\n#{e.backtrace.join("\n")}\n", :completed_at => Time.now)
|
85
|
+
|
86
|
+
if out_of_attempts? || e.is_a?(Step::Fatal)
|
87
|
+
update(:state => "failed")
|
88
|
+
process.step_failed
|
89
|
+
raise
|
90
|
+
elsif SignalException === e && e.signm == "SIGTERM" # we are shutting down
|
91
|
+
update(:state => "interrupted")
|
92
|
+
raise
|
93
|
+
else
|
94
|
+
update(:state => "pending")
|
95
|
+
start_after_delay
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def to_public_h
|
102
|
+
{
|
103
|
+
"seq" => seq,
|
104
|
+
"name" => name,
|
105
|
+
"state" => state
|
106
|
+
}
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
require "stringio"
|
2
|
+
require "forwardable"
|
3
|
+
|
4
|
+
module Yawl
|
5
|
+
module Steps
|
6
|
+
class SetDefiner
|
7
|
+
attr_reader :step_names
|
8
|
+
|
9
|
+
def initialize(name, &block)
|
10
|
+
@name = name
|
11
|
+
@step_names = []
|
12
|
+
@block = block
|
13
|
+
end
|
14
|
+
|
15
|
+
def run
|
16
|
+
instance_eval(&@block)
|
17
|
+
end
|
18
|
+
|
19
|
+
def step(name, &block)
|
20
|
+
Steps.step(name, &block).tap do |k|
|
21
|
+
step_names.push(k.name)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.sets
|
27
|
+
@sets ||= {}
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.set(name, &block)
|
31
|
+
name = name.to_sym
|
32
|
+
definer = SetDefiner.new(name, &block)
|
33
|
+
definer.run
|
34
|
+
sets[name] = definer.step_names
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.step(name, &block)
|
38
|
+
name = name.to_sym
|
39
|
+
Class.new(Base, &block).tap do |klass|
|
40
|
+
klass.instance_eval "def name; #{name.inspect} end", __FILE__, __LINE__
|
41
|
+
Base.register(klass)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.realize_set_on(name, process)
|
46
|
+
unless set_step_names = sets[name.to_sym]
|
47
|
+
raise "Set #{name} not found"
|
48
|
+
end
|
49
|
+
|
50
|
+
set_step_names.each do |set_step_name|
|
51
|
+
process.add_step(:name => set_step_name)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.for(name, process_step)
|
56
|
+
name = name.to_sym
|
57
|
+
klass = Base.all_steps[name] || RealStepMissing
|
58
|
+
klass.new(process_step)
|
59
|
+
end
|
60
|
+
|
61
|
+
class Base
|
62
|
+
extend Forwardable
|
63
|
+
|
64
|
+
def self.all_steps
|
65
|
+
@all_steps ||= {}
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.register(step)
|
69
|
+
all_steps[step.name] = step
|
70
|
+
end
|
71
|
+
|
72
|
+
attr_reader :output_io
|
73
|
+
|
74
|
+
def initialize(process_step)
|
75
|
+
@process_step = process_step
|
76
|
+
@output_io = StringIO.new("")
|
77
|
+
end
|
78
|
+
|
79
|
+
def puts(*a)
|
80
|
+
@output_io.puts(*a)
|
81
|
+
end
|
82
|
+
|
83
|
+
def output
|
84
|
+
@output_io.string
|
85
|
+
end
|
86
|
+
|
87
|
+
def attempts
|
88
|
+
3
|
89
|
+
end
|
90
|
+
|
91
|
+
def delay_between_attempts
|
92
|
+
10
|
93
|
+
end
|
94
|
+
|
95
|
+
def process
|
96
|
+
@process_step.process
|
97
|
+
end
|
98
|
+
|
99
|
+
def name
|
100
|
+
self.class.name
|
101
|
+
end
|
102
|
+
|
103
|
+
def run_with_log
|
104
|
+
log(fn: "run") do
|
105
|
+
run
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def sleep
|
110
|
+
raise Step::Tired
|
111
|
+
end
|
112
|
+
|
113
|
+
def fatal!
|
114
|
+
raise Step::Fatal
|
115
|
+
end
|
116
|
+
|
117
|
+
def log(data, &block)
|
118
|
+
Log.log({ ns: "step_#{name}" }.merge(data), &block)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
class RealStepMissing < Base
|
123
|
+
def self.name
|
124
|
+
"real_step_missing"
|
125
|
+
end
|
126
|
+
|
127
|
+
def run
|
128
|
+
raise "The real step is missing"
|
129
|
+
end
|
130
|
+
|
131
|
+
def attempts
|
132
|
+
0
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
data/lib/yawl/version.rb
ADDED
data/lib/yawl/worker.rb
ADDED
data/lib/yawl.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require "yawl/version"
|
2
|
+
|
3
|
+
module Yawl
|
4
|
+
# Your code goes here...
|
5
|
+
end
|
6
|
+
|
7
|
+
require "yawl/config"
|
8
|
+
require "yawl/log"
|
9
|
+
|
10
|
+
require "yawl/process"
|
11
|
+
require "yawl/process_definitions"
|
12
|
+
require "yawl/step"
|
13
|
+
require "yawl/step_attempt"
|
14
|
+
|
15
|
+
require "yawl/steps/base"
|
@@ -0,0 +1,34 @@
|
|
1
|
+
Sequel.migration do
|
2
|
+
change do
|
3
|
+
create_table(:processes) do
|
4
|
+
primary_key :id
|
5
|
+
String :desired_state, :text=>true, :null=>false
|
6
|
+
String :state, :default=>"pending", :text=>true, :null=>false
|
7
|
+
DateTime :created_at
|
8
|
+
String :name, :text=>true
|
9
|
+
String :config
|
10
|
+
String :request_id, :text=>true
|
11
|
+
String :specified_attributes
|
12
|
+
end
|
13
|
+
|
14
|
+
create_table(:steps) do
|
15
|
+
primary_key :id
|
16
|
+
Integer :process_id, :null=>false
|
17
|
+
Integer :seq, :null=>false
|
18
|
+
String :name, :text=>true, :null=>false
|
19
|
+
String :state, :default=>"pending", :text=>true, :null=>false
|
20
|
+
|
21
|
+
index [:process_id]
|
22
|
+
end
|
23
|
+
|
24
|
+
create_table(:step_attempts) do
|
25
|
+
primary_key :id
|
26
|
+
Integer :step_id, :null=>false
|
27
|
+
File :output
|
28
|
+
DateTime :started_at
|
29
|
+
DateTime :completed_at
|
30
|
+
|
31
|
+
index [:step_id]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/yawl.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'yawl/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "yawl"
|
8
|
+
spec.version = Yawl::VERSION
|
9
|
+
spec.authors = ["Ricardo Chimal, Jr."]
|
10
|
+
spec.email = ["kiwi@null.cx"]
|
11
|
+
spec.description = %q{Yet Another Workflow Library for Ruby}
|
12
|
+
spec.summary = %q{Yet Another Workflow Library for Ruby}
|
13
|
+
spec.homepage = "https://github.com/ricardochimal/yawl"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "sequel"
|
22
|
+
spec.add_dependency "scrolls"
|
23
|
+
spec.add_dependency "queue_classic"
|
24
|
+
spec.add_dependency "queue_classic-later"
|
25
|
+
|
26
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
27
|
+
spec.add_development_dependency "rake"
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,169 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: yawl
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Ricardo Chimal, Jr.
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-01-23 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: sequel
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: scrolls
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: queue_classic
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: queue_classic-later
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: bundler
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ~>
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '1.3'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '1.3'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: rake
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
description: Yet Another Workflow Library for Ruby
|
111
|
+
email:
|
112
|
+
- kiwi@null.cx
|
113
|
+
executables: []
|
114
|
+
extensions: []
|
115
|
+
extra_rdoc_files: []
|
116
|
+
files:
|
117
|
+
- .gitignore
|
118
|
+
- Gemfile
|
119
|
+
- LICENSE.txt
|
120
|
+
- README.md
|
121
|
+
- Rakefile
|
122
|
+
- examples/cook.rb
|
123
|
+
- examples/cook_worker.rb
|
124
|
+
- examples/steps/scrambled_eggs.rb
|
125
|
+
- lib/yawl.rb
|
126
|
+
- lib/yawl/config.rb
|
127
|
+
- lib/yawl/log.rb
|
128
|
+
- lib/yawl/process.rb
|
129
|
+
- lib/yawl/process_definitions.rb
|
130
|
+
- lib/yawl/step.rb
|
131
|
+
- lib/yawl/step_attempt.rb
|
132
|
+
- lib/yawl/steps/base.rb
|
133
|
+
- lib/yawl/version.rb
|
134
|
+
- lib/yawl/worker.rb
|
135
|
+
- migrations/01_setup_tables.rb
|
136
|
+
- migrations/02_setup_queue_classic.rb
|
137
|
+
- yawl.gemspec
|
138
|
+
homepage: https://github.com/ricardochimal/yawl
|
139
|
+
licenses:
|
140
|
+
- MIT
|
141
|
+
post_install_message:
|
142
|
+
rdoc_options: []
|
143
|
+
require_paths:
|
144
|
+
- lib
|
145
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
146
|
+
none: false
|
147
|
+
requirements:
|
148
|
+
- - ! '>='
|
149
|
+
- !ruby/object:Gem::Version
|
150
|
+
version: '0'
|
151
|
+
segments:
|
152
|
+
- 0
|
153
|
+
hash: -1330465653979215358
|
154
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
155
|
+
none: false
|
156
|
+
requirements:
|
157
|
+
- - ! '>='
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
segments:
|
161
|
+
- 0
|
162
|
+
hash: -1330465653979215358
|
163
|
+
requirements: []
|
164
|
+
rubyforge_project:
|
165
|
+
rubygems_version: 1.8.23
|
166
|
+
signing_key:
|
167
|
+
specification_version: 3
|
168
|
+
summary: Yet Another Workflow Library for Ruby
|
169
|
+
test_files: []
|