banter 0.8.0 → 1.0.0

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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/.travis.yml +8 -0
  4. data/Gemfile +2 -0
  5. data/Rakefile +29 -0
  6. data/lib/banter/subscriber.rb +18 -0
  7. data/lib/banter/version.rb +1 -1
  8. data/lib/banter/web.rb +6 -0
  9. data/lib/banter/web/application.rb +62 -13
  10. data/lib/banter/web/helpers.rb +6 -2
  11. data/lib/banter/web/models/banter_message.rb +45 -5
  12. data/lib/banter/web/models/banter_worker.rb +5 -0
  13. data/lib/banter/web/serializers/banter_message_serializer.rb +34 -0
  14. data/lib/banter/web/serializers/banter_worker_serializer.rb +30 -0
  15. data/lib/banter/web/serializers/subscriber_serializer.rb +26 -0
  16. data/spec/banter/subscriber_spec.rb +82 -1
  17. data/spec/banter/web/application_spec.rb +173 -2
  18. data/spec/factories/banter_messages.rb +28 -0
  19. data/spec/spec_helper.rb +0 -1
  20. data/web/assets/javascripts/banter.js +449 -0
  21. data/web/assets/javascripts/dashboard_app/app.coffee +4 -0
  22. data/web/assets/javascripts/dashboard_app/controllers/message_controller.coffee +27 -0
  23. data/web/assets/javascripts/dashboard_app/controllers/messages_controller.coffee +57 -0
  24. data/web/assets/javascripts/dashboard_app/controllers/subscribers_controller.coffee +25 -0
  25. data/web/assets/javascripts/dashboard_app/controllers/worker_controller.coffee +30 -0
  26. data/web/assets/javascripts/dashboard_app/controllers/workers_list_controller.coffee +28 -0
  27. data/web/assets/javascripts/dashboard_app/routes.coffee +82 -0
  28. data/web/assets/javascripts/dashboard_app/services/common_methods_service.coffee +6 -0
  29. data/web/assets/javascripts/dashboard_app/services/message_methods_service.coffee +18 -0
  30. data/web/assets/javascripts/dashboard_app/services/messages_service.coffee +17 -0
  31. data/web/assets/javascripts/dashboard_app/services/subscribers_service.coffee +8 -0
  32. data/web/assets/javascripts/dashboard_app/services/worker_methods_service.coffee +11 -0
  33. data/web/assets/javascripts/dashboard_app/services/worker_service.coffee +15 -0
  34. data/web/assets/javascripts/ng-prettyjson.js +15 -0
  35. data/web/assets/stylesheets/banter.css +13 -1
  36. data/web/views/_message.haml +59 -0
  37. data/web/views/_messages.haml +77 -0
  38. data/web/views/_subscribers.haml +22 -0
  39. data/web/views/_worker.haml +52 -0
  40. data/web/views/_workers_list.haml +35 -0
  41. data/web/views/index.haml +16 -29
  42. data/web/views/layout.haml +16 -10
  43. metadata +28 -7
  44. data/web/views/message.haml +0 -52
  45. data/web/views/messages.haml +0 -20
  46. data/web/views/subscribers.haml +0 -17
  47. data/web/views/worker.haml +0 -51
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 11d9e4fde9f27b5c99163b5abb6ddd4f8cb29607
4
- data.tar.gz: 754895e5e7af5773b072e7fb7f2024728bbaa555
3
+ metadata.gz: 51871d5d895dbf72454f67a614a14aa8af5cd6ee
4
+ data.tar.gz: 37321de3cd2605a5b7d99ffce5e979167b7597df
5
5
  SHA512:
6
- metadata.gz: c4418fba824671130e639689e5c9f158256a45f7dfff640366bbafa50eac3a98478a7daa0bb2d3170148a05a01acc7e92d14d89f32d9e0e7558d8a87621c5db7
7
- data.tar.gz: c15e928646a3f46129dfa639fb637a33fd3d0dfd98acc871ed17de0656dc4042a91c85e7dc396cda395efc4e8af15eeb939fd156bdad8cb0d8a03d867429ec30
6
+ metadata.gz: 9dba68030359665879dbd785c613523282f97a5b91a721824c91dd4375e87a8299e39f68af759595ffe5963b584b7981b526a7a5d0071f05b2a4a02cc1c95669
7
+ data.tar.gz: 16ba97865260c2e81463d374f1c4806cf17ad65628022aa6abc90da7e6d8af8741a0166afd67887f59a5d87c5f9f8dc53ccb989e7609f376e379212d5707173a
data/.gitignore CHANGED
@@ -19,4 +19,4 @@ test/version_tmp
19
19
  tmp
