q-ruby-driver 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,57 @@
1
+ module QRubyDriver
2
+
3
+ # Provided for legacy compatibility with previous versions of q-ruby-driver
4
+ # Usage of this class is deprecated
5
+ class QMessage
6
+
7
+ attr :message_type
8
+ attr :exception
9
+ attr :value
10
+ attr :message_type
11
+ attr :message
12
+ attr :timing
13
+
14
+ @exception = false
15
+
16
+ def create(value, sync = false)
17
+ @value = value
18
+ sync == true ? @message_type = :sync : @message_type = :async
19
+
20
+ start_time = Time.now
21
+
22
+ qio = QIO.new
23
+ qio.write_message(value, sync)
24
+ qio.pos=0
25
+ @message = qio.read
26
+ puts [@message].inspect
27
+ @length = @message.length
28
+ @timing = Time.now - start_time
29
+
30
+ self
31
+ end
32
+
33
+ # Decodes a binary message into a QMessage
34
+ def decode(message)
35
+ start_time= Time.now
36
+ @message = message
37
+ qio = QIO.new(@message)
38
+ begin
39
+ @length, @message_type = qio.message_header()
40
+ @value = qio.read_item()
41
+ rescue QException => qe
42
+ @exception = qe
43
+ end
44
+ end
45
+
46
+ def to_s
47
+ if @message.nil?
48
+ "QMessage [None]"
49
+ elsif !@exception.nil?
50
+ "QException [#{@message.unpack("H*")}] Type[#{@message_type}] Length [#{@length}] Value[#{@value}]"
51
+ else
52
+ "QMessage [#{@message.unpack("H*")}] Type[#{@message_type}] Length [#{@length}] Value[#{@value}]"
53
+ end
54
+ end
55
+
56
+ end
57
+ end
@@ -0,0 +1,186 @@
1
+ require File.expand_path(
2
+ File.join(File.dirname(__FILE__), %w[.. lib q-ruby-driver]))
3
+
4
+ include QRubyDriver
5
+
6
+ # RSpec tests require a live Q instance to which we can connect.
7
+ # See TestUnit tests for a self-contained test
8
+
9
+ describe QRubyDriver do
10
+
11
+ HOST, PORT = "localhost", 5001
12
+
13
+ it "should allow a basic Q connection to a Q instance" do
14
+ q_conn = QConnection.new HOST, PORT
15
+ q_conn.close
16
+ end
17
+
18
+ it "should allow us to send a hand crafted message via a Q connection with async" do
19
+ q_conn = QConnection.new HOST, PORT
20
+ q_conn.send_raw("01000000120000000a0004000000613a6062")
21
+ q_conn.close
22
+ end
23
+
24
+ it "should allow us to send a hand crafted message via a Q connection with sync" do
25
+ q_conn = QConnection.new HOST, PORT
26
+ q_conn.send_raw("01010000110000000a0003000000613a31")
27
+ q_conn.close
28
+ end
29
+
30
+ it "should allow us to get a String" do
31
+ q_conn = QConnection.new HOST, PORT
32
+ response = q_conn.get("2+2")
33
+ q_conn.close
34
+ response.should.== 4
35
+ puts response
36
+ end
37
+
38
+ it "should allow us to get a variable to set its value" do
39
+ q_conn = QConnection.new HOST, PORT
40
+ q_conn.get("a:`cheesy")
41
+ response = q_conn.get("a")
42
+ q_conn.close
43
+ response.should.== "cheesy"
44
+ end
45
+
46
+ it "should allow us to get a dictionary" do
47
+ q_conn = QConnection.new HOST, PORT
48
+ response = q_conn.get("`a`b!2 3")
49
+ q_conn.close
50
+ response.should.is_a? Hash
51
+ response.length.should.== 2
52
+ response["a"].should.== 2
53
+ response["b"].should.== 3
54
+
55
+ puts response.inspect
56
+ end
57
+
58
+ it "should allow us to get vectors" do
59
+ q_conn = QConnection.new HOST, PORT
60
+ q_conn.get("a:`IBM`GOOG`APPL")
61
+ response = q_conn.get("a")
62
+ q_conn.close
63
+ response.should.is_a? Array
64
+ response.length.should.== 3
65
+ response[0].should.== "IBM"
66
+ response[1].should.== "GOOG"
67
+ response[2].should.== "APPL"
68
+
69
+ puts response.inspect
70
+ end
71
+
72
+ it "should allow us to create a 1,000,000 trades" do
73
+ q_conn = QConnection.new HOST, PORT
74
+ q_conn.get("""trade:( []date:`date$();
75
+ time:`time$();
76
+ sym:`symbol$();
77
+ price:`float$();
78
+ size:`int$();
79
+ exchange:`symbol$();
80
+ c:()
81
+ );
82
+ """)
83
+ q_conn.get("portfolio:`IBM`GOOG`VOD`BA`AIB`MSFT`BOI / define variable portfolio")
84
+ q_conn.get("countries:(portfolio!`USA`USA`UK`USA`IRL`USA`IRL) / some countries")
85
+ q_conn.get("dts: .z.D-til 200 / A few dates")
86
+ q_conn.get("st: 09:30:00.000 / market open")
87
+ q_conn.get("et: 16:00:00.000 / market close")
88
+ q_conn.get("exchanges:`N`L`O`C / Some exchanges")
89
+ q_conn.get("n:1000000 / The number of trades to create")
90
+ response = q_conn.get("\\t insert[`trade;(n?dts;st+n?et-st;n?portfolio;n?100f;n?1000;n?exchanges;n?.Q.A,'reverse .Q.a)] / create 1m random trades")
91
+
92
+ puts "Created a million trades in #{response}ms"
93
+
94
+ response = q_conn.get("count trade")
95
+ response.should.== 1000000
96
+ q_conn.close
97
+ end
98
+
99
+ it "should allow us to select from the trades table" do
100
+ q_conn = QConnection.new HOST, PORT
101
+ response = q_conn.get("select max price by sym from trade")
102
+ response.keys[0].length.should.== 1
103
+
104
+ puts response.inspect
105
+
106
+ response[response.keys[0]]["price"].length.should.== 7
107
+ response.keys[0]["sym"].length.should.== 7
108
+ response.keys[0]["sym"].include?("GOOG").should.== true
109
+ response.keys[0]["sym"].include?("BA").should.== true
110
+ response.keys[0]["sym"].include?("BOI").should.== true
111
+ response.keys[0]["sym"].include?("IBM").should.== true
112
+ response.keys[0]["sym"].include?("MSFT").should.== true
113
+ response.keys[0]["sym"].include?("VOD").should.== true
114
+ response.keys[0]["sym"].include?("CHEESE").should.== false
115
+
116
+ q_conn.close
117
+ end
118
+
119
+ it "should support getting functions" do
120
+ q_conn = QConnection.new HOST, PORT
121
+ response = q_conn.get("show_me:{[something] show something;show_me};")
122
+ puts response
123
+ q_conn.get("show_me(`hello)")
124
+
125
+ q_conn.close
126
+ end
127
+
128
+
129
+ it "should return exceptions" do
130
+ q_conn = QConnection.new HOST, PORT
131
+ begin
132
+ q_conn.get("somethingmissing")
133
+ rescue QRubyDriver::QException => e
134
+ puts e.message
135
+ end
136
+ q_conn.close
137
+ end
138
+
139
+ it "should be able to handle multi-type dictionaries" do
140
+ q_conn = QConnection.new HOST, PORT
141
+ response = q_conn.get("(`b,1)!(`a,3)")
142
+ q_conn.close
143
+ response.should.is_a? Hash
144
+ response.length.should.== 2
145
+
146
+ response["b"].should.== "a"
147
+ response[1].should.== 3
148
+
149
+ end
150
+
151
+ it "should be able to handle lists" do
152
+ q_conn = QConnection.new HOST, PORT
153
+ response = q_conn.get("enlist til 5")
154
+ q_conn.close
155
+ response.should.is_a? Array
156
+ response.length.should.== 1
157
+ response[0].should.is_a? Array
158
+
159
+ response[0][0].should.== 0
160
+ response[0][1].should.== 1
161
+ response[0][2].should.== 2
162
+ response[0][3].should.== 3
163
+ response[0][4].should.== 4
164
+
165
+ end
166
+
167
+ it "should support character arrays" do
168
+ q_conn = QConnection.new HOST, PORT
169
+ response = q_conn.get("\"hello\"")
170
+ q_conn.close
171
+
172
+ response.should.is_a? Array
173
+ response.length.should.== 5
174
+ response.should.== "hello"
175
+ end
176
+
177
+ # it "should be able to support arrays as parameters" do
178
+ # q_conn = QConnection.new HOST, PORT
179
+ # values = ["a",1,2,3,4,5]
180
+ # response = q_conn.get(values)
181
+ # response.length.should.==values
182
+ # q_conn.close
183
+ # end
184
+
185
+ end
186
+
@@ -0,0 +1,17 @@
1
+
2
+ require File.expand_path(
3
+ File.join(File.dirname(__FILE__), %w[.. lib q-ruby-driver-ruby-driver]))
4
+
5
+ include QRubyDriver
6
+
7
+ Spec::Runner.configure do |config|
8
+ # == Mock Framework
9
+ #
10
+ # RSpec uses it's own mocking framework by default. If you prefer to
11
+ # use mocha, flexmock or RR, uncomment the appropriate line:
12
+ #
13
+ # config.mock_with :mocha
14
+ # config.mock_with :flexmock
15
+ # config.mock_with :rr
16
+ end
17
+
@@ -0,0 +1,63 @@
1
+ require './test_helper'
2
+ require 'test/unit'
3
+ require 'socket'
4
+
5
+ # Tests the socket connection using a dummy server
6
+ # which gives a spoof response
7
+
8
+ class TestConnection < Test::Unit::TestCase
9
+
10
+ HOST = "127.0.0.1"
11
+ @@port = 12300
12
+
13
+ def setup
14
+ @@serv = TCPServer.new(HOST, @@port)
15
+ end
16
+
17
+ def teardown
18
+ @@conn.close
19
+ @@port += 1
20
+ end
21
+
22
+ # helper methods
23
+ def string_to_int_array(str)
24
+ str.each_byte.map{|x| x}
25
+ end
26
+
27
+ def test_async
28
+ request = "Hello world!"
29
+ Thread.new() {
30
+ # listen for initial connection
31
+ s = @@serv.accept
32
+ s.recv(100)
33
+ s.write ['0','1','0','2'].pack('H4')
34
+ sleep(1) # wait a bit
35
+ expected = [1, 0, 0, 0, request.length+14, 0, 0, 0, 10, 0, request.length, 0, 0, 0] + string_to_int_array(request)
36
+ assert_equal(expected,string_to_int_array(s.recv(100)))
37
+ }
38
+ @@conn=QRubyDriver::QConnection.new(HOST, @@port)
39
+ @@conn.set(request)
40
+ sleep(5)
41
+ assert(true)
42
+ end
43
+
44
+ def test_sync
45
+ request = "Hello world!"
46
+ obj = ["foo",[91,34],["bar",[true,false,true]]]
47
+ Thread.new() {
48
+ # listen for initial connection
49
+ s = @@serv.accept
50
+ s.recv(100)
51
+ s.write ['0','1','0','2'].pack('H4')
52
+ sleep(1) # wait a bit
53
+ expected = [1, 1, 0, 0, request.length+14, 0, 0, 0, 10, 0, request.length, 0, 0, 0] + string_to_int_array(request)
54
+ assert_equal(expected,string_to_int_array(s.recv(100)))
55
+ qio=QRubyDriver::QIO.new()
56
+ qio.write_message(obj, :response)
57
+ qio.pos=0
58
+ s.write qio.read
59
+ }
60
+ @@conn=QRubyDriver::QConnection.new(HOST, @@port)
61
+ assert_equal(obj, @@conn.get(request))
62
+ end
63
+ end
@@ -0,0 +1,15 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+
4
+ begin
5
+ Bundler.setup(:default, :development)
6
+ rescue Bundler::BundlerError => e
7
+ $stderr.puts e.message
8
+ $stderr.puts "Run `bundle install` to install missing gems"
9
+ exit e.status_code
10
+ end
11
+
12
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
13
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
14
+
15
+ require 'q-ruby-driver'
data/test/test_io.rb ADDED
@@ -0,0 +1,167 @@
1
+ require './test_helper'
2
+ require 'test/unit'
3
+
4
+ # These tests do round-trip integration testing
5
+ # (i.e. write then read) on the QIO class
6
+ class TestIO < Test::Unit::TestCase
7
+
8
+ def setup
9
+ @qio=QRubyDriver::QIO.new()
10
+ end
11
+
12
+ # helper methods
13
+ def string_to_int_array(str)
14
+ str.each_byte.map{|x| x}
15
+ end
16
+
17
+ def test_string
18
+ str="This is a %1$5#!@-ing string"
19
+
20
+ expected=[1, 0, 0, 0, str.length+14, 0, 0, 0, 10, 0, 28, 0, 0, 0]+string_to_int_array(str)
21
+
22
+ @qio.write_message(str)
23
+ @qio.pos=0
24
+ assert_equal(expected, string_to_int_array(@qio.read()))
25
+ @qio.pos=0
26
+ assert_equal(str, @qio.read_message())
27
+ end
28
+
29
+ def test_int
30
+ int=1
31
+
32
+ expected=[1, 0, 0, 0, 13, 0, 0, 0, 250, 1, 0, 0, 0]
33
+
34
+ @qio.write_message(int)
35
+ @qio.pos=0
36
+ assert_equal(expected, string_to_int_array(@qio.read()))
37
+ @qio.pos=0
38
+ assert_equal(int, @qio.read_message())
39
+ end
40
+
41
+ def test_float
42
+ float=3.68423
43
+
44
+ expected=[1, 0, 0, 0, 17, 0, 0, 0, 247, 97, 137, 7, 148, 77, 121, 13, 64]
45
+
46
+ @qio.write_message(float)
47
+ @qio.pos=0
48
+ assert_equal(expected, string_to_int_array(@qio.read()))
49
+ @qio.pos=0
50
+ assert_equal(float, @qio.read_message())
51
+ end
52
+
53
+ def test_symbol
54
+ sym="This is a %1$5#!@-ing symbol".to_sym
55
+ expected=[1, 0, 0, 0, 38, 0, 0, 0, 245]+string_to_int_array(sym.to_s)+[0]
56
+
57
+ @qio.write_message(sym)
58
+ @qio.pos=0
59
+ assert_equal(expected, string_to_int_array(@qio.read()))
60
+ @qio.pos=0
61
+ assert_equal(sym.to_s, @qio.read_message())
62
+ end
63
+
64
+ def test_exception
65
+ exc=Exception.new("This is an exception")
66
+ expected=[1, 0, 0, 0, 30, 0, 0, 0, 128]+string_to_int_array(exc.message)+[0]
67
+
68
+ @qio.write_message(exc)
69
+ @qio.pos=0
70
+ assert_equal(expected, string_to_int_array(@qio.read()))
71
+ @qio.pos=0
72
+
73
+ # should throw the exception when reading
74
+ begin
75
+ @qio.read_message()
76
+ rescue QRubyDriver::QException => qe
77
+ assert_equal(exc.message, qe.message)
78
+ end
79
+ end
80
+
81
+ def test_int_vector
82
+ vector=[1,3]
83
+
84
+ expected=[1, 0, 0, 0, 22, 0, 0, 0, 6, 0, 2, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0]
85
+
86
+ @qio.write_message(vector)
87
+ @qio.pos=0
88
+ assert_equal(expected, string_to_int_array(@qio.read()))
89
+ @qio.pos=0
90
+ assert_equal(vector, @qio.read_message())
91
+ end
92
+
93
+ def test_bool_vector
94
+ vector=[true,false,true]
95
+
96
+ msg_header=[1, 0, 0, 0, 17, 0, 0, 0]
97
+ bool_vector = [1, 0, 3, 0, 0, 0, 1, 0, 1]
98
+ expected=msg_header+bool_vector
99
+
100
+ @qio.write_message(vector)
101
+ @qio.pos=0
102
+ assert_equal(expected, string_to_int_array(@qio.read()))
103
+ @qio.pos=0
104
+ assert_equal(vector, @qio.read_message())
105
+ end
106
+
107
+ def test_symbol_vector
108
+ vector=[:symbol1, :symbol2, :symbol3]
109
+
110
+ msg_header = [1, 0, 0, 0, 38, 0, 0, 0]
111
+ sym_vector = [11, 0, 3, 0, 0, 0]+vector.map{|x| string_to_int_array(x.to_s)+[0]}.flatten
112
+ expected=msg_header+sym_vector
113
+
114
+ @qio.write_message(vector)
115
+ @qio.pos=0
116
+ assert_equal(expected, string_to_int_array(@qio.read()))
117
+ @qio.pos=0
118
+ assert_equal(vector.map{|x|x.to_s}, @qio.read_message())
119
+ end
120
+
121
+ def test_list
122
+ list=["foo",[91,34],["bar",[true,false,true]]]
123
+
124
+ msg_header = [1, 0, 0, 0, 61, 0, 0, 0]
125
+ list1_header = [0, 0, 3, 0, 0, 0]
126
+ string_foo = [10, 0, 3, 0, 0, 0] + string_to_int_array("foo")
127
+ int_vector = [6, 0, 2, 0, 0, 0, 91, 0, 0, 0, 34, 0, 0, 0]
128
+ list2_header = [0, 0, 2, 0, 0, 0]
129
+ string_bar = [10, 0, 3, 0, 0, 0] + string_to_int_array("bar")
130
+ bool_vector = [1, 0, 3, 0, 0, 0, 1, 0, 1]
131
+ expected=msg_header+list1_header+string_foo+int_vector+list2_header+string_bar+bool_vector
132
+
133
+ @qio.write_message(list)
134
+ @qio.pos=0
135
+ assert_equal(expected, string_to_int_array(@qio.read()))
136
+ @qio.pos=0
137
+ assert_equal(list, @qio.read_message())
138
+ end
139
+
140
+ def test_flip
141
+ hash={:foo => [91,34, 20], :bar => [true,false,true], :woof => ["beatles","stones","zeppelin"], :meow => [:how, :now, :cow]}
142
+
143
+ msg_header = [1, 0, 0, 0, 131, 0, 0, 0]
144
+ flip_dict_header = [98, 0, 99]
145
+ sym_vector_1 = [11, 0, 4, 0, 0, 0] + hash.keys.map{|x|string_to_int_array(x.to_s)+[0]}.flatten
146
+ list_header = [0, 0, 4, 0, 0, 0]
147
+ int_vector = [6, 0, 3, 0, 0, 0, 91, 0, 0, 0, 34, 0, 0, 0, 20, 0, 0, 0]
148
+ bool_vector = [1, 0, 3, 0, 0, 0, 1, 0, 1]
149
+ # I'm not actually sure if "char_vector_list" is the correct IPC format
150
+ # but it is my expectation for now until I learn otherwise
151
+ char_vector_list = [0, 0, 3, 0, 0, 0] + hash[:woof].map{|x| [10, 0, x.length, 0, 0, 0] + string_to_int_array(x.to_s)}.flatten
152
+ sym_vector_2 = [11, 0, 3, 0, 0, 0] + hash[:meow].map{|x|string_to_int_array(x.to_s)+[0]}.flatten
153
+
154
+ expected = msg_header + flip_dict_header + sym_vector_1 + list_header + int_vector + bool_vector + char_vector_list + sym_vector_2
155
+
156
+ @qio.write_message(hash)
157
+ @qio.pos=0
158
+ assert_equal(expected, string_to_int_array(@qio.read()))
159
+ @qio.pos=0
160
+
161
+ expected_hash={}
162
+ hash.keys.each {|k| expected_hash[k.to_s]=hash[k]}
163
+ expected_hash['meow'] = expected_hash['meow'].map{|x|x.to_s}
164
+
165
+ assert_equal(expected_hash, @qio.read_message())
166
+ end
167
+ end