em-irc 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -1
- data/.rvmrc +1 -1
- data/.travis.yml +6 -2
- data/Gemfile +5 -1
- data/Guardfile +8 -1
- data/README.md +33 -25
- data/Rakefile +9 -1
- data/lib/em-irc.rb +5 -0
- data/lib/em-irc/client.rb +62 -98
- data/lib/em-irc/commands.rb +358 -0
- data/lib/em-irc/dispatcher.rb +2 -6
- data/lib/em-irc/responses.rb +88 -0
- data/lib/em-irc/version.rb +1 -1
- data/lib/support/dsl_accessor.rb +22 -0
- data/spec/integration/integration_spec.rb +31 -11
- data/spec/lib/em-irc/client_spec.rb +44 -36
- data/spec/lib/em-irc/commands_spec.rb +166 -0
- data/spec/lib/em-irc/dispatcher_spec.rb +16 -16
- data/spec/lib/em-irc/responses_spec.rb +46 -0
- metadata +13 -6
data/lib/em-irc/version.rb
CHANGED
@@ -0,0 +1,22 @@
|
|
1
|
+
module DslAccessor
|
2
|
+
def self.included(base)
|
3
|
+
base.extend Macros
|
4
|
+
end
|
5
|
+
|
6
|
+
module Macros
|
7
|
+
def dsl_accessor(*keys)
|
8
|
+
keys.map(&:to_s).each do |k|
|
9
|
+
class_eval <<-ACCESSORS
|
10
|
+
attr_writer :#{k}
|
11
|
+
def #{k}(v = nil)
|
12
|
+
if v
|
13
|
+
@#{k} = v
|
14
|
+
else
|
15
|
+
@#{k}
|
16
|
+
end
|
17
|
+
end
|
18
|
+
ACCESSORS
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -19,11 +19,16 @@ end
|
|
19
19
|
|
20
20
|
shared_examples_for "integration" do
|
21
21
|
it 'should work' do
|
22
|
-
dan = client
|
23
|
-
bob = client
|
22
|
+
dan = client
|
23
|
+
bob = client
|
24
|
+
ace = client
|
24
25
|
|
25
|
-
dan.on(:connect) {dan.
|
26
|
-
bob.on(:connect) {bob.
|
26
|
+
dan.on(:connect) {dan.nick('dan')}
|
27
|
+
bob.on(:connect) {bob.nick('bob')}
|
28
|
+
ace.on(:connect) {ace.nick('bob')}
|
29
|
+
|
30
|
+
dan.on(:nick) {dan.join('#americano-test')}
|
31
|
+
bob.on(:nick) {bob.join('#americano-test')}
|
27
32
|
|
28
33
|
bob.on(:join) do |who, channel|
|
29
34
|
bob.message(channel, "dan: hello bob")
|
@@ -31,19 +36,34 @@ shared_examples_for "integration" do
|
|
31
36
|
# dan.quit
|
32
37
|
end
|
33
38
|
|
39
|
+
dan.on(:message) do |who, channel, message|
|
40
|
+
@who = who
|
41
|
+
@channel = channel
|
42
|
+
@message = message
|
43
|
+
end
|
44
|
+
|
34
45
|
EM.run {
|
35
46
|
dan.connect
|
36
|
-
|
37
|
-
|
47
|
+
bob.connect
|
48
|
+
ace.connect
|
49
|
+
EM::add_timer(2) {EM::stop}
|
38
50
|
}
|
39
51
|
|
40
52
|
# TODO: matchers for commands
|
53
|
+
dan.nick.should == 'dan'
|
41
54
|
dan.history.should =~ /Welcome/
|
42
55
|
dan.history.should =~ /JOIN :#americano-test/
|
43
|
-
dan.history.should =~ /dan: hello bob/
|
44
56
|
|
57
|
+
@who.should == "bob"
|
58
|
+
@channel.should == "#americano-test"
|
59
|
+
@message.should == "dan: hello bob"
|
60
|
+
|
61
|
+
bob.nick.should == 'bob'
|
45
62
|
bob.history.should =~ /Welcome/
|
46
63
|
bob.history.should =~ /JOIN :#americano-test/
|
64
|
+
|
65
|
+
ace.nick.should == nil
|
66
|
+
ace.history.should =~ /Nickname already in use/
|
47
67
|
end
|
48
68
|
end
|
49
69
|
|
@@ -51,8 +71,8 @@ end
|
|
51
71
|
describe EventMachine::IRC::Client, :integration => true do
|
52
72
|
let(:options) do
|
53
73
|
{
|
54
|
-
host
|
55
|
-
port
|
74
|
+
:host => '127.0.0.1',
|
75
|
+
:port => '16667'
|
56
76
|
}.merge(@options || {})
|
57
77
|
end
|
58
78
|
|
@@ -71,8 +91,8 @@ describe EventMachine::IRC::Client, :integration => true do
|
|
71
91
|
before :all do
|
72
92
|
raise "encrypted ircd not on :16697" unless `lsof -i :16697`.chomp.size > 1
|
73
93
|
@options = {
|
74
|
-
port
|
75
|
-
ssl
|
94
|
+
:port => '16697',
|
95
|
+
:ssl => true
|
76
96
|
}
|
77
97
|
end
|
78
98
|
it_behaves_like "integration"
|
@@ -26,16 +26,23 @@ describe EventMachine::IRC::Client do
|
|
26
26
|
subject.should_not be_connected
|
27
27
|
end
|
28
28
|
|
29
|
-
it 'should
|
30
|
-
subject.nick.
|
29
|
+
it 'should have blank nick' do
|
30
|
+
subject.nick.should be_blank
|
31
31
|
end
|
32
32
|
|
33
|
-
it 'should yield self' do
|
34
|
-
|
33
|
+
it 'should optionally yield self' do
|
34
|
+
described_class.new do |c|
|
35
35
|
c.should be_kind_of(described_class)
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
+
it 'should optionally instance eval' do
|
40
|
+
client = described_class.new do
|
41
|
+
host 'stella.net'
|
42
|
+
end
|
43
|
+
client.host.should == 'stella.net'
|
44
|
+
end
|
45
|
+
|
39
46
|
it 'should allow a custom logger' do
|
40
47
|
subject.logger = Logger.new(STDOUT)
|
41
48
|
subject.logger = nil
|
@@ -43,15 +50,15 @@ describe EventMachine::IRC::Client do
|
|
43
50
|
end
|
44
51
|
|
45
52
|
context 'connect' do
|
46
|
-
it 'should create an EM TCP connection with host, port, handler, and
|
47
|
-
EventMachine.should_receive(:connect).with('irc.net', '9999', EventMachine::IRC::Dispatcher, parent: subject)
|
53
|
+
it 'should create an EM TCP connection with host, port, handler, and ssl' do
|
54
|
+
EventMachine.should_receive(:connect).with('irc.net', '9999', EventMachine::IRC::Dispatcher, :parent => subject, :ssl => subject.ssl)
|
48
55
|
subject.host = 'irc.net'
|
49
56
|
subject.port = '9999'
|
50
57
|
subject.connect
|
51
58
|
end
|
52
59
|
|
53
60
|
it 'should be idempotent' do
|
54
|
-
EventMachine.stub(connect
|
61
|
+
EventMachine.stub(:connect => mock('Connection'))
|
55
62
|
EventMachine.should_receive(:connect).exactly(1).times
|
56
63
|
subject.connect
|
57
64
|
subject.connect
|
@@ -61,17 +68,17 @@ describe EventMachine::IRC::Client do
|
|
61
68
|
context 'send_data' do
|
62
69
|
before do
|
63
70
|
@connection = mock('Connection')
|
64
|
-
subject.stub(conn
|
65
|
-
subject.stub(connected
|
71
|
+
subject.stub(:conn => @connection)
|
72
|
+
subject.stub(:connected? => true)
|
66
73
|
end
|
67
74
|
|
68
75
|
it 'should return false if not connected' do
|
69
|
-
subject.stub(connected
|
76
|
+
subject.stub(:connected? => nil)
|
70
77
|
subject.send_data("NICK jch").should be_false
|
71
78
|
end
|
72
79
|
|
73
80
|
it 'should send message to irc server' do
|
74
|
-
subject.stub(conn
|
81
|
+
subject.stub(:conn => @connection)
|
75
82
|
@connection.should_receive(:send_data).with("NICK jch\r\n")
|
76
83
|
subject.send_data("NICK jch")
|
77
84
|
end
|
@@ -79,7 +86,7 @@ describe EventMachine::IRC::Client do
|
|
79
86
|
|
80
87
|
context 'ready' do
|
81
88
|
before do
|
82
|
-
subject.stub(conn
|
89
|
+
subject.stub(:conn => mock.as_null_object)
|
83
90
|
end
|
84
91
|
|
85
92
|
it 'should call :connect callback' do
|
@@ -122,48 +129,49 @@ describe EventMachine::IRC::Client do
|
|
122
129
|
parsed = subject.parse_message('PING :irc.net')
|
123
130
|
parsed[:params] =~ ['irc.net']
|
124
131
|
end
|
132
|
+
|
133
|
+
it 'should return strings as a single param' do
|
134
|
+
parsed = subject.parse_message(":irc.the.net 001 jessie :Welcome to the Internet Relay Network jessie!~jessie@localhost")
|
135
|
+
parsed[:params] =~ ['jessie', 'Welcome to the Internet Relay Network jessie!~jessie@localhost']
|
136
|
+
end
|
125
137
|
end
|
126
138
|
end
|
127
139
|
|
128
140
|
context 'receive_data' do
|
129
|
-
|
130
|
-
|
131
|
-
subject.stub(:handle_parsed_message).and_return(mock.as_null_object)
|
132
|
-
end
|
133
|
-
|
134
|
-
it 'should parse messages separated by \r\n' do
|
135
|
-
data = [
|
141
|
+
let(:data) {
|
142
|
+
[
|
136
143
|
":irc.the.net 001 jessie :Welcome to the Internet Relay Network jessie!~jessie@localhost",
|
137
144
|
":irc.the.net 002 jessie :Your host is irc.the.net, running version ngircd-17.1 (i386/apple/darwin11.2.0)",
|
138
145
|
":irc.the.net 003 jessie :This server has been started Fri Feb 03 2012 at 14:42:38 (PST)"
|
139
146
|
].join("\r\n")
|
147
|
+
}
|
148
|
+
let(:parsed_message) {mock.as_null_object}
|
149
|
+
|
150
|
+
before do
|
151
|
+
subject.stub(:parse_message).and_return(parsed_message)
|
152
|
+
subject.stub(:handle_parsed_message)
|
153
|
+
end
|
140
154
|
|
155
|
+
it 'should parse messages separated by \r\n' do
|
141
156
|
subject.should_receive(:parse_message).exactly(3).times
|
142
|
-
subject.should_receive(:handle_parsed_message).exactly(3).times
|
143
157
|
subject.receive_data(data)
|
144
158
|
end
|
145
159
|
|
146
160
|
it 'should handle parsed messages' do
|
161
|
+
subject.should_receive(:handle_parsed_message).exactly(3).times
|
162
|
+
subject.receive_data(data)
|
147
163
|
end
|
148
|
-
end
|
149
164
|
|
150
|
-
|
151
|
-
|
152
|
-
subject.
|
153
|
-
subject.handle_parsed_message({prefix: 'irc.net', command: 'PING', params: ['irc.net']})
|
165
|
+
it 'should trigger :raw callbacks' do
|
166
|
+
subject.should_receive(:trigger).with(:raw, parsed_message).exactly(3).times
|
167
|
+
subject.receive_data(data)
|
154
168
|
end
|
169
|
+
end
|
155
170
|
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
# subject.handle_parsed_message({prefix: 'jessie!jessie@localhost', command: 'PRIVMSG', params: ['#general', 'hello world'])
|
161
|
-
# end
|
162
|
-
|
163
|
-
it 'should trigger :raw callbacks' do
|
164
|
-
m = mock('Parsed Response').as_null_object
|
165
|
-
subject.should_receive(:trigger).with(:raw, m)
|
166
|
-
subject.handle_parsed_message(m)
|
171
|
+
context 'handle_parsed_message' do
|
172
|
+
it 'should trigger error for messages 400-599' do
|
173
|
+
subject.should_receive(:trigger).with(:error, 'ERR_NICKNAMEINUSE')
|
174
|
+
subject.handle_parsed_message(:command => '433', :params => ['ERR_NICKNAMEINUSE'])
|
167
175
|
end
|
168
176
|
end
|
169
177
|
|
@@ -0,0 +1,166 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe EventMachine::IRC::Commands do
|
4
|
+
subject {EventMachine::IRC::Client.new}
|
5
|
+
|
6
|
+
context "nick" do
|
7
|
+
it 'should set nick' do
|
8
|
+
subject.should_receive(:send_data).with("NICK jch")
|
9
|
+
subject.nick('jch')
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should get nick' do
|
13
|
+
subject.instance_eval {@nick = 'jch'}
|
14
|
+
subject.nick.should == 'jch'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context "join" do
|
19
|
+
it 'should join single channel' do
|
20
|
+
subject.should_receive(:send_data).with("JOIN #general")
|
21
|
+
subject.join("#general")
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should join multiple channels' do
|
25
|
+
subject.should_receive(:send_data).with("JOIN #general,#foo")
|
26
|
+
subject.join('#general', '#foo')
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should join channels with keys' do
|
30
|
+
subject.should_receive(:send_data).with("JOIN #foo,&bar fubar,bazly")
|
31
|
+
subject.join(['#foo', 'fubar'], ['&bar', 'bazly'])
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should join channels with and without keys' do
|
35
|
+
subject.should_receive(:send_data).with("JOIN #foo,#cat,&bar fubar,dog")
|
36
|
+
subject.join(['#foo', 'fubar'], '&bar', ["#cat", "dog"])
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should leave all currently joined channels' do
|
40
|
+
subject.should_receive(:join).with('0')
|
41
|
+
subject.join('0')
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context "part_all" do
|
46
|
+
it 'should leave all currently joined channels' do
|
47
|
+
subject.should_receive(:join).with('0')
|
48
|
+
subject.part_all
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "part" do
|
53
|
+
it 'should part single' do
|
54
|
+
subject.should_receive(:send_data).with('PART #general :Leaving...')
|
55
|
+
subject.part('#general')
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should part single with custom message' do
|
59
|
+
subject.should_receive(:send_data).with('PART #general :Goodbye everybody!')
|
60
|
+
subject.part('#general', 'Goodbye everybody!')
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'should part multiple' do
|
64
|
+
subject.should_receive(:send_data).with('PART #general,#foo :Leaving...')
|
65
|
+
subject.part('#general', '#foo')
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'should part multiple with custom message' do
|
69
|
+
subject.should_receive(:send_data).with('PART #general,#foo :Goodbye everybody!')
|
70
|
+
subject.part('#general', '#foo', 'Goodbye everybody!')
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context "topic" do
|
75
|
+
it 'should get topic' do
|
76
|
+
subject.should_receive(:send_data).with("TOPIC #foo")
|
77
|
+
subject.topic('#foo')
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'should set topic' do
|
81
|
+
subject.should_receive(:send_data).with("TOPIC #foo :awesome channel")
|
82
|
+
subject.topic('#foo', "awesome channel")
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'should unset topic' do
|
86
|
+
subject.should_receive(:send_data).with("TOPIC #foo :")
|
87
|
+
subject.topic('#foo', '')
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context "names" do
|
92
|
+
it 'should list names for a single channel' do
|
93
|
+
subject.should_receive(:send_data).with("NAMES #foo")
|
94
|
+
subject.names("#foo")
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'should list names for multiple channels' do
|
98
|
+
subject.should_receive(:send_data).with("NAMES #foo,&bar")
|
99
|
+
subject.names("#foo", "&bar")
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'should list names with a target server' do
|
103
|
+
subject.should_receive(:send_data).with("NAMES #foo,&bar irc.net")
|
104
|
+
subject.names("#foo", "&bar", :target => 'irc.net')
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
context "list" do
|
109
|
+
it 'should list channel and topic' do
|
110
|
+
subject.should_receive(:send_data).with("LIST #foo")
|
111
|
+
subject.list("#foo")
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'should list channels and topics for multiple channels' do
|
115
|
+
subject.should_receive(:send_data).with("LIST #foo,&bar")
|
116
|
+
subject.list("#foo", "&bar")
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'should list names with a target server' do
|
120
|
+
subject.should_receive(:send_data).with("LIST #foo,&bar irc.net")
|
121
|
+
subject.list("#foo", "&bar", :target => 'irc.net')
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
context 'invite' do
|
126
|
+
it 'should invite user to a channel' do
|
127
|
+
subject.should_receive(:send_data).with("INVITE jch #foo")
|
128
|
+
subject.invite("jch", "#foo")
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
context 'kick' do
|
133
|
+
it 'should kick user from channel' do
|
134
|
+
subject.should_receive(:send_data).with("KICK #general jch")
|
135
|
+
subject.kick("#general", "jch")
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'should kick multiple users from channels' do
|
139
|
+
subject.should_receive(:send_data).with("KICK #general,&bar jch,wcc")
|
140
|
+
subject.kick("#general", "&bar", "jch", "wcc")
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'should raise error if no channels' do
|
144
|
+
expect {
|
145
|
+
subject.kick('jch')
|
146
|
+
}.to raise_error(ArgumentError)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
context 'whois' do
|
151
|
+
it 'should query user' do
|
152
|
+
subject.should_receive(:send_data).with("WHOIS jch")
|
153
|
+
subject.whois('jch')
|
154
|
+
end
|
155
|
+
|
156
|
+
it 'should query users' do
|
157
|
+
subject.should_receive(:send_data).with("WHOIS jch,bar")
|
158
|
+
subject.whois('jch', 'bar')
|
159
|
+
end
|
160
|
+
|
161
|
+
it 'should query user from a target server' do
|
162
|
+
subject.should_receive(:send_data).with("WHOIS irc.net jch,bar")
|
163
|
+
subject.whois('jch', 'bar', :target => 'irc.net')
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
@@ -2,12 +2,14 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
shared_examples_for 'dispatcher' do
|
4
4
|
it 'should delegate connection methods to parent' do
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
$ssl
|
5
|
+
module TestServer
|
6
|
+
def post_init
|
7
|
+
send_data "message"
|
9
8
|
end
|
9
|
+
end
|
10
10
|
|
11
|
+
parent = Class.new do
|
12
|
+
attr_accessor :conn
|
11
13
|
def receive_data(data)
|
12
14
|
EventMachine::stop_event_loop
|
13
15
|
raise "Didn't get expected message" unless data == 'message'
|
@@ -16,11 +18,9 @@ shared_examples_for 'dispatcher' do
|
|
16
18
|
parent.should_receive(:ready)
|
17
19
|
parent.should_receive(:unbind)
|
18
20
|
EventMachine.run {
|
19
|
-
EventMachine::start_server('127.0.0.1', '198511',
|
20
|
-
EventMachine::connect('127.0.0.1', '198511'
|
21
|
-
|
22
|
-
end
|
23
|
-
EventMachine::add_timer(2) {
|
21
|
+
EventMachine::start_server('127.0.0.1', '198511', TestServer)
|
22
|
+
EventMachine::connect('127.0.0.1', '198511', EventMachine::IRC::Dispatcher, :parent => parent, :ssl => ssl)
|
23
|
+
EventMachine::add_timer(3) {
|
24
24
|
EventMachine::stop_event_loop
|
25
25
|
raise "Never reached receive_data or took too long"
|
26
26
|
}
|
@@ -30,15 +30,15 @@ end
|
|
30
30
|
|
31
31
|
describe EventMachine::IRC::Dispatcher do
|
32
32
|
context 'ssl integration' do
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
end
|
37
|
-
# it_behaves_like "dispatcher"
|
33
|
+
it "how to test ssl?"
|
34
|
+
# it_behaves_like "dispatcher" do
|
35
|
+
# let(:ssl) {true}
|
36
|
+
# end
|
38
37
|
end
|
39
38
|
|
40
39
|
context 'non-ssl integration' do
|
41
|
-
|
42
|
-
|
40
|
+
it_behaves_like "dispatcher" do
|
41
|
+
let(:ssl) {false}
|
42
|
+
end
|
43
43
|
end
|
44
44
|
end
|