20
20
  .rspec
21
21
  .idea
22
- test_pubsub.log
22
+ *.log
@@ -8,4 +8,12 @@ services:
8
8
  before_script:
9
9
  - sleep 5
10
10
  - mongo mydb_test --eval 'db.addUser("travis", "test");'
11
+ notifications:
12
+ email:
13
+ - webadmin@honest.com
14
+ - tushar@honest.com
15
+ on_success: always
16
+ on_failure: always
17
+
18
+
11
19
 
data/Gemfile CHANGED
@@ -3,6 +3,8 @@ source 'https://rubygems.org'
3
3
  # Specify your gem's dependencies in banter.gemspec
4
4
  gemspec
5
5
 
6
+ gem 'coffee-script'
7
+ gem 'json'
6
8
  group :test do
7
9
  gem 'rack-test'
8
10
  gem 'sinatra'
data/Rakefile CHANGED
@@ -5,3 +5,32 @@ begin
5
5
  RSpec::Core::RakeTask.new(:spec)
6
6
  task :default => :spec
7
7
  end
8
+
9
+ begin
10
+ require 'coffee-script'
11
+
12
+ namespace :js do
13
+ def add_javascript(js_file, output_file)
14
+ js_file_souce = File.read("#{File.dirname(__FILE__)}/web/assets/javascripts/#{js_file}.js")
15
+ output_file.puts js_file_souce
16
+ end
17
+
18
+ desc "compile coffee-scripts from ./assets/javascripts"
19
+ task :compile do
20
+ source = "#{File.dirname(__FILE__)}/web/assets/javascripts/**/*.coffee"
21
+ target = "#{File.dirname(__FILE__)}/web/assets/javascripts/banter.js"
22
+ file = File.open(target, 'w+')
23
+
24
+ add_javascript("ng-prettyjson", file)
25
+
26
+ Dir.glob(source) do |cf|
27
+ puts cf
28
+ file.puts CoffeeScript.compile File.read(cf)
29
+ end
30
+
31
+ file.close
32
+ end
33
+ end
34
+
35
+
36
+ end
@@ -90,6 +90,24 @@ module Banter
90
90
 
91
91
  private
92
92
 
93
+ # @param [Integer] number The expected number of total objects that may be processed due to the message
94
+ def total(number)
95
+ return unless Banter::Configuration.web_enabled
96
+ BanterMessage.update_progress_total(number)
97
+ end
98
+
99
+ # @param [Integer] number The current progress of the execution of the message
100
+ def at(number)
101
+ return unless Banter::Configuration.web_enabled
102
+ BanterMessage.update_progress_at(number)
103
+ end
104
+
105
+ # @param [Object] object An object that indicates the current progress of the message
106
+ def progress(object)
107
+ return unless Banter::Configuration.web_enabled
108
+ BanterMessage.update_progress(object)
109
+ end
110
+
93
111
  def self.validate_routing_key_name(key)
94
112
  return true if key.blank?
95
113
  key.match(/\A([a-z]+\.?)*([a-z]+)\Z/).present?
@@ -1,3 +1,3 @@
1
1
  module Banter
2
- VERSION = "0.8.0"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -20,6 +20,12 @@ module Banter
20
20
  module Web
21
21
  autoload :Application, 'banter/web/application'
22
22
  autoload :Helpers, 'banter/web/helpers'
23
+
24
+ module Serializers
25
+ autoload :BanterWorkerSerializer, 'banter/web/serializers/banter_worker_serializer'
26
+ autoload :BanterMessageSerializer, 'banter/web/serializers/banter_message_serializer'
27
+ autoload :SubscriberSerializer, 'banter/web/serializers/subscriber_serializer'
28
+ end
23
29
  end
24
30
  end
25
31
 
@@ -8,29 +8,78 @@ module Banter
8
8
  helpers Banter::Web::Helpers
9
9
 
10
10
  get '/' do
11
- @workers = BanterWorker.running.ordered
12
- haml :index, locals: { workers: @workers }
11
+ redirect "#{root_path}dashboard"
13
12
  end
14
13
 
