msngr 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 80f389f5be19c06e918bbabc6758063ebc871298
4
+ data.tar.gz: d152dc1d6e25696fa09fc917248dfbb599483c1e
5
+ SHA512:
6
+ metadata.gz: 249d9724441d3a927679ed6e8c88a7e3944c7812efe0a787f656bab7878796452d6d6c46d96a69f1f793680521b2989ab972c30251eff5e38bbf54d0aedde2a9
7
+ data.tar.gz: 53dc43080979cd573aae3ca906fe15abc4f61a07bcef3cdf15f6bde205766eabdeac76cb5b64aaa188af654012499161c40932708e788818a9050fd0e681e961
@@ -0,0 +1,18 @@
1
+ require "msngr/version"
2
+ require "msngr/receiver"
3
+ require "msngr/messenger"
4
+
5
+ module Msngr
6
+ module Clients; end
7
+ extend self
8
+
9
+ # Shorthand for writing Msngr::Messenger.new(*args).
10
+ #
11
+ # @param [Array] *args
12
+ # @return [Msngr::Messenger]
13
+ #
14
+ def new(*args)
15
+ Messenger.new(*args)
16
+ end
17
+ end
18
+
@@ -0,0 +1,47 @@
1
+ require "redis"
2
+
3
+ class Msngr::Clients::Redis
4
+
5
+ # (Connectivity) Arguments to initialize the Redis instance with.
6
+ #
7
+ # @return [Array]
8
+ #
9
+ attr_reader :args
10
+
11
+ # Instantiances an instance of Msngr::Clients::Redis.
12
+ #
13
+ # @param [Array] *args the arguments to pass in to the Redis client.
14
+ #
15
+ def initialize(*args)
16
+ @args = args
17
+ end
18
+
19
+ # Yields all events/messages from the Redis server.
20
+ #
21
+ # @yield [event, message]
22
+ # @yieldparam [String] event the name of the received event.
23
+ # @yieldparam [String] message the message of the received event.
24
+ #
25
+ # @note This is an interface for Msngr::Messenger.
26
+ #
27
+ def on_message
28
+ connection.psubscribe("*") do |on|
29
+ on.pmessage { |_, event, message| yield event, message }
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ # Creates and returns a new instance of Redis using @args if present.
36
+ #
37
+ # @return [Redis]
38
+ #
39
+ def connection
40
+ if args.any?
41
+ Redis.new(*args)
42
+ else
43
+ Redis.new
44
+ end
45
+ end
46
+ end
47
+
@@ -0,0 +1,103 @@
1
+ require "thread"
2
+
3
+ class Msngr::Messenger
4
+
5
+ # A client object interface to receive messages from.
6
+ #
7
+ # @return [Msngr::Clients::*]
8
+ #
9
+ attr_reader :client
10
+
11
+ # An Array of Receiver objects to dispatch callbacks/messages to.
12
+ #
13
+ # @return [Array<Receiver>]
14
+ #
15
+ attr_reader :receivers
16
+
17
+ # Initializes a new Messenger.
18
+ #
19
+ # @param [Msngr::Clients::*] client the to receive messages from.
20
+ # @return [Receiver]
21
+ #
22
+ def initialize(client)
23
+ @client = client
24
+ @receivers = []
25
+ @mutex = Mutex.new
26
+ end
27
+
28
+ # Creates and returns a new Receiver and adds it to @receivers so
29
+ # it'll receive messages matching the provided pattern.
30
+ #
31
+ # @param [Regexp] pattern
32
+ # @return [Receiver]
33
+ #
34
+ def subscribe(pattern)
35
+ Msngr::Receiver.new(pattern).tap do |receiver|
36
+ @mutex.synchronize { @receivers << receiver }
37
+ end
38
+ end
39
+
40
+ # Removes the Receiver from @receivers and invokes the receiver's
41
+ # @on_unsubscribe_callbacks.
42
+ #
43
+ # @param [Receiver] receiver
44
+ #
45
+ def unsubscribe(receiver)
46
+ @mutex.synchronize { @receivers.delete(receiver) }
47
+ dispatch(receiver.on_unsubscribe_callbacks, self)
48
+ end
49
+
50
+ # Listens on a new thread. Will auto-restart when crashed
51
+ # in an attempt to recover from exceptions.
52
+ #
53
+ def listen!
54
+ Thread.new do
55
+ loop do
56
+ begin
57
+ listen
58
+ rescue => e
59
+ puts "Messenger error occurred:"
60
+ puts "#{e.class.name}"
61
+ puts "#{e.backtrace.join("\n")}"
62
+ puts "Restarting.."
63
+ sleep 1
64
+ end
65
+ end
66
+ end
67
+ end
68
+
69
+ private
70
+
71
+ # Instructs the @client to yield events/messages when it receives them. Received
72
+ # events are matched with each Receiver's pattern and will dispatch the event's message
73
+ # to all subscribing Receiver instances that match the event's pattern.
74
+ #
75
+ def listen
76
+ client.on_message do |event, message|
77
+ subscribing_receivers(event) do |receiver|
78
+ dispatch(receiver.on_message_callbacks, message)
79
+ end
80
+ end
81
+ end
82
+
83
+ # Yields Receiver objects who's pattern matches the event.
84
+ #
85
+ # @param [String] event
86
+ # @yield [Receiver]
87
+ #
88
+ def subscribing_receivers(event)
89
+ receivers.each do |receiver|
90
+ yield receiver if receiver.pattern.match(event)
91
+ end
92
+ end
93
+
94
+ # Dispatches args to the provided callbacks.
95
+ #
96
+ # @param [Array<Proc>] callbacks
97
+ # @param [Array] *args an Array of arguments to call each Proc with.
98
+ #
99
+ def dispatch(callbacks, *args)
100
+ callbacks.each { |callback| callback.call(*args) }
101
+ end
102
+ end
103
+
@@ -0,0 +1,61 @@
1
+ require "thread"
2
+
3
+ class Msngr::Receiver
4
+
5
+ # Event pattern to match.
6
+ #
7
+ # @return [String]
8
+ #
9
+ attr_reader :pattern
10
+
11
+ # Array of Procs to invoke when a message is received.
12
+ #
13
+ # @return [Array<Proc>]
14
+ #
15
+ attr_reader :on_message_callbacks
16
+
17
+ # Array of Procs to invoke when unsubscribed from a Messenger.
18
+ #
19
+ # @return [Array<Proc>]
20
+ #
21
+ attr_reader :on_unsubscribe_callbacks
22
+
23
+ # Initializes a new Receiver.
24
+ #
25
+ # @param [Regexp] pattern the pattern to listen for.
26
+ # @return [Receiver]
27
+ #
28
+ def initialize(pattern = /.+/)
29
+ @pattern = pattern
30
+ @on_message_callbacks = []
31
+ @on_unsubscribe_callbacks = []
32
+ @mutex = Mutex.new
33
+ end
34
+
35
+ # Define a callback that invokes on each received message.
36
+ #
37
+ # @param [Proc] block
38
+ #
39
+ # @example
40
+ # receiver.on_message do |message|
41
+ # puts "Message Received: #{message}"
42
+ # end
43
+ #
44
+ def on_message(&block)
45
+ @mutex.synchronize { @on_message_callbacks << block }
46
+ end
47
+
48
+ # Define a callback that invokes when unsubscribed from a Messenger.
49
+ #
50
+ # @param [Proc] block
51
+ #
52
+ # @example
53
+ # receiver.on_unsubscribe do |messenger|
54
+ # puts "Unsubscribed From: #{messenger}"
55
+ # end
56
+ #
57
+ def on_unsubscribe(&block)
58
+ @mutex.synchronize { @on_unsubscribe_callbacks << block }
59
+ end
60
+ end
61
+
@@ -0,0 +1,4 @@
1
+ module Msngr
2
+ VERSION = "0.0.1"
3
+ end
4
+
@@ -0,0 +1,42 @@
1
+ require "spec_helper"
2
+ require "msngr/clients/redis"
3
+
4
+ describe Msngr::Clients::Redis do
5
+
6
+ let(:client) { Msngr::Clients::Redis.new }
7
+
8
+ it "should initialize without redis options" do
9
+ client.should have(0).args
10
+ end
11
+
12
+ it "should initialize with arguments" do
13
+ client = Msngr::Clients::Redis.new(host: "127.0.0.1", port: 6379)
14
+ client.args.should == [{host: "127.0.0.1", port: 6379}]
15
+ end
16
+
17
+ it "should establish a connection without any args" do
18
+ Redis.expects(:new)
19
+ client.send(:connection)
20
+ end
21
+
22
+ it "should establish a connection with args" do
23
+ options = { host: "127.0.0.1", port: 6379 }
24
+ client = Msngr::Clients::Redis.new(options)
25
+ Redis.expects(:new).with(options)
26
+ client.send(:connection)
27
+ end
28
+
29
+ it "should yield a message" do
30
+ connection, on = mock, mock
31
+
32
+ client.stubs(:connection).returns(connection)
33
+ connection.expects(:psubscribe).with("*").yields(on)
34
+ on.expects(:pmessage).yields("", "room.1", "John joined the room!")
35
+
36
+ client.on_message do |event, message|
37
+ event.should == "room.1"
38
+ message.should == "John joined the room!"
39
+ end
40
+ end
41
+ end
42
+
@@ -0,0 +1,78 @@
1
+ require "spec_helper"
2
+ require "msngr/receiver"
3
+ require "msngr/messenger"
4
+
5
+ describe Msngr::Messenger do
6
+
7
+ let(:client) { mock }
8
+ let(:messenger) { Msngr::Messenger.new(client) }
9
+
10
+ it "should initialize with an empty array of receivers" do
11
+ messenger.receivers.should be_empty
12
+ end
13
+
14
+ it "should create a new Receiver and add it to @receivers" do
15
+ receiver = messenger.subscribe(/.+/)
16
+ messenger.should have(1).receivers
17
+ messenger.receivers.first.should == receiver
18
+ end
19
+
20
+ it "should unsubscribe a receiver" do
21
+ receiver = messenger.subscribe(/.+/)
22
+ messenger.expects(:dispatch).
23
+ with(receiver.on_unsubscribe_callbacks, messenger)
24
+ messenger.unsubscribe(receiver)
25
+ messenger.should have(0).receivers
26
+ end
27
+
28
+ it "should start listening on a separate thread" do
29
+ Thread.expects(:new).yields
30
+ messenger.expects(:loop).yields
31
+ messenger.expects(:listen).once
32
+ messenger.listen!
33
+ end
34
+
35
+ it "should display a backtrace on error and restart" do
36
+ Thread.expects(:new).yields
37
+ messenger.expects(:loop).yields
38
+ messenger.expects(:listen).raises.then.returns
39
+ messenger.expects(:puts).times(4)
40
+ messenger.expects(:sleep)
41
+ messenger.listen!
42
+ end
43
+
44
+ it "should listen to the client but not dispatch" do
45
+ messenger.subscribe(/room\.2/)
46
+ client.expects(:on_message).yields("room.1", "Hi John")
47
+ messenger.expects(:dispatch).never
48
+ messenger.send(:listen)
49
+ end
50
+
51
+ it "should listen to the client and dispatch a message" do
52
+ messenger.subscribe(/room\.1/).on_message { |m| m.should == "Hi John" }
53
+ client.expects(:on_message).yields("room.1", "Hi John")
54
+ messenger.send(:listen)
55
+ end
56
+
57
+ it "should listen to the client and dispatch 3 messages" do
58
+ 3.times do
59
+ messenger.subscribe(/room\.1/).tap do |receiver|
60
+ cb = proc {}
61
+ cb.expects(:call).with("Hi John")
62
+ receiver.on_message(&cb)
63
+ end
64
+ end
65
+
66
+ 2.times do
67
+ messenger.subscribe(/room\.2/).tap do |receiver|
68
+ cb = proc {}
69
+ cb.expects(:call).never
70
+ receiver.on_message(&cb)
71
+ end
72
+ end
73
+
74
+ client.expects(:on_message).yields("room.1", "Hi John")
75
+ messenger.send(:listen)
76
+ end
77
+ end
78
+
@@ -0,0 +1,37 @@
1
+ require "spec_helper"
2
+ require "msngr/receiver"
3
+
4
+ describe Msngr::Receiver do
5
+
6
+ let(:receiver) { Msngr::Receiver.new }
7
+
8
+ it "should initialize with a default wildcard pattern" do
9
+ receiver.pattern.should == /.+/
10
+ end
11
+
12
+ it "should initialize with a custom pattern" do
13
+ pattern = /rooms\.1/
14
+ Msngr::Receiver.new(pattern).pattern.should == pattern
15
+ end
16
+
17
+ it "should define an message callback" do
18
+ callback = proc { |msg| "invoked: #{msg}" }
19
+ receiver.on_message(&callback)
20
+
21
+ callbacks = receiver.on_message_callbacks
22
+ callbacks.size.should == 1
23
+ callbacks.first.call("message").should == "invoked: message"
24
+ end
25
+
26
+ it "should define an unsubscribe callback" do
27
+ callback = proc { |msngr| "unsubscribed from: #{msngr}" }
28
+ receiver.on_unsubscribe(&callback)
29
+
30
+ callbacks = receiver.on_unsubscribe_callbacks
31
+ callbacks.size.should == 1
32
+
33
+ object = Object.new
34
+ callbacks.first.call(object).should == "unsubscribed from: #{object}"
35
+ end
36
+ end
37
+
@@ -0,0 +1,10 @@
1
+ require "spec_helper"
2
+ require "msngr"
3
+
4
+ describe Msngr do
5
+
6
+ it "should instantiate an instance of Msngr::Messenger" do
7
+ Msngr.new(mock).class.should == Msngr::Messenger
8
+ end
9
+ end
10
+
@@ -0,0 +1,9 @@
1
+ require "simplecov"
2
+ require "bundler"
3
+ SimpleCov.start
4
+ Bundler.require
5
+
6
+ RSpec.configure do |config|
7
+ config.mock_with :mocha
8
+ end
9
+
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: msngr
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Michael van Rooijen
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-12-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 10.1.0
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: 10.1.0
37
+ type: :development
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - "~>"
42
+ - !ruby/object:Gem::Version
43
+ version: 10.1.0
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 10.1.0
47
+ description: A light-weight Ruby library for multi-threaded Ruby applications that
48
+ allows threads to share a single service connection for more efficient messaging.
49
+ email:
50
+ - meskyanichi@gmail.com
51
+ executables: []
52
+ extensions: []
53
+ extra_rdoc_files: []
54
+ files:
55
+ - "./lib/msngr.rb"
56
+ - "./lib/msngr/clients/redis.rb"
57
+ - "./lib/msngr/messenger.rb"
58
+ - "./lib/msngr/receiver.rb"
59
+ - "./lib/msngr/version.rb"
60
+ - "./spec/lib/msngr/clients/redis_spec.rb"
61
+ - "./spec/lib/msngr/messenger_spec.rb"
62
+ - "./spec/lib/msngr/receiver_spec.rb"
63
+ - "./spec/lib/msngr_spec.rb"
64
+ - "./spec/spec_helper.rb"
65
+ homepage: https://github.com/meskyanichi/msngr/
66
+ licenses:
67
+ - MIT
68
+ metadata: {}
69
+ post_install_message:
70
+ rdoc_options: []
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ requirements: []
84
+ rubyforge_project:
85
+ rubygems_version: 2.2.0
86
+ signing_key:
87
+ specification_version: 4
88
+ summary: A light-weight Ruby library for multi-threaded Ruby applications that allows
89
+ threads to share a single service connection for more efficient messaging.
90
+ test_files: []