toro 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +7 -0
  3. data/Appraisals +7 -0
  4. data/Gemfile +3 -0
  5. data/app/assets/javascripts/toro/monitor/abstract_jobs_table.js.coffee +188 -0
  6. data/app/assets/javascripts/toro/monitor/application.js.coffee +14 -0
  7. data/app/assets/javascripts/toro/monitor/chart.js.coffee +203 -0
  8. data/app/assets/javascripts/toro/monitor/initialize.js.coffee.erb +25 -0
  9. data/app/assets/javascripts/toro/monitor/jobs_table.js.coffee +63 -0
  10. data/app/assets/javascripts/toro/monitor/processes_table.js.coffee +96 -0
  11. data/app/assets/javascripts/toro/monitor/queue_jobs_table.js.coffee +91 -0
  12. data/app/assets/stylesheets/toro/monitor/application.css.sass +5 -0
  13. data/app/assets/stylesheets/toro/monitor/jobs_table.css.sass +69 -0
  14. data/app/assets/stylesheets/toro/monitor/layout.css.sass +5 -0
  15. data/app/controllers/toro/monitor/api/jobs_controller.rb +65 -0
  16. data/app/controllers/toro/monitor/api/processes_controller.rb +13 -0
  17. data/app/controllers/toro/monitor/api/queues_controller.rb +24 -0
  18. data/app/controllers/toro/monitor/base_controller.rb +11 -0
  19. data/app/controllers/toro/monitor/jobs_controller.rb +11 -0
  20. data/app/controllers/toro/monitor/processes_controller.rb +8 -0
  21. data/app/controllers/toro/monitor/queues_controller.rb +9 -0
  22. data/app/datatables/toro/monitor/abstract_datatable.rb +71 -0
  23. data/app/datatables/toro/monitor/jobs_datatable.rb +60 -0
  24. data/app/datatables/toro/monitor/processes_datatable.rb +60 -0
  25. data/app/helpers/toro/monitor/toro_monitor_helper.rb +13 -0
  26. data/app/views/toro/monitor/jobs/_jobs.slim +14 -0
  27. data/app/views/toro/monitor/jobs/chart.slim +1 -0
  28. data/app/views/toro/monitor/jobs/index.slim +1 -0
  29. data/app/views/toro/monitor/layouts/application.slim +32 -0
  30. data/app/views/toro/monitor/processes/index.slim +14 -0
  31. data/app/views/toro/monitor/queues/_jobs.slim +14 -0
  32. data/app/views/toro/monitor/queues/index.slim +5 -0
  33. data/config/routes.rb +16 -0
  34. data/examples/chart.png +0 -0
  35. data/examples/job.png +0 -0
  36. data/examples/jobs.png +0 -0
  37. data/examples/processes.png +0 -0
  38. data/examples/queues.png +0 -0
  39. data/gemfiles/rails_3.gemfile +7 -0
  40. data/gemfiles/rails_3.gemfile.lock +146 -0
  41. data/gemfiles/rails_4.gemfile +7 -0
  42. data/gemfiles/rails_4.gemfile.lock +147 -0
  43. data/lib/toro/version.rb +1 -1
  44. data/spec/config/database.yml.example +6 -0
  45. data/spec/lib/cli_spec.rb +28 -0
  46. data/spec/lib/client_spec.rb +26 -0
  47. data/spec/lib/fetcher_spec.rb +105 -0
  48. data/spec/lib/job_spec.rb +17 -0
  49. data/spec/lib/listener_spec.rb +72 -0
  50. data/spec/lib/manager_spec.rb +78 -0
  51. data/spec/lib/middleware/server/error_spec.rb +18 -0
  52. data/spec/lib/middleware/server/error_storage_spec.rb +22 -0
  53. data/spec/lib/middleware/server/properties_spec.rb +18 -0
  54. data/spec/lib/middleware/server/retry_spec.rb +38 -0
  55. data/spec/lib/processor_spec.rb +33 -0
  56. data/spec/lib/worker_spec.rb +50 -0
  57. data/spec/spec_helper.rb +31 -0
  58. data/spec/support/active_record_spec_helper.rb +18 -0
  59. data/spec/support/jobs_helper.rb +4 -0
  60. data/spec/support/workers_helper.rb +56 -0
  61. data/toro.gemspec +29 -0
  62. data/vendor/assets/javascripts/toro/monitor/bootstrap-select.min.js +1 -0
  63. data/vendor/assets/javascripts/toro/monitor/bootstrap.min.js +6 -0
  64. data/vendor/assets/javascripts/toro/monitor/d3.js +8795 -0
  65. data/vendor/assets/javascripts/toro/monitor/datatables.bootstrap.js +96 -0
  66. data/vendor/assets/javascripts/toro/monitor/datatables.js +256 -0
  67. data/vendor/assets/javascripts/toro/monitor/datatables.standing_redraw.js.coffee +5 -0
  68. data/vendor/assets/javascripts/toro/monitor/jquery.timeago.js +27 -0
  69. data/vendor/assets/stylesheets/toro/monitor/bootstrap-select.min.css +1 -0
  70. data/vendor/assets/stylesheets/toro/monitor/bootstrap.min.css +9 -0
  71. metadata +103 -5
