swarm 0.1.0

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.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.rspec +3 -0
  4. data/.ruby-version +1 -0
  5. data/CODE_OF_CONDUCT.md +13 -0
  6. data/Gemfile +4 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +39 -0
  9. data/Rakefile +13 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +7 -0
  12. data/lib/swarm/engine/base/job.rb +31 -0
  13. data/lib/swarm/engine/base/queue.rb +60 -0
  14. data/lib/swarm/engine/volatile/job.rb +57 -0
  15. data/lib/swarm/engine/volatile/queue.rb +85 -0
  16. data/lib/swarm/engine/worker/command.rb +61 -0
  17. data/lib/swarm/engine/worker.rb +73 -0
  18. data/lib/swarm/evaluation/expression_evaluator.rb +40 -0
  19. data/lib/swarm/evaluation/workitem_context.rb +17 -0
  20. data/lib/swarm/expression.rb +107 -0
  21. data/lib/swarm/expressions/activity_expression.rb +11 -0
  22. data/lib/swarm/expressions/branch_expression.rb +44 -0
  23. data/lib/swarm/expressions/concurrence_expression.rb +41 -0
  24. data/lib/swarm/expressions/conditional_expression.rb +36 -0
  25. data/lib/swarm/expressions/sequence_expression.rb +16 -0
  26. data/lib/swarm/expressions/subprocess_expression.rb +14 -0
  27. data/lib/swarm/hive.rb +69 -0
  28. data/lib/swarm/hive_dweller.rb +170 -0
  29. data/lib/swarm/observers/base.rb +17 -0
  30. data/lib/swarm/participant.rb +18 -0
  31. data/lib/swarm/participants/storage_participant.rb +12 -0
  32. data/lib/swarm/participants/trace_participant.rb +27 -0
  33. data/lib/swarm/pollen/parser.rb +95 -0
  34. data/lib/swarm/pollen/reader.rb +22 -0
  35. data/lib/swarm/pollen/transformer.rb +66 -0
  36. data/lib/swarm/process.rb +57 -0
  37. data/lib/swarm/process_definition.rb +53 -0
  38. data/lib/swarm/router.rb +18 -0
  39. data/lib/swarm/storage.rb +56 -0
  40. data/lib/swarm/stored_workitem.rb +15 -0
  41. data/lib/swarm/support.rb +81 -0
  42. data/lib/swarm/version.rb +3 -0
  43. data/lib/swarm.rb +24 -0
  44. data/swarm.gemspec +31 -0
  45. metadata +199 -0
