magent 0.5.4 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -5,3 +5,6 @@ gem 'uuidtools'
5
5
 
6
6
  gem 'em-websocket'
7
7
 
8
+ gem 'sinatra', '~> 1.2.6'
9
+ gem 'haml', '~> 3.1.1'
10
+ gem 'launchy', '~> 0.4.0'
data/Gemfile.lock CHANGED
@@ -3,19 +3,32 @@ GEM
3
3
  specs:
4
4
  addressable (2.2.1)
5
5
  bson (1.1)
6
+ configuration (1.2.0)
6
7
  em-websocket (0.1.4)
7
8
  addressable (>= 2.1.1)
8
9
  eventmachine (>= 0.12.9)
9
10
  eventmachine (0.12.10)
11
+ haml (3.1.1)
12
+ launchy (0.4.0)
13
+ configuration (>= 0.0.5)
14
+ rake (>= 0.8.1)
10
15
  mongo (1.1)
11
16
  bson (>= 1.0.5)
17
+ rack (1.3.0)
18
+ rake (0.9.0)
19
+ sinatra (1.2.6)
20
+ rack (~> 1.1)
21
+ tilt (>= 1.2.2, < 2.0)
22
+ tilt (1.3.2)
12
23
  uuidtools (2.1.1)
13
24
 
14
25
  PLATFORMS
15
26
  ruby
16
27
 
17
28
  DEPENDENCIES
18
- bson
19
29
  em-websocket
20
- mongo
30
+ haml (~> 3.1.1)
31
+ launchy (~> 0.4.0)
32
+ mongo (~> 1.0)
33
+ sinatra (~> 1.2.6)
21
34
  uuidtools
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.4
1
+ 0.6.0
data/bin/magent-web ADDED
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift << File.expand_path("../../lib", __FILE__)
4
+ require 'magent'
5
+ require 'magent_web'
6
+ require 'launchy'
7
+
8
+ Thread.start do
9
+ sleep 2.5
10
+ Launchy.open("http://localhost:9876")
11
+ end
12
+
13
+ Rack::Server.new(:Port => "9876",
14
+ :config => File.dirname(__FILE__)+"/../config.ru",
15
+ :AccessLog => [],
16
+ :pid => nil,
17
+ :Host => "0.0.0.0").start
18
+
data/config.ru ADDED
@@ -0,0 +1,5 @@
1
+ $:.unshift File.expand_path("../lib", __FILE__)
2
+ require 'magent_web'
3
+
4
+ run MagentWeb.app
5
+
data/lib/magent.rb CHANGED
@@ -62,7 +62,7 @@ module Magent
62
62
  end
63
63
 
64
64
  def self.connect(environment, options={})
65
- raise 'Set config before connecting. Magent.config = {...}' if config.blank?
65
+ raise 'Set config before connecting. Magent.config = {...}' if config.nil? || config.empty?
66
66
 
67
67
  env = config_for_environment(environment)
68
68
  Magent.connection = Mongo::Connection.new(env['host'], env['port'], options)
@@ -97,7 +97,7 @@ module Magent
97
97
  def self.config_for_environment(environment)
98
98
  env = environment ? config[environment] : config
99
99
 
100
- return env if env['uri'].blank?
100
+ return env if env['uri'].nil? || env['uri'].blank?
101
101
 
102
102
  uri = URI.parse(env['uri'])
103
103
  raise InvalidScheme.new('must be mongodb') unless uri.scheme == 'mongodb'
@@ -26,6 +26,11 @@ module Magent
26
26
  remove_error(error["_id"])
27
27
  end
28
28
 
29
+ def enqueue_error(error)
30
+ enqueue(error["message"], 1)
31
+ remove_error(error["_id"])
32
+ end
33
+
29
34
  def error_collection
30
35
  @error_collection ||= Magent.database.collection("#{@name}.errors")
31
36
  end
