smartfox 0.2.0 → 0.2.1

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.2.0
1
+ 0.2.1
@@ -1,5 +1,6 @@
1
1
  require 'builder'
2
2
  require 'libxml'
3
+ require 'json'
3
4
 
4
5
  class SmartFox::Client
5
6
  class ConnectionFailureError < SmartFox::SmartFoxError; end
@@ -7,19 +8,27 @@ class SmartFox::Client
7
8
  class TransportTimeoutError < SmartFox::SmartFoxError; end
8
9
 
9
10
  attr_reader :connected, :room_list, :buddy_list, :server, :port
11
+ attr_reader :current_room
10
12
  alias_method :connected?, :connected
11
13
 
12
14
  CLIENT_VERSION = "1.5.8"
13
15
  CONNECTION_TIMEOUT = 5
14
16
  TRANSPORTS = [ SmartFox::Socket::Connection, SmartFox::BlueBox::Connection ]
15
- EVENTS = [ :connected, :logged_in ]
17
+ EVENTS = [ :connected, :logged_in, :rooms_updated ]
16
18
 
17
19
  HEADER_SYSTEM = 'sys'
20
+ HEADER_EXTENDED = 'xt'
21
+
18
22
  ACTION_VERSION_CHECK = 'verChk'
19
23
  ACTION_API_OK = 'apiOK'
20
24
  ACTION_API_OBSOLETE = 'apiKO'
21
25
  ACTION_LOGIN = 'login'
22
26
  ACTION_LOGIN_OK = 'logOK'
27
+ ACTION_AUTO_JOIN = 'autoJoin'
28
+ ACTION_UPDATE_ROOMS = 'getRmList'
29
+ ACTION_ROOM_LIST = 'rmList'
30
+
31
+ EXTENDED_RESPONSE = 'xtRes'
23
32
 
24
33
  def initialize(options = {})
25
34
  @room_list = {}
@@ -30,6 +39,7 @@ class SmartFox::Client
30
39
  @server = options[:server] || 'localhost'
31
40
  @port = options[:port]
32
41
  @events = {}
42
+ @rooms = []
33
43
  end
34
44
 
35
45
  def connect()
@@ -59,13 +69,30 @@ class SmartFox::Client
59
69
  end
60
70
  end
61
71
 
72
+ def extended_command(action, data = nil, room = -1, options = {})
73
+ options[:format] ||= :xml
74
+
75
+ case options[:format]
76
+ when :xml
77
+ send_packet(HEADER_EXTENDED, action, room) { |x| x.cdata!(data || '') }
78
+ end
79
+ end
80
+
62
81
  def add_handler(event, &proc)
63
82
  @events[event.to_sym] = [] unless @events[event.to_sym]
64
83
  @events[event.to_sym] << proc
65
84
  end
66
85
 
86
+ def refresh_rooms
87
+ send_packet(HEADER_SYSTEM, ACTION_UPDATE_ROOMS)
88
+ end
89
+
67
90
  def connect_succeeded
68
- send_packet(HEADER_SYSTEM, ACTION_VERSION_CHECK) { |x| x.ver(:v => CLIENT_VERSION.delete('.')) }
91
+ send_packet(HEADER_SYSTEM, ACTION_VERSION_CHECK, 0) { |x| x.ver(:v => CLIENT_VERSION.delete('.')) }
92
+ end
93
+
94
+ def auto_join
95
+ send_packet(HEADER_SYSTEM, ACTION_AUTO_JOIN)
69
96
  end
70
97
 
71
98
  def packet_recieved(data)
@@ -80,7 +107,7 @@ class SmartFox::Client
80
107
  complete_login(packet)
81
108
  end
82
109
  when HEADER_EXTENSION
83
-
110
+ raise_event :extended_response, self, packet.data
84
111
  end
85
112
  rescue => e
86
113
  SmartFox::Logger.error "In SmartFox::Client#packet_received"
@@ -98,19 +125,40 @@ class SmartFox::Client
98
125
  Thread.pass
99
126
  end
100
127
 
