magent 0.6.2 → 0.7.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 +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
|