15
- get '/workers/:id' do
14
+ get '/dashboard*', provides: :html do
15
+ haml :index
16
+ end
17
+
18
+ get '/workers.json' do
19
+ content_type :json
20
+ workers = BanterWorker.running.ordered.map { |w| serialize_worker(w) }
21
+ { workers: workers }.to_json
22
+ end
23
+
24
+ get '/workers/:id.json' do
25
+ content_type :json
16
26
  worker = BanterWorker.find(params[:id])
17
- haml :worker, locals: { worker: worker }
27
+ { worker: serialize_worker(worker) }.to_json
28
+ end
29
+
30
+ get '/messages/:message_id.json' do
31
+ content_type :json
32
+ message = BanterMessage.find(params[:message_id])
33
+ {
34
+ message: serialize_message(message, true)
35
+ }.to_json
36
+ end
37
+
38
+ get '/messages.json' do
39
+ content_type :json
40
+ offset = 0
41
+ worker = nil
42
+ offset = 50 * (params[:page].to_i - 1) if params[:page]
43
+ messages = BanterMessage.ordered.limit(50).offset(offset)
44
+ if params[:worker_id].present?
45
+ worker = BanterWorker.find(params[:worker_id])
46
+ messages = messages.where(worker_id: worker.id).ordered
47
+ end
48
+
49
+ if params[:subscriber].present?
50
+ messages = messages.where(subscriber_class: params[:subscriber])
51
+ end
52
+
53
+ hash = {
54
+ messages: messages.map { |m| serialize_message(m, true) }
55
+ }
56
+ hash[:worker] = serialize_worker(worker) if worker
57
+ hash.to_json
18
58
  end
19
59
 
20
- get '/messages/:id' do
21
- message = BanterMessage.find(params[:id])
22
- haml :message, locals: { message: message }
60
+ get '/subscribers.json' do
61
+ content_type :json
62
+ {
63
+ subscribers: BanterWorker.subscribers.map { |s| serialize_subscriber(s) }
64
+ }.to_json
23
65
  end
24
66
 
25
- get '/workers/:worker_id/messages' do
26
- messages = BanterMessage.where(worker_id: Moped::BSON::ObjectId.from_string(params[:worker_id])).ordered
27
- worker = BanterWorker.find(params[:worker_id])
28
- haml :messages, locals: { messages: messages, worker: worker }
67
+ private
68
+
69
+ def serialize_message(message, include_worker = false)
70
+ Banter::Web::Serializers::BanterMessageSerializer.new(message).to_hash.tap do |hash|
71
+ hash[:worker] = serialize_worker(message.worker) if include_worker
72
+ end
29
73
  end
30
74
 
31
- get '/subscribers' do
32
- haml :subscribers
75
+ def serialize_worker(worker)
76
+ Banter::Web::Serializers::BanterWorkerSerializer.new(worker).to_hash
33
77
  end
78
+
79
+ def serialize_subscriber(subscriber)
80
+ Banter::Web::Serializers::SubscriberSerializer.new(subscriber).to_hash
81
+ end
82
+
34
83
  end
35
84
  end
36
85
  end
@@ -1,8 +1,12 @@
1
1
  module Banter
2
2
  module Web
3
3
  module Helpers
4
- def root_path
5
- "#{env['SCRIPT_NAME']}/"
4
+ def root_path(path = nil)
5
+ "#{env['SCRIPT_NAME']}/#{path}"
6
+ end
7
+
8
+ def dashboard_path(path = nil)
9
+ root_path("dashboard/#{path}")
6
10
  end
7
11
  end
8
12
  end
@@ -14,17 +14,29 @@ class BanterMessage
14
14
  field :status, type: String
15
15
  field :error_message, type: String
16
16
  field :worker_id
17
- # embeds_one :worker, class_name: 'BanterWorker'
17
+ field :progress_at, type: Integer
18
+ field :progress_total, type: Integer
19
+ field :progress
18
20
 
19
- STATUS_STARTED = 'started'
20
- STATUS_SUCCESS = 'success'
21
- STATUS_ERROR = 'error'
22
- STATUS_VALIDATION_FAILED = 'validation_failed'
21
+ STATUS_STARTED = 'started'
22
+ STATUS_SUCCESS = 'success'
23
+ STATUS_ERROR = 'error'
24
+ STATUS_VALIDATION_FAILED = 'validation_failed'
23
25
 
24
26
  cattr_reader :current
25
27
 
26
28
  scope :ordered, ->() { order_by(:started_at.desc) }