data/lib/magent_web.rb ADDED
@@ -0,0 +1,42 @@
1
+ require 'rubygems'
2
+
3
+ require 'json'
4
+ require 'magent'
5
+
6
+ require 'benchmark'
7
+ require 'sinatra'
8
+ require 'haml'
9
+ require 'yaml'
10
+
11
+ require 'net/http'
12
+ require 'uri'
13
+ require 'cgi'
14
+
15
+ require 'magent_web/mongo_helper'
16
+ require 'magent_web/app'
17
+
18
+ module MagentWeb
19
+ def self.app
20
+ MagentWeb.connect
21
+
22
+ MagentWeb::App
23
+ end
24
+
25
+ def self.connect
26
+ ENV["MAGENT_ENV"] ||= ENV["RACK_ENV"] || ENV["RAILS_ENV"]
27
+ if !ENV["MAGENT_ENV"]
28
+ raise ArgumentError, "please define the env var MAGENT_ENV"
29
+ end
30
+
31
+ if File.exist?("/etc/magent.yml")
32
+ Magent.setup(YAML.load_file("/etc/magent.yml"), ENV["MAGENT_ENV"], {})
33
+ elsif File.exist?("config/magent.yml")
34
+ Magent.setup(YAML.load_file("config/magent.yml"), ENV["MAGENT_ENV"], {})
35
+ elsif File.exist?("magent.yml")
36
+ Magent.setup(YAML.load_file("magent.yml"), ENV["MAGENT_ENV"], {})
37
+ else
38
+ raise ArgumentError, "/etc/magent.yml, ./config/magent.yml or ./magent.yml were not found"
39
+ end
40
+ end
41
+ end
42
+
@@ -0,0 +1,71 @@
1
+ module MagentWeb
2
+ class App < Sinatra::Base
3
+ include MagentWeb::MongoHelper
4
+
5
+ helpers do
6
+ include Rack::Utils
7
+ alias_method :h, :escape_html
8
+ end
9
+
10
+ set :public, File.expand_path("../../../public", __FILE__)
11
+ set :views, File.expand_path("../../../lib/magent_web/views", __FILE__)
12
+
13
+ before do
14
+ @database = Magent.database
15
+ end
16
+
17
+ get "/" do
18
+ @queues = self.queues
19
+
20
+ haml :index
21
+ end
22
+
23
+ get "/status" do
24
+ haml :status
25
+ end
26
+
27
+ get "/queues/:id" do
28
+ @queue = @database.collection(params[:id])
29
+ @messages = document_list(@queue)
30
+
31
+ haml :"queues/show"
32
+ end
33
+
34
+ get "/queues/:id/failed" do
35
+ @queue = @database.collection(params[:id])
36
+ @errors_queue = @database.collection(params[:id]+".errors")
37
+ @errors = document_list(@errors_queue)
38
+
39
+ haml :"queues/failed"
40
+ end
41
+
42
+ get "/queues/:id/stats" do
43
+ @queue = @database.collection(params[:id])
44
+
45
+ haml :"queues/stats"
46
+ end
47
+
48
+ get "/queues/:queue_id/retry/:id" do
49
+ @errors_queue = @database.collection(params[:queue_id]+".errors")
50
+ @channel_name = channel_name_for(params[:queue_id])
51
+
52
+ channel = Magent::AsyncChannel.new(@channel_name)
53
+
54
+ doc = @errors_queue.find({:_id => params[:id]}).next_document
55
+ channel.enqueue_error(doc)
56
+
57
+ redirect "/queues/#{params[:queue_id]}/failed"
58
+ end
59
+
60
+ get "/queues/:queue_id/delete/:id" do
61
+ @errors_queue = @database.collection(params[:queue_id]+".errors")
62
+ @errors_queue.remove(:_id => params[:id])
63
+ redirect "/queues/#{params[:queue_id]}/failed"
64
+ end
65
+
66
+ private
67
+ def error_not_found
68
+ status 404
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,84 @@
1
+ module MagentWeb
2
+ module MongoHelper
3
+ def queues
4
+ q = []
5
+ Magent.database.collections.each do |collection|
6
+ if collection.name =~ /^magent\./ && collection.name !~ /errors$/
7
+ q << collection
8
+ end
9
+ end
10
+
11
+ q
12
+ end
13
+
14
+ def query_options
15
+ options = {}
16
+ options[:limit] = params[:limit].nil? ? 25 : params[:limit].to_i
17
+ options[:sort] = [["_id", (params[:descending] == "true" ? 'descending' : 'ascending')]]
18
+ options[:skip] = params[:skip].nil? ? 0 : params[:skip].to_i
19
+
20
+ options
21
+ end
22
+
23
+ def document_list(collection, query = {})
24
+ collection.find(query, query_options)
25
+ end
26
+
27
+ def file_size(database)
28
+ "%0.2f MB" % (database.stats["fileSize"] / 1024**2)
29
+ end
30
+
31
+ def normalize_stats(stats)
32
+ r={}
33
+ stats.each do |k,v|
34
+ if k =~ /size/i
35
+ if v.kind_of?(Hash)
36
+ v.each do |ki,vi|
37
+ v[ki]="%0.2f MB" % (vi.to_f/1024**2)
38
+ end
39
+ r[k] = v
40
+ else
41
+ r[k]="%0.2f MB" % (v.to_f/1024**2)
42
+ end
43
+ else
44
+ r[k]=v
45
+ end
46
+ end
47
+ r
48
+ end
49
+
50
+ def server_status
51
+ Magent.database.command(:serverStatus => 1)
52
+ end
53
+
54
+ def humanize(v, quote = true)
55
+ if v.nil? && quote
56
+ "null"
57
+ elsif v.kind_of?(Hash)
58
+ JSON.pretty_generate(v)
59
+ elsif v.kind_of?(Array)
60
+ JSON.pretty_generate(v)
61
+ elsif v.kind_of?(Time)
62
+ v.strftime("%d %B %Y %H:%M:%S").inspect
63
+ elsif quote
64
+ v.inspect
65
+ else
66
+ v
67
+ end
68
+ end
69
+
70
+ def humanize_messages(messages)
71
+ return "" if !messages.kind_of?(Array)
72
+
73
+ messages.map do |e|
74
+ name = e.first
75
+ args = e.last.join(", ")
76
+ "#{name}(#{args})"
77
+ end.join(" -> ")
78
+ end
79
+
80
+ def channel_name_for(queue_id)
81
+ queue_id.to_s.match("magent\.([^\.]+)")[1]
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,14 @@
1
+ %div(data-role="page" data-add-back-btn="true")
2
+ %div(data-role="header")
3
+ %h1
4
+ = @title = "All Magent Queues"
5
+ %div(data-role="content")
6
+ %ul(data-role="listview" data-filter="true")
7
+ -@queues.each do |queue|
8
+ %li
9
+ %a(href="/queues/#{queue.name}")
10
+ &=queue.name
11
+ %span{:class=>"ui-li-count"}
12
+ =0
13
+
14
+ %div(data-role="footer")
@@ -0,0 +1,15 @@
1
+ !!! 5
2
+ %html
3
+ %head
4
+ %title
5
+ =@title||"Magent Web"
6
+ %link(rel="stylesheet" href="/stylesheets/jquery.mobile-1.0b1pre.min.css")
7
+ %link(rel="stylesheet" href="/stylesheets/highlight.css")
8
+
9
+ %script(src="http://code.jquery.com/jquery-1.5.2.min.js")
10
+ %script(src="/javascripts/jquery.mobile-1.0b1pre.min.js")
11
+
12
+ %script(src="/javascripts/application.js")
13
+ %body
14
+ = yield
15
+
@@ -0,0 +1,36 @@
1
+ -count=0
2
+ -skip = params[:skip].to_i
3
+ %div(data-role="page" data-add-back-btn="true")
4
+ %div(data-role="header")
5
+ %h1
6
+ Failed Jobs for
7
+ &= channel_name_for(@queue.name)
8
+ %div(data-role="content")
9
+ %h1
10
+ Failed Jobs
11
+ ==(#{@errors.count})
12
+ -@errors.each do |document|
13
+ -message = document["message"]
14
+ -count+=1
15
+ %div(data-role="collapsible" data-collapsed=true)
16
+ %h1
17
+ &= "#{humanize_messages(document["message"].last)}: #{document["error"]}"
18
+ =haml :"shared/values", :layout => false, :locals => {:hash => document}
19
+ %div(data-role="navbar" data-iconpos="bottom")
20
+ %ul
21
+ %li
22
+ %a(href="/queues/#{@queue.name}/retry/#{document["_id"]}" data-icon="refresh")
23
+ Retry
24
+ %li
25
+ %a(href="/queues/#{@queue.name}/delete/#{document["_id"]}" data-icon="delete" data-theme="e")
26
+ Delete
27
+
28
+
29
+ %div(data-role="footer")
30
+ -if skip > 0
31
+ %a(href="/queues/#{@queue.name}/failed?skip=#{params[:skip].to_i-25}")
32
+ Previous
33
+ -if count >= 25
34
+ %a(href="/queues/#{@queue.name}/failed?skip=#{params[:skip].to_i+25}")
35
+ Next
36
+
@@ -0,0 +1,44 @@
1
+ -count=0
2
+ -skip = params[:skip].to_i
3
+ %div(data-role="page" data-add-back-btn="true")
4
+ %div(data-role="header")
5
+ %h1
6
+ Queue:
7
+ &=@title=@database.name+" / #{channel_name_for(@queue.name)}"
8
+ %div{:"data-role" => "navbar"}
9
+ %ul
10
+ %li
11
+ %a(href="/queues/#{@queue.name}/failed" data-icon="alert")
12
+ Failed
13
+ %li
14
+ %a(href="/queues/#{@queue.name}/stats" data-icon="grid")
15
+ Stats
16
+
17
+
18
+ %div(data-role="content")
19
+ -if skip == 0
20
+ %h1
21
+ Information
22
+ -normalize_stats(@queue.stats).each do |k,v|
23
+ %b
24
+ &="#{k}="
25
+ &=humanize v
26
+ %br
27
+ %h1
28
+ Pending Jobs
29
+ ==(#{@messages.count})
30
+ -@messages.each do |document|
31
+ -message = document["message"]
32
+ -count+=1
33
+ %div(data-role="collapsible" data-collapsed=true)
34
+ %h1
35
+ &= "[#{document["priority"]}] #{message.first}: #{humanize_messages(message.last)}"
36
+ =haml :"shared/values", :layout => false, :locals => {:hash => document}
37
+
38
+ %div(data-role="footer")
39
+ -if skip > 0
40
+ %a(href="/queues/#{@queue.name}?skip=#{params[:skip].to_i-25}")
41
+ Previous
42
+ -if count >= 25
43
+ %a(href="/queues/#{@queue.name}?skip=#{params[:skip].to_i+25}")
44
+ Next
@@ -0,0 +1,6 @@
1
+ %div(data-role="page" data-add-back-btn="true")
2
+ %div(data-role="header")
3
+ %h1
4
+ =@title ="Stats"
5
+ %div(data-role="content")
6
+ %div(data-role="footer")
@@ -0,0 +1,74 @@
1
+ -hash.each do |key,value|
2
+ -k = humanize(key)
3
+ -v = humanize(value)
4
+
5
+ -if value.kind_of?(Hash) && value.size > 5
6
+ %div(data-role="collapsible" data-collapsed=true data-theme="b")
7
+ %h2
8
+ &=k
9
+ = haml :"shared/values", :layout => false, :locals => {:hash => value}
10
+ -elsif value.kind_of?(Array) && value.size > 5
11
+ %div(data-role="collapsible" data-collapsed=true data-theme="b")
12
+ %h2
13
+ &=k
14
+ -tmp = []
15
+ -value.each_with_index do |element, index|
16
+ -if element.kind_of?(Hash)
17
+ %div(data-role="collapsible" data-collapsed=true data-theme="e")
18
+ %h2
19
+ &=index
20
+ = haml :"shared/values", :layout => false, :locals => {:hash => element}
21
+ -else
22
+ -tmp << element
23
+ - if !tmp.empty?
24
+ =humanize(tmp)
25
+ %br
26
+
27
+ -else
28
+ %b
29
+ -if key == "_type"
30
+ %span.type_key
31
+ &=k
32
+ -elsif key == "_id"
33
+ %span.pk_key
34
+ &=k
35
+ -elsif key =~ /_id$/
36
+ %span.fk_key
37
+ &=k
38
+ -elsif key =~ /_at$/
39
+ %span.date_key
40
+ &=k
41
+ -elsif ["name", "title", "error", "message"].include?(key)
42
+ %span.name_key
43
+ &=k
44
+ -elsif key =~ /^_/
45
+ %span.private_key
46
+ &=k
47
+ -else
48
+ %span.normal_key
49
+ &=k
50
+ \=
51
+
52
+ -if key == "_type"
53
+ %span.type_value
54
+ &=v
55
+ -elsif key == "_id"
56
+ %span.pk_value
57
+ &=v
58
+ -elsif key =~ /_id$/
59
+ %span.fk_value
60
+ &=v
61
+ -elsif key =~ /_at$/
62
+ %span.date_value
63
+ &=v
64
+ -elsif ["name", "title", "error", "message"].include?(key)
65
+ %span.name_value
66
+ &=v
67
+ -elsif key =~ /^_/
68
+ %span.private_value
69
+ &=v
70
+ -else
71
+ %span.normal_value
72
+ &=v
73
+ %br<
74
+