q-ruby-driver 1.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.
@@ -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