em-irc 0.0.1 → 0.0.2
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.
- 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
|