bumbleworks 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/.rspec +3 -0
- data/.ruby-version +1 -0
- data/.watchr +89 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +84 -0
- data/LICENSE.txt +22 -0
- data/README.md +160 -0
- data/Rakefile +9 -0
- data/bumbleworks.gemspec +30 -0
- data/doc/GUIDE.md +337 -0
- data/doc/TERMS.md +9 -0
- data/lib/bumbleworks.rb +123 -0
- data/lib/bumbleworks/configuration.rb +182 -0
- data/lib/bumbleworks/hash_storage.rb +13 -0
- data/lib/bumbleworks/participant_registration.rb +19 -0
- data/lib/bumbleworks/process_definition.rb +143 -0
- data/lib/bumbleworks/ruote.rb +64 -0
- data/lib/bumbleworks/storage_adapter.rb +23 -0
- data/lib/bumbleworks/support.rb +20 -0
- data/lib/bumbleworks/task.rb +109 -0
- data/lib/bumbleworks/tree_builder.rb +60 -0
- data/lib/bumbleworks/version.rb +3 -0
- data/spec/fixtures/apps/with_default_directories/app/participants/honey_participant.rb +3 -0
- data/spec/fixtures/apps/with_default_directories/app/participants/molasses_participant.rb +3 -0
- data/spec/fixtures/apps/with_default_directories/config_initializer.rb +4 -0
- data/spec/fixtures/apps/with_default_directories/full_initializer.rb +12 -0
- data/spec/fixtures/apps/with_default_directories/lib/process_definitions/garbage_collector.rb +3 -0
- data/spec/fixtures/apps/with_default_directories/lib/process_definitions/make_honey.rb +3 -0
- data/spec/fixtures/apps/with_default_directories/lib/process_definitions/make_molasses.rb +6 -0
- data/spec/fixtures/apps/with_specified_directories/config_initializer.rb +5 -0
- data/spec/fixtures/apps/with_specified_directories/specific_directory/definitions/.gitkeep +0 -0
- data/spec/fixtures/apps/with_specified_directories/specific_directory/participants/.gitkeep +0 -0
- data/spec/fixtures/definitions/a_list_of_jams.rb +4 -0
- data/spec/fixtures/definitions/nested_folder/test_nested_process.rb +3 -0
- data/spec/fixtures/definitions/test_process.rb +5 -0
- data/spec/integration/configuration_spec.rb +43 -0
- data/spec/integration/sample_application_spec.rb +45 -0
- data/spec/lib/bumbleworks/configuration_spec.rb +162 -0
- data/spec/lib/bumbleworks/participant_registration_spec.rb +13 -0
- data/spec/lib/bumbleworks/process_definition_spec.rb +178 -0
- data/spec/lib/bumbleworks/ruote_spec.rb +107 -0
- data/spec/lib/bumbleworks/storage_adapter_spec.rb +41 -0
- data/spec/lib/bumbleworks/support_spec.rb +40 -0
- data/spec/lib/bumbleworks/task_spec.rb +274 -0
- data/spec/lib/bumbleworks/tree_builder_spec.rb +95 -0
- data/spec/lib/bumbleworks_spec.rb +133 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/support/path_helpers.rb +11 -0
- metadata +262 -0
data/doc/TERMS.md
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
Process Definitions - a map of the assembly line, which dictates where the activities, gateways, events, etc. will be encountered by the product.
|
2
|
+
Process Instances - a trip through the assembly line, taken by a single product
|
3
|
+
Activities - actions performed by factory workers or robots on the product - carving, painting, cleaning, filling, soldering, etc.
|
4
|
+
Gateways - decision points on where the product should go, when the assembly line branches (e.g. larger widgets go to the Big Widget Cleaning Station, smaller ones go to the Small Widget Cleaning Station)
|
5
|
+
Message Events, Schedules - pausing the line for a shift change or lunch break, waiting at a certain stage until a needed part has been delivered, etc.
|
6
|
+
Workitem/Payload - the Product
|
7
|
+
Worker - turning on the power to the Conveyor Belt
|
8
|
+
Workflow Engine - the Factory
|
9
|
+
This is an abstract term that encompasses all of the above. It's a term that's often used in comparison with a "state machine."
|
data/lib/bumbleworks.rb
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
require "forwardable"
|
2
|
+
require "bumbleworks/version"
|
3
|
+
require "bumbleworks/configuration"
|
4
|
+
require "bumbleworks/support"
|
5
|
+
require "bumbleworks/process_definition"
|
6
|
+
require "bumbleworks/task"
|
7
|
+
require "bumbleworks/participant_registration"
|
8
|
+
require "bumbleworks/ruote"
|
9
|
+
require "bumbleworks/hash_storage"
|
10
|
+
|
11
|
+
module Bumbleworks
|
12
|
+
class UnsupportedMode < StandardError; end
|
13
|
+
class UndefinedSetting < StandardError; end
|
14
|
+
class InvalidSetting < StandardError; end
|
15
|
+
|
16
|
+
class << self
|
17
|
+
extend Forwardable
|
18
|
+
attr_accessor :env
|
19
|
+
|
20
|
+
Configuration.defined_settings.each do |setting|
|
21
|
+
def_delegators :configuration, setting, "#{setting.to_s}="
|
22
|
+
end
|
23
|
+
|
24
|
+
def_delegators Bumbleworks::Ruote, :dashboard, :start_worker!
|
25
|
+
def_delegator Bumbleworks::ProcessDefinition, :define, :define_process
|
26
|
+
|
27
|
+
# @public
|
28
|
+
# Returns the global configuration, or initializes a new
|
29
|
+
# configuration object if it doesn't exist yet.
|
30
|
+
def configuration
|
31
|
+
@configuration ||= begin
|
32
|
+
configuration = Bumbleworks::Configuration.new
|
33
|
+
configuration.add_storage_adapter(Bumbleworks::HashStorage)
|
34
|
+
if defined?(Bumbleworks::Redis::Adapter) && Bumbleworks::Redis::Adapter.auto_register?
|
35
|
+
configuration.add_storage_adapter(Bumbleworks::Redis::Adapter)
|
36
|
+
end
|
37
|
+
if defined?(Bumbleworks::Sequel::Adapter) && Bumbleworks::Sequel::Adapter.auto_register?
|
38
|
+
configuration.add_storage_adapter(Bumbleworks::Sequel::Adapter)
|
39
|
+
end
|
40
|
+
configuration
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# @public
|
45
|
+
# Yields the global configurtion to a block.
|
46
|
+
# @yield [configuration] global configuration
|
47
|
+
#
|
48
|
+
# @example
|
49
|
+
# Bumbleworks.configure do |c|
|
50
|
+
# c.root = 'path/to/ruote/assets'
|
51
|
+
# end
|
52
|
+
# @see Bumbleworks::Configuration
|
53
|
+
def configure(&block)
|
54
|
+
unless block
|
55
|
+
raise ArgumentError.new("You tried to .configure without a block!")
|
56
|
+
end
|
57
|
+
yield configuration
|
58
|
+
end
|
59
|
+
|
60
|
+
# @public
|
61
|
+
# Same as .configure, but clears all existing configuration
|
62
|
+
# settings first.
|
63
|
+
# @yield [configuration] global configuration
|
64
|
+
# @see Bumbleworks.configure
|
65
|
+
def configure!(&block)
|
66
|
+
@configuration = nil
|
67
|
+
configure(&block)
|
68
|
+
end
|
69
|
+
|
70
|
+
# @public
|
71
|
+
# Accepts a block for registering participants which
|
72
|
+
# is envoked when start! is called. Notice that a
|
73
|
+
# 'catchall' storage participant is always added to
|
74
|
+
# the end of the list (unless it is defined in the block).
|
75
|
+
#
|
76
|
+
# @example
|
77
|
+
# Bumbleworks.register_participants do
|
78
|
+
# painter PainterClass
|
79
|
+
# builder BuilderClass
|
80
|
+
# plumber PlumberClass
|
81
|
+
# end
|
82
|
+
def register_participants(&block)
|
83
|
+
@participant_block = block
|
84
|
+
end
|
85
|
+
|
86
|
+
# @public
|
87
|
+
# Starts a Ruote engine, sets up the storage and registers participants
|
88
|
+
# and process_definitions with the Ruote engine.
|
89
|
+
def start!
|
90
|
+
autoload_and_register_participants
|
91
|
+
load_process_definitions
|
92
|
+
end
|
93
|
+
|
94
|
+
# @public
|
95
|
+
# Resets Bumbleworks - clears configuration and setup variables, and
|
96
|
+
# shuts down the dashboard.
|
97
|
+
def reset!
|
98
|
+
@configuration = nil
|
99
|
+
@participant_block = nil
|
100
|
+
@registered_process_definitions = nil
|
101
|
+
Bumbleworks::Ruote.reset!
|
102
|
+
end
|
103
|
+
|
104
|
+
# @public
|
105
|
+
# Launches the workflow engine with the specified process name.
|
106
|
+
# The process_definiton_name should already be registered with
|
107
|
+
# Bumbleworks.
|
108
|
+
def launch!(process_definition_name, options = {})
|
109
|
+
Bumbleworks::Ruote.launch(process_definition_name, options)
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
def autoload_and_register_participants
|
115
|
+
Bumbleworks::ParticipantRegistration.autoload_all
|
116
|
+
Bumbleworks::Ruote.register_participants(&@participant_block)
|
117
|
+
end
|
118
|
+
|
119
|
+
def load_process_definitions
|
120
|
+
Bumbleworks::ProcessDefinition.create_all_from_directory!(definitions_directory)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,182 @@
|
|
1
|
+
module Bumbleworks
|
2
|
+
# Stores configruation information
|
3
|
+
#
|
4
|
+
# Configruation inforamtion is loaded from a configuration block defined within
|
5
|
+
# the client application.
|
6
|
+
#
|
7
|
+
# @example Standard settings
|
8
|
+
# Bumbleworks.configure do |c|
|
9
|
+
# c.definitions_directory = '/path/to/ruote/definitions/directory'
|
10
|
+
# c.storage = Redis.new(:host => '127.0.0.1', :db => 0, :thread_safe => true)
|
11
|
+
# # ...
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
class Configuration
|
15
|
+
attr_reader :storage_adapters
|
16
|
+
|
17
|
+
class << self
|
18
|
+
def define_setting(name)
|
19
|
+
defined_settings << name
|
20
|
+
attr_accessor name
|
21
|
+
end
|
22
|
+
|
23
|
+
def defined_settings
|
24
|
+
@defined_settings ||= []
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
# Path to the root folder where Bumbleworks assets can be found.
|
30
|
+
# This includes the following structure:
|
31
|
+
# /lib
|
32
|
+
# /process_definitions
|
33
|
+
# /participants
|
34
|
+
# /app/participants
|
35
|
+
#
|
36
|
+
# default: non, must be specified
|
37
|
+
# Exceptions: raises Bumbleworks::UndefinedSetting if not defined by the client
|
38
|
+
#
|
39
|
+
define_setting :root
|
40
|
+
|
41
|
+
# Path to the folder which holds the ruote definition files. Bumbleworks
|
42
|
+
# will autoload all definition files by recursively traversing the directory
|
43
|
+
# tree under this folder. No specific loading order is guranteed
|
44
|
+
#
|
45
|
+
# default: ${Bumbleworks.root}/lib/process_definitions
|
46
|
+
define_setting :definitions_directory
|
47
|
+
|
48
|
+
# Path to the folder which holds the ruote participant files. Bumbleworks
|
49
|
+
# will autoload all participant files by recursively traversing the directory
|
50
|
+
# tree under this folder. No specific loading order is guranteed
|
51
|
+
#
|
52
|
+
# Bumbleworks will guarantee that these files are autoloaded before registration
|
53
|
+
# of participants.
|
54
|
+
#
|
55
|
+
# default: ${Bumbleworks.root}/participants then ${Bumbleworks.root}/app/participants
|
56
|
+
define_setting :participants_directory
|
57
|
+
|
58
|
+
# Bumbelworks requires a dedicated key-value storage for process information. Two
|
59
|
+
# storage solutions are currently supported: Redis and Sequel. You can set the storage
|
60
|
+
# as follows:
|
61
|
+
#
|
62
|
+
# @Exammple: Redis
|
63
|
+
# Bumbleworks.storage = Redis.new(:host => '127.0.0.1', :db => 0, :thread_safe => true)
|
64
|
+
#
|
65
|
+
# @Example: Sequel with Postgres db
|
66
|
+
# Bumbleworks.storage = Sequel.connect('postgres://user:password@host:port/database_name')
|
67
|
+
#
|
68
|
+
define_setting :storage
|
69
|
+
|
70
|
+
# By default, a worker will NOT be started when the storage is initialized;
|
71
|
+
# this is the recommended practice since workers should be instantiated in
|
72
|
+
# their own threads, and multiple workers (even on different hosts) can run
|
73
|
+
# simultaneously. However, if you want a worker to start up immediately
|
74
|
+
# (useful for testing or development), set autostart_worker to true.
|
75
|
+
#
|
76
|
+
# default: false
|
77
|
+
define_setting :autostart_worker
|
78
|
+
|
79
|
+
def initialize
|
80
|
+
@storage_adapters = []
|
81
|
+
end
|
82
|
+
|
83
|
+
# Path where Bumbleworks will look for ruote process defintiions to load.
|
84
|
+
# The path can be relative or absolute. Relative paths are
|
85
|
+
# relative to Bumbleworks.root.
|
86
|
+
#
|
87
|
+
def definitions_directory
|
88
|
+
@definitions_folder ||= default_definition_directory
|
89
|
+
end
|
90
|
+
|
91
|
+
# Path where Bumbleworks will look for ruote participants to load.
|
92
|
+
# The path can be relative or absolute. Relative paths are
|
93
|
+
# relative to Bumbleworks.root.
|
94
|
+
#
|
95
|
+
def participants_directory
|
96
|
+
@participants_folder ||= default_participant_directory
|
97
|
+
end
|
98
|
+
|
99
|
+
# Root folder where bumbleworks looks for ruote assets (participants,
|
100
|
+
# process_definitions, ..etc.) The root path must be absolute.
|
101
|
+
# It can be defined throguh a configuration block:
|
102
|
+
# Bumbleworks.configure {|c| c.root = '/somewhere'}
|
103
|
+
#
|
104
|
+
# Or directly:
|
105
|
+
# Bumbleworks.root = '/somewhere/else/'
|
106
|
+
#
|
107
|
+
# If the root is not defined, Bumbleworks will use the root of known
|
108
|
+
# frameworks (Rails, Sinatra and Rory). Otherwise, it will raise an
|
109
|
+
# error if not defined.
|
110
|
+
#
|
111
|
+
def root
|
112
|
+
@root ||= case
|
113
|
+
when defined?(Rails) then Rails.root
|
114
|
+
when defined?(Rory) then Rory.root
|
115
|
+
when defined?(Sinatra::Application) then Sinatra::Application.root
|
116
|
+
else
|
117
|
+
raise UndefinedSetting.new("Bumbleworks.root must be set") unless @root
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# Whether or not we should start a worker when initializing the dashboard
|
122
|
+
# and storage. Only returns true if set explicitly to true.
|
123
|
+
#
|
124
|
+
def autostart_worker
|
125
|
+
@autostart_worker == true
|
126
|
+
end
|
127
|
+
|
128
|
+
# Add a storage adapter to the set of possible adapters. Takes an object
|
129
|
+
# that responds to `driver`, `use?(storage)`, and `display_name`.
|
130
|
+
#
|
131
|
+
def add_storage_adapter(storage_adapter)
|
132
|
+
raise ArgumentError, "#{storage_adapter} is not a Bumbleworks storage adapter" unless
|
133
|
+
storage_adapter.respond_to?(:driver) &&
|
134
|
+
storage_adapter.respond_to?(:use?) &&
|
135
|
+
storage_adapter.respond_to?(:display_name)
|
136
|
+
|
137
|
+
@storage_adapters << storage_adapter
|
138
|
+
@storage_adapters
|
139
|
+
end
|
140
|
+
|
141
|
+
# Clears all memoize variables and configuration settings
|
142
|
+
#
|
143
|
+
def clear!
|
144
|
+
defined_settings.each {|setting| instance_variable_set("@#{setting}", nil)}
|
145
|
+
@storage_adapters = []
|
146
|
+
@definitions_folder = @participants_folder = nil
|
147
|
+
end
|
148
|
+
|
149
|
+
private
|
150
|
+
def defined_settings
|
151
|
+
self.class.defined_settings
|
152
|
+
end
|
153
|
+
|
154
|
+
def default_definition_directory
|
155
|
+
default_folders = ['lib/process_definitions']
|
156
|
+
find_folder(default_folders, @definitions_directory, "Definitions folder not found")
|
157
|
+
end
|
158
|
+
|
159
|
+
def default_participant_directory
|
160
|
+
default_folders = ['participants', 'app/participants']
|
161
|
+
find_folder(default_folders, @participants_directory, "Participants folder not found")
|
162
|
+
end
|
163
|
+
|
164
|
+
def find_folder(default_directories, defined_directory, message)
|
165
|
+
# use defined directory structure if defined
|
166
|
+
if defined_directory
|
167
|
+
defined_directory = File.join(root, defined_directory) unless defined_directory[0] == '/'
|
168
|
+
end
|
169
|
+
|
170
|
+
# next look in default directory structure
|
171
|
+
defined_directory ||= default_directories.detect do |default_folder|
|
172
|
+
folder = File.join(root, default_folder)
|
173
|
+
next unless File.directory?(folder)
|
174
|
+
break folder
|
175
|
+
end
|
176
|
+
|
177
|
+
return defined_directory if File.directory?(defined_directory.to_s)
|
178
|
+
|
179
|
+
raise Bumbleworks::InvalidSetting, "#{message}: #{defined_directory}"
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Bumbleworks
|
2
|
+
class ParticipantRegistration
|
3
|
+
class << self
|
4
|
+
# @public
|
5
|
+
# Autoload all participant classes defined in files in the
|
6
|
+
# participants_directory. The symbol for autoload comes from the
|
7
|
+
# camelized version of the filename, so this method is dependent on
|
8
|
+
# following that convention. For example, file `goat_challenge.rb`
|
9
|
+
# should define `GoatChallenge`.
|
10
|
+
#
|
11
|
+
def autoload_all(options = {})
|
12
|
+
options[:directory] ||= Bumbleworks.participants_directory
|
13
|
+
Bumbleworks::Support.all_files(options[:directory], :camelize => true) do |path, name|
|
14
|
+
Object.autoload name.to_sym, path
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
require "bumbleworks/tree_builder"
|
2
|
+
|
3
|
+
module Bumbleworks
|
4
|
+
class ProcessDefinition
|
5
|
+
class NotFound < StandardError; end
|
6
|
+
class FileNotFound < StandardError; end
|
7
|
+
class Invalid < StandardError; end
|
8
|
+
|
9
|
+
attr_reader :name, :definition, :tree
|
10
|
+
|
11
|
+
# @public
|
12
|
+
# Initialize a new ProcessDefinition, supplying a name (required), and
|
13
|
+
# definition or a tree (one of which is required). The definition should
|
14
|
+
# be a string with a Bumbleworks.define_process block, and the tree should
|
15
|
+
# be an actual Ruote tree.
|
16
|
+
#
|
17
|
+
def initialize(opts = {})
|
18
|
+
@name = opts[:name]
|
19
|
+
@definition = opts[:definition]
|
20
|
+
@tree = opts[:tree]
|
21
|
+
end
|
22
|
+
|
23
|
+
# @public
|
24
|
+
# Validates the ProcessDefinition by checking existence and uniqueness of
|
25
|
+
# name, existence of one of definition or tree, and validity of definition.
|
26
|
+
# Raises a Bumbleworks::ProcessDefinition::Invalid exception if errors are
|
27
|
+
# found, otherwise returns true
|
28
|
+
#
|
29
|
+
def validate!
|
30
|
+
errors = []
|
31
|
+
errors << "Name must be specified" unless @name
|
32
|
+
errors << "Definition or tree must be specified" unless @definition || @tree
|
33
|
+
errors << "Name is not unique" if Bumbleworks.dashboard.variables[@name]
|
34
|
+
begin
|
35
|
+
build_tree!
|
36
|
+
rescue Invalid
|
37
|
+
errors << "Definition is not a valid process definition"
|
38
|
+
end
|
39
|
+
raise Invalid, "Validation failed: #{errors.join(', ')}" unless errors.empty?
|
40
|
+
true
|
41
|
+
end
|
42
|
+
|
43
|
+
# @public
|
44
|
+
# Validates first, then adds the tree (builds it if necessary) to the
|
45
|
+
# dashboard's variables.
|
46
|
+
#
|
47
|
+
def save!
|
48
|
+
validate!
|
49
|
+
Bumbleworks.dashboard.variables[@name] = @tree || build_tree!
|
50
|
+
self
|
51
|
+
end
|
52
|
+
|
53
|
+
# @public
|
54
|
+
# Uses the TreeBuilder to construct a tree from the given definition.
|
55
|
+
# Captures any tree building exceptions and reraises them as
|
56
|
+
# Bumbleworks::ProcessDefinition::Invalid exceptions.
|
57
|
+
#
|
58
|
+
def build_tree!
|
59
|
+
return nil unless @definition
|
60
|
+
@tree = Bumbleworks::TreeBuilder.new(
|
61
|
+
:name => name, :definition => definition
|
62
|
+
).build!
|
63
|
+
rescue Bumbleworks::TreeBuilder::InvalidTree => e
|
64
|
+
raise Invalid, e.message
|
65
|
+
end
|
66
|
+
|
67
|
+
# @public
|
68
|
+
# A definition can be loaded directly from a file (this is the easiest way
|
69
|
+
# to do it, after the .create_all_from_directory! method). Simply reads
|
70
|
+
# the file at the given path, and set this instance's definition to the
|
71
|
+
# contents of that file.
|
72
|
+
#
|
73
|
+
def load_definition_from_file(path)
|
74
|
+
if File.exists?(path)
|
75
|
+
@definition = File.read(path)
|
76
|
+
else
|
77
|
+
raise FileNotFound, "No file found at #{path}"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
class << self
|
82
|
+
# @public
|
83
|
+
# Given a key, will instantiate a new ProcessDefinition populated with the
|
84
|
+
# tree found in the dashboard variables at that key. If the key isn't
|
85
|
+
# found, an exception is thrown.
|
86
|
+
#
|
87
|
+
def find_by_name(search_key)
|
88
|
+
if saved_tree = Bumbleworks.dashboard.variables[search_key]
|
89
|
+
new(:name => search_key, :tree => saved_tree)
|
90
|
+
else
|
91
|
+
raise NotFound, "No definition by the name of \"#{search_key}\" has been registered yet"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# @public
|
96
|
+
# This method provides a way to define a process definition directly,
|
97
|
+
# without having to create it as a string definition or a tree. It takes
|
98
|
+
# a block identical to Ruote.define's block, normalizes the definition's
|
99
|
+
# name, and `#create!`s a ProcessDefinition with the resulting tree.
|
100
|
+
#
|
101
|
+
def define(*args, &block)
|
102
|
+
tree_builder = Bumbleworks::TreeBuilder.from_definition(*args, &block)
|
103
|
+
tree_builder.build!
|
104
|
+
create!(:tree => tree_builder.tree, :name => tree_builder.name)
|
105
|
+
end
|
106
|
+
|
107
|
+
# @public
|
108
|
+
# Instantiates a new ProcessDefinition, then `#save`s it.
|
109
|
+
#
|
110
|
+
def create!(opts)
|
111
|
+
pdef = new(opts)
|
112
|
+
pdef.save!
|
113
|
+
pdef
|
114
|
+
end
|
115
|
+
|
116
|
+
# @public
|
117
|
+
# For every *.rb file in the given directory (recursive), creates a new
|
118
|
+
# ProcessDefinition, reading the file's contents for the definition
|
119
|
+
# string. If the :skip_invalid option is specified, all invalid
|
120
|
+
# definitions are skipped, and everything else is loaded. Otherwise, the
|
121
|
+
# first invalid definition found triggers a rollback and raises the
|
122
|
+
# exception.
|
123
|
+
#
|
124
|
+
def create_all_from_directory!(directory, opts = {})
|
125
|
+
added_names = []
|
126
|
+
Bumbleworks::Support.all_files(directory) do |path, basename|
|
127
|
+
puts "Registering process definition #{basename} from file #{path}" if opts[:verbose] == true
|
128
|
+
begin
|
129
|
+
create!(:name => basename, :definition => File.read(path))
|
130
|
+
added_names << basename
|
131
|
+
rescue Invalid
|
132
|
+
raise unless opts[:skip_invalid] == true
|
133
|
+
end
|
134
|
+
end
|
135
|
+
rescue Invalid
|
136
|
+
added_names.each do |name|
|
137
|
+
Bumbleworks.dashboard.variables[name] = nil
|
138
|
+
end
|
139
|
+
raise
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|