magent 0.5.4 → 0.6.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.
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
+