29
+ scope :for_subscriber, ->(subscriber) { where(subscriber_class: subscriber) }
30
+ scope :successful, ->() { where(status: STATUS_SUCCESS) }
31
+ scope :errored, ->() { where(status: STATUS_ERROR) }
32
+ scope :validation_failed, ->() { where(status: STATUS_VALIDATION_FAILED) }
33
+ scope :started, ->() { where(status: STATUS_STARTED) }
27
34
 
35
+ # Creates a BanterMessage to record a message that is received by a subscriber.
36
+ # It records the created message as BanterMessage.current
37
+ # @param [Subscriber] subscriber_class
38
+ # @param [Banter::Server::ClientQueueListener] worker
39
+ # @param [Object] payload
28
40
  def self.message_received(subscriber_class, worker, payload)
29
41
  return unless BanterWorker.current
30
42
 
@@ -47,6 +59,7 @@ class BanterMessage
47
59
  BanterWorker.record_current!(obj)
48
60
  end
49
61
 
62
+ # Records the current message as successful. Clears the BanterMessage.current
50
63
  def self.message_successful
51
64
  current.status = STATUS_SUCCESS
52
65
  current.done_at = Time.now
@@ -55,6 +68,9 @@ class BanterMessage
55
68
  @@current = nil
56
69
  end
57
70
 
71
+ # Records the current message as errored or validation failed with error message. Clears the BanterMessage.current
72
+ # @param [String] error_message
73
+ # @param [String] status
58
74
  def self.message_failed(error_message, status)
59
75
  current.status = status
60
76
  current.done_at = Time.now
@@ -64,6 +80,29 @@ class BanterMessage
64
80
  @@current = nil
65
81
  end
66
82
 
83
+ # Records the current progress as a number for the current message.
84
+ # @param [Integer] num Current progress
85
+ def self.update_progress_at(num)
86
+ return unless current
87
+ current.progress_at = num
88
+ current.save!
89
+ end
90
+
91
+ # Records the number of objects expected to be processed for the current message.
92
+ # @param [Integer] num Total number of objects expected to be processed.
93
+ def self.update_progress_total(num)
94
+ return unless current
95
+ current.progress_total = num
96
+ current.save!
97
+ end
98
+
99
+ # @param [Object] object Any object that indicates the progress of the message
100
+ def self.update_progress(object)
101
+ return unless current
102
+ current.progress = object
103
+ current.save!
104
+ end
105
+
67
106
  def executing?
68
107
  self.status == STATUS_STARTED
69
108
  end
@@ -76,6 +115,7 @@ class BanterMessage
76
115
  self.status == STATUS_ERROR || self.status == STATUS_VALIDATION_FAILED
77
116
  end
78
117
 
118
+ # @return [BanterWorker] The worker that is responsible for executing the current message
79
119
  def worker
80
120
  @worker ||= BanterWorker.find(self['worker_id'].to_s)
81
121
  end
@@ -37,6 +37,11 @@ class BanterWorker
37
37
  @@current = nil
38
38
  end
39
39
 
40
+ # @return [Array] Unique subscriber classes
41
+ def self.subscribers
42
+ distinct("worker_classes")
43
+ end
44
+
40
45
  # @param [BanterMessage] banter_message Message that is currently being executed
41
46
  def self.record_current!(banter_message)
42
47
  current.current_message = banter_message.attributes
@@ -0,0 +1,34 @@
1
+ module Banter
2
+ module Web
3
+
4
+ module Serializers
5
+ class BanterMessageSerializer
6
+ attr_accessor :message
7
+
8
+ def initialize(message)
9
+ @message = message
10
+ end
11
+
12
+ def to_hash
13
+ {
14
+ id: message.id.to_s,
15
+ subscriber_class: message.subscriber_class,
16
+ queue_name: message.queue_name,
17
+ subscribed_key: message.subscribed_key,
18
+ payload_key: message.payload_key,
19
+ payload: message.payload,
20
+ context: message.context,
21
+ started_at: message.started_at,
22
+ done_at: message.done_at,
23
+ status: message.status,
24
+ error_message: message.error_message,
25
+ worker_id: message.worker_id,
26
+ progress_total: message.progress_total,
27
+ progress_at: message.progress_at,
28
+ progress: message.progress
29
+ }
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,30 @@
1
+ module Banter
2
+ module Web
3
+
4
+ module Serializers
5
+ class BanterWorkerSerializer
6
+ attr_accessor :worker
7
+
8
+ def initialize(worker)
9
+ @worker = worker
10
+ end
11
+
12
+ def to_hash
13
+ {
14
+ id: worker.id.to_s,
15
+ process_name: worker.process_name,
16
+ pid: worker.pid,
17
+ started_at: worker.started_at,
18
+ stopped_at: worker.stopped_at,
19
+ worker_classes: Array.wrap(worker.worker_classes),
20
+ hostname: worker.hostname,
21
+ current_message: worker.current_message,
22
+ job_count: worker.job_count,
23
+ success_count: worker.success_count,
24
+ failed_count: worker.failed_count
25
+ }
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,26 @@
1
+ module Banter
2
+ module Web
3
+
4
+ module Serializers
5
+ class SubscriberSerializer
6
+ attr_accessor :subscriber_class_name
7
+
8
+ def initialize(subscriber_class_name)
9
+ @subscriber_class_name = subscriber_class_name
10
+ end
11
+
12
+ def to_hash
13
+ messages = BanterMessage.for_subscriber(subscriber_class_name)
14
+ {
15
+ class_name: subscriber_class_name,
16
+ total: messages.count,
17
+ success_count: messages.successful.count,
18
+ started_count: messages.started.count,
19
+ errored_count: messages.errored.count,
20
+ validation_failed_count: messages.validation_failed.count
21
+ }
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -16,7 +16,88 @@ describe Banter::Subscriber do
16
16
  end