128
+ def send_extended(extension, action, options)
129
+ options[:format] ||= :xml
130
+ options[:room] ||= (current_room ? current_room.id : 0)
131
+ options[:parameters] ||= {}
132
+
133
+ case options[:format]
134
+ when :xml
135
+
136
+ when :json
137
+ send_extended_json_packet(extension, action, options[:room], options[:parameters])
138
+ end
139
+ end
140
+
101
141
  private
102
- def send_packet(header, action, room_id = 0)
142
+ def send_packet(header, action, room_id = -1)
103
143
  xml = Builder::XmlMarkup.new()
104
144
  xml.msg(:t => header) do |msg|
105
145
  msg.body(:r => room_id, :action => action) do |body|
106
- yield body
146
+ if block_given?
147
+ yield body
148
+ end
107
149
  end
108
150
  end
109
151
  packet = xml.target!
110
- SmartFox::Logger.info "SmartFox::Client#send_packet -> #{packet}"
152
+ SmartFox::Logger.debug "SmartFox::Client#send_packet -> #{packet}"
111
153
  @transport.send_data(packet + "\0")
112
154
  end
113
155
 
156
+ def send_extended_json_packet(name, action, room, object)
157
+ packet = { :t => 'xt', :b => { :x => name, :c => action, :r => room, :p => object } }
158
+ SmartFox::Logger.debug "SmartFox::Client#send_extended_json_packet -> #{packet.to_json}"
159
+ @transport.send_data(packet.to_json + "\0")
160
+ end
161
+
114
162
  def raise_event(event_name, *params)
115
163
  event = @events[event_name.to_sym]
116
164
  return unless event
@@ -138,6 +186,15 @@ class SmartFox::Client
138
186
  @id = packet.data['id']
139
187
  SmartFox::Logger.info "SmartFox::Client logged in as #{@username}"
140
188
  raise_event(:logged_in, self)
189
+ when ACTION_ROOM_LIST
190
+ @rooms.clear
191
+ packet.data.children.each do |room|
192
+ @rooms << SmartFox::Room.parse(room)
193
+ end
194
+
195
+ raise_event(:rooms_updated, self, @rooms)
141
196
  end
142
197
  end
198
+
199
+
143
200
  end
@@ -6,8 +6,8 @@ class SmartFox::Packet
6
6
  def initialize(header, action, room = 0, extra = nil)
7
7
  @header = header
8
8
  @action = action
9
- @room = room
10
- @data = extra
9
+ @room = room.to_i
10
+ @data = (@header == SmartFox::Client::HEADER_EXTENDED ? SmartFox::Packet.parse_extended(extra) : extra)
11
11
  end
12
12
 
13
13
  def self.parse(data)
@@ -22,7 +22,7 @@ class SmartFox::Packet
22
22
  end
23
23
 
24
24
  def self.parse_xml(data)
25
- SmartFox::Logger.debug "SmartFox::Packet#parse_xml('#{data}')"
25
+ SmartFox::Logger.debug "SmartFox::Packet.parse_xml('#{data}')"
26
26
  document = LibXML::XML::Parser.string(data).parse
27
27
  header = document.root['t']
28
28
  action = document.root.child['action']
@@ -32,10 +32,42 @@ class SmartFox::Packet
32
32
  end
33
33
 
34
34
  def self.parse_json(data)
35
- SmartFox::Logger.debug "SmartFox::Packet#parse_json('#{data}')"
35
+ SmartFox::Logger.debug "SmartFox::Packet.parse_json('#{data}')"
36
36
  end
37
37
 
38
38
  def self.parse_string(data)
39
- SmartFox::Logger.debug "SmartFox::Packet#parse_string('#{data}')"
39
+ SmartFox::Logger.debug "SmartFox::Packet.parse_string('#{data}')"
40
+ end
41
+
42
+ def self.parse_extended(data)
43
+ SmartFox::Logger.debug "SmartFox::Packet.parse_extended('#{data}')"
44
+ document = LibXML::XML::Parser.string(data.content).parse
45
+ parse_extended_object document.root
46
+ end
47
+
48
+ def self.parse_extended_scaler(node)
49
+ case node['t']
50
+ when 'n'
51
+ node.content.to_i
52
+ when 's'
53
+ node.content
54
+ when 'b'
55
+ node.content == "1"
56
+ end
57
+ end
58
+
59
+ def self.parse_extended_object(node)
60
+ result = {}
61
+
62
+ node.children.each do |child|
63
+ case child.name
64
+ when 'var'
65
+ result[child["n"].to_sym] = parse_extended_scaler(child)
66
+ when 'obj'
67
+ result[child["o"].to_sym] = parse_extended_object(child)
68
+ end
69
+ end
70
+
71
+ result
40
72
  end
