smartfox 0.1.0 → 0.2.0
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/VERSION +1 -1
- data/lib/smartfox.rb +9 -0
- data/lib/smartfox/client.rb +68 -22
- data/lib/smartfox/packet.rb +1 -1
- data/lib/smartfox/socket/connection.rb +13 -13
- data/smartfox.gemspec +5 -3
- data/spec/smartfox_spec.rb +29 -13
- data/spec/spec_helper.rb +2 -0
- data/spec/waiter.rb +25 -0
- metadata +5 -3
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/lib/smartfox.rb
CHANGED
@@ -10,4 +10,13 @@ module SmartFox
|
|
10
10
|
autoload :Packet, 'smartfox/packet'
|
11
11
|
|
12
12
|
Logger = Logger.new(STDOUT)
|
13
|
+
|
14
|
+
class << Logger
|
15
|
+
def exception(exception)
|
16
|
+
error exception.message
|
17
|
+
exception.backtrace.each do |line|
|
18
|
+
error " #{line}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
13
22
|
end
|
data/lib/smartfox/client.rb
CHANGED
@@ -12,11 +12,14 @@ class SmartFox::Client
|
|
12
12
|
CLIENT_VERSION = "1.5.8"
|
13
13
|
CONNECTION_TIMEOUT = 5
|
14
14
|
TRANSPORTS = [ SmartFox::Socket::Connection, SmartFox::BlueBox::Connection ]
|
15
|
+
EVENTS = [ :connected, :logged_in ]
|
15
16
|
|
16
17
|
HEADER_SYSTEM = 'sys'
|
17
18
|
ACTION_VERSION_CHECK = 'verChk'
|
18
19
|
ACTION_API_OK = 'apiOK'
|
19
20
|
ACTION_API_OBSOLETE = 'apiKO'
|
21
|
+
ACTION_LOGIN = 'login'
|
22
|
+
ACTION_LOGIN_OK = 'logOK'
|
20
23
|
|
21
24
|
def initialize(options = {})
|
22
25
|
@room_list = {}
|
@@ -33,11 +36,15 @@ class SmartFox::Client
|
|
33
36
|
unless @connected
|
34
37
|
TRANSPORTS.each do |transport_class|
|
35
38
|
begin
|
39
|
+
started_at = Time.now
|
36
40
|
@transport = transport_class.new(self)
|
37
41
|
@transport.connect
|
38
|
-
|
39
|
-
|
42
|
+
|
43
|
+
while not connected? and Time.now <= started_at + CONNECTION_TIMEOUT
|
44
|
+
Thread.pass
|
40
45
|
end
|
46
|
+
|
47
|
+
return @transport if connected?
|
41
48
|
rescue
|
42
49
|
end
|
43
50
|
end
|
@@ -45,6 +52,13 @@ class SmartFox::Client
|
|
45
52
|
end
|
46
53
|
end
|
47
54
|
|
55
|
+
def disconnect
|
56
|
+
if @connected
|
57
|
+
@transport.disconnect
|
58
|
+
@connected = false
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
48
62
|
def add_handler(event, &proc)
|
49
63
|
@events[event.to_sym] = [] unless @events[event.to_sym]
|
50
64
|
@events[event.to_sym] << proc
|
@@ -52,22 +66,43 @@ class SmartFox::Client
|
|
52
66
|
|
53
67
|
def connect_succeeded
|
54
68
|
send_packet(HEADER_SYSTEM, ACTION_VERSION_CHECK) { |x| x.ver(:v => CLIENT_VERSION.delete('.')) }
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
69
|
+
end
|
70
|
+
|
71
|
+
def packet_recieved(data)
|
72
|
+
begin
|
73
|
+
SmartFox::Logger.debug "SmartFox::Client#packet_recieved('#{data}')"
|
74
|
+
packet = SmartFox::Packet.parse(data)
|
75
|
+
case packet.header
|
76
|
+
when HEADER_SYSTEM
|
77
|
+
if @connected
|
78
|
+
handle_system_packet(packet)
|
79
|
+
else
|
80
|
+
complete_login(packet)
|
81
|
+
end
|
82
|
+
when HEADER_EXTENSION
|
83
|
+
|
84
|
+
end
|
85
|
+
rescue => e
|
86
|
+
SmartFox::Logger.error "In SmartFox::Client#packet_received"
|
87
|
+
SmartFox::Logger.exception e
|
63
88
|
end
|
64
89
|
end
|
65
90
|
|
91
|
+
def login(zone, username, password = nil)
|
92
|
+
send_packet(HEADER_SYSTEM, ACTION_LOGIN) do |packet|
|
93
|
+
packet.login(:z => zone) do |login|
|
94
|
+
login.nick { |nick| nick.cdata! username }
|
95
|
+
login.pword { |pword| pword.cdata! password || '' }
|
96
|
+
end
|
97
|
+
end
|
98
|
+
Thread.pass
|
99
|
+
end
|
100
|
+
|
66
101
|
private
|
67
102
|
def send_packet(header, action, room_id = 0)
|
68
103
|
xml = Builder::XmlMarkup.new()
|
69
104
|
xml.msg(:t => header) do |msg|
|
70
|
-
msg.body(:
|
105
|
+
msg.body(:r => room_id, :action => action) do |body|
|
71
106
|
yield body
|
72
107
|
end
|
73
108
|
end
|
@@ -76,17 +111,6 @@ class SmartFox::Client
|
|
76
111
|
@transport.send_data(packet + "\0")
|
77
112
|
end
|
78
113
|
|
79
|
-
def wait_for_packet(timeout)
|
80
|
-
begin_at = Time.now
|
81
|
-
while Time.now <= (begin_at + timeout)
|
82
|
-
if @transport.data_available?
|
83
|
-
return SmartFox::Packet.parse(@transport.read_packet)
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
raise TransportTimeoutError
|
88
|
-
end
|
89
|
-
|
90
114
|
def raise_event(event_name, *params)
|
91
115
|
event = @events[event_name.to_sym]
|
92
116
|
return unless event
|
@@ -94,4 +118,26 @@ class SmartFox::Client
|
|
94
118
|
event_handler.call(*params)
|
95
119
|
end
|
96
120
|
end
|
121
|
+
|
122
|
+
def complete_login(connect_response)
|
123
|
+
if connect_response.header == HEADER_SYSTEM and connect_response.action == ACTION_API_OK
|
124
|
+
@connected = true
|
125
|
+
SmartFox::Logger.info "SmartFox::Client successfully connected with transport #{@transport.inspect}"
|
126
|
+
raise_event :connected, self
|
127
|
+
else
|
128
|
+
raise ApiIncompatibleError if connect_response.action == ACTION_API_OBSOLETE
|
129
|
+
raise ConnectionFailureError.new "Did not recieve an expected response from the server."
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def handle_system_packet(packet)
|
134
|
+
case packet.action
|
135
|
+
when ACTION_LOGIN_OK
|
136
|
+
@username = packet.data['n']
|
137
|
+
@moderator = packet.data['mod'] != "0"
|
138
|
+
@id = packet.data['id']
|
139
|
+
SmartFox::Logger.info "SmartFox::Client logged in as #{@username}"
|
140
|
+
raise_event(:logged_in, self)
|
141
|
+
end
|
142
|
+
end
|
97
143
|
end
|
data/lib/smartfox/packet.rb
CHANGED
@@ -11,6 +11,7 @@ class SmartFox::Socket::Connection
|
|
11
11
|
@event_thread = nil
|
12
12
|
@disconnecting = false
|
13
13
|
@buffer = String.new
|
14
|
+
@packets = []
|
14
15
|
end
|
15
16
|
|
16
17
|
def connect
|
@@ -26,39 +27,38 @@ class SmartFox::Socket::Connection
|
|
26
27
|
|
27
28
|
rescue => e
|
28
29
|
SmartFox::Logger.error "In SmartFox::Socket::Connection#connect:"
|
29
|
-
SmartFox::Logger.
|
30
|
+
SmartFox::Logger.exception e
|
31
|
+
raise e
|
30
32
|
end
|
31
33
|
end
|
32
34
|
|
33
35
|
def disconnect
|
34
36
|
@disconnecting = true
|
35
37
|
while @connected
|
36
|
-
|
38
|
+
Thread.pass
|
37
39
|
end
|
40
|
+
@socket.close if @socket
|
38
41
|
end
|
39
42
|
|
40
43
|
def send_data(data)
|
41
44
|
@socket.write(data)
|
42
45
|
end
|
43
46
|
|
44
|
-
def data_available?
|
45
|
-
not @buffer.empty?
|
46
|
-
end
|
47
|
-
|
48
|
-
def read_packet
|
49
|
-
packet = @buffer
|
50
|
-
@buffer = ""
|
51
|
-
return packet
|
52
|
-
end
|
53
|
-
|
54
47
|
def event_loop
|
55
48
|
SmartFox::Logger.info "SmartFox::Socket::Connection#event_loop began"
|
56
49
|
ticks = 0
|
57
50
|
until @disconnecting
|
58
51
|
SmartFox::Logger.debug "SmartFox::Socket::Connection#event_loop tick #{ticks}"
|
59
52
|
|
60
|
-
@buffer
|
53
|
+
@buffer += @socket.readpartial(4096)
|
54
|
+
|
55
|
+
while index = @buffer.index("\0")
|
56
|
+
@packets << @buffer.slice!(0..index)
|
57
|
+
end
|
61
58
|
|
59
|
+
@packets.each { |packet| @client.packet_recieved(packet) }
|
60
|
+
@packets.clear
|
61
|
+
|
62
62
|
ticks += 1
|
63
63
|
end
|
64
64
|
|
data/smartfox.gemspec
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{smartfox}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.2.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Richard Penwell"]
|
@@ -41,7 +41,8 @@ Gem::Specification.new do |s|
|
|
41
41
|
"smartfox.gemspec",
|
42
42
|
"spec/smartfox_spec.rb",
|
43
43
|
"spec/spec.opts",
|
44
|
-
"spec/spec_helper.rb"
|
44
|
+
"spec/spec_helper.rb",
|
45
|
+
"spec/waiter.rb"
|
45
46
|
]
|
46
47
|
s.homepage = %q{http://github.com/penwellr/smartfox}
|
47
48
|
s.rdoc_options = ["--charset=UTF-8"]
|
@@ -50,7 +51,8 @@ Gem::Specification.new do |s|
|
|
50
51
|
s.summary = %q{Client library for SmartFoxServer}
|
51
52
|
s.test_files = [
|
52
53
|
"spec/smartfox_spec.rb",
|
53
|
-
"spec/spec_helper.rb"
|
54
|
+
"spec/spec_helper.rb",
|
55
|
+
"spec/waiter.rb"
|
54
56
|
]
|
55
57
|
|
56
58
|
if s.respond_to? :specification_version then
|
data/spec/smartfox_spec.rb
CHANGED
@@ -1,28 +1,44 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
2
|
|
3
|
-
describe
|
3
|
+
describe SmartFox::Client do
|
4
|
+
before(:each) do
|
5
|
+
@connection = SmartFox::Client.new()
|
6
|
+
end
|
7
|
+
|
8
|
+
after(:each) do
|
9
|
+
@connection.disconnect if @connection
|
10
|
+
end
|
11
|
+
|
4
12
|
it "should create a connection" do
|
5
|
-
|
6
|
-
connection.connect
|
13
|
+
login_waiter = Waiter.new
|
14
|
+
@connection.connect
|
7
15
|
end
|
8
16
|
|
9
17
|
it "should be connected after calling connect" do
|
10
|
-
|
11
|
-
connection.connect
|
12
|
-
connection.connected?.should be_true
|
18
|
+
login_waiter = Waiter.new
|
19
|
+
@connection.connect
|
20
|
+
@connection.connected?.should be_true
|
13
21
|
end
|
14
22
|
|
15
23
|
it "should raise the 'connected' event after connecting" do
|
16
|
-
|
17
|
-
connection
|
18
|
-
connection.
|
19
|
-
|
20
|
-
connected.should be_true
|
24
|
+
connected_waiter = Waiter.new
|
25
|
+
@connection.add_handler(:connected) { |connection| puts "connected_waiter fired"; connected_waiter.fire }
|
26
|
+
@connection.connect
|
27
|
+
connected_waiter.wait
|
21
28
|
end
|
22
29
|
|
23
30
|
it "should fail when attempting to connect to a non-existant server" do
|
24
|
-
|
25
|
-
lambda { connection.connect }.should raise_error(SmartFox::Client::ConnectionFailureError)
|
31
|
+
TCPSocket.should_receive(:new).once.with('localhost', 9339).and_raise(Errno::ETIMEDOUT)
|
32
|
+
lambda { @connection.connect }.should raise_error(SmartFox::Client::ConnectionFailureError)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should login to the default install" do
|
36
|
+
login_waiter = Waiter.new
|
37
|
+
@connection.add_handler(:connected) { |connection| connection.login 'simpleChat', 'penwellr' }
|
38
|
+
@connection.add_handler(:logged_in) { login_waiter.fire }
|
39
|
+
@connection.connect
|
40
|
+
|
41
|
+
login_waiter.wait(10)
|
26
42
|
end
|
27
43
|
|
28
44
|
it "should fall back on BlueBox if needed"
|
data/spec/spec_helper.rb
CHANGED
data/spec/waiter.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
class Waiter
|
2
|
+
class TimeoutError < Exception; end
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@fired = false
|
6
|
+
end
|
7
|
+
|
8
|
+
def fire
|
9
|
+
@fired = true
|
10
|
+
Thread.pass
|
11
|
+
end
|
12
|
+
|
13
|
+
def wait(timeout = 5)
|
14
|
+
started_at = Time.now
|
15
|
+
while Time.now <= started_at + timeout
|
16
|
+
if @fired
|
17
|
+
return
|
18
|
+
else
|
19
|
+
Thread.pass
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
raise TimeoutError
|
24
|
+
end
|
25
|
+
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: smartfox
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 23
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 2
|
9
9
|
- 0
|
10
|
-
version: 0.
|
10
|
+
version: 0.2.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Richard Penwell
|
@@ -97,6 +97,7 @@ files:
|
|
97
97
|
- spec/smartfox_spec.rb
|
98
98
|
- spec/spec.opts
|
99
99
|
- spec/spec_helper.rb
|
100
|
+
- spec/waiter.rb
|
100
101
|
has_rdoc: true
|
101
102
|
homepage: http://github.com/penwellr/smartfox
|
102
103
|
licenses: []
|
@@ -134,3 +135,4 @@ summary: Client library for SmartFoxServer
|
|
134
135
|
test_files:
|
135
136
|
- spec/smartfox_spec.rb
|
136
137
|
- spec/spec_helper.rb
|
138
|
+
- spec/waiter.rb
|