pokan 0.1.0rc1

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.
@@ -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