toro 0.2.1 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +7 -0
- data/Appraisals +7 -0
- data/Gemfile +3 -0
- data/app/assets/javascripts/toro/monitor/abstract_jobs_table.js.coffee +188 -0
- data/app/assets/javascripts/toro/monitor/application.js.coffee +14 -0
- data/app/assets/javascripts/toro/monitor/chart.js.coffee +203 -0
- data/app/assets/javascripts/toro/monitor/initialize.js.coffee.erb +25 -0
- data/app/assets/javascripts/toro/monitor/jobs_table.js.coffee +63 -0
- data/app/assets/javascripts/toro/monitor/processes_table.js.coffee +96 -0
- data/app/assets/javascripts/toro/monitor/queue_jobs_table.js.coffee +91 -0
- data/app/assets/stylesheets/toro/monitor/application.css.sass +5 -0
- data/app/assets/stylesheets/toro/monitor/jobs_table.css.sass +69 -0
- data/app/assets/stylesheets/toro/monitor/layout.css.sass +5 -0
- data/app/controllers/toro/monitor/api/jobs_controller.rb +65 -0
- data/app/controllers/toro/monitor/api/processes_controller.rb +13 -0
- data/app/controllers/toro/monitor/api/queues_controller.rb +24 -0
- data/app/controllers/toro/monitor/base_controller.rb +11 -0
- data/app/controllers/toro/monitor/jobs_controller.rb +11 -0
- data/app/controllers/toro/monitor/processes_controller.rb +8 -0
- data/app/controllers/toro/monitor/queues_controller.rb +9 -0
- data/app/datatables/toro/monitor/abstract_datatable.rb +71 -0
- data/app/datatables/toro/monitor/jobs_datatable.rb +60 -0
- data/app/datatables/toro/monitor/processes_datatable.rb +60 -0
- data/app/helpers/toro/monitor/toro_monitor_helper.rb +13 -0
- data/app/views/toro/monitor/jobs/_jobs.slim +14 -0
- data/app/views/toro/monitor/jobs/chart.slim +1 -0
- data/app/views/toro/monitor/jobs/index.slim +1 -0
- data/app/views/toro/monitor/layouts/application.slim +32 -0
- data/app/views/toro/monitor/processes/index.slim +14 -0
- data/app/views/toro/monitor/queues/_jobs.slim +14 -0
- data/app/views/toro/monitor/queues/index.slim +5 -0
- data/config/routes.rb +16 -0
- data/examples/chart.png +0 -0
- data/examples/job.png +0 -0
- data/examples/jobs.png +0 -0
- data/examples/processes.png +0 -0
- data/examples/queues.png +0 -0
- data/gemfiles/rails_3.gemfile +7 -0
- data/gemfiles/rails_3.gemfile.lock +146 -0
- data/gemfiles/rails_4.gemfile +7 -0
- data/gemfiles/rails_4.gemfile.lock +147 -0
- data/lib/toro/version.rb +1 -1
- data/spec/config/database.yml.example +6 -0
- data/spec/lib/cli_spec.rb +28 -0
- data/spec/lib/client_spec.rb +26 -0
- data/spec/lib/fetcher_spec.rb +105 -0
- data/spec/lib/job_spec.rb +17 -0
- data/spec/lib/listener_spec.rb +72 -0
- data/spec/lib/manager_spec.rb +78 -0
- data/spec/lib/middleware/server/error_spec.rb +18 -0
- data/spec/lib/middleware/server/error_storage_spec.rb +22 -0
- data/spec/lib/middleware/server/properties_spec.rb +18 -0
- data/spec/lib/middleware/server/retry_spec.rb +38 -0
- data/spec/lib/processor_spec.rb +33 -0
- data/spec/lib/worker_spec.rb +50 -0
- data/spec/spec_helper.rb +31 -0
- data/spec/support/active_record_spec_helper.rb +18 -0
- data/spec/support/jobs_helper.rb +4 -0
- data/spec/support/workers_helper.rb +56 -0
- data/toro.gemspec +29 -0
- data/vendor/assets/javascripts/toro/monitor/bootstrap-select.min.js +1 -0
- data/vendor/assets/javascripts/toro/monitor/bootstrap.min.js +6 -0
- data/vendor/assets/javascripts/toro/monitor/d3.js +8795 -0
- data/vendor/assets/javascripts/toro/monitor/datatables.bootstrap.js +96 -0
- data/vendor/assets/javascripts/toro/monitor/datatables.js +256 -0
- data/vendor/assets/javascripts/toro/monitor/datatables.standing_redraw.js.coffee +5 -0
- data/vendor/assets/javascripts/toro/monitor/jquery.timeago.js +27 -0
- data/vendor/assets/stylesheets/toro/monitor/bootstrap-select.min.css +1 -0
- data/vendor/assets/stylesheets/toro/monitor/bootstrap.min.css +9 -0
- metadata +103 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d512053c857d51305148c1d6e40ef0bad33c67b7
|
4
|
+
data.tar.gz: d5f16d52befe81638484fa1b8dea32d8a7e6ecab
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7f8e7812ff28d22e87f77e98eb11a15e3ce503671d9b6988f67f5e42896e1e0301c6dff2e0d16b535aabe51f58ce6c10485c8edbd96c1cafd161a5bc692a9a15
|
7
|
+
data.tar.gz: 59a44752ec748288e189d87f6f31f781173d07845e2a9a4ee00c0d3d5d58c090cf62d1431c04b8876f0614d1e17433f56b80835625b5a08fa5cfc70d62e59e91
|
data/.gitignore
ADDED
data/Appraisals
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,188 @@
|
|
1
|
+
class ToroMonitor.AbstractJobsTable
|
2
|
+
|
3
|
+
constructor: ->
|
4
|
+
@api_params = {}
|
5
|
+
@initialize()
|
6
|
+
|
7
|
+
initialize_with_options: (options) =>
|
8
|
+
@options = options
|
9
|
+
@table = $(@options.table_selector)
|
10
|
+
|
11
|
+
@columns = @options.columns
|
12
|
+
@status_filter = null
|
13
|
+
|
14
|
+
@table.dataTable
|
15
|
+
bProcessing: true
|
16
|
+
bServerSide: true
|
17
|
+
sAjaxSource: @table.data('source')
|
18
|
+
iDisplayLength: 10
|
19
|
+
aaSorting: [[@columns.created_at, 'desc']]
|
20
|
+
sPaginationType: 'bootstrap'
|
21
|
+
aoColumns: @options.column_options
|
22
|
+
oLanguage:
|
23
|
+
sInfo: '_TOTAL_ jobs'
|
24
|
+
sInfoFiltered: ' (filtered from _MAX_)'
|
25
|
+
sLengthMenu: 'Per page: _MENU_'
|
26
|
+
sSearch: ''
|
27
|
+
fnRowCallback: (nRow, aData, iDisplayIndex) =>
|
28
|
+
$('.timeago', nRow).timeago() if nRow
|
29
|
+
fnInitComplete: () =>
|
30
|
+
filter_container = @table.siblings('.dataTables_filter')
|
31
|
+
filter_container.find('input').attr('placeholder', 'Search...')
|
32
|
+
$.getJSON ToroMonitor.settings.api_url('jobs/statuses'), (statuses) =>
|
33
|
+
status_filter_html = ''
|
34
|
+
$.each statuses, (key, status) =>
|
35
|
+
status_filter_html += """<button type="button" class="btn btn-small" data-value="#{status}">#{status}</button>"""
|
36
|
+
status_filter_html = """<div class="btn-group status-filter" data-toggle="buttons-radio">#{status_filter_html}</div>"""
|
37
|
+
filter_container.prepend status_filter_html
|
38
|
+
@status_filter = filter_container.find('.status-filter')
|
39
|
+
fnServerData: (sSource, aoData, fnCallback) =>
|
40
|
+
$.each @api_params, (key, value) =>
|
41
|
+
aoData.push
|
42
|
+
name: key
|
43
|
+
value: @api_params[key]
|
44
|
+
$.getJSON sSource, aoData, (json) -> fnCallback(json)
|
45
|
+
|
46
|
+
@table.parents('.dataTables_wrapper').addClass('jobs-table-wrapper')
|
47
|
+
|
48
|
+
@initialize_ui()
|
49
|
+
|
50
|
+
initialize_ui: =>
|
51
|
+
|
52
|
+
@table.on 'click', '.status-value', (e) =>
|
53
|
+
tr = $(e.target).parents('tr:first')[0]
|
54
|
+
job = @table.fnGetData(tr)
|
55
|
+
@show_job(job)
|
56
|
+
false
|
57
|
+
@table.on 'click', '.retry-job', (e) =>
|
58
|
+
id = $(e.target).attr('data-job-id')
|
59
|
+
$.getJSON ToroMonitor.settings.api_url('jobs/retry/'+id), =>
|
60
|
+
@reload_table()
|
61
|
+
false
|
62
|
+
|
63
|
+
$('body').on 'click', '.status-filter .btn', (e) =>
|
64
|
+
e.stopPropagation()
|
65
|
+
btn = $(e.target)
|
66
|
+
btn.siblings('.btn').removeClass('active')
|
67
|
+
btn.toggleClass('active')
|
68
|
+
value = @status_filter.find('.active:first').attr('data-value')
|
69
|
+
value = '' if !value?
|
70
|
+
@table.fnFilter(value, @columns.status - 1)
|
71
|
+
|
72
|
+
@start_polling()
|
73
|
+
|
74
|
+
show_job: (job) =>
|
75
|
+
return false if !job?
|
76
|
+
|
77
|
+
id = job[@columns.id]
|
78
|
+
started_by = job[@columns.started_by]
|
79
|
+
class_name = job[@columns.class_name]
|
80
|
+
name = job[@columns.name]
|
81
|
+
started_at = job[@columns.started_at]
|
82
|
+
duration = job[@columns.duration]
|
83
|
+
status = job[@columns.status]
|
84
|
+
properties = job[@columns.properties]
|
85
|
+
args = job[@columns.args]
|
86
|
+
|
87
|
+
# TODO: Make this cleaner; is there a way to grab the original value returned by the server?
|
88
|
+
status = $("<div>#{status}</div>").find('.status-value').text()
|
89
|
+
|
90
|
+
properties_html = ''
|
91
|
+
if properties?
|
92
|
+
rows_html = ''
|
93
|
+
if status == 'failed'
|
94
|
+
properties_html += """
|
95
|
+
<h4>Error</h4>
|
96
|
+
#{properties['error:message']}
|
97
|
+
<h5>Backtrace</h5>
|
98
|
+
<pre>
|
99
|
+
#{properties['error:backtrace'].join("\n")}
|
100
|
+
</pre>
|
101
|
+
"""
|
102
|
+
for key, value of properties
|
103
|
+
rows_html += "<tr><td>#{key}</td><td>#{JSON.stringify(value, null, 2)}</td></tr>"
|
104
|
+
if rows_html
|
105
|
+
properties_html += """
|
106
|
+
<h4>Properties</h4>
|
107
|
+
<table class="table table-striped">
|
108
|
+
#{rows_html}
|
109
|
+
</table>
|
110
|
+
"""
|
111
|
+
|
112
|
+
modal_html = """
|
113
|
+
<div class="modal hide fade job-modal" role="dialog">
|
114
|
+
<div class="modal-header">
|
115
|
+
<button type="button" class="close" data-dismiss="modal">×</button>
|
116
|
+
<h3>Job</h3>
|
117
|
+
</div>
|
118
|
+
<div class="modal-body">
|
119
|
+
<table class="table table-striped">
|
120
|
+
<tr>
|
121
|
+
<th>ID</th>
|
122
|
+
<td>#{id}</td>
|
123
|
+
</tr>
|
124
|
+
<tr>
|
125
|
+
<th>Class</th>
|
126
|
+
<td>#{class_name}</td>
|
127
|
+
</tr>
|
128
|
+
<tr>
|
129
|
+
<th>Name</th>
|
130
|
+
<td>#{name}</td>
|
131
|
+
</tr>
|
132
|
+
<tr>
|
133
|
+
<th>Args</th>
|
134
|
+
<td>#{JSON.stringify(args, null, 2)}</td>
|
135
|
+
</tr>
|
136
|
+
<tr>
|
137
|
+
<th>Started</th>
|
138
|
+
<td>#{started_at}</td>
|
139
|
+
</tr>
|
140
|
+
<tr>
|
141
|
+
<th>Duration</th>
|
142
|
+
<td>#{duration}</td>
|
143
|
+
</tr>
|
144
|
+
<tr>
|
145
|
+
<th>Process</th>
|
146
|
+
<td>#{started_by}</td>
|
147
|
+
</tr>
|
148
|
+
<tr>
|
149
|
+
<th>Status</th>
|
150
|
+
<td>#{status}</td>
|
151
|
+
</tr>
|
152
|
+
</table>
|
153
|
+
#{properties_html}
|
154
|
+
<div class="job-custom-views"></div>
|
155
|
+
</div>
|
156
|
+
</div>
|
157
|
+
"""
|
158
|
+
$('.job-modal').modal('hide')
|
159
|
+
$('body').append(modal_html)
|
160
|
+
modal = $('.job-modal:last')
|
161
|
+
modal.modal
|
162
|
+
width: 480
|
163
|
+
$.getJSON ToroMonitor.settings.api_url("jobs/custom_views/#{id}"), (views) ->
|
164
|
+
html = ''
|
165
|
+
for view in views
|
166
|
+
html += """
|
167
|
+
<h4>#{view['name']}</h4>
|
168
|
+
#{view['html']}
|
169
|
+
"""
|
170
|
+
$('.job-custom-views', modal).html(html)
|
171
|
+
|
172
|
+
on_poll: =>
|
173
|
+
if document.hasFocus()
|
174
|
+
@reload_table()
|
175
|
+
|
176
|
+
reload_table: =>
|
177
|
+
@table.dataTable().fnStandingRedraw()
|
178
|
+
|
179
|
+
start_polling: =>
|
180
|
+
setInterval =>
|
181
|
+
@on_poll()
|
182
|
+
, ToroMonitor.settings.poll_interval
|
183
|
+
|
184
|
+
format_time_ago: (time) =>
|
185
|
+
if time?
|
186
|
+
"""<span class="timeago" title="#{time}">#{time}</span>"""
|
187
|
+
else
|
188
|
+
""
|
@@ -0,0 +1,14 @@
|
|
1
|
+
#= require jquery
|
2
|
+
#= require toro/monitor/bootstrap.min
|
3
|
+
#= require toro/monitor/bootstrap-select.min
|
4
|
+
#= require toro/monitor/jquery.timeago
|
5
|
+
#= require toro/monitor/d3
|
6
|
+
#= require toro/monitor/datatables
|
7
|
+
#= require toro/monitor/datatables.bootstrap
|
8
|
+
#= require toro/monitor/datatables.standing_redraw
|
9
|
+
#= require toro/monitor/initialize
|
10
|
+
#= require toro/monitor/abstract_jobs_table
|
11
|
+
#= require toro/monitor/jobs_table
|
12
|
+
#= require toro/monitor/processes_table
|
13
|
+
#= require toro/monitor/queue_jobs_table
|
14
|
+
#= require toro/monitor/chart
|
@@ -0,0 +1,203 @@
|
|
1
|
+
class ToroMonitor.Chart
|
2
|
+
|
3
|
+
constructor: ->
|
4
|
+
options =
|
5
|
+
selector: '.chart'
|
6
|
+
poll_interval: ToroMonitor.settings.poll_interval
|
7
|
+
@initialize(options)
|
8
|
+
|
9
|
+
initialize: (options) =>
|
10
|
+
@options = options
|
11
|
+
@width = 960
|
12
|
+
@height = 580
|
13
|
+
@padding = [120, 50, 30, 20]
|
14
|
+
|
15
|
+
return null unless $(options.selector).length
|
16
|
+
|
17
|
+
# These correspond to Job.statuses: queued, running, complete, failed, [custom statuses]
|
18
|
+
colors = ['lightblue', 'blue', 'green', 'red', 'gray', 'purple', 'yellow']
|
19
|
+
|
20
|
+
@color_scale = d3.scale.ordinal().range(colors)
|
21
|
+
|
22
|
+
@x_scale = d3.scale.ordinal().rangeRoundBands([0, @width - @padding[1] - @padding[3]])
|
23
|
+
@y_scale = d3.scale.linear().range([0, @height - @padding[0] - @padding[2]])
|
24
|
+
@z_scale = d3.scale.ordinal().range(colors)
|
25
|
+
|
26
|
+
@render()
|
27
|
+
@start_polling()
|
28
|
+
|
29
|
+
start_polling: =>
|
30
|
+
setInterval =>
|
31
|
+
if document.hasFocus()
|
32
|
+
@render()
|
33
|
+
, @options.poll_interval
|
34
|
+
|
35
|
+
render: =>
|
36
|
+
d3.json ToroMonitor.settings.api_url('jobs/chart'), (data) =>
|
37
|
+
|
38
|
+
# Clear out the previously rendered chart
|
39
|
+
$(@options.selector).text('')
|
40
|
+
|
41
|
+
queues = data.queues_status_counts.map (queues_status_count) -> queues_status_count.queue
|
42
|
+
charts_config = ToroMonitor.settings.charts || { 'ALL': null }
|
43
|
+
charts_config = @standardize_charts_config(charts_config, queues)
|
44
|
+
|
45
|
+
for index, chart_config of charts_config
|
46
|
+
chart_config['show_legend'] = true if index == '0'
|
47
|
+
@render_chart(data, chart_config)
|
48
|
+
|
49
|
+
render_chart: (data, chart_config) =>
|
50
|
+
@svg = d3.select(@options.selector).append('svg:svg')
|
51
|
+
.attr('width', @width)
|
52
|
+
.attr('height', @height)
|
53
|
+
.append('svg:g')
|
54
|
+
.attr('transform', 'translate(' + @padding[3] + ',' + (@height - @padding[2]) + ')')
|
55
|
+
|
56
|
+
statuses = data.statuses
|
57
|
+
queues_status_counts = []
|
58
|
+
for queue_status_counts in data.queues_status_counts
|
59
|
+
if chart_config['queues'].indexOf(queue_status_counts.queue) > -1
|
60
|
+
queues_status_counts.push queue_status_counts
|
61
|
+
data = queues_status_counts
|
62
|
+
|
63
|
+
@color_scale.domain(statuses)
|
64
|
+
|
65
|
+
# Transpose the data into layers by queue
|
66
|
+
queues = d3.layout.stack()(statuses.map( (queue) =>
|
67
|
+
layers = []
|
68
|
+
for d in data
|
69
|
+
if d[queue]?
|
70
|
+
layers.push {x: d.queue, y: d[queue]+0}
|
71
|
+
else
|
72
|
+
layers.push {x: d.queue, y: 0}
|
73
|
+
layers
|
74
|
+
))
|
75
|
+
|
76
|
+
# Compute the x-domain (by queue) and y-domain (by top).
|
77
|
+
@x_scale.domain(queues[0].map( (d) => d.x))
|
78
|
+
@y_scale.domain([0, d3.max(queues[queues.length - 1], (d) => d.y0 + d.y )])
|
79
|
+
|
80
|
+
# Groups for queues
|
81
|
+
queue = @svg.selectAll('g.queue')
|
82
|
+
.data(queues)
|
83
|
+
.enter().append('svg:g')
|
84
|
+
.attr('class', 'queue')
|
85
|
+
.style('fill', (d, i) => @z_scale(i) )
|
86
|
+
.style('stroke', (d, i) => d3.rgb(@z_scale(i)).darker() )
|
87
|
+
|
88
|
+
# Rects for queues
|
89
|
+
rect = queue.selectAll('rect')
|
90
|
+
.data(Object)
|
91
|
+
.enter().append('svg:rect')
|
92
|
+
.attr('x', (d) => @x_scale(d.x) )
|
93
|
+
.attr('y', (d) => -@y_scale(d.y0) - @y_scale(d.y) )
|
94
|
+
.attr('height', (d) => @y_scale(d.y) )
|
95
|
+
.attr('width', @x_scale.rangeBand())
|
96
|
+
.append('title')
|
97
|
+
.text((d) => if d.y == 1 then "#{d.y} job" else "#{d.y} jobs")
|
98
|
+
|
99
|
+
# Labels for queues
|
100
|
+
label = @svg.selectAll('text')
|
101
|
+
.data(@x_scale.domain())
|
102
|
+
.enter().append('foreignObject')
|
103
|
+
.attr('x', (d) => @x_scale(d) )
|
104
|
+
.attr('y', 6)
|
105
|
+
.attr('dy', '.71em')
|
106
|
+
.attr('height', 20)
|
107
|
+
.attr('width', @x_scale.rangeBand())
|
108
|
+
.append('xhtml:div')
|
109
|
+
.text((d) => d)
|
110
|
+
.attr('style', 'font-size: 11px; text-align: center; word-wrap:break-word; padding: 0 3px')
|
111
|
+
|
112
|
+
# Y-axis rules
|
113
|
+
rule = @svg.selectAll('g.rule')
|
114
|
+
.data(@y_scale.ticks(5))
|
115
|
+
.enter().append('svg:g')
|
116
|
+
.attr('class', 'rule')
|
117
|
+
.attr('transform', (d) => 'translate(0,' + -@y_scale(d) + ')' )
|
118
|
+
|
119
|
+
rule.append('svg:text')
|
120
|
+
.attr('x', @width - @padding[1] - @padding[3] + 6)
|
121
|
+
.attr('dy', '.35em')
|
122
|
+
.text(d3.format(',d'))
|
123
|
+
|
124
|
+
# Title
|
125
|
+
if chart_config['title']
|
126
|
+
label = @svg.selectAll('g.text').data(['Test 1']).enter()
|
127
|
+
.append('text')
|
128
|
+
.attr('x', 0)
|
129
|
+
.attr('y', 90 - @height)
|
130
|
+
.attr('height', 20)
|
131
|
+
.attr('font-size', '24px')
|
132
|
+
.attr('fill', '#777')
|
133
|
+
.text(chart_config['title'])
|
134
|
+
|
135
|
+
# Legend
|
136
|
+
if chart_config['show_legend']
|
137
|
+
legend_x = @width - 130
|
138
|
+
legend_y = 30 - @height
|
139
|
+
legend_box_width = 18
|
140
|
+
legend_box_height = 18
|
141
|
+
|
142
|
+
legend = @svg.selectAll('.legend')
|
143
|
+
.data(@color_scale.domain().reverse().slice())
|
144
|
+
.enter().append('g')
|
145
|
+
.attr('class', 'legend')
|
146
|
+
.attr('transform', (d, i) => 'translate(0,' + i * 20 + ')')
|
147
|
+
|
148
|
+
# White background for the legend
|
149
|
+
legend.append('rect')
|
150
|
+
.attr('x', legend_x - 10)
|
151
|
+
.attr('y', legend_y)
|
152
|
+
.attr('width', legend_box_width + 100)
|
153
|
+
.attr('height', legend_box_height + 10)
|
154
|
+
.style('fill', '#fff')
|
155
|
+
|
156
|
+
# Legend boxes
|
157
|
+
legend.append('rect')
|
158
|
+
.attr('x', legend_x)
|
159
|
+
.attr('y', legend_y)
|
160
|
+
.attr('width', legend_box_width)
|
161
|
+
.attr('height', legend_box_height)
|
162
|
+
.style('fill', @color_scale)
|
163
|
+
|
164
|
+
# Legend labels
|
165
|
+
legend.append('text')
|
166
|
+
.attr('x', legend_x + 25)
|
167
|
+
.attr('y', legend_y + 9)
|
168
|
+
.attr('dy', '.35em')
|
169
|
+
.text((d) => d)
|
170
|
+
|
171
|
+
standardize_charts_config: (chart_config, queues) =>
|
172
|
+
charts = []
|
173
|
+
matched_queues = []
|
174
|
+
for pattern, title of chart_config
|
175
|
+
chart = {
|
176
|
+
title: title
|
177
|
+
pattern: pattern
|
178
|
+
}
|
179
|
+
if pattern == 'OTHER' || pattern == 'ALL'
|
180
|
+
charts[pattern] = chart
|
181
|
+
continue
|
182
|
+
else
|
183
|
+
pattern_queues = []
|
184
|
+
for queue in queues
|
185
|
+
re = new RegExp(pattern, 'i')
|
186
|
+
if re.test(queue)
|
187
|
+
pattern_queues.push queue
|
188
|
+
matched_queues.push queue
|
189
|
+
chart['queues'] = pattern_queues
|
190
|
+
charts[pattern] = chart
|
191
|
+
remaining_queues = @array_difference(queues, matched_queues)
|
192
|
+
charts['OTHER']['queues'] = remaining_queues if charts['OTHER']
|
193
|
+
charts['ALL']['queues'] = queues if charts['ALL']
|
194
|
+
@object_values(charts)
|
195
|
+
|
196
|
+
array_difference: (array1, array2) =>
|
197
|
+
array1.filter (value) -> !(array2.indexOf(value) > -1)
|
198
|
+
|
199
|
+
object_values: (object) =>
|
200
|
+
value for key, value of object
|
201
|
+
|
202
|
+
$ ->
|
203
|
+
new ToroMonitor.Chart
|
@@ -0,0 +1,25 @@
|
|
1
|
+
window.ToroMonitor =
|
2
|
+
settings:
|
3
|
+
api_url: (path) ->
|
4
|
+
"<%= Toro::Monitor.root_path %>api/#{path}"
|
5
|
+
charts: <%= Toro::Monitor.options[:charts].to_json %>
|
6
|
+
poll_interval: <%= Toro::Monitor.options[:poll_interval] %>
|
7
|
+
|
8
|
+
$.timeago.settings.strings =
|
9
|
+
prefixAgo: null
|
10
|
+
prefixFromNow: null
|
11
|
+
suffixAgo: " ago"
|
12
|
+
suffixFromNow: " from now"
|
13
|
+
seconds: "1m"
|
14
|
+
minute: "1m"
|
15
|
+
minutes: "%dm"
|
16
|
+
hour: "1h"
|
17
|
+
hours: "%dh"
|
18
|
+
day: "1d"
|
19
|
+
days: "%dd"
|
20
|
+
month: "1mo"
|
21
|
+
months: "%dmo"
|
22
|
+
year: "1yr"
|
23
|
+
years: "%dyr"
|
24
|
+
wordSeparator: " "
|
25
|
+
numbers: []
|