rbbt-rest 1.8.8 → 1.8.9
Sign up to get free protection for your applications and to get access to all the features.
- 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
|