41
73
  end
@@ -0,0 +1,3 @@
1
+ class SmartFox::Room
2
+
3
+ end
@@ -50,14 +50,21 @@ class SmartFox::Socket::Connection
50
50
  until @disconnecting
51
51
  SmartFox::Logger.debug "SmartFox::Socket::Connection#event_loop tick #{ticks}"
52
52
 
53
- @buffer += @socket.readpartial(4096)
53
+ buffer = String.new
54
+ begin
55
+ @socket.read_nonblock(4096, buffer)
56
+
57
+ @buffer += buffer if buffer
54
58
 
55
- while index = @buffer.index("\0")
56
- @packets << @buffer.slice!(0..index)
57
- end
59
+ while index = @buffer.index("\0")
60
+ @packets << @buffer.slice!(0..index)
61
+ end
58
62
 
59
- @packets.each { |packet| @client.packet_recieved(packet) }
60
- @packets.clear
63
+ @packets.each { |packet| @client.packet_recieved(packet) }
64
+ @packets.clear
65
+ rescue => e
66
+ Thread.pass
67
+ end
61
68
 
62
69
  ticks += 1
63
70
  end
data/smartfox.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{smartfox}
8
- s.version = "0.2.0"
8
+ s.version = "0.2.1"
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"]
12
- s.date = %q{2010-06-07}
12
+ s.date = %q{2010-06-08}
13
13
  s.description = %q{Provides a client library for the SmartFox realtime communication server, including BlueBox extensions.}
14
14
  s.email = %q{self@richardpenwell.me}
