pokan 0.1.0rc1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,27 @@
1
+ # lib/pokan/pokan_messages.rb
2
+
3
+ module Pokan
4
+ module ServerMessages
5
+ # Gets a message for the introduction of the new peer to the seed
6
+ def hello_message
7
+ { action: 'hello', origin:"#{address}:#{port}" }.to_json
8
+ end
9
+
10
+ ##
11
+ # Gets the message that the peer needs to send when is going to stop participating of the group
12
+ def goodbye_message
13
+ { action: 'goodbye', origin:"#{address}:#{port}" }.to_json
14
+ end
15
+
16
+ ##
17
+ # Gets a digest containing all peers' key/timestamp of all peers
18
+ # It returns a json with all the pairs for each peer in a Digest Structure
19
+ def digest_message
20
+ data= Hash.new
21
+ peers = Query.new(Peer).where(role:['peer','seed'], status:'alive')
22
+ peers.each { |peer| data[peer.id] = peer.digest }
23
+
24
+ { action: 'digest', data: data, origin:"#{address}:#{port}" }.to_json
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,3 @@
1
+ module Pokan #:nodoc:
2
+ VERSION = "0.1.0rc1" unless defined? Pokan::VERSION
3
+ end
data/pokan.gemspec ADDED
@@ -0,0 +1,53 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "pokan"
8
+ s.version = "0.1.0rc1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Renato Mascarenhas", "Rafael Regis do Prado", "Fabio Lima Pereira"]
12
+ s.date = "2011-11-10"
13
+ s.description = "pokan is an implementation of the Gossip protocol \n (best described in http://www.cs.cornell.edu/home/rvr/papers/flowgossip.pdf),\n which aims to have a simple, event based API capable of being used in large,\n not previously known networks."
14
+ s.email = "haze-gem@googlegroups.com"
15
+ s.files = [
16
+ "pokan.gemspec"
17
+ ]
18
+ s.homepage = "http://github.com/haze/pokan"
19
+ s.licenses = ["MIT"]
20
+ s.require_paths = ["lib"]
21
+ s.rubygems_version = "1.8.10"
22
+ s.summary = "Gossip-protocol implementation with an event based API"
23
+
24
+ if s.respond_to? :specification_version then
25
+ s.specification_version = 3
26
+
27
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
28
+ s.add_runtime_dependency(%q<eventmachine>, [">= 0"])
29
+ s.add_runtime_dependency(%q<redis>, [">= 0"])
30
+ s.add_development_dependency(%q<rspec>, [">= 0"])
31
+ s.add_development_dependency(%q<bundler>, [">= 0"])
32
+ s.add_development_dependency(%q<jeweler>, [">= 0"])
33
+ s.add_development_dependency(%q<guard>, [">= 0"])
34
+ s.add_development_dependency(%q<guard-rspec>, [">= 0"])
35
+ else
36
+ s.add_dependency(%q<eventmachine>, [">= 0"])
37
+ s.add_dependency(%q<redis>, [">= 0"])
38
+ s.add_dependency(%q<rspec>, [">= 0"])
39
+ s.add_dependency(%q<bundler>, [">= 0"])
40
+ s.add_dependency(%q<jeweler>, [">= 0"])
41
+ s.add_dependency(%q<guard>, [">= 0"])
42
+ s.add_dependency(%q<guard-rspec>, [">= 0"])
43
+ end
44
+ else
45
+ s.add_dependency(%q<eventmachine>, [">= 0"])
46
+ s.add_dependency(%q<redis>, [">= 0"])
47
+ s.add_dependency(%q<rspec>, [">= 0"])
48
+ s.add_dependency(%q<bundler>, [">= 0"])
49
+ s.add_dependency(%q<jeweler>, [">= 0"])
50
+ s.add_dependency(%q<guard>, [">= 0"])
51
+ s.add_dependency(%q<guard-rspec>, [">= 0"])
52
+ end
53
+ end
@@ -0,0 +1,10 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Pokan::Connection do
4
+ it "should instance only one Redis object" do
5
+ @client1 = Pokan::Connection.redis
6
+ @client2 = Pokan::Connection.redis
7
+ @client1.should == @client2
8
+ end
9
+ end
10
+
@@ -0,0 +1,147 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Pokan::Entity do
4
+ context 'when comparing key-values' do
5
+ before :all do
6
+ Pokan::Connection.redis.flushall
7
+ @entity = Pokan::Entity.new
8
+ @entity.id = '1'
9
+ @entity.store(:k1, "v1")
10
+ @entity.store(:k2, 5.0)
11
+ end
12
+
13
+ it 'should return true if the internal key have the same value' do
14
+ @entity.match?(k1: 'v1').should be_true
15
+ end
16
+
17
+ it 'should return false if the internal key doesnt have the same value' do
18
+ @entity.match?(k1: 'v2').should_not be_true
19
+ end
20
+
21
+ it 'should return true if the internal key is one of the array' do
22
+ @entity.match?(k1: ['v1', 'v2']).should be_true
23
+ end
24
+
25
+ it 'should return false if the internal key isnt one of the array' do
26
+ @entity.match?(k1: ['v3', 'v2']).should_not be_true
27
+ end
28
+
29
+ it 'should return true if the value for a given key is in the range' do
30
+ @entity.match?(k2: { min: 3.0, max: 10.0 }).should be_true
31
+ end
32
+
33
+ it 'should return true if the value for a given key is in the range' do
34
+ @entity.match?(k2: { min:6.0, max: 10.0 }).should_not be_true
35
+ end
36
+ end
37
+
38
+ context 'when storing a value' do
39
+ before :all do
40
+ Pokan::Connection.redis.flushall
41
+ @entity = Pokan::Entity.new
42
+ @entity.id = '1'
43
+ @entity.store(:test, 'OK')
44
+ @entity.save
45
+ @entity.reload
46
+ end
47
+
48
+ it 'should store a new value for a key' do
49
+ @entity.value(:test).should == 'OK'
50
+ end
51
+
52
+ it 'should store a timestamp for a key' do
53
+ @entity.timestamp(:test).should < Time.now
54
+ end
55
+
56
+ it "should update the timestamp of an updated key" do
57
+ new = @entity.timestamp(:status)
58
+ @entity.store(:local, 'somewhere')
59
+ @entity.save
60
+ @entity.reload
61
+ newer = @entity.timestamp(:local)
62
+ newer.to_f.should > new.to_f
63
+ end
64
+ end
65
+
66
+ context 'when retrieving a value' do
67
+ before :all do
68
+ Pokan::Connection.redis.flushall
69
+ @entity = Pokan::Peer.new
70
+ @entity.id = '1'
71
+ @entity.store(:test, 'OK')
72
+ @entity.save
73
+ @entity.reload
74
+ end
75
+
76
+ it 'should get the value for a given key' do
77
+ @entity.value(:test).should == 'OK'
78
+ end
79
+
80
+ it 'should return nil if a value doesn\'t exist' do
81
+ @entity.value(:other_test).should be_nil
82
+ end
83
+
84
+ it 'should return zero to timestamp if a value doesn\'t exist' do
85
+ @entity.timestamp(:other_test).should == Time.at(0)
86
+ end
87
+ end
88
+
89
+ context 'when merging new data to a entity' do
90
+ before :each do
91
+ Pokan::Connection.redis.flushall
92
+ @entity = Pokan::Entity.new
93
+ @entity.id = '1'
94
+ @entity.store(:local, 'GREAT')
95
+ @entity.save
96
+ @entity.reload
97
+ end
98
+
99
+ it 'should merge with a new set preserving newer values' do
100
+ @entity.merge({
101
+ local: { value: 'GREATER', timestamp: Time.now.to_f + 2000 }
102
+ })
103
+ @entity.value(:local).should == 'GREATER'
104
+ end
105
+
106
+ it 'should merge with a new set discarding older values' do
107
+ @entity.merge({
108
+ local: {value: 'NOT GREAT', timestamp: 50}
109
+ })
110
+ @entity.value(:local).should == 'GREAT'
111
+ end
112
+ end
113
+
114
+ context 'when getting data for gossip' do
115
+ before :all do
116
+ Pokan::Connection.redis.flushall
117
+ @entity = Pokan::Entity.new
118
+ @entity.id = '1'
119
+ @entity.store(:foo, 'bar')
120
+ @entity.store(:local, 'OK')
121
+ @entity.save
122
+ @entity.reload
123
+ end
124
+
125
+ it 'should return all key/timestamps of a peer' do
126
+ @entity.digest.keys.should =~ [:local, :foo]
127
+ end
128
+
129
+ it 'should return all older keys of a peer' do
130
+ digest = {local: 0, foo: (Time.now.to_f + 1000).to_s}
131
+ @entity.older(digest).should == [:foo]
132
+ end
133
+
134
+ it 'should return all newer key/value/timestamps of an peer' do
135
+ digest = {local: 0, foo: (Time.now.to_f + 1000).to_s}
136
+ @entity.newer(digest)[:local][:value].should_not == nil
137
+ end
138
+
139
+ it 'should return all the peer\'s keys' do
140
+ @entity.keys.should =~ [:foo, :local]
141
+ end
142
+
143
+ it 'should return the keys, values and timestamps of all required keys' do
144
+ @entity.values([:local])[:local][:value].should == 'OK'
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,84 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Pokan::EventHandler do
4
+
5
+ before :each do
6
+ @event_handler = Pokan::EventHandler.new
7
+ end
8
+
9
+ it 'should register a new event' do
10
+ register_callback(:dummy)
11
+
12
+ @event_handler.events.should include :dummy
13
+ end
14
+
15
+ it 'should raise ArgumentError if event name is not a symbol' do
16
+ expect { @event_handler.register 'dummy' }.to raise_exception ArgumentError
17
+ end
18
+
19
+ it 'should not allow a call to `register` without a block' do
20
+ expect { @event_handler.register :dummy }.to raise_exception Pokan::NoBlockGivenError
21
+ end
22
+
23
+ it 'should list no subscribers for a not registered event' do
24
+ @event_handler.subscribers(:dummy).should be_empty
25
+ end
26
+
27
+ it 'should list all subscribers for an event' do
28
+ 3.times { register_callback(:dummy) }
29
+
30
+ @event_handler.subscribers(:dummy).size.should == 3
31
+ end
32
+
33
+ it 'should do nothing when `emit` is called for an unexisting event' do
34
+ @event_handler.emit(:dummy).should == @event_handler
35
+ end
36
+
37
+ it 'should call registered callbacks after `emit`' do
38
+ @block_called = false
39
+ register_callback(:dummy)
40
+
41
+ @event_handler.emit(:dummy)
42
+
43
+ @block_called.should be_true
44
+ end
45
+
46
+ it 'should call registered callbacks twice' do
47
+ @block_called = false
48
+ register_callback(:dummy)
49
+
50
+ @event_handler.emit(:dummy)
51
+ @block_called.should be_true
52
+
53
+ @block_called = false
54
+ @event_handler.emit(:dummy)
55
+
56
+ @block_called.should be_true
57
+ end
58
+
59
+ it 'should pass correct parameters when `emit` is called using `with` option' do
60
+ @parameter = false
61
+
62
+ @event_handler.register :dummy do |message|
63
+ @parameter = message
64
+ end
65
+
66
+ @event_handler.emit :dummy, :with => [true]
67
+ @parameter.should be_true
68
+ end
69
+
70
+ it 'should reset registered callbacks for an event' do
71
+ 3.times { register_callback(:dummy) }
72
+
73
+ @event_handler.reset(:dummy)
74
+
75
+ @event_handler.subscribers(:dummy).should be_empty
76
+ end
77
+
78
+ def register_callback(event)
79
+ @event_handler.register event do
80
+ @block_called = true
81
+ end
82
+ end
83
+
84
+ end
@@ -0,0 +1,32 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Pokan::Network do
4
+
5
+ context 'when sending via UDP' do
6
+
7
+ it 'should send the message' do
8
+ Pokan::Network.udp('dummy', '127.0.0.1', 7777).should be_true
9
+ end
10
+
11
+ end
12
+
13
+ context 'when sending a message via TCP without a server to accept it' do
14
+
15
+ it 'should not send the message' do
16
+ Pokan::Network.tcp('dummy', '127.0.0.1', 7777).should be_false
17
+ end
18
+
19
+ end
20
+
21
+ context 'when sending a message via TCP with a server accepting' do
22
+
23
+ before { @server = TCPServer.open(7777) }
24
+ after { @server.close }
25
+
26
+ it 'should send the message' do
27
+ Pokan::Network.tcp('dummy', '127.0.0.1', 7777).should be_true
28
+ end
29
+
30
+ end
31
+
32
+ end
@@ -0,0 +1,94 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ describe Pokan::Peer do
4
+ context 'when checking the status of the peer' do
5
+ before :all do
6
+ Pokan::Connection.redis.flushall
7
+ end
8
+
9
+ it 'should check if a peer is dead' do
10
+ peer = Pokan::Peer.new
11
+ peer.address = peer.udp_port = '3'
12
+ peer.kill
13
+ peer.save
14
+ peer.reload
15
+ peer.dead?.should be_true
16
+ end
17
+
18
+ it 'should check if a peer is not dead' do
19
+ peer = Pokan::Peer.new
20
+ peer.address = peer.udp_port = '3'
21
+ peer.save
22
+ peer.reload
23
+ peer.dead?.should be_false
24
+ end
25
+
26
+ it 'should check if a peer is not alive' do
27
+ peer = Pokan::Peer.new
28
+ peer.address = peer.udp_port = '3'
29
+ peer.kill
30
+ peer.save
31
+ peer.alive?.should be_false
32
+ end
33
+
34
+ it 'should check if peer is alive' do
35
+ peer = Pokan::Peer.new
36
+ peer.address = peer.udp_port = '3'
37
+ peer.save
38
+ peer.alive?.should be_true
39
+ end
40
+ end
41
+
42
+ context 'when changing the status of a peer' do
43
+ before :all do
44
+ Pokan::Connection.redis.flushall
45
+ end
46
+
47
+ it 'should not set a new status if the timestamp is not greater' do
48
+ peer = Pokan::Peer.new
49
+ peer.address = peer.udp_port = '3'
50
+ peer.store(:status, 'dead', Time.at(0))
51
+ peer.save
52
+ peer.reload
53
+ peer.dead?.should be_false
54
+ end
55
+
56
+ it 'should set a new status if the timestamp is greater' do
57
+ peer = Pokan::Peer.new
58
+ peer.address = peer.udp_port = '3'
59
+ peer.store('status', 'dead', Time.now + 1000)
60
+ peer.save
61
+ peer.reload
62
+ peer.dead?.should be_true
63
+ end
64
+ end
65
+
66
+ context 'when setting up a peer' do
67
+ before :all do
68
+ Pokan::Connection.redis.flushall
69
+ @peer = Pokan::Peer.new
70
+ @peer.address = @peer.udp_port = '3'
71
+ @peer.save
72
+ end
73
+
74
+ it 'should start as a normal peer' do
75
+ @peer.seed?.should be_false
76
+ end
77
+
78
+ it 'should start act as a seed' do
79
+ @peer.act_as_seed
80
+ @peer.save
81
+ @peer.seed?.should be_true
82
+ end
83
+
84
+ it 'should kill a peer' do
85
+ @peer.kill
86
+ @peer.dead?.should be_true
87
+ end
88
+
89
+ it 'should revive a peer' do
90
+ @peer.revive
91
+ @peer.dead?.should be_false
92
+ end
93
+ end
94
+ end