rbbt-rest 1.8.8 → 1.8.9
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.
- checksums.yaml +4 -4
- data/lib/rbbt/rest/common/render.rb +8 -3
- data/lib/rbbt/rest/file_server.rb +0 -2
- data/lib/rbbt/rest/main.rb +7 -5
- data/lib/rbbt/rest/workflow.rb +14 -2
- data/lib/rbbt/rest/workflow/jobs.rb +30 -5
- data/lib/rbbt/rest/workflow/render.rb +1 -1
- data/lib/rbbt/rest/workflow/stream_task.rb +169 -0
- data/share/views/error.haml +2 -2
- data/share/views/job_info.haml +18 -1
- data/share/views/job_result/job_control.haml +1 -1
- data/share/views/public/js/helpers/_ajax_replace.js +2 -1
- data/share/views/public/js/rbbt/actions.js +4 -0
- data/share/views/wait.haml +23 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3efb056fd9e11310126a6cbec3dbdc9fea9ee093
|
4
|
+
data.tar.gz: e7fb374a165521846f5e10a2491f8dcceaaef8a5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a0e7197f529598c35dc7f2f0f33931d48cd208205f8b068df899007eaa905cf7550de75255d2e1c052f54b409acc483f5781ddf92a2ecf83003c0dd8217168d9
|
7
|
+
data.tar.gz: d73a4246be17d0b5056c80b686ac24776c51192fdfbc4bcaccbbd0360ef0647a374a4810435d104c2dfabf44879554c703f1f30ff0a3e7f549a84ced1eb2c2a1
|
@@ -109,9 +109,10 @@ module RbbtRESTHelpers
|
|
109
109
|
|
110
110
|
pid = @step.child{
|
111
111
|
begin
|
112
|
-
|
112
|
+
Log.low("Fragment started: #{ fragment_file } - #{Process.pid}")
|
113
|
+
res = capture_haml fragment_code, &block
|
113
114
|
Open.write(fragment_file, res)
|
114
|
-
Log.low("Fragment done: #{ fragment_file }")
|
115
|
+
Log.low("Fragment done: #{ fragment_file } - #{Process.pid}")
|
115
116
|
rescue Exception
|
116
117
|
Open.write(fragment_file + '.error', [$!.class.to_s, $!.message] * ": ")
|
117
118
|
Open.write(fragment_file + '.backtrace', $!.backtrace * "\n") if $!.backtrace
|
@@ -129,7 +130,11 @@ module RbbtRESTHelpers
|
|
129
130
|
if link.nil?
|
130
131
|
html_tag('a', "", :href => fragment_url, :class => 'fragment', "data-text" => text)
|
131
132
|
else
|
132
|
-
if
|
133
|
+
if FalseClass === link
|
134
|
+
return fragment_code
|
135
|
+
elsif TrueClass === link
|
136
|
+
return fragment_url
|
137
|
+
elsif link =~ / href=/
|
133
138
|
link = link.sub(/ href=('|")/," href='#{fragment_url}'")
|
134
139
|
else
|
135
140
|
link = link.sub(/<a /,"<a href='#{fragment_url}' ")
|
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'rbbt'
|
2
2
|
require 'sinatra/base'
|
3
|
-
require 'sinatra/streaming'
|
4
3
|
|
5
4
|
|
6
5
|
module Sinatra
|
@@ -8,7 +7,6 @@ module Sinatra
|
|
8
7
|
|
9
8
|
def self.registered(base)
|
10
9
|
base.module_eval do
|
11
|
-
helpers Sinatra::Streaming
|
12
10
|
|
13
11
|
get '/resource/:resource/get_directory' do
|
14
12
|
directory, resource, create = params.values_at :directory, :resource, :create
|
data/lib/rbbt/rest/main.rb
CHANGED
@@ -8,8 +8,11 @@ require 'sinatra/base'
|
|
8
8
|
require 'sinatra/cross_origin'
|
9
9
|
require "sinatra/multi_route"
|
10
10
|
require "sinatra/cookies"
|
11
|
+
require 'sinatra/streaming'
|
11
12
|
require 'json'
|
12
13
|
|
14
|
+
require 'nakayoshi_fork'
|
15
|
+
|
13
16
|
module Sinatra
|
14
17
|
module RbbtRESTMain
|
15
18
|
|
@@ -46,6 +49,7 @@ module Sinatra
|
|
46
49
|
helpers RbbtRESTHelpers
|
47
50
|
register Sinatra::RbbtAuth
|
48
51
|
helpers Sinatra::Cookies
|
52
|
+
helpers Sinatra::Streaming
|
49
53
|
|
50
54
|
add_sass_load_path Rbbt.share.views.compass.find(:lib)
|
51
55
|
|
@@ -66,7 +70,7 @@ module Sinatra
|
|
66
70
|
if production?
|
67
71
|
set :haml, { :ugly => true }
|
68
72
|
set :clean_trace, true
|
69
|
-
set :static_cache_control , [:public, {:max_age =>
|
73
|
+
set :static_cache_control , [:public, {:max_age => 360_000}]
|
70
74
|
else
|
71
75
|
set :static_cache_control , [:public, {:max_age => 0}]
|
72
76
|
end
|
@@ -122,6 +126,7 @@ module Sinatra
|
|
122
126
|
printer.print(:path => dir, :profile => 'profile')
|
123
127
|
Log.info{ "Profile saved at #{ dir }: #{request.env["REQUEST_URI"]}" }
|
124
128
|
end
|
129
|
+
|
125
130
|
response.header["URI"] = request.env["REQUEST_URI"]
|
126
131
|
end
|
127
132
|
|
@@ -239,14 +244,11 @@ module Sinatra
|
|
239
244
|
end
|
240
245
|
|
241
246
|
require 'rbbt/rest/monitor'
|
247
|
+
|
242
248
|
register Sinatra::RbbtRESTMonitor
|
243
249
|
end
|
244
250
|
end
|
245
251
|
|
246
252
|
end
|
247
|
-
|
248
|
-
|
249
|
-
#require 'rack/stream'
|
250
|
-
#use Rack::Stream
|
251
253
|
end
|
252
254
|
|
data/lib/rbbt/rest/workflow.rb
CHANGED
@@ -46,7 +46,9 @@ module Sinatra
|
|
46
46
|
workflow_render('tasks', workflow, nil, @clean_params)
|
47
47
|
when :json
|
48
48
|
content_type "application/json"
|
49
|
-
|
49
|
+
|
50
|
+
@can_stream = ENV["RBBT_WORKFLOW_TASK_STREAM"] == 'true'
|
51
|
+
{:exec => workflow.exec_exports, :synchronous => workflow.synchronous_exports, :asynchronous => workflow.asynchronous_exports, :can_stream => !!@can_stream}.to_json
|
50
52
|
else
|
51
53
|
raise "Unsupported format specified: #{ format }"
|
52
54
|
end
|
@@ -63,6 +65,7 @@ module Sinatra
|
|
63
65
|
raise "Unsupported format specified: #{ format }"
|
64
66
|
end
|
65
67
|
end
|
68
|
+
|
66
69
|
get "/#{workflow.to_s}/:task/info" do
|
67
70
|
task = consume_parameter(:task)
|
68
71
|
|
@@ -157,7 +160,7 @@ module Sinatra
|
|
157
160
|
raise RbbtRESTHelpers::Retry
|
158
161
|
end
|
159
162
|
else
|
160
|
-
halt 404, "Job not found"
|
163
|
+
halt 404, "Job not found: #{job.status}"
|
161
164
|
end
|
162
165
|
end
|
163
166
|
rescue RbbtRESTHelpers::Retry
|
@@ -264,6 +267,15 @@ module Sinatra
|
|
264
267
|
def self.registered(base)
|
265
268
|
base.module_eval do
|
266
269
|
helpers WorkflowRESTHelpers
|
270
|
+
|
271
|
+
if ENV["RBBT_WORKFLOW_TASK_STREAM"] == 'true'
|
272
|
+
require 'rbbt/rest/workflow/stream_task'
|
273
|
+
Log.info "Preparing server for streaming workflow tasks"
|
274
|
+
use StreamWorkflowTask if not @can_stream
|
275
|
+
@can_stream = true
|
276
|
+
else
|
277
|
+
@can_stream = false
|
278
|
+
end
|
267
279
|
end
|
268
280
|
end
|
269
281
|
end
|
@@ -77,17 +77,20 @@ module WorkflowRESTHelpers
|
|
77
77
|
halt 200, result.to_json
|
78
78
|
when :tsv
|
79
79
|
content_type "text/tab-separated-values"
|
80
|
-
|
80
|
+
result = result.to_s unless String === result or result.respond_to? :gets
|
81
|
+
halt 200, result
|
81
82
|
when :literal, :raw
|
82
83
|
content_type "text/plain"
|
83
84
|
case workflow.task_info(task)[:result_type]
|
84
85
|
when :array
|
85
86
|
halt 200, result * "\n"
|
86
87
|
else
|
87
|
-
|
88
|
+
result = result.to_s unless String === result or result.respond_to? :gets
|
89
|
+
halt 200, result
|
88
90
|
end
|
89
91
|
when :binary
|
90
92
|
content_type "application/octet-stream"
|
93
|
+
result = result.to_s unless String === result or result.respond_to? :gets
|
91
94
|
halt 200, result.to_s
|
92
95
|
when :jobname
|
93
96
|
halt 200, nil
|
@@ -146,6 +149,21 @@ module WorkflowRESTHelpers
|
|
146
149
|
end
|
147
150
|
end
|
148
151
|
|
152
|
+
def stream_job(job)
|
153
|
+
unless job.started? or job.done?
|
154
|
+
job.run(:stream)
|
155
|
+
job.soft_grace
|
156
|
+
end
|
157
|
+
|
158
|
+
s = TSV.get_stream job
|
159
|
+
|
160
|
+
sout, sin = Misc.pipe
|
161
|
+
|
162
|
+
Misc.consume_stream(s, true, sin)
|
163
|
+
|
164
|
+
halt 200, sout
|
165
|
+
end
|
166
|
+
|
149
167
|
def issue_job(workflow, task, jobname = nil, params = {})
|
150
168
|
inputs = prepare_job_inputs(workflow, task, params)
|
151
169
|
job = workflow.job(task, jobname, inputs)
|
@@ -157,7 +175,15 @@ module WorkflowRESTHelpers
|
|
157
175
|
|
158
176
|
case execution_type.to_sym
|
159
177
|
when :exec
|
160
|
-
show_exec_result job.exec, workflow, task
|
178
|
+
show_exec_result job.exec(:stream), workflow, task
|
179
|
+
when :stream
|
180
|
+
if update == :reload
|
181
|
+
job.abort
|
182
|
+
job.clean
|
183
|
+
end
|
184
|
+
|
185
|
+
stream_job(job)
|
186
|
+
|
161
187
|
when :synchronous, :sync
|
162
188
|
if update == :reload
|
163
189
|
job.abort
|
@@ -169,11 +195,10 @@ module WorkflowRESTHelpers
|
|
169
195
|
job_url += "?_format=#{@format}" if @format
|
170
196
|
|
171
197
|
if not job.started?
|
172
|
-
job.run
|
198
|
+
job.run
|
173
199
|
job.join
|
174
200
|
end
|
175
201
|
|
176
|
-
#halt 200, job.name if format == :jobname
|
177
202
|
if format == :jobname
|
178
203
|
job.name
|
179
204
|
else
|
@@ -0,0 +1,169 @@
|
|
1
|
+
class StreamWorkflowTask
|
2
|
+
def initialize(app)
|
3
|
+
@app = app
|
4
|
+
end
|
5
|
+
|
6
|
+
def read_normal_inputs(io, boundary, stream_input)
|
7
|
+
content = ""
|
8
|
+
content_start = false
|
9
|
+
variable = nil
|
10
|
+
filename = nil
|
11
|
+
inputs = {}
|
12
|
+
|
13
|
+
while line = io.gets
|
14
|
+
line.chomp!
|
15
|
+
|
16
|
+
if line == "--" + boundary
|
17
|
+
if variable
|
18
|
+
inputs[variable] = content
|
19
|
+
end
|
20
|
+
content_start = false
|
21
|
+
content = ""
|
22
|
+
elsif line =~ /^Content.* name="([^\s;"]*)"/
|
23
|
+
variable = $1
|
24
|
+
filename = line.match(/filename="([^"]+)"/)[1] if line =~ /filename/
|
25
|
+
content = ""
|
26
|
+
elsif line.empty?
|
27
|
+
content_start = true
|
28
|
+
break if variable == stream_input
|
29
|
+
else
|
30
|
+
content << line if content_start
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
[inputs, filename]
|
35
|
+
end
|
36
|
+
|
37
|
+
def parse_uri(env)
|
38
|
+
uri = env["REQUEST_URI"]
|
39
|
+
_n, workflow, task = uri.split("/")
|
40
|
+
workflow = begin
|
41
|
+
Kernel.const_get(workflow)
|
42
|
+
rescue
|
43
|
+
raise "Could not accept task for workflow: #{ workflow }"
|
44
|
+
end
|
45
|
+
[workflow, task]
|
46
|
+
end
|
47
|
+
|
48
|
+
EOL = "\r\n"
|
49
|
+
def read_chunk(sin, rest = "")
|
50
|
+
parts = []
|
51
|
+
c = sin.gets
|
52
|
+
c = rest << c unless rest.empty?
|
53
|
+
c = c[2..-1] if c[0..1] == EOL
|
54
|
+
index = c.index EOL
|
55
|
+
while index
|
56
|
+
part = c[0..index-1]
|
57
|
+
parts << part
|
58
|
+
c = c[index+2..-1]
|
59
|
+
index = c.index EOL
|
60
|
+
end
|
61
|
+
rest = c
|
62
|
+
[parts, rest]
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
def copy_chunked_stream(sin, sout, boundary)
|
67
|
+
|
68
|
+
rest = ""
|
69
|
+
done = false
|
70
|
+
content = true
|
71
|
+
|
72
|
+
while not done
|
73
|
+
parts, rest = read_chunk(sin, rest)
|
74
|
+
while parts.any?
|
75
|
+
part = parts.shift
|
76
|
+
if content
|
77
|
+
part.split("\n").each do |line|
|
78
|
+
sout.puts line
|
79
|
+
if line.include? boundary
|
80
|
+
done = true
|
81
|
+
break
|
82
|
+
end
|
83
|
+
end
|
84
|
+
content = false
|
85
|
+
else
|
86
|
+
content = true
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
sout.write rest
|
92
|
+
sout.close
|
93
|
+
end
|
94
|
+
|
95
|
+
def call(env)
|
96
|
+
if env["REQUEST_METHOD"] == "POST" and env["rack.hijack"] and env["CONTENT_TYPE"] and env["CONTENT_TYPE"].include? "Rbbt_Param_Stream" and env["HTTP_TRANSFER_ENCODING"] == 'chunked'
|
97
|
+
Log.high "Hijacking post data"
|
98
|
+
inputs = {}
|
99
|
+
content_type = env["CONTENT_TYPE"]
|
100
|
+
boundary = content_type.match(/boundary=([^\s;]*)/)[1]
|
101
|
+
stream_input = content_type.match(/stream=([^\s;]*)/)[1]
|
102
|
+
post_stream = env["rack.hijack"].call
|
103
|
+
begin
|
104
|
+
inputs, filename = read_normal_inputs(post_stream, boundary, stream_input)
|
105
|
+
|
106
|
+
input_stream_out, input_stream_in = Misc.pipe
|
107
|
+
Misc.add_stream_filename(input_stream_out, filename) if filename
|
108
|
+
inputs[stream_input] = input_stream_out
|
109
|
+
|
110
|
+
workflow, task = parse_uri(env)
|
111
|
+
name = inputs.delete "jobname"
|
112
|
+
job = workflow.job(task, name, inputs)
|
113
|
+
|
114
|
+
task = task.to_sym
|
115
|
+
execution_type = case
|
116
|
+
when workflow.exec_exports.include?(task)
|
117
|
+
"exec"
|
118
|
+
when workflow.synchronous_exports.include?(task)
|
119
|
+
"synchronous"
|
120
|
+
when workflow.asynchronous_exports.include?(task)
|
121
|
+
"asynchronous"
|
122
|
+
else
|
123
|
+
raise "No known export type for #{ workflow } #{ task }. Accesses denied"
|
124
|
+
end
|
125
|
+
|
126
|
+
execution_type = "exec" if inputs["_cache_type"] == 'exec'
|
127
|
+
Log.info "Streaming task with execution_type: #{ execution_type }"
|
128
|
+
|
129
|
+
case execution_type
|
130
|
+
when "exec", nil
|
131
|
+
job.exec(:stream)
|
132
|
+
when "sync", "synchronous", "async", "asynchronous"
|
133
|
+
job.run(:stream)
|
134
|
+
else
|
135
|
+
raise "Unknown execution_type: #{execution_type}"
|
136
|
+
end
|
137
|
+
|
138
|
+
t_in = Thread.new do
|
139
|
+
begin
|
140
|
+
copy_chunked_stream(post_stream, input_stream_in, boundary)
|
141
|
+
rescue
|
142
|
+
Log.exception $!
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
job_output = TSV.get_stream job
|
147
|
+
t_out = Thread.new do
|
148
|
+
begin
|
149
|
+
post_stream.write "HTTP/1.1 200\r\n\r\n"
|
150
|
+
while c = job_output.read(1024)
|
151
|
+
post_stream.write c
|
152
|
+
end
|
153
|
+
job_output.join if job_output.respond_to? :join
|
154
|
+
post_stream.close
|
155
|
+
rescue
|
156
|
+
Log.exception $!
|
157
|
+
job.abort
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
[200, {}, nil]
|
163
|
+
else
|
164
|
+
Log.high "NOT Hijacking post data"
|
165
|
+
@app.call(env)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
data/share/views/error.haml
CHANGED
@@ -7,10 +7,10 @@
|
|
7
7
|
|
8
8
|
- if defined? job.info_file and File.exists? job.info_file and request.env["REQUEST_URI"].include? job.name
|
9
9
|
- clean_url = add_GET_param(request.env["REQUEST_URI"], "_update", "clean")
|
10
|
-
%a.ui.button.blue(href=clean_url) Clean
|
10
|
+
%a.ui.button.blue.clean(href=clean_url) Clean
|
11
11
|
- else
|
12
12
|
- clean_url = add_GET_param(request.env["REQUEST_URI"], "_update", "reload")
|
13
|
-
%a.ui.blue.button(href=clean_url) Reload
|
13
|
+
%a.ui.blue.button.reload(href=clean_url) Reload
|
14
14
|
|
15
15
|
- backtrace = job.info[:backtrace]
|
16
16
|
- if backtrace and backtrace.any?
|
data/share/views/job_info.haml
CHANGED
@@ -1 +1,18 @@
|
|
1
|
-
=
|
1
|
+
- new_info = {}
|
2
|
+
- info.each{|k,v| new_info[k] = v unless k.to_s == 'dependencies'}
|
3
|
+
= hash2dl(new_info)
|
4
|
+
|
5
|
+
- if info[:dependencies] and info[:dependencies].any?
|
6
|
+
%h3 Dependencies
|
7
|
+
%ul
|
8
|
+
|
9
|
+
- task_exports = workflow.synchronous_exports + workflow.asynchronous_exports
|
10
|
+
- task_exports = task_exports.collect{|t| t.to_s}
|
11
|
+
- info[:dependencies].each do |task,name,path|
|
12
|
+
- url = "/" + [workflow, task, name] * "/"
|
13
|
+
%li
|
14
|
+
(#{workflow} #{task})
|
15
|
+
- if task_exports.include? task.to_s
|
16
|
+
%a(href=url) #{ name }
|
17
|
+
- else
|
18
|
+
= name
|
@@ -6,7 +6,7 @@
|
|
6
6
|
%a.ui.small.button(href="/#{[workflow.to_s, task, jobname, "info"] * "/"}") job info
|
7
7
|
%a.ui.small.button(href="/#{[workflow.to_s, task, jobname] * "/"}?_format=raw") get raw result file
|
8
8
|
|
9
|
-
- files = job.files - ["html", "html.info"]
|
9
|
+
- files = job.files - ["html", "html.files", "html.info", "html.pid"]
|
10
10
|
- if files.any?
|
11
11
|
%dt.ui.header Files
|
12
12
|
%dd.job_files
|
@@ -115,7 +115,8 @@ function replace_object(object, href, embedd, complete){
|
|
115
115
|
error_message = error.sub(/\n\n.*/,'')
|
116
116
|
|
117
117
|
error_span = $('<span>').html(error_message).addClass('error')
|
118
|
-
error = $('<div>')
|
118
|
+
error = $('<div>') //.append(error_span)
|
119
|
+
error.append(req.responseText)
|
119
120
|
object.removeClass(_reloading_class).addClass("error").css('height', 0).html(error).css('height', 'auto').attr('target-href', clean_href);
|
120
121
|
},
|
121
122
|
|
@@ -287,6 +287,10 @@ $.widget("rbbt.action_controller", {
|
|
287
287
|
action_controller.addClass('loading');
|
288
288
|
|
289
289
|
replace_object(action_div, href, true, this.options.complete);
|
290
|
+
var tool = this
|
291
|
+
action_div.on('click', '.reload', function(e){
|
292
|
+
update_embedded(action_div, true, tool.options.complete); return false
|
293
|
+
});
|
290
294
|
|
291
295
|
return false
|
292
296
|
}
|
data/share/views/wait.haml
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
- @reload_page = true
|
2
2
|
|
3
|
+
:sass
|
4
|
+
span.progress_time
|
5
|
+
color: grey
|
3
6
|
.wait.ui.segment.info.message
|
4
7
|
%h3.ui.header Waiting on #{format_name File.basename(job.name)}
|
5
8
|
|
@@ -10,10 +13,29 @@
|
|
10
13
|
.footer.ui.basic.segment
|
11
14
|
- progress = job.file(:progress).yaml
|
12
15
|
- if progress[:last_percent]
|
16
|
+
- start = progress[:start]
|
17
|
+
- last_time = progress[:last_time]
|
18
|
+
- if progress[:last_percent].to_i > 0
|
19
|
+
- ellapsed = last_time - start
|
20
|
+
- if progress[:mean]
|
21
|
+
- missing = progress[:max].to_f - progress[:ticks].to_f
|
22
|
+
- eta = missing / progress[:mean].to_f
|
23
|
+
- else
|
24
|
+
- ratio = progress[:ticks].to_f/progress[:max].to_f
|
25
|
+
- eta = ellapsed * (1 - ratio)
|
26
|
+
- eta = Misc.format_seconds(eta)
|
27
|
+
- ellapsed = Misc.format_seconds(ellapsed)
|
28
|
+
- else
|
29
|
+
- ellapsed = '?'
|
30
|
+
- eta = '?'
|
13
31
|
.ui.teal.step.percent.progress.indicating.meta(data-percent="#{progress[:last_percent]}" data-total=100)
|
14
32
|
.bar
|
15
33
|
.progress
|
16
|
-
.label
|
34
|
+
.label
|
35
|
+
= progress[:desc]
|
36
|
+
- if progress[:last_percent].to_i > 0
|
37
|
+
%span.progress_time
|
38
|
+
(#{ eta } ⇒ #{ellapsed})
|
17
39
|
:deferjs
|
18
40
|
$('.step.progress').progress()
|
19
41
|
- else
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rbbt-rest
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.8.
|
4
|
+
version: 1.8.9
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Miguel Vazquez
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-05-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -196,6 +196,7 @@ files:
|
|
196
196
|
- lib/rbbt/rest/workflow/jobs.rb
|
197
197
|
- lib/rbbt/rest/workflow/locate.rb
|
198
198
|
- lib/rbbt/rest/workflow/render.rb
|
199
|
+
- lib/rbbt/rest/workflow/stream_task.rb
|
199
200
|
- share/views/association/Default.haml
|
200
201
|
- share/views/compass/app.sass
|
201
202
|
- share/views/compass/card_templates.sass
|