smartfox 0.0.0 → 0.1.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/LICENSE +1 -1
- data/Rakefile +4 -0
- data/VERSION +1 -1
- data/lib/smartfox.rb +13 -0
- data/lib/smartfox/blue_box.rb +3 -0
- data/lib/smartfox/blue_box/connection.rb +2 -0
- data/lib/smartfox/client.rb +97 -0
- data/lib/smartfox/packet.rb +41 -0
- data/lib/smartfox/socket.rb +3 -0
- data/lib/smartfox/socket/connection.rb +71 -0
- data/nbproject/configs/Spec.properties +1 -0
- data/nbproject/private/config.properties +1 -0
- data/nbproject/private/configs/Spec.properties +1 -0
- data/nbproject/private/private.properties +3 -0
- data/nbproject/private/private.xml +4 -0
- data/nbproject/private/rake-d.txt +0 -0
- data/nbproject/project.properties +8 -0
- data/nbproject/project.xml +15 -0
- data/smartfox.gemspec +75 -0
- data/spec/smartfox_spec.rb +24 -2
- data/spec/spec_helper.rb +2 -0
- metadata +46 -3
data/LICENSE
CHANGED
data/Rakefile
CHANGED
@@ -11,6 +11,8 @@ begin
|
|
11
11
|
gem.homepage = "http://github.com/penwellr/smartfox"
|
12
12
|
gem.authors = ["Richard Penwell"]
|
13
13
|
gem.add_development_dependency "rspec", ">= 1.2.9"
|
14
|
+
gem.add_dependency 'json'
|
15
|
+
gem.add_dependency 'builder'
|
14
16
|
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
15
17
|
end
|
16
18
|
Jeweler::GemcutterTasks.new
|
@@ -43,3 +45,5 @@ Rake::RDocTask.new do |rdoc|
|
|
43
45
|
rdoc.rdoc_files.include('README*')
|
44
46
|
rdoc.rdoc_files.include('lib/**/*.rb')
|
45
47
|
end
|
48
|
+
|
49
|
+
task :gem => :build
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.1.0
|
data/lib/smartfox.rb
CHANGED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
module SmartFox
|
5
|
+
class SmartFoxError < Exception; end
|
6
|
+
|
7
|
+
autoload :Client, 'smartfox/client'
|
8
|
+
autoload :Socket, 'smartfox/socket'
|
9
|
+
autoload :BlueBox, 'smartfox/blue_box'
|
10
|
+
autoload :Packet, 'smartfox/packet'
|
11
|
+
|
12
|
+
Logger = Logger.new(STDOUT)
|
13
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'builder'
|
2
|
+
require 'libxml'
|
3
|
+
|
4
|
+
class SmartFox::Client
|
5
|
+
class ConnectionFailureError < SmartFox::SmartFoxError; end
|
6
|
+
class ApiIncompatibleError < SmartFox::SmartFoxError; end
|
7
|
+
class TransportTimeoutError < SmartFox::SmartFoxError; end
|
8
|
+
|
9
|
+
attr_reader :connected, :room_list, :buddy_list, :server, :port
|
10
|
+
alias_method :connected?, :connected
|
11
|
+
|
12
|
+
CLIENT_VERSION = "1.5.8"
|
13
|
+
CONNECTION_TIMEOUT = 5
|
14
|
+
TRANSPORTS = [ SmartFox::Socket::Connection, SmartFox::BlueBox::Connection ]
|
15
|
+
|
16
|
+
HEADER_SYSTEM = 'sys'
|
17
|
+
ACTION_VERSION_CHECK = 'verChk'
|
18
|
+
ACTION_API_OK = 'apiOK'
|
19
|
+
ACTION_API_OBSOLETE = 'apiKO'
|
20
|
+
|
21
|
+
def initialize(options = {})
|
22
|
+
@room_list = {}
|
23
|
+
@connected = false
|
24
|
+
@buddy_list = []
|
25
|
+
@user_id = options[:user_id]
|
26
|
+
@user_name = options[:user_name]
|
27
|
+
@server = options[:server] || 'localhost'
|
28
|
+
@port = options[:port]
|
29
|
+
@events = {}
|
30
|
+
end
|
31
|
+
|
32
|
+
def connect()
|
33
|
+
unless @connected
|
34
|
+
TRANSPORTS.each do |transport_class|
|
35
|
+
begin
|
36
|
+
@transport = transport_class.new(self)
|
37
|
+
@transport.connect
|
38
|
+
if connected?
|
39
|
+
return @transport
|
40
|
+
end
|
41
|
+
rescue
|
42
|
+
end
|
43
|
+
end
|
44
|
+
raise ConnectionFailureError.new "Could not negotiate any transport with server."
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def add_handler(event, &proc)
|
49
|
+
@events[event.to_sym] = [] unless @events[event.to_sym]
|
50
|
+
@events[event.to_sym] << proc
|
51
|
+
end
|
52
|
+
|
53
|
+
def connect_succeeded
|
54
|
+
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."
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
def send_packet(header, action, room_id = 0)
|
68
|
+
xml = Builder::XmlMarkup.new()
|
69
|
+
xml.msg(:t => header) do |msg|
|
70
|
+
msg.body(:action => action, :r => room_id) do |body|
|
71
|
+
yield body
|
72
|
+
end
|
73
|
+
end
|
74
|
+
packet = xml.target!
|
75
|
+
SmartFox::Logger.info "SmartFox::Client#send_packet -> #{packet}"
|
76
|
+
@transport.send_data(packet + "\0")
|
77
|
+
end
|
78
|
+
|
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
|
+
def raise_event(event_name, *params)
|
91
|
+
event = @events[event_name.to_sym]
|
92
|
+
return unless event
|
93
|
+
event.each do |event_handler|
|
94
|
+
event_handler.call(*params)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'libxml'
|
2
|
+
|
3
|
+
class SmartFox::Packet
|
4
|
+
attr_reader :action, :header, :room, :data
|
5
|
+
|
6
|
+
def initialize(header, action, room = 0, extra = nil)
|
7
|
+
@header = header
|
8
|
+
@action = action
|
9
|
+
@room = room
|
10
|
+
@extra = extra
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.parse(data)
|
14
|
+
case data[0, 1]
|
15
|
+
when '<'
|
16
|
+
return parse_xml(data)
|
17
|
+
when '{'
|
18
|
+
return parse_json(data)
|
19
|
+
else
|
20
|
+
return parse_string(data)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.parse_xml(data)
|
25
|
+
SmartFox::Logger.debug "SmartFox::Packet#parse_xml('#{data}')"
|
26
|
+
document = LibXML::XML::Parser.string(data).parse
|
27
|
+
header = document.root['t']
|
28
|
+
action = document.root.child['action']
|
29
|
+
room = document.root.child['r']
|
30
|
+
extra = document.root.child.child
|
31
|
+
new(header, action, room, extra)
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.parse_json(data)
|
35
|
+
SmartFox::Logger.debug "SmartFox::Packet#parse_json('#{data}')"
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.parse_string(data)
|
39
|
+
SmartFox::Logger.debug "SmartFox::Packet#parse_string('#{data}')"
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
class SmartFox::Socket::Connection
|
4
|
+
DEFAULT_PORT = 9339
|
5
|
+
attr_reader :connected
|
6
|
+
alias_method :connected?, :connected
|
7
|
+
|
8
|
+
def initialize(client)
|
9
|
+
@client = client
|
10
|
+
@connected = false
|
11
|
+
@event_thread = nil
|
12
|
+
@disconnecting = false
|
13
|
+
@buffer = String.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def connect
|
17
|
+
begin
|
18
|
+
SmartFox::Logger.info "SmartFox::Socket::Connection#connect began"
|
19
|
+
@socket = TCPSocket.new(@client.server, @client.port || DEFAULT_PORT)
|
20
|
+
# SmartFox sockets will send a cross domain policy, receive this packet
|
21
|
+
# before we do anything else or the connection will hang
|
22
|
+
@socket.readpartial(4096)
|
23
|
+
@connected = true
|
24
|
+
@event_thread = Thread.start(self) { |connection| connection.event_loop }
|
25
|
+
@client.connect_succeeded
|
26
|
+
|
27
|
+
rescue => e
|
28
|
+
SmartFox::Logger.error "In SmartFox::Socket::Connection#connect:"
|
29
|
+
SmartFox::Logger.error " #{e.inspect}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def disconnect
|
34
|
+
@disconnecting = true
|
35
|
+
while @connected
|
36
|
+
sleep
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def send_data(data)
|
41
|
+
@socket.write(data)
|
42
|
+
end
|
43
|
+
|
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
|
+
def event_loop
|
55
|
+
SmartFox::Logger.info "SmartFox::Socket::Connection#event_loop began"
|
56
|
+
ticks = 0
|
57
|
+
until @disconnecting
|
58
|
+
SmartFox::Logger.debug "SmartFox::Socket::Connection#event_loop tick #{ticks}"
|
59
|
+
|
60
|
+
@buffer << @socket.readpartial(4096)
|
61
|
+
|
62
|
+
ticks += 1
|
63
|
+
end
|
64
|
+
|
65
|
+
@connected = false
|
66
|
+
end
|
67
|
+
|
68
|
+
def inspect
|
69
|
+
"#<#{self.class.name}:#{object_id} server:#{@client.server} port:#{@client.port || DEFAULT_PORT}>"
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
main.file=spec/spec_helper.rb
|
@@ -0,0 +1 @@
|
|
1
|
+
config=Spec
|
@@ -0,0 +1 @@
|
|
1
|
+
work.dir=/Users/penwellr/Development/smartfox
|
File without changes
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<project xmlns="http://www.netbeans.org/ns/project/1">
|
3
|
+
<type>org.netbeans.modules.ruby.rubyproject</type>
|
4
|
+
<configuration>
|
5
|
+
<data xmlns="http://www.netbeans.org/ns/ruby-project/1">
|
6
|
+
<name>smartfox</name>
|
7
|
+
<source-roots>
|
8
|
+
<root id="src.dir"/>
|
9
|
+
</source-roots>
|
10
|
+
<test-roots>
|
11
|
+
<root id="test.src.dir"/>
|
12
|
+
</test-roots>
|
13
|
+
</data>
|
14
|
+
</configuration>
|
15
|
+
</project>
|
data/smartfox.gemspec
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{smartfox}
|
8
|
+
s.version = "0.1.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Richard Penwell"]
|
12
|
+
s.date = %q{2010-06-07}
|
13
|
+
s.description = %q{Provides a client library for the SmartFox realtime communication server, including BlueBox extensions.}
|
14
|
+
s.email = %q{self@richardpenwell.me}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".gitignore",
|
22
|
+
"LICENSE",
|
23
|
+
"README.rdoc",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION",
|
26
|
+
"lib/smartfox.rb",
|
27
|
+
"lib/smartfox/blue_box.rb",
|
28
|
+
"lib/smartfox/blue_box/connection.rb",
|
29
|
+
"lib/smartfox/client.rb",
|
30
|
+
"lib/smartfox/packet.rb",
|
31
|
+
"lib/smartfox/socket.rb",
|
32
|
+
"lib/smartfox/socket/connection.rb",
|
33
|
+
"nbproject/configs/Spec.properties",
|
34
|
+
"nbproject/private/config.properties",
|
35
|
+
"nbproject/private/configs/Spec.properties",
|
36
|
+
"nbproject/private/private.properties",
|
37
|
+
"nbproject/private/private.xml",
|
38
|
+
"nbproject/private/rake-d.txt",
|
39
|
+
"nbproject/project.properties",
|
40
|
+
"nbproject/project.xml",
|
41
|
+
"smartfox.gemspec",
|
42
|
+
"spec/smartfox_spec.rb",
|
43
|
+
"spec/spec.opts",
|
44
|
+
"spec/spec_helper.rb"
|
45
|
+
]
|
46
|
+
s.homepage = %q{http://github.com/penwellr/smartfox}
|
47
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
48
|
+
s.require_paths = ["lib"]
|
49
|
+
s.rubygems_version = %q{1.3.7}
|
50
|
+
s.summary = %q{Client library for SmartFoxServer}
|
51
|
+
s.test_files = [
|
52
|
+
"spec/smartfox_spec.rb",
|
53
|
+
"spec/spec_helper.rb"
|
54
|
+
]
|
55
|
+
|
56
|
+
if s.respond_to? :specification_version then
|
57
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
58
|
+
s.specification_version = 3
|
59
|
+
|
60
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
61
|
+
s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
|
62
|
+
s.add_runtime_dependency(%q<json>, [">= 0"])
|
63
|
+
s.add_runtime_dependency(%q<builder>, [">= 0"])
|
64
|
+
else
|
65
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
66
|
+
s.add_dependency(%q<json>, [">= 0"])
|
67
|
+
s.add_dependency(%q<builder>, [">= 0"])
|
68
|
+
end
|
69
|
+
else
|
70
|
+
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
71
|
+
s.add_dependency(%q<json>, [">= 0"])
|
72
|
+
s.add_dependency(%q<builder>, [">= 0"])
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
data/spec/smartfox_spec.rb
CHANGED
@@ -1,7 +1,29 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
2
|
|
3
3
|
describe "Smartfox" do
|
4
|
-
it "
|
5
|
-
|
4
|
+
it "should create a connection" do
|
5
|
+
connection = SmartFox::Client.new(:server => 'localhost')
|
6
|
+
connection.connect
|
6
7
|
end
|
8
|
+
|
9
|
+
it "should be connected after calling connect" do
|
10
|
+
connection = SmartFox::Client.new()
|
11
|
+
connection.connect
|
12
|
+
connection.connected?.should be_true
|
13
|
+
end
|
14
|
+
|
15
|
+
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
|
21
|
+
end
|
22
|
+
|
23
|
+
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)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should fall back on BlueBox if needed"
|
7
29
|
end
|
data/spec/spec_helper.rb
CHANGED
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: 27
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
+
- 1
|
8
9
|
- 0
|
9
|
-
|
10
|
-
version: 0.0.0
|
10
|
+
version: 0.1.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Richard Penwell
|
@@ -34,6 +34,34 @@ dependencies:
|
|
34
34
|
version: 1.2.9
|
35
35
|
type: :development
|
36
36
|
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: json
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ">="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 3
|
46
|
+
segments:
|
47
|
+
- 0
|
48
|
+
version: "0"
|
49
|
+
type: :runtime
|
50
|
+
version_requirements: *id002
|
51
|
+
- !ruby/object:Gem::Dependency
|
52
|
+
name: builder
|
53
|
+
prerelease: false
|
54
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
hash: 3
|
60
|
+
segments:
|
61
|
+
- 0
|
62
|
+
version: "0"
|
63
|
+
type: :runtime
|
64
|
+
version_requirements: *id003
|
37
65
|
description: Provides a client library for the SmartFox realtime communication server, including BlueBox extensions.
|
38
66
|
email: self@richardpenwell.me
|
39
67
|
executables: []
|
@@ -51,6 +79,21 @@ files:
|
|
51
79
|
- Rakefile
|
52
80
|
- VERSION
|
53
81
|
- lib/smartfox.rb
|
82
|
+
- lib/smartfox/blue_box.rb
|
83
|
+
- lib/smartfox/blue_box/connection.rb
|
84
|
+
- lib/smartfox/client.rb
|
85
|
+
- lib/smartfox/packet.rb
|
86
|
+
- lib/smartfox/socket.rb
|
87
|
+
- lib/smartfox/socket/connection.rb
|
88
|
+
- nbproject/configs/Spec.properties
|
89
|
+
- nbproject/private/config.properties
|
90
|
+
- nbproject/private/configs/Spec.properties
|
91
|
+
- nbproject/private/private.properties
|
92
|
+
- nbproject/private/private.xml
|
93
|
+
- nbproject/private/rake-d.txt
|
94
|
+
- nbproject/project.properties
|
95
|
+
- nbproject/project.xml
|
96
|
+
- smartfox.gemspec
|
54
97
|
- spec/smartfox_spec.rb
|
55
98
|
- spec/spec.opts
|
56
99
|
- spec/spec_helper.rb
|