ruby-iarm 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG ADDED
@@ -0,0 +1 @@
1
+ v0.0.1. First version
data/LICENSE ADDED
@@ -0,0 +1,26 @@
1
+ Copyright (c) 2010, Andrew Snow
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright notice,
8
+ * this list of conditions and the following disclaimer.
9
+ * Redistributions in binary form must reproduce the above copyright
10
+ * notice, this list of conditions and the following disclaimer in the
11
+ * documentation and/or other materials provided with the distribution.
12
+ * Neither the name of the author nor the names of its
13
+ * contributors may be used to endorse or promote products derived from
14
+ * this software without specific prior written permission.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26
+ POSSIBILITY OF SUCH DAMAGE.
data/Manifest ADDED
@@ -0,0 +1,21 @@
1
+ CHANGELOG
2
+ LICENSE
3
+ README
4
+ README.textile
5
+ Rakefile
6
+ bin/IARMserver.rb
7
+ bin/chattest.rb
8
+ bin/chattest_sniffer.rb
9
+ lib/iarm.rb
10
+ lib/iarm/client.rb
11
+ lib/iarm/msg.rb
12
+ lib/iarm/msg/channel_member.rb
13
+ lib/iarm/msg/join.rb
14
+ lib/iarm/msg/part.rb
15
+ lib/iarm/msg/timeout.rb
16
+ lib/iarm/msg/topic.rb
17
+ lib/iarm/server.rb
18
+ lib/iarm/timer.rb
19
+ test/performance_test.rb
20
+ test/test_iarm.rb
21
+ Manifest
data/README ADDED
@@ -0,0 +1,3 @@
1
+
2
+ See README.textile
3
+
data/README.textile ADDED
@@ -0,0 +1,52 @@
1
+
2
+ h2. IARM(Intra-Application Relay Messaging for Ruby): IRC-inspired Messaging Server for Ruby
3
+
4
+ bc. require 'iarm'
5
+
6
+ h3. Start a Server:
7
+
8
+ bc. Iarm::Server.start('drbunix:/tmp/.s.iarm_socket')
9
+
10
+ h3. Connect, join a channel, say something:
11
+
12
+ Clients connect to a central server via DRb, join channels, and send
13
+ messages to the channel.
14
+
15
+ bc. c = Iarm::Client.connect('drbunix:/tmp/.s.iarm_socket')
16
+ c.join('nickname', 'channelname')
17
+ c.say('nickname', 'channelname', 'Hello world')
18
+
19
+ You supply your current nickname to every call, which allows use of multiple
20
+ nicks in a session.
21
+
22
+ h3. Read messages
23
+
24
+ bc. msg = c.getmsg('nickname', timeout)
25
+ puts "Message received #{msg.class}: #{msg.data}
26
+ from: #{msg.from}
27
+ on channel: #{msg.channel}"
28
+
29
+ As well as regular @Iarm::Msg@ data messages, there are informational types
30
+ about the channel, generated by the server: @Join@, @Part@, @Timeout@, and @Topic@.
31
+
32
+
33
+
34
+ h2. Features
35
+
36
+ * Join and depart channels
37
+ * Channels exist while there are >0 members
38
+ * Get list of channels
39
+ * Get list of channel members
40
+ * Get/set channel topic
41
+ * Auto-notified on join, depart, timeout of other members. Notified on topic changes.
42
+ * Messages are any type of marshalable ruby data
43
+ * Configurable timeout: messages are saved upon disconnect until timeout
44
+ * Poll for messages, or wait for a new message with optional timeout. To help with refresh loops in web apps, clients can disconnect and reconnect within a configurable timeout, and not lose any messages. This makes it trivial to implement, say, the back-end of a (poor!) Campfire clone in Rails.
45
+ * There is no persistence mechanism
46
+ * There is no security except channels may have passwords. Also, DRb supports SSL (untested).
47
+
48
+ h3. Contact
49
+
50
+ Andrew Snow <andrew@modulus.org>
51
+ Andys^ on irc.freenode.net
52
+
data/Rakefile ADDED
@@ -0,0 +1,3 @@
1
+ require 'echoe'
2
+ Echoe.new('ruby-iarm')
3
+
data/bin/IARMserver.rb ADDED
@@ -0,0 +1,10 @@
1
+
2
+ require 'drb'
3
+ require 'iarm'
4
+
5
+ Thread.abort_on_exception = true
6
+
7
+ server = Iarm::Server.new
8
+ DRb.start_service(ARGV[0].nil? ? 'drbunix:/tmp/.s.iarm' : ARGV[0], server)
9
+ DRb.thread.join
10
+
data/bin/chattest.rb ADDED
@@ -0,0 +1,57 @@
1
+ require '../iarm'
2
+
3
+ Thread.abort_on_exception = true
4
+
5
+ puts "Name?"
6
+ nick = gets
7
+ nick.chomp!
8
+
9
+ reader = Thread.new do
10
+ puts "reader: Connecting"
11
+ iarm = Iarm::Client.connect('drbunix:/tmp/.s.iarm')
12
+ msg = nil
13
+ loop do
14
+ #sleep 1 if(msg.nil?)
15
+ puts "reader: waiting for message"
16
+ msg = iarm.getmsg(nick, 30)
17
+ if(msg)
18
+ if(msg.kind_of?(Iarm::Msg::Join))
19
+ puts "#{msg.channel}: *** #{msg.from} #{msg.kind_of?(Iarm::Msg::ChannelMember) ? 'is in' : 'has joined'} the channel"
20
+ elsif(msg.kind_of?(Iarm::Msg::Part))
21
+ puts "#{msg.channel}: *** #{msg.from} has #{msg.kind_of?(Iarm::Msg::Timeout) ? 'timed out of' : 'departed'} the channel"
22
+ else
23
+ puts "#{msg.channel}: <#{msg.from}> #{msg.data}"
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ puts "writer: connecting"
30
+ ia = Iarm::Client.connect('drbunix:/tmp/.s.iarm')
31
+ ch = nil
32
+
33
+ loop do
34
+ puts "Input?"
35
+ input = gets
36
+ if(input)
37
+ input.chomp!
38
+ puts "writer: posting"
39
+ if(input =~ /\/join (.*)$/)
40
+ ch = $1
41
+ puts "*** #{nick} joining #{ch}"
42
+ ia.join(nick, ch)
43
+ elsif(input =~ /\/part (.*)$/)
44
+ ia.depart(nick, $1)
45
+ puts "*** #{nick} departing #{$1}"
46
+ ch = nil if(ch == $1)
47
+ elsif(input =~ /\/quit$/)
48
+ ia.depart(nick)
49
+ reader.kill
50
+ exit
51
+ elsif(ch)
52
+ puts "<#{nick}:#{ch}> #{input}"
53
+ ia.post(Iarm::Msg.new(ch, nick, input))
54
+ end
55
+ end
56
+ end
57
+
@@ -0,0 +1,14 @@
1
+ require 'iarm'
2
+ iarm = Iarm::Client.connect('drbunix:/tmp/.s.iarm')
3
+ loop do
4
+ msg = iarm.getmsg(nil, 50)
5
+ if(msg)
6
+ if(msg.kind_of?(Iarm::Msg::Join))
7
+ puts "#{msg.channel}: *** #{msg.from} has joined the channel"
8
+ elsif(msg.kind_of?(Iarm::Msg::Part))
9
+ puts "#{msg.channel}: *** #{msg.from} has #{msg.kind_of?(Iarm::Msg::Timeout) ? 'timed out of' : 'departed'} the channel"
10
+ else
11
+ puts "#{msg.channel}: <#{msg.from}> #{msg.data}"
12
+ end
13
+ end
14
+ end
data/lib/iarm.rb ADDED
@@ -0,0 +1,5 @@
1
+
2
+ ['timer', 'msg', 'server', 'client'].each do |x|
3
+ require("#{File.dirname(__FILE__)}/iarm/" + x)
4
+ end
5
+
@@ -0,0 +1,11 @@
1
+
2
+ require 'drb'
3
+
4
+ module Iarm
5
+ class Client
6
+ def self.connect(server)
7
+ #DRb.start_service
8
+ DRbObject.new(nil, server)
9
+ end
10
+ end
11
+ end
data/lib/iarm/msg.rb ADDED
@@ -0,0 +1,8 @@
1
+
2
+
3
+ Iarm::Msg = Struct.new(:channel, :from, :data)
4
+
5
+ ['join', 'part', 'channel_member', 'timeout', 'topic'].each do |x|
6
+ require("#{File.dirname(__FILE__)}/msg/" + x)
7
+ end
8
+
@@ -0,0 +1 @@
1
+ class Iarm::Msg::ChannelMember < Iarm::Msg::Join ; end
@@ -0,0 +1,2 @@
1
+ class Iarm::Msg::Join < Iarm::Msg ; end # data = time joined
2
+
@@ -0,0 +1 @@
1
+ class Iarm::Msg::Part < Iarm::Msg ; end
@@ -0,0 +1 @@
1
+ class Iarm::Msg::Timeout < Iarm::Msg::Part ; end
@@ -0,0 +1 @@
1
+ class Iarm::Msg::Topic < Iarm::Msg ; end
@@ -0,0 +1,241 @@
1
+
2
+ require 'thread'
3
+ require 'drb'
4
+
5
+
6
+
7
+ module Iarm
8
+ class Server
9
+
10
+ def ping
11
+ 'pong'
12
+ end
13
+ def ttl(ttl_secs)
14
+ @ttl_secs = ttl_secs
15
+ end
16
+
17
+ def list(pattern=nil)
18
+ pattern ? @channels.keys.grep(pattern) : @channels.keys
19
+ end
20
+
21
+ def who(channel)
22
+ if(@channels.has_key?(channel))
23
+ @channel_members[channel] #.each {|w,time| post_msg(who, Msg::ChannelMember.new(channel, w, time)) }
24
+ else
25
+ {}
26
+ end
27
+ end
28
+
29
+ def join(who, channel, key=nil) # returns true if joined, false if denied, and nil if new channel formed
30
+ retval = nil
31
+ touch_nickname(who)
32
+ @mutex.synchronize do
33
+ if(@channels.has_key?(channel))
34
+ retval = (@channels[channel] == key)
35
+ else
36
+ @channels[channel] = key
37
+ end
38
+
39
+ if(retval != false) # if retval is true (joined existing) or nil (new channel formed)
40
+ if(!@channel_members[channel].has_key?(who)) # don't re-join them if they've already joined before
41
+ @channel_members[channel][who] = clockval
42
+ @channels_joined[who] << channel
43
+ send_msg(Msg::Join.new(channel, who, @channel_members[channel][who]))
44
+ post_msg(who, @topics[channel]) if @topics.has_key?(channel)
45
+ end
46
+ end
47
+ end
48
+ retval
49
+ end
50
+
51
+ def depart(who, channel=nil) # nil=depart ALL channels and log out client
52
+ @mutex.synchronize do
53
+ (channel.nil? ? @channels_joined[who] : [ channel ]).each do |ch|
54
+ @channels_joined[who].delete(ch)
55
+ if @channel_members[ch].delete(who)
56
+ send_msg(Msg::Part.new(ch, who))
57
+ end
58
+ check_channel_empty(ch)
59
+ end
60
+ kill_client(who) if(channel.nil?)
61
+ end
62
+
63
+ end
64
+
65
+ # getmsg(): NOTES
66
+ # returns msg or nil if no messages and timed out.
67
+ # also serves as a keep-alive to avoid getting killed by ttl
68
+ # if who=nil then it listens on all channels, but only one client can do this at once
69
+ # if another client is already listening with the same who-id, it has the effect of making them return immediately (before their timeout is up)
70
+ def getmsg(who, timeout=0)
71
+ if(@msgs[who].empty? && timeout != 0)
72
+ wait_existing = false
73
+ msg = @mutex.synchronize do
74
+ wait_existing = Iarm::Timer.poke(@listeners[who])
75
+ next_msg(who)
76
+ end
77
+ return msg if(msg)
78
+
79
+ if(wait_existing)
80
+ Thread.pass while(@mutex.synchronize { @listeners.has_key?(who) })
81
+ end
82
+
83
+ #puts "Timer.wait: timeout=#{timeout}"
84
+ Iarm::Timer.wait(timeout) do |mode|
85
+ @mutex.synchronize do
86
+ mode ? @listeners[who] = Thread.current : @listeners.delete(who)
87
+ end
88
+ #puts "IARM getmsg: #{who} #{mode ? 'entering' : 'exiting'} wait with msgcount=#{@msgs[who].length}"
89
+ Iarm::Timer.poke(Thread.current) if mode && @msgs[who].length>0 # don't bother sleeping if we already have a new message waiting
90
+ end
91
+ end
92
+ @mutex.synchronize { next_msg(who) }
93
+ end
94
+
95
+ def getmsgs(who, timeout=0)
96
+ res = [ getmsg(who, timeout) ]
97
+ while(!res.empty? && (msg = getmsg(who, 0)))
98
+ res << msg
99
+ end
100
+ res
101
+ end
102
+
103
+ def say(who, channel, data)
104
+ post(Iarm::Msg.new(channel, who, data))
105
+ end
106
+
107
+ def set_topic(who, channel, data)
108
+ touch_nickname(who)
109
+ if @channels.has_key?(channel)
110
+ data = Msg::Topic.new(channel, who, data) unless data.kind_of?(Msg::Topic)
111
+ if(@topics[channel] != data)
112
+ @mutex.synchronize { @topics[channel] = data }
113
+ post(data)
114
+ data
115
+ end
116
+ end
117
+ end
118
+
119
+ def get_topic(channel)
120
+ @topics[channel]
121
+ end
122
+
123
+ def post(msg)
124
+ @mutex.synchronize { send_msg(msg) } if(msg.kind_of?(Msg))
125
+ end
126
+
127
+ def self.start(uri=nil)
128
+ DRb.start_service(uri, self.new)
129
+ DRb.thread
130
+ end
131
+
132
+ private
133
+ REAPER_GRANULARITY = 5 #seconds
134
+
135
+ def initialize
136
+ @mutex = Mutex.new()
137
+ @reaper_mutex = Mutex.new()
138
+ @ttl_secs = 60
139
+ @listeners = Hash.new() # { who => Thread }
140
+ @msgs = Hash.new() {|hsh,key| hsh[key] = [ ] } # { who => [ msg1, msg2, ...] }
141
+ @clients = Hash.new() # { who => time_of_last_activity }
142
+ @channel_members = Hash.new() {|hsh,key| hsh[key] = { } } # { channelname => { who1 => join_time }, who2 => ...] }
143
+ @channels_joined = Hash.new() {|hsh,key| hsh[key] = [ ] } # { who => [ channel1, channel2 ] }
144
+ @channels = Hash.new() # { channelname => password }
145
+ @topics = Hash.new() # { channelname => topic }
146
+ @timeout_queue = []
147
+ reaper_thread
148
+ end
149
+
150
+ def touch_nickname(nickname) #TODO: call this
151
+ # UPTO THERE
152
+ timeout_box = @ttl_secs / REAPER_GRANULARITY #/
153
+ @reaper_mutex.synchronize do
154
+ @timeout_queue[timeout_box] ||= []
155
+ @timeout_queue[timeout_box] << nickname
156
+ @clients[nickname] = clockval
157
+ end
158
+ end
159
+
160
+
161
+ =begin
162
+ reaper ideas
163
+ ------------
164
+
165
+ have a linked list which is in order of things to timeout
166
+ when taking something off the list, check its actual timeout value and put it back to sleep if needed
167
+ this could be a binary search down the track, for performance
168
+
169
+ =end
170
+
171
+ def timed_out?(nickname)
172
+ (tla = @clients[nickname]) && (tla + @ttl_secs) < clockval
173
+ end
174
+
175
+ def reaper_thread
176
+ @reaper ||= Thread.new do
177
+ loop do
178
+ kill_list = []
179
+ sleep REAPER_GRANULARITY
180
+ @reaper_mutex.synchronize do
181
+ timeoutlist = @timeout_queue.shift
182
+ if timeoutlist
183
+ timeoutlist.each do |who|
184
+ kill_list << who if timed_out?(who)
185
+ end
186
+ end
187
+ end
188
+ @mutex.synchronize do
189
+ kill_list.each do |who|
190
+ if(@channels_joined.has_key?(who))
191
+ @channels_joined[who].each do |ch|
192
+ @channel_members[ch].delete(who)
193
+ send_msg(Msg::Timeout.new(ch, who))
194
+ check_channel_empty(ch)
195
+ end
196
+ end
197
+ kill_client(who)
198
+ end
199
+ end
200
+ end
201
+ end
202
+ end
203
+
204
+ def clockval
205
+ Time.new.to_i
206
+ end
207
+
208
+ def send_msg(msg)
209
+ @channel_members[msg.channel].each_key {|w| post_msg(w, msg) }
210
+ post_msg(nil, msg) if(@clients.has_key?(nil))
211
+ end
212
+ def post_msg(who, msg)
213
+ if(msg.kind_of?(Msg::Topic) || who != msg.from)
214
+ @msgs[who] << msg
215
+ Iarm::Timer.poke(@listeners[who]) if(@listeners.has_key?(who))
216
+ end
217
+ end
218
+ def next_msg(who) # returns msg or nil
219
+ touch_nickname(who)
220
+ @msgs[who].shift
221
+ end
222
+ def check_channel_empty(channel)
223
+ if(@channel_members[channel].empty?)
224
+ @channels.delete(channel)
225
+ @channel_members.delete(channel)
226
+ @topics.delete(channel)
227
+ end
228
+ end
229
+ def kill_client(who)
230
+ @channels_joined[who].each do |ch|
231
+ @channel_members[ch].delete(who)
232
+ check_channel_empty(ch)
233
+ end
234
+ @channels_joined.delete(who)
235
+ @clients.delete(who)
236
+ @msgs.delete(who)
237
+ @listeners.delete(who)
238
+ end
239
+
240
+ end
241
+ end
data/lib/iarm/timer.rb ADDED
@@ -0,0 +1,50 @@
1
+
2
+ require 'thread'
3
+
4
+ module Iarm
5
+ class Timer
6
+ class Timeout < Exception; end
7
+ class Poke < Exception; end
8
+
9
+ def self.poke(thr)
10
+ crit do
11
+ if(thr)# && thr.stop?)
12
+ thr.raise(Poke.new)
13
+ true
14
+ else
15
+ false
16
+ end
17
+ end
18
+ end
19
+
20
+ def self.wait(timeout)
21
+ timer = create_timer(timeout)
22
+ yield(true) if block_given?
23
+ Thread.stop
24
+ rescue Timeout
25
+ return false
26
+ rescue Poke
27
+ return true
28
+ ensure
29
+ Thread.kill(timer) if(timer && timer.alive?)
30
+ yield(false) if block_given?
31
+ end
32
+
33
+ def self.crit
34
+ yield
35
+ end
36
+
37
+ private
38
+ def self.create_timer(timeout)
39
+ return nil if(timeout.nil?)
40
+
41
+ waiter = Thread.current
42
+ Thread.start do
43
+ Thread.pass
44
+ sleep(timeout)
45
+ # Thread.critical = true
46
+ waiter.raise(Timeout.new)
47
+ end
48
+ end
49
+ end
50
+ end
data/ruby-iarm.gemspec ADDED
@@ -0,0 +1,32 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{ruby-iarm}
5
+ s.version = "0.0.1"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = [""]
9
+ s.date = %q{2010-06-07}
10
+ s.description = %q{}
11
+ s.email = %q{}
12
+ s.executables = ["IARMserver.rb", "chattest.rb", "chattest_sniffer.rb"]
13
+ s.extra_rdoc_files = ["CHANGELOG", "LICENSE", "README", "README.textile", "bin/IARMserver.rb", "bin/chattest.rb", "bin/chattest_sniffer.rb", "lib/iarm.rb", "lib/iarm/client.rb", "lib/iarm/msg.rb", "lib/iarm/msg/channel_member.rb", "lib/iarm/msg/join.rb", "lib/iarm/msg/part.rb", "lib/iarm/msg/timeout.rb", "lib/iarm/msg/topic.rb", "lib/iarm/server.rb", "lib/iarm/timer.rb"]
14
+ s.files = ["CHANGELOG", "LICENSE", "README", "README.textile", "Rakefile", "bin/IARMserver.rb", "bin/chattest.rb", "bin/chattest_sniffer.rb", "lib/iarm.rb", "lib/iarm/client.rb", "lib/iarm/msg.rb", "lib/iarm/msg/channel_member.rb", "lib/iarm/msg/join.rb", "lib/iarm/msg/part.rb", "lib/iarm/msg/timeout.rb", "lib/iarm/msg/topic.rb", "lib/iarm/server.rb", "lib/iarm/timer.rb", "test/performance_test.rb", "test/test_iarm.rb", "Manifest", "ruby-iarm.gemspec"]
15
+ s.homepage = %q{}
16
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Ruby-iarm", "--main", "README"]
17
+ s.require_paths = ["lib"]
18
+ s.rubyforge_project = %q{ruby-iarm}
19
+ s.rubygems_version = %q{1.3.7}
20
+ s.summary = %q{}
21
+ s.test_files = ["test/test_iarm.rb", "test/performance_test.rb"]
22
+
23
+ if s.respond_to? :specification_version then
24
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
25
+ s.specification_version = 3
26
+
27
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
28
+ else
29
+ end
30
+ else
31
+ end
32
+ end
@@ -0,0 +1 @@
1
+
data/test/test_iarm.rb ADDED
@@ -0,0 +1,134 @@
1
+
2
+ require '../iarm'
3
+ require 'test/unit'
4
+
5
+ Thread.abort_on_exception = true
6
+
7
+ def socket_path
8
+ 'drbunix:/tmp/.s.testiarm'
9
+ end
10
+
11
+ Iarm::Server.start(socket_path)
12
+
13
+ module TestIarmServer
14
+
15
+
16
+ def setup
17
+ @client1 = new_client
18
+ @client2 = new_client
19
+ end
20
+
21
+ def teardown
22
+ @client1.depart('client1')
23
+ @client1 = nil
24
+ @client2.depart('client2')
25
+ @client2 = nil
26
+ end
27
+
28
+ protected
29
+ def new_client
30
+ Iarm::Client.connect(socket_path) or raise 'Cannot connect'
31
+ end
32
+
33
+
34
+ end
35
+
36
+ class TestIarm < Test::Unit::TestCase
37
+ include TestIarmServer
38
+
39
+ def test_join_and_speak
40
+ @client1.join('client1', 'test_channel')
41
+ @client2.join('client2', 'test_channel')
42
+
43
+ join_msg = @client1.getmsg('client1', 1)
44
+ assert_instance_of Iarm::Msg::Join, join_msg
45
+ assert_equal 'client2', join_msg.from
46
+ assert_equal 'test_channel', join_msg.channel
47
+ end
48
+
49
+ def test_topic
50
+ @client1.join('client1', 'test_channel')
51
+
52
+
53
+ topic = @client1.get_topic('test_channel')
54
+ assert_nil topic
55
+
56
+ @client1.set_topic('client1', 'test_channel', 'Channel Topic')
57
+ topic = @client1.get_topic('test_channel')
58
+ assert_kind_of Iarm::Msg::Topic, topic
59
+ assert_equal 'Channel Topic', topic.data
60
+ assert_equal 'client1', topic.from
61
+ assert_equal 'test_channel', topic.channel
62
+
63
+ @client2.join('client2', 'test_channel')
64
+ topic = @client2.getmsg('client2', 1)
65
+ assert_kind_of Iarm::Msg::Topic, topic
66
+ assert_equal 'Channel Topic', topic.data
67
+ assert_equal 'client1', topic.from
68
+ assert_equal 'test_channel', topic.channel
69
+
70
+ @client2.set_topic('client2', 'test_channel', 'New Topic')
71
+ topic = @client1.get_topic('test_channel')
72
+ assert_kind_of Iarm::Msg::Topic, topic
73
+ assert_equal 'New Topic', topic.data
74
+ assert_equal 'client2', topic.from
75
+ assert_equal 'test_channel', topic.channel
76
+ end
77
+
78
+ def test_queued_msg
79
+ @client1.join('client1', 'test_channel')
80
+ @client2.join('client2', 'test_channel')
81
+ @client1.say('client1', 'test_channel', 'test message')
82
+
83
+ new_connection = new_client
84
+ msg = new_connection.getmsg('client2', 1)
85
+ assert_instance_of Iarm::Msg, msg
86
+ assert_equal 'client1', msg.from
87
+ assert_equal 'test_channel', msg.channel
88
+ assert_equal 'test message', msg.data
89
+ end
90
+
91
+ def test_who
92
+ @client1.join('client1', 'test_channel')
93
+ channel_members = @client2.who('test_channel')
94
+ assert_equal ['client1'], channel_members.keys.sort
95
+
96
+ @client2.join('client2', 'test_channel')
97
+ channel_members = @client2.who('test_channel')
98
+ assert_equal ['client1', 'client2'], channel_members.keys.sort
99
+ end
100
+
101
+ def test_depart
102
+ @client1.join('client1', 'test_channel')
103
+ @client2.join('client2', 'test_channel')
104
+ @client1.depart('client1', 'test_channel')
105
+ msg = @client2.getmsg('client2', 1)
106
+ assert_instance_of Iarm::Msg::Part, msg
107
+
108
+ channel_members = @client2.who('test_channel')
109
+ assert_equal ['client2'], channel_members.keys.sort
110
+ end
111
+
112
+ def test_depart_all
113
+ @client1.join('client1', 'test_channel')
114
+ @client1.join('client1', 'test_channel2')
115
+ @client1.depart('client1')
116
+
117
+ assert_equal [], @client2.who('test_channel').keys
118
+ assert_equal [], @client2.who('test_channel2').keys
119
+
120
+ assert_equal [], @client2.list
121
+ end
122
+
123
+ def test_server_running
124
+ assert_equal 'pong', @client1.ping
125
+ end
126
+
127
+ def test_timeout
128
+ @client1.ttl(2)
129
+ @client1.join('client1', 'test_channel')
130
+ assert_equal ['client1'], @client2.who('test_channel').keys
131
+ sleep(Iarm::Server::REAPER_GRANULARITY + 1)
132
+ assert_equal [], @client2.who('test_channel').keys
133
+ end
134
+ end
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ruby-iarm
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - ""
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-06-07 00:00:00 +10:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: ""
23
+ email: ""
24
+ executables:
25
+ - IARMserver.rb
26
+ - chattest.rb
27
+ - chattest_sniffer.rb
28
+ extensions: []
29
+
30
+ extra_rdoc_files:
31
+ - CHANGELOG
32
+ - LICENSE
33
+ - README
34
+ - README.textile
35
+ - bin/IARMserver.rb
36
+ - bin/chattest.rb
37
+ - bin/chattest_sniffer.rb
38
+ - lib/iarm.rb
39
+ - lib/iarm/client.rb
40
+ - lib/iarm/msg.rb
41
+ - lib/iarm/msg/channel_member.rb
42
+ - lib/iarm/msg/join.rb
43
+ - lib/iarm/msg/part.rb
44
+ - lib/iarm/msg/timeout.rb
45
+ - lib/iarm/msg/topic.rb
46
+ - lib/iarm/server.rb
47
+ - lib/iarm/timer.rb
48
+ files:
49
+ - CHANGELOG
50
+ - LICENSE
51
+ - README
52
+ - README.textile
53
+ - Rakefile
54
+ - bin/IARMserver.rb
55
+ - bin/chattest.rb
56
+ - bin/chattest_sniffer.rb
57
+ - lib/iarm.rb
58
+ - lib/iarm/client.rb
59
+ - lib/iarm/msg.rb
60
+ - lib/iarm/msg/channel_member.rb
61
+ - lib/iarm/msg/join.rb
62
+ - lib/iarm/msg/part.rb
63
+ - lib/iarm/msg/timeout.rb
64
+ - lib/iarm/msg/topic.rb
65
+ - lib/iarm/server.rb
66
+ - lib/iarm/timer.rb
67
+ - test/performance_test.rb
68
+ - test/test_iarm.rb
69
+ - Manifest
70
+ - ruby-iarm.gemspec
71
+ has_rdoc: true
72
+ homepage: ""
73
+ licenses: []
74
+
75
+ post_install_message:
76
+ rdoc_options:
77
+ - --line-numbers
78
+ - --inline-source
79
+ - --title
80
+ - Ruby-iarm
81
+ - --main
82
+ - README
83
+ require_paths:
84
+ - lib
85
+ required_ruby_version: !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ hash: 3
91
+ segments:
92
+ - 0
93
+ version: "0"
94
+ required_rubygems_version: !ruby/object:Gem::Requirement
95
+ none: false
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ hash: 11
100
+ segments:
101
+ - 1
102
+ - 2
103
+ version: "1.2"
104
+ requirements: []
105
+
106
+ rubyforge_project: ruby-iarm
107
+ rubygems_version: 1.3.7
108
+ signing_key:
109
+ specification_version: 3
110
+ summary: ""
111
+ test_files:
112
+ - test/test_iarm.rb
113
+ - test/performance_test.rb