15
15
  s.extra_rdoc_files = [
@@ -28,6 +28,7 @@ Gem::Specification.new do |s|
28
28
  "lib/smartfox/blue_box/connection.rb",
29
29
  "lib/smartfox/client.rb",
30
30
  "lib/smartfox/packet.rb",
31
+ "lib/smartfox/room.rb",
31
32
  "lib/smartfox/socket.rb",
32
33
  "lib/smartfox/socket/connection.rb",
33
34
  "nbproject/configs/Spec.properties",
@@ -39,6 +40,7 @@ Gem::Specification.new do |s|
39
40
  "nbproject/project.properties",
40
41
  "nbproject/project.xml",
41
42
  "smartfox.gemspec",
43
+ "spec/packet_spec.rb",
42
44
  "spec/smartfox_spec.rb",
43
45
  "spec/spec.opts",
44
46
  "spec/spec_helper.rb",
@@ -50,7 +52,8 @@ Gem::Specification.new do |s|
50
52
  s.rubygems_version = %q{1.3.7}
51
53
  s.summary = %q{Client library for SmartFoxServer}
52
54
  s.test_files = [
53
- "spec/smartfox_spec.rb",
55
+ "spec/packet_spec.rb",
56
+ "spec/smartfox_spec.rb",
54
57
  "spec/spec_helper.rb",
55
58
  "spec/waiter.rb"
56
59
  ]
@@ -0,0 +1,26 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe SmartFox::Packet do
4
+ it "should parse system packets" do
5
+ SYSTEM_PACKET = "<msg t='sys'><body action='apiOK' r='0'></body></msg>\0"
6
+
7
+ packet = SmartFox::Packet.parse(SYSTEM_PACKET)
8
+
9
+ packet.header.should == SmartFox::Client::HEADER_SYSTEM
10
+ packet.action.should == SmartFox::Client::ACTION_API_OK
11
+ packet.room.should == 0
12
+ packet.data.should be_nil
13
+ end
14
+
15
+ it "should parse extended packets" do
16
+ EXTENDED_PACKET = "<msg t='xt'><body action='xtRes' r='-1'><![CDATA[<dataObj><var n='uid' t='n'>481942</var><var n='_cmd' t='s'>logOK</var><obj o='ent' t='a'><var n='chat_priv' t='s'>1</var><var n='init_block' t='s'>1</var><var n='chat_join_rooms' t='s'>10</var><var n='chat_occupancy' t='s'>200</var><var n='init_chat' t='s'>1</var><var n='chat_admin' t='s'>0</var></obj></dataObj>]]></body></msg>\0"
17
+
18
+ packet = SmartFox::Packet.parse(EXTENDED_PACKET)
19
+
20
+ packet.header.should == SmartFox::Client::HEADER_EXTENDED
21
+ packet.action.should == SmartFox::Client::EXTENDED_RESPONSE
22
+ packet.room.should == -1
23
+ packet.data.should == { :uid => 481942, :_cmd => 'logOK', :ent => { :chat_priv => "1", :init_block => "1", :chat_join_rooms => "10", :chat_occupancy => "200", :init_chat => "1", :chat_admin => "0" } }
24
+
25
+ end
26
+ end
@@ -1,6 +1,27 @@
1
+ require 'json'
2
+
1
3
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
4
 
3
5
  describe SmartFox::Client do
6
+ def transport
7
+ @connection.instance_variable_get(:@transport)
8
+ end
9
+
10
+ def expect_send_data
11
+ transport.should_receive(:send_data) do |data|
12
+ yield data
13
+ end
14
+ end
15
+
16
+ def login_to_connection
17
+ login_waiter = Waiter.new
18
+ @connection.add_handler(:connected) { |connection| connection.login 'simpleChat', 'penwellr' }
19
+ @connection.add_handler(:logged_in) { login_waiter.fire }
20
+ @connection.connect
21
+
22
+ login_waiter.wait(10)
23
+ end
24
+
4
25
  before(:each) do
5
26
  @connection = SmartFox::Client.new()
6
27
  end
@@ -40,6 +61,27 @@ describe SmartFox::Client do
40
61
 
41
62
  login_waiter.wait(10)
42
63
  end
64
+
65
+ it "should properly serialize extended json packets" do
66
+ @connection.connect
67
+ expect_send_data do |data|
68
+ object = JSON.parse(data.chop)
69
+ object.should == { "t" => 'xt', "b" => { "x" => 'ManChatXT', "c" => 'grlbk', "r" => 6, "p" => {} } }
70
+ end
71
+
72
+ @connection.send_extended('ManChatXT', 'grlbk', :room => 6, :format => :json)
73
+ end
74
+
75
+ it "should retrieve the room list asyncronously" do
76
+ login_to_connection
77
+ updated_waiter = Waiter.new
78
+ room_list = nil
79
+ @connection.add_handler(:rooms_updated) { |client, rooms| room_list = rooms; updated_waiter.fire }
80
+ @connection.refresh_rooms
81
+ updated_waiter.wait
82
+
83
+ room_list.should_not be_blank
84
+ end
43
85
 
44
86
  it "should fall back on BlueBox if needed"
45
87
  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: 23
4
+ hash: 21
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 2
9
- - 0
10
- version: 0.2.0
9
+ - 1
10
+ version: 0.2.1
11
11
  platform: ruby
12
12
  authors:
13
13
  - Richard Penwell
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-06-07 00:00:00 -04:00
18
+ date: 2010-06-08 00:00:00 -04:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -83,6 +83,7 @@ files:
83
83
  - lib/smartfox/blue_box/connection.rb
84
84
  - lib/smartfox/client.rb
85
85
  - lib/smartfox/packet.rb
86
+ - lib/smartfox/room.rb
86
87
  - lib/smartfox/socket.rb
87
88
  - lib/smartfox/socket/connection.rb
88
89
  - nbproject/configs/Spec.properties
@@ -94,6 +95,7 @@ files:
94
95
  - nbproject/project.properties
95
96
  - nbproject/project.xml
96
97
  - smartfox.gemspec
98
+ - spec/packet_spec.rb
97
99
  - spec/smartfox_spec.rb
98
100
  - spec/spec.opts
99
101
  - spec/spec_helper.rb
@@ -133,6 +135,7 @@ signing_key:
133
135
  specification_version: 3
134
136
  summary: Client library for SmartFoxServer
135
137
  test_files:
138
+ - spec/packet_spec.rb
136
139
  - spec/smartfox_spec.rb
137
140
  - spec/spec_helper.rb
138
141
  - spec/waiter.rb