gush-mmx 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2f55fd6914dcc9cf58d63c7bf6df6489c8f9eb76
4
+ data.tar.gz: aa4bcedcb43924b69197c361e5c4964ac42e196d
5
+ SHA512:
6
+ metadata.gz: 51223f508abf7a0e6286bc34f118553d49ecc7e9b49dc9c62e1a5adf7e83ae453a59efd02679d49bfa6d3b01a5bf41a068f0cc79224e5d60d9f1f9aea4677dd5
7
+ data.tar.gz: 200c6e51fe0d8bebc12d7ec8989bd7883392f5ea7e51463ef6acfe2df29df7440d925875980f2e676062d5feb625d313412ec00ef74d9f129a75f59f95d6085c
@@ -0,0 +1,21 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ workflows/
18
+ tmp
19
+ test.rb
20
+ /Gushfile.rb
21
+ dump.rdb
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,13 @@
1
+ language: ruby
2
+ script: "bundle exec rspec"
3
+ rvm:
4
+ - 2.0.0
5
+ - 2.1.6
6
+ - 2.2.2
7
+ services:
8
+ - redis-server
9
+ email:
10
+ recipients:
11
+ - piotrek@okonski.org
12
+ on_success: change
13
+ on_failure: always
@@ -0,0 +1,3 @@
1
+ # 0.4
2
+
3
+ - remove hard dependency on Yajl, so Gush can work with non-MRI Rubies ([#31](https://github.com/chaps-io/gush/pull/31) by [Nick Rakochy](https://github.com/chaps-io/gush/pull/31))
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
3
+
4
+ platforms :mri, :ruby do
5
+ gem 'yajl-ruby'
6
+ end
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Chaps sp. z o.o.
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.
@@ -0,0 +1,258 @@
1
+ # Gush [![Build Status](https://travis-ci.org/chaps-io/gush.svg?branch=master)](https://travis-ci.org/chaps-io/gush)
2
+
3
+ ## [![](http://i.imgur.com/ya8Wnyl.png)](https://chaps.io) proudly made by [Chaps](https://chaps.io)
4
+
5
+ Gush is a parallel workflow runner using only Redis as its message broker and Sidekiq for workers.
6
+
7
+
8
+ # New
9
+
10
+ Options for config:
11
+ * redis_prefix - Redis namespace
12
+ * sidekiq_queue - queue name in Sidekiq
13
+
14
+
15
+
16
+ ## Theory
17
+
18
+ Gush relies on directed acyclic graphs to store dependencies, see [Parallelizing Operations With Dependencies](https://msdn.microsoft.com/en-us/magazine/dd569760.aspx) by Stephen Toub.
19
+ ## Installation
20
+
21
+ Add this line to your application's Gemfile:
22
+
23
+ gem 'gush'
24
+
25
+ And then execute:
26
+
27
+ $ bundle
28
+
29
+ Or install it yourself as:
30
+
31
+ $ gem install gush
32
+
33
+ ## Usage
34
+
35
+ ### Defining workflows
36
+
37
+ The DSL for defining jobs consists of a single `run` method.
38
+ Here is a complete example of a workflow you can create:
39
+
40
+ ```ruby
41
+ # workflows/sample_workflow.rb
42
+ class SampleWorkflow < Gush::Workflow
43
+ def configure(url_to_fetch_from)
44
+ run FetchJob1, params: { url: url_to_fetch_from }
45
+ run FetchJob2, params: {some_flag: true, url: 'http://url.com'}
46
+
47
+ run PersistJob1, after: FetchJob1
48
+ run PersistJob2, after: FetchJob2
49
+
50
+ run Normalize,
51
+ after: [PersistJob1, PersistJob2],
52
+ before: Index
53
+
54
+ run Index
55
+ end
56
+ end
57
+ ```
58
+
59
+ **Hint:** For debugging purposes you can vizualize the graph using `viz` command:
60
+
61
+ ```
62
+ bundle exec gush viz SampleWorkflow
63
+ ```
64
+
65
+ For the Workflow above, the graph will look like this:
66
+
67
+ ![SampleWorkflow](http://i.imgur.com/SmeRRVT.png)
68
+
69
+
70
+ #### Passing parameters to jobs
71
+
72
+ You can pass any primitive arguments into jobs while defining your workflow:
73
+
74
+ ```ruby
75
+ # app/workflows/sample_workflow.rb
76
+ class SampleWorkflow < Gush::Workflow
77
+ def configure
78
+ run FetchJob1, params: { url: "http://some.com/url" }
79
+ end
80
+ end
81
+ ```
82
+
83
+ See below to learn how to access those params inside your job.
84
+
85
+ #### Defining jobs
86
+
87
+ Jobs are classes inheriting from `Gush::Job`:
88
+
89
+ ```ruby
90
+ # app/jobs/fetch_job.rb
91
+ class FetchJob < Gush::Job
92
+ def work
93
+ # do some fetching from remote APIs
94
+
95
+ params #=> {url: "http://some.com/url"}
96
+ end
97
+ end
98
+ ```
99
+
100
+ `params` method is a hash containing your (optional) parameters passed to `run` method in the workflow.
101
+
102
+ #### Passing arguments to workflows
103
+
104
+ Workflows can accept any primitive arguments in their constructor, which then will be available in your
105
+ `configure` method.
106
+
107
+ Here's an example of a workflow responsible for publishing a book:
108
+
109
+ ```ruby
110
+ # app/workflows/sample_workflow.rb
111
+ class PublishBookWorkflow < Gush::Workflow
112
+ def configure(url, isbn)
113
+ run FetchBook, params: { url: url }
114
+ run PublishBook, params: { book_isbn: isbn }
115
+ end
116
+ end
117
+ ```
118
+
119
+ and then create your workflow with those arguments:
120
+
121
+ ```ruby
122
+ PublishBookWorkflow.new("http://url.com/book.pdf", "978-0470081204")
123
+ ```
124
+
125
+
126
+ ### Running workflows
127
+
128
+ Now that we have defined our workflow we can use it:
129
+
130
+ #### 1. Initialize and save it
131
+
132
+ ```ruby
133
+ flow = SampleWorkflow.new(optional, arguments)
134
+ flow.save # saves workflow and its jobs to Redis
135
+ ```
136
+
137
+ **or:** you can also use a shortcut:
138
+
139
+ ```ruby
140
+ flow = SampleWorkflow.create(optional, arguments)
141
+ ```
142
+
143
+ #### 2. Start workflow
144
+
145
+ First you need to start Sidekiq workers:
146
+
147
+ ```
148
+ bundle exec gush workers
149
+ ```
150
+
151
+ and then start your workflow:
152
+
153
+ ```ruby
154
+ flow.start!
155
+ ```
156
+
157
+ Now Gush will start processing jobs in background using Sidekiq
158
+ in the order defined in `configure` method inside Workflow.
159
+
160
+ ### Pipelining
161
+
162
+ Gush offers a useful feature which lets you pass results of a job to its dependencies, so they can act accordingly.
163
+
164
+ **Example:**
165
+
166
+ Let's assume you have two jobs, `DownloadVideo`, `EncodeVideo`.
167
+ The latter needs to know where the first one downloaded the file to be able to open it.
168
+
169
+
170
+ ```ruby
171
+ class DownloadVideo < Gush::Job
172
+ def work
173
+ downloader = VideoDownloader.fetch("http://youtube.com/?v=someytvideo")
174
+
175
+ output(downloader.file_path)
176
+ end
177
+ end
178
+ ```
179
+
180
+ `output` method is Gush's way of saying: "I want to pass this down to my descendants".
181
+
182
+ Now, since `DownloadVideo` finished and its dependant job `EncodeVideo` started, we can access that payload down the (pipe)line:
183
+
184
+ ```ruby
185
+ class EncodeVideo < Gush::Job
186
+ def work
187
+ video_path = payloads["DownloadVideo"]
188
+ end
189
+ end
190
+ ```
191
+
192
+ `payloads` is a hash containing outputs from all parent jobs, where job class names are the keys.
193
+
194
+ **Note:** `payloads` will only contain outputs of the job's ancestors. So if job `A` depends on `B` and `C`,
195
+ the `payloads` hash will look like this:
196
+
197
+ ```ruby
198
+ {
199
+ "B" => (...),
200
+ "C" => (...)
201
+ }
202
+ ```
203
+
204
+
205
+ ### Checking status:
206
+
207
+ #### In Ruby:
208
+
209
+ ```ruby
210
+ flow.reload
211
+ flow.status
212
+ #=> :running|:finished|:failed
213
+ ```
214
+
215
+ `reload` is needed to see the latest status, since workflows are updated asynchronously.
216
+
217
+ #### Via CLI:
218
+
219
+ - of a specific workflow:
220
+
221
+ ```
222
+ bundle exec gush show <workflow_id>
223
+ ```
224
+
225
+ - of all created workflows:
226
+
227
+ ```
228
+ bundle exec gush list
229
+ ```
230
+
231
+
232
+ ### Requiring workflows inside your projects
233
+
234
+ When using Gush and its CLI commands you need a Gushfile.rb in root directory.
235
+ Gushfile should require all your Workflows and jobs, for example:
236
+
237
+ ```ruby
238
+ require_relative './lib/your_project'
239
+
240
+ Dir[Rails.root.join("app/workflows/**/*.rb")].each do |file|
241
+ require file
242
+ end
243
+ ```
244
+
245
+ ## Contributors
246
+
247
+ - [Mateusz Lenik](https://github.com/mlen)
248
+ - [Michał Krzyżanowski](https://github.com/krzyzak)
249
+ - [Maciej Nowak](https://github.com/keqi)
250
+ - [Maciej Kołek](https://github.com/ferusinfo)
251
+
252
+ ## Contributing
253
+
254
+ 1. Fork it ( http://github.com/chaps-io/gush/fork )
255
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
256
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
257
+ 4. Push to the branch (`git push origin my-new-feature`)
258
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+ require "pathname"
3
+ require "bundler"
4
+ Bundler.require
5
+
6
+ bin_file = Pathname.new(__FILE__).realpath
7
+ # add self to libpath
8
+ $:.unshift File.expand_path("../../lib", bin_file)
9
+
10
+ require 'gush'
11
+
12
+ begin
13
+ Gush::CLI.start(ARGV)
14
+ rescue Gush::WorkflowNotFound
15
+ puts "Workflow not found".red
16
+ rescue Gush::DependencyLevelTooDeep
17
+ puts "Dependency level too deep. Perhaps you have a dependency cycle?".red
18
+ end
@@ -0,0 +1,34 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "gush-mmx"
7
+ spec.version = "0.4.2"
8
+ spec.authors = ["Piotrek Okoński", "Max Ivak"]
9
+ spec.email = ["piotrek@okonski.org", "maxivak@gmail.com"]
10
+ spec.summary = "Fast and distributed workflow runner using only Sidekiq and Redis"
11
+ spec.description = "Gush is a parallel workflow runner using only Redis as its message broker and Sidekiq for workers."
12
+ spec.homepage = "https://github.com/pokonski/gush"
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = "gush"
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_dependency "sidekiq", "~> 4.0"
21
+ spec.add_dependency "multi_json", "~> 1.11"
22
+ spec.add_dependency "redis", "~> 3.2"
23
+ spec.add_dependency "hiredis", "~> 0.6"
24
+ spec.add_dependency "ruby-graphviz", "~> 1.2"
25
+ spec.add_dependency "terminal-table", "~> 1.4"
26
+ spec.add_dependency "colorize", "~> 0.7"
27
+ spec.add_dependency "thor", "~> 0.19"
28
+ spec.add_dependency "launchy", "~> 2.4"
29
+ spec.add_development_dependency "bundler", "~> 1.5"
30
+ spec.add_development_dependency "rake", "~> 10.4"
31
+ spec.add_development_dependency "rspec", '~> 3.0'
32
+ spec.add_development_dependency "pry", '~> 0.10'
33
+ spec.add_development_dependency 'fakeredis', '~> 0.5'
34
+ end
@@ -0,0 +1,61 @@
1
+ require "bundler/setup"
2
+
3
+ require "graphviz"
4
+ require "hiredis"
5
+ require "pathname"
6
+ require "redis"
7
+ require "securerandom"
8
+ require "sidekiq"
9
+ require "multi_json"
10
+
11
+ require "gush/json"
12
+ require "gush/cli"
13
+ require "gush/cli/overview"
14
+ require "gush/graph"
15
+ require "gush/client"
16
+ require "gush/configuration"
17
+ require "gush/errors"
18
+ require "gush/job"
19
+ require "gush/worker"
20
+ require "gush/workflow"
21
+
22
+ module Gush
23
+ def self.gushfile
24
+ configuration.gushfile
25
+ end
26
+
27
+ def self.root
28
+ Pathname.new(__FILE__).parent.parent
29
+ end
30
+
31
+ def self.configuration
32
+ @configuration ||= Configuration.new
33
+ end
34
+
35
+ def self.configure
36
+ yield configuration
37
+ reconfigure_sidekiq
38
+ end
39
+
40
+ def self.reconfigure_sidekiq
41
+ #puts "^^^^^^^^ reconfigure_sidekiq ^^^"
42
+ Sidekiq.configure_server do |config|
43
+ #config.redis = { url: configuration.redis_url, queue: configuration.namespace}
44
+
45
+ opts = { url: configuration.redis_url, namespace: configuration.redis_prefix, queue: configuration.sidekiq_queue }
46
+ #puts "sidekiq server opts: #{opts}"
47
+ config.redis = { url: configuration.redis_url, namespace: configuration.redis_prefix, queue: configuration.sidekiq_queue }
48
+ end
49
+
50
+ Sidekiq.configure_client do |config|
51
+ #config.redis = { url: configuration.redis_url, queue: configuration.namespace}
52
+
53
+ opts = { url: configuration.redis_url, namespace: configuration.redis_prefix, queue: configuration.sidekiq_queue }
54
+ #puts "sidekiq client opts: #{opts}"
55
+
56
+ config.redis = { url: configuration.redis_url, namespace: configuration.redis_prefix, queue: configuration.sidekiq_queue }
57
+ end
58
+ end
59
+ end
60
+
61
+ #Gush.reconfigure_sidekiq