@@ -0,0 +1,63 @@
1
+ class ToroMonitor.JobsTable extends ToroMonitor.AbstractJobsTable
2
+
3
+ initialize: =>
4
+ options =
5
+ table_selector: 'table.jobs'
6
+ columns:
7
+ id: 0
8
+ started_by: 1
9
+ queue: 2
10
+ class_name: 3
11
+ name: 4
12
+ created_at: 5
13
+ started_at: 6
14
+ duration: 7
15
+ message: 8
16
+ status: 9
17
+ properties: 10
18
+ args: 11
19
+ column_options: [
20
+ { bVisible: false }
21
+ { bVisible: false }
22
+ null
23
+ null
24
+ { bSortable: false }
25
+ {
26
+ fnRender: (oObj) =>
27
+ @format_time_ago(oObj.aData[@columns.created_at])
28
+ }
29
+ {
30
+ fnRender: (oObj) =>
31
+ @format_time_ago(oObj.aData[@columns.started_at])
32
+ }
33
+ null
34
+ { bSortable: false }
35
+ {
36
+ fnRender: (oObj) =>
37
+ status = oObj.aData[@columns.status]
38
+ class_name = switch status
39
+ when 'failed'
40
+ 'danger'
41
+ when 'complete'
42
+ 'success'
43
+ when 'running'
44
+ 'primary'
45
+ when 'scheduled'
46
+ 'inverse'
47
+ else
48
+ 'info'
49
+ html = """<a href="#" class="btn btn-#{class_name} btn-mini status-value">#{oObj.aData[@columns.status]}</a>"""
50
+ if status == 'failed'
51
+ html += """<a href="#" class="btn btn-mini btn-primary retry-job" data-job-id="#{oObj.aData[@columns.id]}">Retry<a>"""
52
+ """<span class="action-buttons">#{html}</span>"""
53
+ }
54
+ { bVisible: false }
55
+ { bVisible: false }
56
+ ]
57
+
58
+ return null unless $(options.table_selector).length
59
+
60
+ @initialize_with_options(options)
61
+
62
+ $ ->
63
+ new ToroMonitor.JobsTable
@@ -0,0 +1,96 @@
1
+ class ToroMonitor.ProcessesTable extends ToroMonitor.AbstractJobsTable
2
+
3
+ initialize: =>
4
+ @options =
5
+ table_selector: 'table.processes'
6
+ columns:
7
+ id: 0
8
+ started_by: 1
9
+ queue: 2
10
+ class_name: 3
11
+ name: 4
12
+ created_at: 5
13
+ started_at: 6
14
+ duration: 7
15
+ message: 8
16
+ status: 9
17
+ properties: 10
18
+ args: 11
19
+ column_options: [
20
+ { bVisible: false }
21
+ {
22
+ fnRender: (oObj) =>
23
+ """<span class="nowrap">#{oObj.aData[@columns.started_by]}</span>"""
24
+ }
25
+ null
26
+ null
27
+ { bSortable: false }
28
+ {
29
+ fnRender: (oObj) =>
30
+ @format_time_ago(oObj.aData[@columns.created_at])
31
+ }
32
+ {
33
+ fnRender: (oObj) =>
34
+ @format_time_ago(oObj.aData[@columns.started_at])
35
+ }
36
+ null
37
+ { bSortable: false }
38
+ {
39
+ fnRender: (oObj) =>
40
+ status = oObj.aData[@columns.status]
41
+ class_name = switch status
42
+ when 'failed'
43
+ 'danger'
44
+ when 'complete'
45
+ 'success'
46
+ when 'running'
47
+ 'primary'
48
+ else
49
+ 'info'
50
+ html = """<a href="#" class="btn btn-#{class_name} btn-mini status-value">#{oObj.aData[@columns.status]}</a>"""
51
+ if status == 'failed'
52
+ html += """<a href="#" class="btn btn-mini btn-primary retry-job" data-job-id="#{oObj.aData[@columns.id]}">Retry<a>"""
53
+ """<span class="action-buttons">#{html}</span>"""
54
+ }
55
+ { bVisible: false }
56
+ { bVisible: false }
57
+ ]
58
+
59
+ return null unless $(@options.table_selector).length
60
+
61
+ @table = $(@options.table_selector)
62
+
63
+ @columns = @options.columns
64
+ @status_filter = null
65
+
66
+ @table.dataTable
67
+ bProcessing: true
68
+ bServerSide: true
69
+ sAjaxSource: @table.data('source')
70
+ iDisplayLength: 10
71
+ aaSorting: [[@columns.created_at, 'desc']]
72
+ sPaginationType: 'bootstrap'
73
+ aoColumns: @options.column_options
74
+ oLanguage:
75
+ sInfo: '_TOTAL_ jobs'
76
+ sInfoFiltered: ' (filtered from _MAX_)'
77
+ sLengthMenu: 'Per page: _MENU_'
78
+ sSearch: ''
79
+ fnRowCallback: (nRow, aData, iDisplayIndex) =>
80
+ $('.timeago', nRow).timeago()
81
+ fnInitComplete: () =>
82
+ filter_container = @table.siblings('.dataTables_filter')
83
+ filter_container.find('input').attr('placeholder', 'Search...')
84
+ fnServerData: (sSource, aoData, fnCallback) =>
85
+ $.each @api_params, (key, value) =>
86
+ aoData.push
87
+ name: key
88
+ value: @api_params[key]
89
+ $.getJSON sSource, aoData, (json) -> fnCallback(json)
90
+
91
+ @table.parents('.dataTables_wrapper').addClass('jobs-table-wrapper')
92
+
93
+ @initialize_ui()
94
+
95
+ $ ->
96
+ new ToroMonitor.ProcessesTable
@@ -0,0 +1,91 @@
1
+ class ToroMonitor.QueueJobsTable extends ToroMonitor.AbstractJobsTable
2
+
3
+ initialize: =>
4
+ options =
5
+ table_selector: 'table.queue-jobs'
6
+ columns:
7
+ id: 0
8
+ started_by: 1
9
+ queue: 2
10
+ class_name: 3
11
+ name: 4
12
+ created_at: 5
13
+ started_at: 6
14
+ duration: 7
15
+ message: 8
16
+ status: 9
17
+ properties: 10
18
+ args: 11
19
+ column_options: [
20
+ { bVisible: false }
21
+ { bVisible: false }
22
+ { bVisible: false }
23
+ null
24
+ { bSortable: false }
25
+ {
26
+ fnRender: (oObj) =>
27
+ @format_time_ago(oObj.aData[@columns.created_at])
28
+ }
29
+ {
30
+ fnRender: (oObj) =>
31
+ @format_time_ago(oObj.aData[@columns.started_at])
32
+ }
33
+ null
34
+ { bSortable: false }
35
+ {
36
+ fnRender: (oObj) =>
37
+ status = oObj.aData[@columns.status]
38
+ class_name = switch status
39
+ when 'failed'
40
+ 'danger'
41
+ when 'complete'
42
+ 'success'
43
+ when 'running'
44
+ 'primary'
45
+ else
46
+ 'info'
47
+ html = """<a href="#" class="btn btn-#{class_name} btn-mini status-value">#{oObj.aData[@columns.status]}</a>"""
48
+ if status == 'failed'
49
+ html += """<a href="#" class="btn btn-mini btn-primary retry-job" data-job-id="#{oObj.aData[@columns.id]}">Retry<a>"""
50
+ """<span class="action-buttons">#{html}</span>"""
51
+ }
52
+ { bVisible: false }
53
+ { bVisible: false }
54
+ ]
55
+ return null unless $(options.table_selector).length
56
+
57
+ @queue_select = $('[name=queue_select]')
58
+ @queue_select.selectpicker()
59
+ @queue_select.change =>
60
+ @load_selected_queue()
61
+ @reload_table()
62
+ @queue_select.val(@queue_select.find('option:first').val())
63
+ @load_selected_queue()
64
+
65
+ @initialize_with_options(options)
66
+
67
+ show_queue_stats: =>
68
+ $.getJSON ToroMonitor.settings.api_url("queues/#{@queue}"), (stats) =>
69
+ if stats
70
+ header_cells = ""
71
+ value_cells = ""
72
+ $.each stats.status_counts, (status, count) =>
73
+ header_cells += "<th>#{status}</th>"
74
+ value_cells += "<td>#{count}</td>"
75
+ html = """
76
+ <table class="table table-striped table-condensed table-bordered">
77
+ <tr>#{header_cells}</tr>
78
+ <tr>#{value_cells}</tr>
79
+ </table>
80
+ """
81
+ else
82
+ html = ''
83
+ $('.queue-stats').html(html)
84
+
85
+ load_selected_queue: =>
86
+ @queue = @queue_select.val()
87
+ @api_params['queue'] = @queue
88
+ @show_queue_stats()
89
+
90
+ $ ->
91
+ new ToroMonitor.QueueJobsTable
@@ -0,0 +1,5 @@
1
+ @import toro/monitor/bootstrap.min
2
+ @import toro/monitor/bootstrap-select.min
3
+ @import dataTables/bootstrap/2/jquery.dataTables.bootstrap
4
+ @import toro/monitor/layout
5
+ @import toro/monitor/jobs_table
@@ -0,0 +1,69 @@
1
+ div.dataTables_length label
2
+ float: right
3
+
4
+ .dataTables_processing
5
+ display: none
6
+
7
+ .modal-body
8
+ max-height: 500px
9
+
10
+ .dataTable
11
+ .nowrap
12
+ white-space: nowrap
13
+
14
+ .jobs-table
15
+ td
16
+ max-width: 200px
17
+ overflow: auto
18
+ text-overflow: ellipsis
19
+ white-space: nowrap
20
+
21
+ .jobs-table-wrapper
22
+ clear: both
23
+
24
+ .status-value
25
+ text-transform: capitalize
26
+
27
+ .status-filter
28
+ float: left
29
+ margin-right: 15px
30
+ button
31
+ text-transform: capitalize
32
+
33
+ .action-buttons
34
+ white-space: nowrap
35
+
36
+ .retry-job
37
+ margin-left: 5px
38
+
39
+ .dataTables_filter > label
40
+ float: left
41
+
42
+ .dataTables_filter > label > input
43
+ width: 120px
44
+
45
+ .queue-stats
46
+ float: right
47
+ font-size: 12px
48
+ margin-left: 10px
49
+
50
+ &:after
51
+ content: "."
52
+ display: block
53
+ height: 0
54
+ clear: both
55
+ visibility: hidden
56
+
57
+ table
58
+ margin-bottom: 10px
59
+
60
+ th, td
61
+ padding: 0 5px
62
+ text-align: center
63
+
64
+ th
65
+ text-transform: capitalize
66
+ font-weight: normal
67
+
68
+ td
69
+ font-weight: bold
@@ -0,0 +1,5 @@
1
+ body
2
+ padding-top: 60px
3
+
4
+ .navbar .brand
5
+ font-weight: bold
@@ -0,0 +1,65 @@
1
+ module Toro
2
+ module Monitor
3
+ module Api
4
+ class JobsController < ActionController::Base
5
+ protect_from_forgery
6
+
7
+ def index
8
+ render json: JobsDatatable.new(view_context)
9
+ end
10
+
11
+ def chart
12
+ queues_jobs = Job.select('queue, status').all.group_by(&:queue)
13
+ queues = []
14
+ statuses = Job.statuses
15
+ queues_status_counts = queues_jobs.collect do |queue, jobs|
16
+ statuses_jobs = jobs.group_by(&:status)
17
+ statuses_job_counts = statuses_jobs.collect do |status, jobs|
18
+ next unless statuses.include?(status)
19
+ [status, jobs.length]
20
+ end
21
+ statuses_job_counts = Hash[statuses_job_counts.compact]
22
+ queues << queue unless queues.include?(queue)
23
+ { queue: queue }.merge(statuses_job_counts)
24
+ end
25
+ queues_status_counts = queues_status_counts.sort_by { |q| q[:queue] }
26
+ render json: {
27
+ queues_status_counts: queues_status_counts,
28
+ statuses: statuses
29
+ }
30
+ end
31
+
32
+ def custom_views
33
+ job = Job.find(params[:id])
34
+ render json: {}, status: 404 and return if job.blank?
35
+
36
+ views = CustomViews.for_job(job)
37
+ views = views.collect do |view|
38
+ {
39
+ name: view[:name],
40
+ html: render_to_string(view[:path], locals: {job: job, path: view[:path]})
41
+ }
42
+ end
43
+ render json: views, status: :ok
44
+ end
45
+
46
+ def retry
47
+ id = params[:id]
48
+ render json: {}, status: 404 and return if id.blank?
49
+
50
+ job = Job.find(id)
51
+ render json: {}, status: 404 and return if job.blank?
52
+
53
+ args = job.args
54
+ worker = job.class_name.constantize
55
+ worker.perform_async(*args)
56
+ render json: {}, status: :ok
57
+ end
58
+
59
+ def statuses
60
+ render json: Toro::Job.statuses, status: :ok
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,13 @@
1
+ module Toro
2
+ module Monitor
3
+ module Api
4
+ class ProcessesController < ActionController::Base
5
+ protect_from_forgery
6
+
7
+ def index
8
+ render json: ProcessesDatatable.new(view_context)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,24 @@
1
+ module Toro
2
+ module Monitor
3
+ module Api
4
+ class QueuesController < ActionController::Base
5
+ protect_from_forgery
6
+
7
+ def show
8
+ queue = params[:queue]
9
+ render json: {}, status: 404 and return if queue.blank?
10
+
11
+ status_counts = Toro::Job.where(queue: queue).count(group: 'status')
12
+ ordered_status_counts = {}
13
+ Job.statuses.each do |status|
14
+ ordered_status_counts[status] = status_counts.has_key?(status) ? status_counts[status] : 0
15
+ end
16
+ response = {
17
+ status_counts: ordered_status_counts
18
+ }
19
+ render json: response.to_json, status: :ok
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,11 @@
1
+ module Toro
2
+ module Monitor
3
+ class BaseController < ActionController::Base
4
+ protect_from_forgery
5
+
6
+ layout 'toro/monitor/layouts/application'
7
+
8
+ helper Toro::Monitor::ToroMonitorHelper
9
+ end
10
+ end
11
+ end