17
17
  end
18
18
 
19
- describe "self.validates_payload_with" do
19
+ describe '#at' do
20
+ let(:subscriber) { Banter::Subscriber.new({}, {}, {}) }
21
+ context 'web not enabled' do
22
+ before do
23
+ allow(Banter::Configuration).to receive(:web_enabled).and_return(false)
24
+ subscriber.send(:at, 123)
25
+ end
26
+
27
+ it 'doesnt do a thing' do
28
+ expect(BanterMessage.count).to eq(0)
29
+ end
30
+ end
31
+
32
+ context 'web enabled' do
33
+ let(:banter_message) { FactoryGirl.create(:banter_message)}
34
+ before do
35
+ allow(Banter::Configuration).to receive(:web_enabled).and_return(true)
36
+ allow(BanterMessage).to receive(:current).and_return(banter_message)
37
+ subscriber.send(:at, 123)
38
+ end
39
+
40
+ it 'updates progress_at on current message' do
41
+ expect(banter_message.progress_at).to eq(123)
42
+ end
43
+ end
44
+ end
45
+
46
+ describe '#total' do
47
+ let(:subscriber) { Banter::Subscriber.new({}, {}, {}) }
48
+ context 'web not enabled' do
49
+ before do
50
+ allow(Banter::Configuration).to receive(:web_enabled).and_return(false)
51
+ subscriber.send(:total, 1230)
52
+ end
53
+
54
+ it 'doesnt do a thing' do
55
+ expect(BanterMessage.count).to eq(0)
56
+ end
57
+ end
58
+
59
+ context 'web enabled' do
60
+ let(:banter_message) { FactoryGirl.create(:banter_message)}
61
+ before do
62
+ allow(Banter::Configuration).to receive(:web_enabled).and_return(true)
63
+ allow(BanterMessage).to receive(:current).and_return(banter_message)
64
+ subscriber.send(:total, 1230)
65
+ end
66
+
67
+ it 'updates progress_at on current message' do
68
+ expect(banter_message.progress_total).to eq(1230)
69
+ end
70
+ end
71
+ end
72
+
73
+ describe '#progress' do
74
+ let(:subscriber) { Banter::Subscriber.new({}, {}, {}) }
75
+ context 'web not enabled' do
76
+ before do
77
+ allow(Banter::Configuration).to receive(:web_enabled).and_return(false)
78
+ subscriber.send(:progress, foo: 'bar')
79
+ end
80
+
81
+ it 'doesnt do a thing' do
82
+ expect(BanterMessage.count).to eq(0)
83
+ end
84
+ end
85
+
86
+ context 'web enabled' do
87
+ let(:banter_message) { FactoryGirl.create(:banter_message)}
88
+ before do
89
+ allow(Banter::Configuration).to receive(:web_enabled).and_return(true)
90
+ allow(BanterMessage).to receive(:current).and_return(banter_message)
91
+ subscriber.send(:progress, foo: 'bar')
92
+ end
93
+
94
+ it 'updates progress_at on current message' do
95
+ expect(banter_message.progress).to eq(foo: 'bar')
96
+ end
97
+ end
98
+ end
99
+
100
+ describe ".validates_payload_with" do
20
101
  before do
21
102
  validators.each do |validator|
22
103
  Klass.validates_payload_with validator