@@ -0,0 +1,57 @@
1
+ require "timeout"
2
+
3
+ module Swarm
4
+ class Process < HiveDweller
5
+ set_columns :process_definition_id, :workitem, :root_expression_id, :parent_expression_id
6
+ many_to_one :process_definition, :class_name => "Swarm::ProcessDefinition"
7
+ many_to_one :parent_expression, :class_name => "Swarm::Expression"
8
+
9
+ def wait_until_finished(timeout: 5)
10
+ Swarm::Support.wait_until(timeout: timeout) { finished? }
11
+ end
12
+
13
+ def launch
14
+ hive.queue('launch', self)
15
+ self
16
+ end
17
+
18
+ def _launch
19
+ root_expression = SequenceExpression.create(
20
+ :hive => hive,
21
+ :parent_id => id,
22
+ :position => [0],
23
+ :workitem => workitem,
24
+ :process_id => id
25
+ )
26
+ root_expression.apply
27
+ self.root_expression_id = root_expression.id
28
+ save
29
+ end
30
+
31
+ def root_expression
32
+ @root_expression ||= begin
33
+ reload! unless root_expression_id
34
+ if root_expression_id
35
+ Expression.fetch(root_expression_id, hive: hive)
36
+ end
37
+ end
38
+ end
39
+
40
+ def finished?
41
+ root_expression && root_expression.replied?
42
+ end
43
+
44
+ def node_at_position(position)
45
+ raise ArgumentError unless position == 0
46
+ process_definition.tree
47
+ end
48
+
49
+ def move_on_from(expression)
50
+ self.workitem = expression.workitem
51
+ save
52
+ if parent_expression
53
+ parent_expression.move_on_from(self)
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,53 @@
1
+ require "json"
2
+
3
+ module Swarm
4
+ class ProcessDefinition < HiveDweller
5
+ class NotYetPersistedError < StandardError; end
6
+
7
+ set_columns :tree, :name, :version
8
+
9
+ class << self
10
+ def create_from_json(json, hive: Hive.default)
11
+ create(**parse_json_definition(json).merge(:hive => hive))
12
+ end
13
+
14
+ def create_from_pollen(pollen, hive: Hive.default)
15
+ json = Swarm::Pollen::Reader.new(pollen).to_json
16
+ create_from_json(json, hive: hive)
17
+ end
18
+
19
+ def parse_json_definition(json)
20
+ parsed = JSON.parse(json)
21
+ if parsed.is_a?(Array)
22
+ { :tree => parsed }
23
+ else
24
+ {
25
+ :name => parsed["name"],
26
+ :version => parsed["version"],
27
+ :tree => parsed["definition"]
28
+ }
29
+ end
30
+ end
31
+
32
+ def find_by_name(name)
33
+ detect { |definition| definition.name == name }
34
+ end
35
+ end
36
+
37
+ def create_process(workitem:, **args)
38
+ raise NotYetPersistedError unless id
39
+ Process.create(
40
+ args.merge({
41
+ :workitem => workitem,
42
+ :hive => hive,
43
+ :process_definition_id => id
44
+ })
45
+ )
46
+ end
47
+
48
+ def launch_process(workitem:, **args)
49
+ process = create_process(workitem: workitem, **args)
50
+ process.launch
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,18 @@
1
+ module Swarm
2
+ class Router
3
+ class << self
4
+ def expression_class_for_node(node)
5
+ command = node[0]
6
+ expression_type = case command
7
+ when "if", "unless"
8
+ "conditional"
9
+ when "sequence", "concurrence", "subprocess"
10
+ command
11
+ else
12
+ "activity"
13
+ end
14
+ Swarm::Support.constantize("swarm/#{expression_type}_expression")
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,56 @@
1
+ module Swarm
2
+ class Storage
3
+ attr_reader :backend
4
+
5
+ def initialize(backend)
6
+ @backend = backend
7
+ end
8
+
9
+ def regex_for_type(type)
10
+ /^#{type}\:(.*)/
11
+ end
12
+
13
+ def ids_for_type(type)
14
+ keys = if backend.is_a?(Redis)
15
+ backend.keys("#{type}:*")
16
+ else
17
+ backend.keys.select { |key| key.match(regex_for_type(type)) }
18
+ end
19
+ keys.map { |key| key.gsub(regex_for_type(type), '\1') }
20
+ end
21
+
22
+ def serialize(value)
23
+ return nil if value.nil?
24
+ value.to_json
25
+ end
26
+
27
+ def deserialize(value)
28
+ return nil if value.nil?
29
+ JSON.parse(value)
30
+ end
31
+
32
+ def [](key)
33
+ deserialize(backend[key])
34
+ end
35
+
36
+ def []=(key, value)
37
+ backend[key] = serialize(value)
38
+ end
39
+
40
+ def delete(key)
41
+ if backend.respond_to?(:del)
42
+ backend.del(key)
43
+ else
44
+ backend.delete(key)
45
+ end
46
+ end
47
+
48
+ def truncate
49
+ if backend.respond_to?(:flushdb)
50
+ backend.flushdb
51
+ else
52
+ backend.clear
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,15 @@
1
+ module Swarm
2
+ class StoredWorkitem < HiveDweller
3
+ extend Forwardable
4
+
5
+ def_delegators :expression, :node, :command, :arguments, :process_id, :workitem
6
+
7
+ set_columns :expression_id
8
+ many_to_one :expression, :class_name => "Swarm::Expression"
9
+
10
+ def proceed
11
+ delete
12
+ expression.reply
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,81 @@
1
+ require "securerandom"
2
+
3
+ module Swarm
4
+ module Support
5
+ class << self
6
+ def deep_merge(hsh1, hsh2, combine_arrays: :override)
7
+ hsh1.merge(hsh2) { |key, v1, v2|
8
+ if [v1, v2].all? { |v| v.is_a?(Array) }
9
+ combine_arrays(v1, v2, method: combine_arrays)
10
+ elsif [v1, v2].all? { |v| v.is_a?(Hash) }
11
+ deep_merge(v1, v2)
12
+ else
13
+ v2
14
+ end
15
+ }
16
+ end
17
+
18
+ def symbolize_keys!(hsh)
19
+ hsh.keys.each do |key|
20
+ hsh[key.to_sym] = hsh.delete(key)
21
+ end
22
+ hsh
23
+ end
24
+
25
+ def symbolize_keys(hsh)
26
+ symbolize_keys!(hsh.dup)
27
+ end
28
+
29
+ def combine_arrays(v1, v2, method: :concat)
30
+ case method.to_s
31
+ when "uniq"
32
+ v1 | v2
33
+ when "concat"
34
+ v1.concat v2
35
+ when "override"
36
+ v2
37
+ else
38
+ raise ArgumentError, "unknown array combination method: #{method}"
39
+ end
40
+ end
41
+
42
+ def uuid_with_timestamp
43
+ "#{Time.now.strftime("%Y%m%d-%H%M%S")}-#{SecureRandom.uuid}"
44
+ end
45
+
46
+ def camelize(string)
47
+ string = string.sub(/^[a-z\d]*/) { $&.capitalize }
48
+ string = string.gsub(/(?:_|(\/))([a-z\d]*)/) { "#{$1}#{$2.capitalize}" }.gsub('/', '::')
49
+ end
50
+
51
+ def constantize(string)
52
+ name_parts = camelize(string).split('::')
53
+ name_parts.shift if name_parts.first.empty?
54
+ constant = Object
55
+
56
+ name_parts.each do |name_part|
57
+ const_defined_args = [name_part]
58
+ const_defined_args << false unless Module.method(:const_defined?).arity == 1
59
+ constant_defined = constant.const_defined?(*const_defined_args)
60
+ constant = constant_defined ? constant.const_get(name_part) : constant.const_missing(name_part)
61
+ end
62
+ constant
63
+ end
64
+
65
+ def slice(hash, *keys)
66
+ {}.tap { |h|
67
+ keys.each { |k|
68
+ h[k] = hash[k] if hash.has_key?(k)
69
+ }
70
+ }
71
+ end
72
+
73
+ def wait_until(timeout: 5, initial_delay: nil)
74
+ sleep(initial_delay) if initial_delay.is_a?(Numeric)
75
+ Timeout::timeout(timeout) do
76
+ sleep 0.05 until yield
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,3 @@
1
+ module Swarm
2
+ VERSION = "0.1.0"
3
+ end
data/lib/swarm.rb ADDED
@@ -0,0 +1,24 @@
1
+ require "swarm/version"
2
+ require "swarm/support"
3
+ require "swarm/engine/base/queue"
4
+ require "swarm/engine/volatile/queue"
5
+ require "swarm/engine/worker"
6
+ require "swarm/hive"
7
+ require "swarm/hive_dweller"
8
+ require "swarm/process_definition"
9
+ require "swarm/process"
10
+ require "swarm/expression"
11
+ require "swarm/expressions/conditional_expression"
12
+ require "swarm/expressions/concurrence_expression"
13
+ require "swarm/expressions/sequence_expression"
14
+ require "swarm/expressions/activity_expression"
15
+ require "swarm/expressions/subprocess_expression"
16
+ require "swarm/stored_workitem"
17
+ require "swarm/participants/trace_participant"
18
+ require "swarm/participants/storage_participant"
19
+ require "swarm/pollen/reader"
20
+ require "redis"
21
+
22
+ module Swarm
23
+ # Your code goes here...
24
+ end
data/swarm.gemspec ADDED
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'swarm/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "swarm"
8
+ spec.version = Swarm::VERSION
9
+ spec.authors = ["Ravi Gadad"]
10
+ spec.email = ["ravi@gadad.net"]
11
+
12
+ spec.summary = %q{A Ruby workflow engine}
13
+ spec.description = %q{A Ruby workflow engine}
14
+ spec.homepage = "https://github.com/bumbleworks/swarm"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_dependency "redis"
23
+ spec.add_dependency "parslet"
24
+
25
+ spec.add_development_dependency "bundler", "~> 1.8"
26
+ spec.add_development_dependency "rake", "~> 10.0"
27
+ spec.add_development_dependency "rspec", "~> 3.0"
28
+ spec.add_development_dependency "timecop", "~> 0.7"
29
+ spec.add_development_dependency 'simplecov'
30
+ spec.add_development_dependency "pry"
31
+ end
metadata ADDED
@@ -0,0 +1,199 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: swarm
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Ravi Gadad
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-01-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: redis
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: parslet
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.8'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.8'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: timecop
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.7'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.7'
97
+ - !ruby/object:Gem::Dependency
98
+ name: simplecov
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: pry
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ description: A Ruby workflow engine
126
+ email:
127
+ - ravi@gadad.net
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files: []
131
+ files:
132
+ - ".gitignore"
133
+ - ".rspec"
134
+ - ".ruby-version"
135
+ - CODE_OF_CONDUCT.md
136
+ - Gemfile
137
+ - LICENSE.txt
138
+ - README.md
139
+ - Rakefile
140
+ - bin/console
141
+ - bin/setup
142
+ - lib/swarm.rb
143
+ - lib/swarm/engine/base/job.rb
144
+ - lib/swarm/engine/base/queue.rb
145
+ - lib/swarm/engine/volatile/job.rb
146
+ - lib/swarm/engine/volatile/queue.rb
147
+ - lib/swarm/engine/worker.rb
148
+ - lib/swarm/engine/worker/command.rb
149
+ - lib/swarm/evaluation/expression_evaluator.rb
150
+ - lib/swarm/evaluation/workitem_context.rb
151
+ - lib/swarm/expression.rb
152
+ - lib/swarm/expressions/activity_expression.rb
153
+ - lib/swarm/expressions/branch_expression.rb
154
+ - lib/swarm/expressions/concurrence_expression.rb
155
+ - lib/swarm/expressions/conditional_expression.rb
156
+ - lib/swarm/expressions/sequence_expression.rb
157
+ - lib/swarm/expressions/subprocess_expression.rb
158
+ - lib/swarm/hive.rb
159
+ - lib/swarm/hive_dweller.rb
160
+ - lib/swarm/observers/base.rb
161
+ - lib/swarm/participant.rb
162
+ - lib/swarm/participants/storage_participant.rb
163
+ - lib/swarm/participants/trace_participant.rb
164
+ - lib/swarm/pollen/parser.rb
165
+ - lib/swarm/pollen/reader.rb
166
+ - lib/swarm/pollen/transformer.rb
167
+ - lib/swarm/process.rb
168
+ - lib/swarm/process_definition.rb
169
+ - lib/swarm/router.rb
170
+ - lib/swarm/storage.rb
171
+ - lib/swarm/stored_workitem.rb
172
+ - lib/swarm/support.rb
173
+ - lib/swarm/version.rb
174
+ - swarm.gemspec
175
+ homepage: https://github.com/bumbleworks/swarm
176
+ licenses:
177
+ - MIT
178
+ metadata: {}
179
+ post_install_message:
180
+ rdoc_options: []
181
+ require_paths:
182
+ - lib
183
+ required_ruby_version: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - ">="
186
+ - !ruby/object:Gem::Version
187
+ version: '0'
188
+ required_rubygems_version: !ruby/object:Gem::Requirement
189
+ requirements:
190
+ - - ">="
191
+ - !ruby/object:Gem::Version
192
+ version: '0'
193
+ requirements: []
194
+ rubyforge_project:
195
+ rubygems_version: 2.2.5
196
+ signing_key:
197
+ specification_version: 4
198
+ summary: A Ruby workflow engine
199
+ test_files: []