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 CHANGED
@@ -1 +1 @@
1
- 0.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
@@ -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
- if connected?
39
- return @transport
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
- connect_response = wait_for_packet(CONNECTION_TIMEOUT)
56
- if connect_response.header == HEADER_SYSTEM and connect_response.action == ACTION_API_OK
57
- @connected = true
58
- SmartFox::Logger.info "SmartFox::Client successfully connected with transport #{@transport.inspect}"
59
- raise_event :connected, self
60
- else
61
- raise ApiIncompatibleError if connect_response.action == ACTION_API_OBSOLETE
62
- raise ConnectionFailureError.new "Did not recieve an expected response from the server."
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(:action => action, :r => room_id) do |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
@@ -7,7 +7,7 @@ class SmartFox::Packet
7
7
  @header = header
8
8
  @action = action
9
9
  @room = room
10
- @extra = extra
10
+ @data = extra
11
11
  end
12
12
 
13
13
  def self.parse(data)
@@ -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.error " #{e.inspect}"
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
- sleep
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 << @socket.readpartial(4096)
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.1.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
@@ -1,28 +1,44 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
2
 
3
- describe "Smartfox" do
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
- connection = SmartFox::Client.new(:server => 'localhost')
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
- connection = SmartFox::Client.new()
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
- connected = false
17
- connection = SmartFox::Client.new()
18
- connection.add_handler(:connected) { |connection| connected = true}
19
- connection.connect
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
- connection = SmartFox::Client.new(:server => '10.2.3.4')
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
@@ -4,6 +4,8 @@ require 'smartfox'
4
4
  require 'spec'
5
5
  require 'spec/autorun'
6
6
 
7
+ require File.join(File.dirname(__FILE__), 'waiter')
8
+
7
9
  SmartFox::Logger.level = Logger::DEBUG
8
10
 
9
11
  Spec::Runner.configure do |config|
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: 27
4
+ hash: 23
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 1
8
+ - 2
9
9
  - 0
10
- version: 0.1.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