magent 0.6.2 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +5 -4
- data/Gemfile.lock +21 -21
- data/Rakefile +4 -3
- data/VERSION +1 -1
- data/bin/magent +1 -3
- data/examples/findable.rb +36 -0
- data/examples/retries.rb +25 -0
- data/examples/test_mode.rb +20 -0
- data/lib/magent.rb +13 -7
- data/lib/magent/async.rb +16 -19
- data/lib/magent/async_channel.rb +10 -18
- data/lib/magent/failure.rb +12 -3
- data/lib/magent/generic_channel.rb +46 -1
- data/lib/magent/processor.rb +10 -10
- data/lib/magent/railtie.rb +1 -1
- data/lib/{tasks/magent.rake → magent/tasks.rb} +13 -10
- data/magent.gemspec +24 -33
- metadata +96 -147
- data/examples/comm/run.rb +0 -22
- data/examples/comm/worker.rb +0 -30
- data/examples/error/error.rb +0 -33
- data/examples/mongomapper/async.rb +0 -41
- data/examples/simple/bot.rb +0 -38
- data/examples/stats/stats.rb +0 -27
- data/lib/magent/actor.rb +0 -82
- data/lib/magent/actor_channel.rb +0 -34
- data/lib/magent/push.rb +0 -13
- data/lib/magent/web_socket_channel.rb +0 -16
- data/lib/magent/web_socket_server.rb +0 -107
data/examples/comm/run.rb
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
$:.unshift File.dirname(__FILE__)+"/../../lib/"
|
4
|
-
require 'magent'
|
5
|
-
|
6
|
-
id = "#{rand(16)}#{rand(16)}#{rand(16)}#{rand(16)}"
|
7
|
-
|
8
|
-
values = (1..5).to_a.map { rand(10) }
|
9
|
-
puts values.join(" + ")
|
10
|
-
Magent.push("workers", :sum, id, *values)
|
11
|
-
|
12
|
-
channel = Magent::GenericChannel.new("+#{id}")
|
13
|
-
|
14
|
-
loop do
|
15
|
-
v = channel.dequeue;
|
16
|
-
if v
|
17
|
-
$stdout.puts v.inspect
|
18
|
-
break
|
19
|
-
end
|
20
|
-
sleep 0.1
|
21
|
-
end
|
22
|
-
|
data/examples/comm/worker.rb
DELETED
@@ -1,30 +0,0 @@
|
|
1
|
-
$:.unshift File.dirname(__FILE__)+"/../../lib/"
|
2
|
-
require 'magent'
|
3
|
-
|
4
|
-
# Use: magent /path/to/this/file
|
5
|
-
|
6
|
-
class Worker
|
7
|
-
include Magent::Actor
|
8
|
-
channel_name "workers"
|
9
|
-
expose :sum
|
10
|
-
|
11
|
-
def sum(payload)
|
12
|
-
id, *args = payload
|
13
|
-
|
14
|
-
s = args.inject(0) { |v, a| a += v }
|
15
|
-
send_to_client(id, {:method => :sum, :result => s})
|
16
|
-
end
|
17
|
-
|
18
|
-
private
|
19
|
-
def send_to_client(id, message)
|
20
|
-
c = Magent::GenericChannel.new("+#{id}")
|
21
|
-
c.enqueue(message)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
Magent.register(Worker.new)
|
26
|
-
|
27
|
-
if $0 == __FILE__
|
28
|
-
Magent::Processor.new(Worker.channel).run!
|
29
|
-
end
|
30
|
-
|
data/examples/error/error.rb
DELETED
@@ -1,33 +0,0 @@
|
|
1
|
-
$:.unshift File.dirname(__FILE__)+"/../../lib/"
|
2
|
-
require 'magent'
|
3
|
-
|
4
|
-
Magent.push("errors", :fail, "this is a fail")
|
5
|
-
|
6
|
-
class Error
|
7
|
-
include Magent::Actor
|
8
|
-
|
9
|
-
channel_name "errors"
|
10
|
-
expose :fail
|
11
|
-
|
12
|
-
def fail(payload)
|
13
|
-
@count ||= 0
|
14
|
-
errors = self.class.channel.errors
|
15
|
-
|
16
|
-
errors.each do |error|
|
17
|
-
@count += 1
|
18
|
-
$stderr.puts "Retrying: #{error["method"]}(#{error["payload"].inspect})"
|
19
|
-
self.class.channel.retry_error(error)
|
20
|
-
end
|
21
|
-
|
22
|
-
if @count == 0
|
23
|
-
raise payload.inspect
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
Magent.register(Error.new)
|
29
|
-
|
30
|
-
if $0 == __FILE__
|
31
|
-
Magent::Processor.new(Error.channel).run!
|
32
|
-
end
|
33
|
-
|
@@ -1,41 +0,0 @@
|
|
1
|
-
$:.unshift File.dirname(__FILE__)+"/../../lib/"
|
2
|
-
require 'rubygems'
|
3
|
-
require 'magent'
|
4
|
-
require 'mongo_mapper'
|
5
|
-
|
6
|
-
|
7
|
-
MongoMapper.database = "test"
|
8
|
-
Magent.database = "test"
|
9
|
-
|
10
|
-
class Thing
|
11
|
-
include MongoMapper::Document
|
12
|
-
include Magent::Async
|
13
|
-
|
14
|
-
key :_id, String
|
15
|
-
key :name
|
16
|
-
|
17
|
-
def process_something(arg)
|
18
|
-
puts "Processing: #{arg}"
|
19
|
-
end
|
20
|
-
|
21
|
-
def process_nothing
|
22
|
-
puts "Processing..."
|
23
|
-
end
|
24
|
-
|
25
|
-
def self.foo
|
26
|
-
puts "MAX PRIORITY"
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
|
31
|
-
thing = Thing.create(:_id => "foo")
|
32
|
-
|
33
|
-
# 3 messages
|
34
|
-
thing.async.process_something("testing").commit!
|
35
|
-
thing.async.process_nothing.commit!
|
36
|
-
Thing.async.find(thing.id).process_something("testing2").commit!
|
37
|
-
Thing.async.foo.commit!(1)
|
38
|
-
|
39
|
-
Magent::Processor.new(Magent::AsyncChannel.new(:default)).run!
|
40
|
-
|
41
|
-
|
data/examples/simple/bot.rb
DELETED
@@ -1,38 +0,0 @@
|
|
1
|
-
$:.unshift File.dirname(__FILE__)+"/../../lib/"
|
2
|
-
require 'rubygems'
|
3
|
-
require 'magent'
|
4
|
-
|
5
|
-
# Use: magent /path/to/this/file
|
6
|
-
|
7
|
-
Magent.push("bots", :echo, "hello, world")
|
8
|
-
Magent.push("bots", :do_task, "File", :exist?, "/etc/passwd")
|
9
|
-
Magent.push("bots", :echo, "Press ctrl+c to close")
|
10
|
-
Magent.push("bots", :do_not_exist, "you should not see this message")
|
11
|
-
|
12
|
-
class Bot
|
13
|
-
include Magent::Actor
|
14
|
-
channel_name "bots"
|
15
|
-
expose :echo, :do_task
|
16
|
-
|
17
|
-
def echo(payload)
|
18
|
-
$stderr.puts payload.inspect
|
19
|
-
end
|
20
|
-
|
21
|
-
def do_task(payload)
|
22
|
-
klass, *args = payload
|
23
|
-
|
24
|
-
result = Object.module_eval(klass).send(*args)
|
25
|
-
$stderr.puts "RESULT: #{result}"
|
26
|
-
end
|
27
|
-
|
28
|
-
at_least_every 15 do
|
29
|
-
puts "Hi there!, you'll see this message again in ~15 seconds"
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
Magent.register(Bot.new)
|
34
|
-
|
35
|
-
if $0 == __FILE__
|
36
|
-
Magent::Processor.new(Bot.channel).run!
|
37
|
-
end
|
38
|
-
|
data/examples/stats/stats.rb
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
$:.unshift File.dirname(__FILE__)+"/../../lib/"
|
2
|
-
require 'magent'
|
3
|
-
|
4
|
-
Magent.push("stats", :calc)
|
5
|
-
Magent.push("stats", :calc)
|
6
|
-
Magent.push("stats", :calc)
|
7
|
-
Magent.push("stats", :calc)
|
8
|
-
|
9
|
-
class Stats
|
10
|
-
include Magent::Actor
|
11
|
-
|
12
|
-
channel_name "stats"
|
13
|
-
expose :calc
|
14
|
-
|
15
|
-
def calc(payload)
|
16
|
-
$stderr.puts "messages in queue: #{self.class.channel.queue_count}"
|
17
|
-
$stderr.puts "total messages count: #{self.class.channel.message_count}"
|
18
|
-
$stderr.puts "total errors: #{self.class.channel.error_count}"
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
Magent.register(Stats.new)
|
23
|
-
|
24
|
-
if $0 == __FILE__
|
25
|
-
Magent::Processor.new(Stats.channel).run!
|
26
|
-
end
|
27
|
-
|
data/lib/magent/actor.rb
DELETED
@@ -1,82 +0,0 @@
|
|
1
|
-
module Magent
|
2
|
-
module Actor
|
3
|
-
def self.included(klass)
|
4
|
-
klass.class_eval do
|
5
|
-
extend Actor::ClassMethods
|
6
|
-
include Actor::InstanceMethods
|
7
|
-
end
|
8
|
-
end
|
9
|
-
|
10
|
-
module ClassMethods
|
11
|
-
def expose(*methods)
|
12
|
-
methods.each do |m|
|
13
|
-
actions << m.to_s
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
def channel_name(name = nil)
|
18
|
-
@channel_name ||= (name || Magent::Utils.underscore(self.name)).to_s
|
19
|
-
end
|
20
|
-
|
21
|
-
def actions
|
22
|
-
@actions ||= Set.new
|
23
|
-
end
|
24
|
-
|
25
|
-
def can_handle?(action)
|
26
|
-
actions.include?(action.to_s)
|
27
|
-
end
|
28
|
-
|
29
|
-
def channel
|
30
|
-
@channel ||= begin
|
31
|
-
ActorChannel.new(self.channel_name)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def tasks
|
36
|
-
@tasks ||= []
|
37
|
-
end
|
38
|
-
|
39
|
-
def at_least_every(seconds, &block)
|
40
|
-
tasks << {:every => seconds, :last_time => Time.now, :block => block}
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
module InstanceMethods
|
45
|
-
def _run_tasks
|
46
|
-
tasks = self.class.tasks
|
47
|
-
|
48
|
-
return false if tasks.empty?
|
49
|
-
performed = false
|
50
|
-
|
51
|
-
tasks.each do |task|
|
52
|
-
delta = Time.now - task[:last_time]
|
53
|
-
|
54
|
-
if delta >= task[:every]
|
55
|
-
task[:last_time] = Time.now
|
56
|
-
begin
|
57
|
-
instance_eval(&task[:block])
|
58
|
-
rescue Exception => e
|
59
|
-
$stderr.puts "Failed periodical task: #{e.message}"
|
60
|
-
$stderr.puts e.backtrace.join("\n\t")
|
61
|
-
end
|
62
|
-
performed = true
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
performed
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end # Actor
|
70
|
-
|
71
|
-
def self.register(actor)
|
72
|
-
@current_actor = actor
|
73
|
-
end
|
74
|
-
|
75
|
-
def self.current_actor
|
76
|
-
@current_actor
|
77
|
-
end
|
78
|
-
|
79
|
-
def self.current_channel
|
80
|
-
self.current_actor.channel
|
81
|
-
end
|
82
|
-
end
|
data/lib/magent/actor_channel.rb
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
module Magent
|
2
|
-
class ActorChannel < GenericChannel
|
3
|
-
def push(message, args)
|
4
|
-
enqueue([message, args])
|
5
|
-
end
|
6
|
-
|
7
|
-
def process!(message)
|
8
|
-
@actor = Magent.current_actor
|
9
|
-
|
10
|
-
@method, @payload = message
|
11
|
-
return false if @method.nil?
|
12
|
-
|
13
|
-
sucess = @actor._run_tasks
|
14
|
-
|
15
|
-
$stderr.puts "#{@actor.class}##{@method}(#{@payload.inspect})"
|
16
|
-
if @actor.class.can_handle?(@method)
|
17
|
-
@actor.send(@method, @payload)
|
18
|
-
return true
|
19
|
-
else
|
20
|
-
$stderr.puts "Unknown action: #{@method} (payload=#{@payload.inspect})"
|
21
|
-
end
|
22
|
-
|
23
|
-
@method, @payload = nil
|
24
|
-
|
25
|
-
sucess
|
26
|
-
end
|
27
|
-
|
28
|
-
def on_shutdown
|
29
|
-
if @method
|
30
|
-
self.enqueue(@method, @payload)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end # Channel
|
34
|
-
end
|
data/lib/magent/push.rb
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
module Magent
|
2
|
-
def self.push(channel_name, method, *args)
|
3
|
-
self.channel(channel_name.to_s).push(method, args)
|
4
|
-
end
|
5
|
-
|
6
|
-
def self.channel(name)
|
7
|
-
self.channels[name] ||= ActorChannel.new(name)
|
8
|
-
end
|
9
|
-
|
10
|
-
def self.channels
|
11
|
-
@channels ||= {}
|
12
|
-
end
|
13
|
-
end
|
@@ -1,16 +0,0 @@
|
|
1
|
-
module Magent
|
2
|
-
class WebSocketChannel < Magent::GenericChannel
|
3
|
-
def self.push(message)
|
4
|
-
self.instance.enqueue(message)
|
5
|
-
self.instance
|
6
|
-
end
|
7
|
-
|
8
|
-
def self.dequeue
|
9
|
-
self.instance.dequeue
|
10
|
-
end
|
11
|
-
|
12
|
-
def self.instance
|
13
|
-
@channel ||= self.new(Magent.config["websocket_channel"]||"magent.websocket")
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
@@ -1,107 +0,0 @@
|
|
1
|
-
require 'magent/web_socket_channel'
|
2
|
-
module Magent
|
3
|
-
class WebSocketServer
|
4
|
-
def initialize(options = {})
|
5
|
-
options = {:host => "0.0.0.0", :port => 34567}.merge(options)
|
6
|
-
|
7
|
-
$stdout.puts ">> Server running and up! #{options}}" if options[:debug]
|
8
|
-
|
9
|
-
EventMachine.run do
|
10
|
-
setup
|
11
|
-
|
12
|
-
EM.run do
|
13
|
-
EventMachine.add_periodic_timer(options.delete(:interval)||10) do
|
14
|
-
while message = Magent::WebSocketChannel.dequeue
|
15
|
-
if (channel = @channels[message["channel_id"]])
|
16
|
-
channel.push(message.to_json)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
EventMachine::WebSocket.start(options) do |ws|
|
23
|
-
ws.onopen do
|
24
|
-
ws.onmessage do |msg|
|
25
|
-
data = JSON.parse(msg) rescue {}
|
26
|
-
|
27
|
-
if !handle_message(ws, data)
|
28
|
-
case data["id"]
|
29
|
-
when 'start'
|
30
|
-
if data["channel_id"].present? && (channel_id = validate_channel_id(data["channel_id"]))
|
31
|
-
key = generate_unique_key(data["key"])
|
32
|
-
@channels[channel_id] ||= EM::Channel.new
|
33
|
-
@channel_ids[key] = channel_id
|
34
|
-
|
35
|
-
sid = @channels[channel_id].subscribe { |msg| ws.send(msg) }
|
36
|
-
|
37
|
-
@sids[key] = sid
|
38
|
-
ws.onclose do
|
39
|
-
@channel_ids.delete(key)
|
40
|
-
@channels[channel_id].unsubscribe(sid)
|
41
|
-
@sids.delete(key)
|
42
|
-
end
|
43
|
-
|
44
|
-
ws.send({:id => "ack", :key => key}.to_json)
|
45
|
-
send(:on_ack, ws, channel_id) if respond_to?(:on_ack)
|
46
|
-
else
|
47
|
-
ws.close_connection
|
48
|
-
end
|
49
|
-
when 'chatmessage'
|
50
|
-
key = data["key"]
|
51
|
-
return invalid_key(ws) if key.blank? || @sids[key].blank?
|
52
|
-
|
53
|
-
channel_id = @channel_ids[key]
|
54
|
-
|
55
|
-
if channel_id
|
56
|
-
chat_message = {:id => 'chatmessage', :from => user_name(key, @sids[key]), :message => data["message"]}
|
57
|
-
|
58
|
-
@channels[channel_id].push(validate_chat_message(channel_id, chat_message).to_json)
|
59
|
-
else
|
60
|
-
ws.send({:id => 'announcement', :type => "error", :message => "cannot find the channel"}.to_json)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end # if
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end # EM::WebSocket
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
def self.start(options = {})
|
71
|
-
self.new(options)
|
72
|
-
end
|
73
|
-
|
74
|
-
def setup
|
75
|
-
@channel = EM::Channel.new
|
76
|
-
@channels = {}
|
77
|
-
@channel_ids = {}
|
78
|
-
@sids = {}
|
79
|
-
end
|
80
|
-
|
81
|
-
protected
|
82
|
-
def invalid_key(ws)
|
83
|
-
ws.send({:id => 'announcement', :type => "error", :message => "you must provide your unique key"}.to_json)
|
84
|
-
end
|
85
|
-
|
86
|
-
def handle_message(ws, data)
|
87
|
-
false
|
88
|
-
end
|
89
|
-
|
90
|
-
def validate_chat_message(channel_id, chat_message)
|
91
|
-
chat_message
|
92
|
-
end
|
93
|
-
|
94
|
-
protected
|
95
|
-
def generate_unique_key(default = nil)
|
96
|
-
default || UUIDTools::UUID.random_create.hexdigest
|
97
|
-
end
|
98
|
-
|
99
|
-
def validate_channel_id(id)
|
100
|
-
return id if !id.blank?
|
101
|
-
end
|
102
|
-
|
103
|
-
def user_name(socket_key, sid)
|
104
|
-
"Guest #{sid}"
|
105
|
-
end
|
106
|
-
end # Class
|
107
|
-
end
|