opentick-ruby 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,6 @@
1
+ 0.1.1
2
+ * RDoc documentation added
3
+ * Cancel commands released
4
+ * Performance optimizations
5
+ * method_missing added for AbstractMessage classes
6
+ * session_id auto assignment for any outgoing message
data/README ADDED
@@ -0,0 +1,16 @@
1
+ Ruby implementation for Opentick API. Copyright(c) 2007 Adigamov Timur (timur at adigamov dot com)
2
+
3
+ License
4
+
5
+ This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.
6
+
7
+ This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
8
+
9
+ You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
10
+ Use At Your Own Risk
11
+
12
+ As the license indicates, this code is provided AS IS, WITH NO WARRANTY WHATSOEVER, not even an implied warranty of merchantability or fitness for a particular purpose.
13
+
14
+ *ANY USE YOU MAKE OF THIS CODE IS ENTIRELY AT YOUR OWN RISK.*
15
+
16
+ This code may contain any number of errors or bugs, both known and unknown. Use of this code may result in monetary loss due to known or unknown bugs and errors. In no event shall the author be liable or responsible for any loss whatsoever, direct or indirect, that may occur as a result of your use of this code.
@@ -0,0 +1 @@
1
+ Thu, 08 Nov 2007 00:59:41 +0300
@@ -0,0 +1,49 @@
1
+
2
+ <?xml version="1.0" encoding="iso-8859-1"?>
3
+ <!DOCTYPE html
4
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
5
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
6
+
7
+ <!--
8
+
9
+ Classes
10
+
11
+ -->
12
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
13
+ <head>
14
+ <title>Classes</title>
15
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
16
+ <link rel="stylesheet" href="rdoc-style.css" type="text/css" />
17
+ <base target="docwin" />
18
+ </head>
19
+ <body>
20
+ <div id="index">
21
+ <h1 class="section-bar">Classes</h1>
22
+ <div id="index-entries">
23
+ <a href="classes/OpenTick.html">OpenTick</a><br />
24
+ <a href="classes/OpenTick/Debug.html">OpenTick::Debug</a><br />
25
+ <a href="classes/OpenTick/IncomingMessages.html">OpenTick::IncomingMessages</a><br />
26
+ <a href="classes/OpenTick/IncomingMessages/AbstractMessage.html">OpenTick::IncomingMessages::AbstractMessage</a><br />
27
+ <a href="classes/OpenTick/IncomingMessages/ErrorResponse.html">OpenTick::IncomingMessages::ErrorResponse</a><br />
28
+ <a href="classes/OpenTick/IncomingMessages/HistoryStreamResponse.html">OpenTick::IncomingMessages::HistoryStreamResponse</a><br />
29
+ <a href="classes/OpenTick/IncomingMessages/ListExchangesResponse.html">OpenTick::IncomingMessages::ListExchangesResponse</a><br />
30
+ <a href="classes/OpenTick/IncomingMessages/LoginResponse.html">OpenTick::IncomingMessages::LoginResponse</a><br />
31
+ <a href="classes/OpenTick/IncomingMessages/LogoutReponse.html">OpenTick::IncomingMessages::LogoutReponse</a><br />
32
+ <a href="classes/OpenTick/IncomingMessages/TickStreamResponse.html">OpenTick::IncomingMessages::TickStreamResponse</a><br />
33
+ <a href="classes/OpenTick/OpenTick.html">OpenTick::OpenTick</a><br />
34
+ <a href="classes/OpenTick/OutgoingMessages.html">OpenTick::OutgoingMessages</a><br />
35
+ <a href="classes/OpenTick/OutgoingMessages/AbstractMessage.html">OpenTick::OutgoingMessages::AbstractMessage</a><br />
36
+ <a href="classes/OpenTick/OutgoingMessages/CancelHistDataRequest.html">OpenTick::OutgoingMessages::CancelHistDataRequest</a><br />
37
+ <a href="classes/OpenTick/OutgoingMessages/CancelTickStreamRequest.html">OpenTick::OutgoingMessages::CancelTickStreamRequest</a><br />
38
+ <a href="classes/OpenTick/OutgoingMessages/HeartBeat.html">OpenTick::OutgoingMessages::HeartBeat</a><br />
39
+ <a href="classes/OpenTick/OutgoingMessages/HistTicksRequest.html">OpenTick::OutgoingMessages::HistTicksRequest</a><br />
40
+ <a href="classes/OpenTick/OutgoingMessages/HistoryStreamRequest.html">OpenTick::OutgoingMessages::HistoryStreamRequest</a><br />
41
+ <a href="classes/OpenTick/OutgoingMessages/ListExchangesRequest.html">OpenTick::OutgoingMessages::ListExchangesRequest</a><br />
42
+ <a href="classes/OpenTick/OutgoingMessages/LoginRequest.html">OpenTick::OutgoingMessages::LoginRequest</a><br />
43
+ <a href="classes/OpenTick/OutgoingMessages/LogoutRequest.html">OpenTick::OutgoingMessages::LogoutRequest</a><br />
44
+ <a href="classes/OpenTick/OutgoingMessages/TickSnapshotRequest.html">OpenTick::OutgoingMessages::TickSnapshotRequest</a><br />
45
+ <a href="classes/OpenTick/OutgoingMessages/TickStreamExRequest.html">OpenTick::OutgoingMessages::TickStreamExRequest</a><br />
46
+ </div>
47
+ </div>
48
+ </body>
49
+ </html>
@@ -0,0 +1,27 @@
1
+
2
+ <?xml version="1.0" encoding="iso-8859-1"?>
3
+ <!DOCTYPE html
4
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
5
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
6
+
7
+ <!--
8
+
9
+ Files
10
+
11
+ -->
12
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
13
+ <head>
14
+ <title>Files</title>
15
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
16
+ <link rel="stylesheet" href="rdoc-style.css" type="text/css" />
17
+ <base target="docwin" />
18
+ </head>
19
+ <body>
20
+ <div id="index">
21
+ <h1 class="section-bar">Files</h1>
22
+ <div id="index-entries">
23
+ <a href="files/lib/opentick_rb.html">lib/opentick.rb</a><br />
24
+ </div>
25
+ </div>
26
+ </body>
27
+ </html>
@@ -0,0 +1,81 @@
1
+
2
+ <?xml version="1.0" encoding="iso-8859-1"?>
3
+ <!DOCTYPE html
4
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
5
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
6
+
7
+ <!--
8
+
9
+ Methods
10
+
11
+ -->
12
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
13
+ <head>
14
+ <title>Methods</title>
15
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
16
+ <link rel="stylesheet" href="rdoc-style.css" type="text/css" />
17
+ <base target="docwin" />
18
+ </head>
19
+ <body>
20
+ <div id="index">
21
+ <h1 class="section-bar">Methods</h1>
22
+ <div id="index-entries">
23
+ <a href="classes/OpenTick/OpenTick.html#M000050">close (OpenTick::OpenTick)</a><br />
24
+ <a href="classes/OpenTick/OpenTick.html#M000051">dispatch (OpenTick::OpenTick)</a><br />
25
+ <a href="classes/OpenTick.html#M000003">get_exchange_list (OpenTick)</a><br />
26
+ <a href="classes/OpenTick.html#M000001">get_historical_quotes (OpenTick)</a><br />
27
+ <a href="classes/OpenTick.html#M000002">get_historical_ticks (OpenTick)</a><br />
28
+ <a href="classes/OpenTick/OutgoingMessages/AbstractMessage.html#M000031">header (OpenTick::OutgoingMessages::AbstractMessage)</a><br />
29
+ <a href="classes/OpenTick/OpenTick.html#M000055">heart_beat (OpenTick::OpenTick)</a><br />
30
+ <a href="classes/OpenTick/IncomingMessages/AbstractMessage.html#M000011">inherited (OpenTick::IncomingMessages::AbstractMessage)</a><br />
31
+ <a href="classes/OpenTick/IncomingMessages/LoginResponse.html#M000005">load (OpenTick::IncomingMessages::LoginResponse)</a><br />
32
+ <a href="classes/OpenTick/IncomingMessages/TickStreamResponse.html#M000016">load (OpenTick::IncomingMessages::TickStreamResponse)</a><br />
33
+ <a href="classes/OpenTick/IncomingMessages/LogoutReponse.html#M000007">load (OpenTick::IncomingMessages::LogoutReponse)</a><br />
34
+ <a href="classes/OpenTick/IncomingMessages/ErrorResponse.html#M000018">load (OpenTick::IncomingMessages::ErrorResponse)</a><br />
35
+ <a href="classes/OpenTick/IncomingMessages/HistoryStreamResponse.html#M000020">load (OpenTick::IncomingMessages::HistoryStreamResponse)</a><br />
36
+ <a href="classes/OpenTick/IncomingMessages/ListExchangesResponse.html#M000014">load (OpenTick::IncomingMessages::ListExchangesResponse)</a><br />
37
+ <a href="classes/OpenTick/IncomingMessages/AbstractMessage.html#M000012">load (OpenTick::IncomingMessages::AbstractMessage)</a><br />
38
+ <a href="classes/OpenTick/OpenTick.html#M000048">login (OpenTick::OpenTick)</a><br />
39
+ <a href="classes/OpenTick/IncomingMessages/AbstractMessage.html#M000008">message_id (OpenTick::IncomingMessages::AbstractMessage)</a><br />
40
+ <a href="classes/OpenTick/IncomingMessages/TickStreamResponse.html#M000015">message_id (OpenTick::IncomingMessages::TickStreamResponse)</a><br />
41
+ <a href="classes/OpenTick/IncomingMessages/HistoryStreamResponse.html#M000019">message_id (OpenTick::IncomingMessages::HistoryStreamResponse)</a><br />
42
+ <a href="classes/OpenTick/IncomingMessages/ListExchangesResponse.html#M000013">message_id (OpenTick::IncomingMessages::ListExchangesResponse)</a><br />
43
+ <a href="classes/OpenTick/IncomingMessages/ErrorResponse.html#M000017">message_id (OpenTick::IncomingMessages::ErrorResponse)</a><br />
44
+ <a href="classes/OpenTick/OutgoingMessages/ListExchangesRequest.html#M000022">message_id (OpenTick::OutgoingMessages::ListExchangesRequest)</a><br />
45
+ <a href="classes/OpenTick/IncomingMessages/LoginResponse.html#M000004">message_id (OpenTick::IncomingMessages::LoginResponse)</a><br />
46
+ <a href="classes/OpenTick/OutgoingMessages/CancelHistDataRequest.html#M000024">message_id (OpenTick::OutgoingMessages::CancelHistDataRequest)</a><br />
47
+ <a href="classes/OpenTick/OutgoingMessages/HeartBeat.html#M000044">message_id (OpenTick::OutgoingMessages::HeartBeat)</a><br />
48
+ <a href="classes/OpenTick/OutgoingMessages/HistTicksRequest.html#M000026">message_id (OpenTick::OutgoingMessages::HistTicksRequest)</a><br />
49
+ <a href="classes/OpenTick/OutgoingMessages/LogoutRequest.html#M000042">message_id (OpenTick::OutgoingMessages::LogoutRequest)</a><br />
50
+ <a href="classes/OpenTick/OutgoingMessages/AbstractMessage.html#M000028">message_id (OpenTick::OutgoingMessages::AbstractMessage)</a><br />
51
+ <a href="classes/OpenTick/OutgoingMessages/HistoryStreamRequest.html#M000040">message_id (OpenTick::OutgoingMessages::HistoryStreamRequest)</a><br />
52
+ <a href="classes/OpenTick/OutgoingMessages/TickStreamExRequest.html#M000038">message_id (OpenTick::OutgoingMessages::TickStreamExRequest)</a><br />
53
+ <a href="classes/OpenTick/IncomingMessages/LogoutReponse.html#M000006">message_id (OpenTick::IncomingMessages::LogoutReponse)</a><br />
54
+ <a href="classes/OpenTick/OutgoingMessages/LoginRequest.html#M000034">message_id (OpenTick::OutgoingMessages::LoginRequest)</a><br />
55
+ <a href="classes/OpenTick/OutgoingMessages/TickSnapshotRequest.html#M000036">message_id (OpenTick::OutgoingMessages::TickSnapshotRequest)</a><br />
56
+ <a href="classes/OpenTick/OutgoingMessages/CancelTickStreamRequest.html#M000032">message_id (OpenTick::OutgoingMessages::CancelTickStreamRequest)</a><br />
57
+ <a href="classes/OpenTick/OutgoingMessages/AbstractMessage.html#M000029">method_missing (OpenTick::OutgoingMessages::AbstractMessage)</a><br />
58
+ <a href="classes/OpenTick/IncomingMessages/AbstractMessage.html#M000010">method_missing (OpenTick::IncomingMessages::AbstractMessage)</a><br />
59
+ <a href="classes/OpenTick/Debug.html#M000021">method_name (OpenTick::Debug)</a><br />
60
+ <a href="classes/OpenTick/IncomingMessages/AbstractMessage.html#M000009">new (OpenTick::IncomingMessages::AbstractMessage)</a><br />
61
+ <a href="classes/OpenTick/OpenTick.html#M000046">new (OpenTick::OpenTick)</a><br />
62
+ <a href="classes/OpenTick/OutgoingMessages/AbstractMessage.html#M000030">new (OpenTick::OutgoingMessages::AbstractMessage)</a><br />
63
+ <a href="classes/OpenTick/OpenTick.html#M000047">open (OpenTick::OpenTick)</a><br />
64
+ <a href="classes/OpenTick/OpenTick.html#M000053">read_from_socket (OpenTick::OpenTick)</a><br />
65
+ <a href="classes/OpenTick/OpenTick.html#M000054">reader (OpenTick::OpenTick)</a><br />
66
+ <a href="classes/OpenTick/OpenTick.html#M000049">reconnect (OpenTick::OpenTick)</a><br />
67
+ <a href="classes/OpenTick/OutgoingMessages/HeartBeat.html#M000045">send (OpenTick::OutgoingMessages::HeartBeat)</a><br />
68
+ <a href="classes/OpenTick/OutgoingMessages/CancelHistDataRequest.html#M000025">send (OpenTick::OutgoingMessages::CancelHistDataRequest)</a><br />
69
+ <a href="classes/OpenTick/OutgoingMessages/LogoutRequest.html#M000043">send (OpenTick::OutgoingMessages::LogoutRequest)</a><br />
70
+ <a href="classes/OpenTick/OutgoingMessages/ListExchangesRequest.html#M000023">send (OpenTick::OutgoingMessages::ListExchangesRequest)</a><br />
71
+ <a href="classes/OpenTick/OutgoingMessages/HistTicksRequest.html#M000027">send (OpenTick::OutgoingMessages::HistTicksRequest)</a><br />
72
+ <a href="classes/OpenTick/OutgoingMessages/HistoryStreamRequest.html#M000041">send (OpenTick::OutgoingMessages::HistoryStreamRequest)</a><br />
73
+ <a href="classes/OpenTick/OutgoingMessages/CancelTickStreamRequest.html#M000033">send (OpenTick::OutgoingMessages::CancelTickStreamRequest)</a><br />
74
+ <a href="classes/OpenTick/OutgoingMessages/LoginRequest.html#M000035">send (OpenTick::OutgoingMessages::LoginRequest)</a><br />
75
+ <a href="classes/OpenTick/OutgoingMessages/TickSnapshotRequest.html#M000037">send (OpenTick::OutgoingMessages::TickSnapshotRequest)</a><br />
76
+ <a href="classes/OpenTick/OutgoingMessages/TickStreamExRequest.html#M000039">send (OpenTick::OutgoingMessages::TickStreamExRequest)</a><br />
77
+ <a href="classes/OpenTick/OpenTick.html#M000052">subscribe (OpenTick::OpenTick)</a><br />
78
+ </div>
79
+ </div>
80
+ </body>
81
+ </html>
@@ -0,0 +1,24 @@
1
+ <?xml version="1.0" encoding="iso-8859-1"?>
2
+ <!DOCTYPE html
3
+ PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
4
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
5
+
6
+ <!--
7
+
8
+ RDoc Documentation
9
+
10
+ -->
11
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
12
+ <head>
13
+ <title>RDoc Documentation</title>
14
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
15
+ </head>
16
+ <frameset rows="20%, 80%">
17
+ <frameset cols="25%,35%,45%">
18
+ <frame src="fr_file_index.html" title="Files" name="Files" />
19
+ <frame src="fr_class_index.html" name="Classes" />
20
+ <frame src="fr_method_index.html" name="Methods" />
21
+ </frameset>
22
+ <frame src="files/lib/opentick_rb.html" name="docwin" />
23
+ </frameset>
24
+ </html>
@@ -0,0 +1,208 @@
1
+
2
+ body {
3
+ font-family: Verdana,Arial,Helvetica,sans-serif;
4
+ font-size: 90%;
5
+ margin: 0;
6
+ margin-left: 40px;
7
+ padding: 0;
8
+ background: white;
9
+ }
10
+
11
+ h1,h2,h3,h4 { margin: 0; color: #efefef; background: transparent; }
12
+ h1 { font-size: 150%; }
13
+ h2,h3,h4 { margin-top: 1em; }
14
+
15
+ a { background: #eef; color: #039; text-decoration: none; }
16
+ a:hover { background: #039; color: #eef; }
17
+
18
+ /* Override the base stylesheet's Anchor inside a table cell */
19
+ td > a {
20
+ background: transparent;
21
+ color: #039;
22
+ text-decoration: none;
23
+ }
24
+
25
+ /* and inside a section title */
26
+ .section-title > a {
27
+ background: transparent;
28
+ color: #eee;
29
+ text-decoration: none;
30
+ }
31
+
32
+ /* === Structural elements =================================== */
33
+
34
+ div#index {
35
+ margin: 0;
36
+ margin-left: -40px;
37
+ padding: 0;
38
+ font-size: 90%;
39
+ }
40
+
41
+
42
+ div#index a {
43
+ margin-left: 0.7em;
44
+ }
45
+
46
+ div#index .section-bar {
47
+ margin-left: 0px;
48
+ padding-left: 0.7em;
49
+ background: #ccc;
50
+ font-size: small;
51
+ }
52
+
53
+
54
+ div#classHeader, div#fileHeader {
55
+ width: auto;
56
+ color: white;
57
+ padding: 0.5em 1.5em 0.5em 1.5em;
58
+ margin: 0;
59
+ margin-left: -40px;
60
+ border-bottom: 3px solid #006;
61
+ }
62
+
63
+ div#classHeader a, div#fileHeader a {
64
+ background: inherit;
65
+ color: white;
66
+ }
67
+
68
+ div#classHeader td, div#fileHeader td {
69
+ background: inherit;
70
+ color: white;
71
+ }
72
+
73
+
74
+ div#fileHeader {
75
+ background: #057;
76
+ }
77
+
78
+ div#classHeader {
79
+ background: #048;
80
+ }
81
+
82
+
83
+ .class-name-in-header {
84
+ font-size: 180%;
85
+ font-weight: bold;
86
+ }
87
+
88
+
89
+ div#bodyContent {
90
+ padding: 0 1.5em 0 1.5em;
91
+ }
92
+
93
+ div#description {
94
+ padding: 0.5em 1.5em;
95
+ background: #efefef;
96
+ border: 1px dotted #999;
97
+ }
98
+
99
+ div#description h1,h2,h3,h4,h5,h6 {
100
+ color: #125;;
101
+ background: transparent;
102
+ }
103
+
104
+ div#validator-badges {
105
+ text-align: center;
106
+ }
107
+ div#validator-badges img { border: 0; }
108
+
109
+ div#copyright {
110
+ color: #333;
111
+ background: #efefef;
112
+ font: 0.75em sans-serif;
113
+ margin-top: 5em;
114
+ margin-bottom: 0;
115
+ padding: 0.5em 2em;
116
+ }
117
+
118
+
119
+ /* === Classes =================================== */
120
+
121
+ table.header-table {
122
+ color: white;
123
+ font-size: small;
124
+ }
125
+
126
+ .type-note {
127
+ font-size: small;
128
+ color: #DEDEDE;
129
+ }
130
+
131
+ .xxsection-bar {
132
+ background: #eee;
133
+ color: #333;
134
+ padding: 3px;
135
+ }
136
+
137
+ .section-bar {
138
+ color: #333;
139
+ border-bottom: 1px solid #999;
140
+ margin-left: -20px;
141
+ }
142
+
143
+
144
+ .section-title {
145
+ background: #79a;
146
+ color: #eee;
147
+ padding: 3px;
148
+ margin-top: 2em;
149
+ margin-left: -30px;
150
+ border: 1px solid #999;
151
+ }
152
+
153
+ .top-aligned-row { vertical-align: top }
154
+ .bottom-aligned-row { vertical-align: bottom }
155
+
156
+ /* --- Context section classes ----------------------- */
157
+
158
+ .context-row { }
159
+ .context-item-name { font-family: monospace; font-weight: bold; color: black; }
160
+ .context-item-value { font-size: small; color: #448; }
161
+ .context-item-desc { color: #333; padding-left: 2em; }
162
+
163
+ /* --- Method classes -------------------------- */
164
+ .method-detail {
165
+ background: #efefef;
166
+ padding: 0;
167
+ margin-top: 0.5em;
168
+ margin-bottom: 1em;
169
+ border: 1px dotted #ccc;
170
+ }
171
+ .method-heading {
172
+ color: black;
173
+ background: #ccc;
174
+ border-bottom: 1px solid #666;
175
+ padding: 0.2em 0.5em 0 0.5em;
176
+ }
177
+ .method-signature { color: black; background: inherit; }
178
+ .method-name { font-weight: bold; }
179
+ .method-args { font-style: italic; }
180
+ .method-description { padding: 0 0.5em 0 0.5em; }
181
+
182
+ /* --- Source code sections -------------------- */
183
+
184
+ a.source-toggle { font-size: 90%; }
185
+ div.method-source-code {
186
+ background: #262626;
187
+ color: #ffdead;
188
+ margin: 1em;
189
+ padding: 0.5em;
190
+ border: 1px dashed #999;
191
+ overflow: hidden;
192
+ }
193
+
194
+ div.method-source-code pre { color: #ffdead; overflow: hidden; }
195
+
196
+ /* --- Ruby keyword styles --------------------- */
197
+
198
+ .standalone-code { background: #221111; color: #ffdead; overflow: hidden; }
199
+
200
+ .ruby-constant { color: #7fffd4; background: transparent; }
201
+ .ruby-keyword { color: #00ffff; background: transparent; }
202
+ .ruby-ivar { color: #eedd82; background: transparent; }
203
+ .ruby-operator { color: #00ffee; background: transparent; }
204
+ .ruby-identifier { color: #ffdead; background: transparent; }
205
+ .ruby-node { color: #ffa07a; background: transparent; }
206
+ .ruby-comment { color: #b22222; font-weight: bold; background: transparent; }
207
+ .ruby-regexp { color: #ffa07a; background: transparent; }
208
+ .ruby-value { color: #7fffd4; background: transparent; }
@@ -0,0 +1,543 @@
1
+ =begin
2
+ * Ruby implementation for Opentick API *
3
+ Messages:
4
+ 1. OTLogin - done
5
+ @TODO: reconnect on redirect flag
6
+ 2. OTLogout - done
7
+ 3. OTRequestTickStream (deprecated - see OTRequestTickStreamEx)
8
+ 4. OTCancelTickStream - done
9
+ 5. OTRequestHistData - done
10
+ 6. OTCancelHistData - done
11
+ 7. OTRequestListExchanges - done
12
+ 8. OTRequestListSymbols
13
+ 9. OTHeartBeat - done
14
+ 10. OTRequestEqInit
15
+ 11. OTRequestOptionChain (deprecated - see OTRequestOptionChainEx)
16
+ 12. OTCancelOptionChain
17
+ 13. OTRequestBookStream
18
+ 14. OTCancelBookStream
19
+ 15. OTRequestTickStreamEx - done
20
+ 16. OTRequestOptionChainEx
21
+ 17. OTRequestHistTicks - done
22
+ 18. OTRequestSplits
23
+ 19. OTRequestDividends
24
+ 20. OTRequestHistBooks
25
+ 21. OTRequestBookStreamEx
26
+ 22. OTRequestOptionChainU
27
+ 23. OTRequestOptionInit
28
+ 24. OTRequestListSymbolsEx
29
+ 25. OTRequestTickSnapshot - done
30
+ 26. OTRequestOptionChainSnapshot
31
+ =end
32
+
33
+ require 'socket'
34
+ require 'logger'
35
+ require 'thread'
36
+ require 'stringio'
37
+
38
+ module OpenTick
39
+
40
+ OT_HEADER_LEN = 12
41
+ OT_COMMAND_STATUS_OK=1
42
+ OT_COMMAND_STATUS_ERROR=2
43
+ # ���� ��������
44
+ OT_TICK_TYPE_TRADE=4
45
+ OT_TICK_TYPE_QUOTE=1
46
+ OT_HIST_RAW_TICKS=1
47
+ OT_HIST_OHLC_DAILY=5
48
+
49
+ module Debug
50
+ # return current class and method name
51
+ def method_name
52
+ self.class.name + "." + caller[0][/`([^']*)'/, 1]
53
+ end # method_name
54
+ end # module
55
+
56
+ class OpenTick
57
+ include Debug
58
+ attr_accessor :sessionId
59
+
60
+ def initialize(host, port)
61
+ OTLogger.debug(method_name)
62
+ @host = host
63
+ @port = port
64
+ @server = Hash.new
65
+ @sessionId = nil
66
+ @listeners = Hash.new { |hash, key|
67
+ hash[key] = Array.new
68
+ }
69
+ self.open
70
+ end # initialize
71
+
72
+ # open TCPSocket with opentick server
73
+ # create threads for reader and heartBeat functions
74
+ def open
75
+ OTLogger.debug(method_name)
76
+ @reqId = 1
77
+ @server[:socket] = TCPSocket.open(@host, @port)
78
+ Thread.abort_on_exception = true
79
+ @server[:sync_thread] = Thread.new {
80
+ self.heart_beat
81
+ }
82
+ @server[:reader_thread] = Thread.new {
83
+ self.reader
84
+ }
85
+ end # open
86
+
87
+ def login(user, password)
88
+ self.subscribe(IncomingMessages::LoginResponse, lambda {|msg|
89
+ if msg.redirectFlag.to_i == 1
90
+ ot.reconnect(msg.redirectHost, msg.redirectHost.to_i)
91
+ else
92
+ @sessionId = msg.sessionId
93
+ end
94
+ })
95
+ self.dispatch(OutgoingMessages::LoginRequest.new(:login => user, :password => password))
96
+ # waiting for login response
97
+ while @sessionId.nil? ; end
98
+ end # login
99
+
100
+ # reconnect to new opentick server
101
+ def reconnect(host, port)
102
+ @server[:socket] = TCPSocket.open(@host, @port)
103
+ Thread.abort_on_exception = true
104
+ end
105
+
106
+ # kill reader and heart_beat threads
107
+ # close TCPSocket
108
+ def close
109
+ OTLogger.debug(method_name)
110
+ @server[:reader_thread].kill
111
+ @server[:sync_thread].kill
112
+ @server[:socket].close()
113
+ @server = Hash.new
114
+ end # close
115
+
116
+ # send message to server
117
+ def dispatch(message)
118
+ OTLogger.debug(method_name)
119
+ raise Exception.new("dispatch() must be given an OutgoingMessages::AbstractMessage subclass") unless message.is_a?(OutgoingMessages::AbstractMessage)
120
+ message.sessionId = @sessionId unless @sessionId.nil?
121
+ message.reqId = @reqId += 1
122
+ queue = message.header(@server) + message.send
123
+ @server[:socket].syswrite [queue.length].pack("l") + queue
124
+ end # dispatch
125
+
126
+ # subscribe listener(Proc) to type of message
127
+ def subscribe(messageClass, code)
128
+ OTLogger.debug(method_name)
129
+ raise(Exception.new("Invalid argument type (#{messageClass}, #{code.class}) - " +
130
+ " must be (IncomingMessages::AbstractMessage, Proc)")) unless
131
+ messageClass <= IncomingMessages::AbstractMessage && code.is_a?(Proc)
132
+ @listeners[messageClass].push(code)
133
+ end
134
+
135
+ protected
136
+
137
+ def read_from_socket( server, len=server[:len] )
138
+ buf = StringIO.new
139
+ while buf.size < len
140
+ begin
141
+ IO.select([server[:socket]])
142
+ buf.write server[:socket].readpartial(len - buf.size )
143
+ end
144
+ end
145
+ buf
146
+ end # read_from_socket
147
+
148
+ # read messages from server
149
+ # parse message
150
+ # call listeners
151
+ def reader
152
+ OTLogger.debug(method_name)
153
+ while true
154
+ # temporary buffer
155
+ buf = StringIO.new
156
+ # reading message length and message header
157
+ buf = read_from_socket( @server, OT_HEADER_LEN + 4 )
158
+ # unpacking header
159
+ msgLen, msgType, cmdStatus, cmdType, reqId = buf.string.unpack("lccxxll")
160
+ OTLogger.debug("msgLen = #{msgLen} msgType = #{IncomingMessages::Table[cmdType].name} cmdStatus = #{cmdStatus} cmdType = #{cmdType} reqId = #{reqId}")
161
+ # saving request id
162
+ if cmdStatus != OT_COMMAND_STATUS_OK
163
+ # server return error
164
+ OTLogger.error("Error command status msgLen = #{msgLen} msgType = #{msgType} cmdStatus = #{cmdStatus} cmdType = #{cmdType} reqId = #{reqId}")
165
+ # reading rest of the message
166
+ p @server[:socket].sysread(msgLen - OT_HEADER_LEN)
167
+ else
168
+ # saving message length
169
+ @server[:len] = msgLen - OT_HEADER_LEN
170
+ if IncomingMessages::Table[cmdType] != nil and msgLen > 0
171
+ msg = IncomingMessages::Table[cmdType].new( read_from_socket( @server ))
172
+ msg.reqId = @reqId
173
+ @listeners[msg.class].each { |listener|
174
+ listener.call(msg)
175
+ }
176
+ else
177
+ OTLogger.error("Error command type msgLen = #{msgLen} msgType = #{msgType} cmdStatus = #{cmdStatus} cmdType = #{cmdType} reqId = #{reqId}")
178
+ @server[:socket].sysread(msgLen - OT_HEADER_LEN - 4)
179
+ end # if IncomingMessages::Table[cmdType] != nil and msgLen > 0
180
+ end # if cmdStatus != OT_COMMAND_STATUS_OK
181
+ end # while
182
+ end # reader
183
+
184
+ # send hearbeat message every 4 seconds
185
+ def heart_beat
186
+ OTLogger.debug(method_name)
187
+ msgSync = OutgoingMessages::HeartBeat.new
188
+ while true
189
+ sleep(4)
190
+ dispatch(msgSync)
191
+ end # while
192
+ end # heartBeat
193
+
194
+ end # class OpenTick
195
+
196
+ module OutgoingMessages
197
+
198
+ OT_MSG_CLIENT_REQUEST = 1
199
+
200
+ class AbstractMessage
201
+ include Debug
202
+
203
+ def self.message_id
204
+ raise "AbstractMessage.message_id called - you need to override this in a subclass."
205
+ end
206
+ def method_missing(m, *args)
207
+ if args[0].is_a?(String)
208
+ instance_eval("@#{m}\"#{args[0]}\"")
209
+ else
210
+ instance_eval("@#{m}#{args[0]}")
211
+ end
212
+ end # method_missing
213
+ def initialize(data=nil)
214
+ OTLogger.debug("* initialize #{self.class.name}")
215
+ @data = Hash.new.replace(data) unless data == nil
216
+ end # initialize
217
+ def header(server)
218
+ [OT_MSG_CLIENT_REQUEST, OT_COMMAND_STATUS_OK, self.class.message_id, @reqId].to_a.pack("ccxxll")
219
+ end # header
220
+ end # class AbstractMessage
221
+
222
+ # OTLogin request message
223
+ # Data format is { :login => string, :password => string }
224
+ class LoginRequest < AbstractMessage
225
+ def self.message_id
226
+ 1
227
+ end
228
+ def send
229
+ OTLogger.debug(method_name)
230
+ [3, 8, 4, "*"*16, 0x00.chr+0x19.chr+0x21.chr+0x2b.chr+0x14.chr+0x32.chr, @data[:login], @data[:password]].to_a.pack("sccA16A6a64a64")
231
+ end # send
232
+ end # class Login
233
+
234
+ # OTLogout request message
235
+ # Data format is { }
236
+ class LogoutRequest < AbstractMessage
237
+ def self.message_id
238
+ 2
239
+ end
240
+ def send
241
+ [@data[:sessionId]].to_a.pack("a64")
242
+ end # send
243
+ end # class Logout
244
+
245
+ # OTCancelTickStream
246
+ # Data format is { :cancelId => int }
247
+ class CancelTickStreamRequest < AbstractMessage
248
+ def self.message_id
249
+ 4
250
+ end
251
+ def send
252
+ [@data[:sessionId], @data[:cancelId]].to_a.pack("a64i")
253
+ end # send
254
+ end # class CancelTickStreamRequest
255
+
256
+ # OTRequestHistData request message
257
+ # Data format is { :exchange => string, :ticker => string, :from => int, :to => int, type => byte, interval => short }
258
+ class HistoryStreamRequest < AbstractMessage
259
+ def self.message_id
260
+ 5
261
+ end
262
+ def send
263
+ OTLogger.debug(method_name)
264
+ [@data[:sessionId],@data[:exchange],@data[:ticker],@data[:from],@data[:to],@data[:type],@data[:interval]].to_a.pack("a64a15a15xxiicxs")
265
+ end
266
+ end # class HistoryStreamRequest
267
+
268
+ # OTCancelHistData
269
+ # Data format is { :cancelId => int }
270
+ class CancelHistDataRequest < AbstractMessage
271
+ def self.message_id
272
+ 6
273
+ end
274
+ def send
275
+ [@data[:sessionId], @data[:cancelId]].to_a.pack("a64i")
276
+ end # send
277
+ end # class CancelHistDataRequest
278
+
279
+ # OTRequestListExchanges request message
280
+ # Data format is { }
281
+ class ListExchangesRequest < AbstractMessage
282
+ def self.message_id
283
+ 7
284
+ end
285
+ def send
286
+ OTLogger.debug(method_name)
287
+ [@sessionId].to_a.pack("a64")
288
+ end # send
289
+ end # class ListExchangesRequest
290
+
291
+ # OTHeartBeat request message
292
+ # Data format is { }
293
+ class HeartBeat < AbstractMessage
294
+ def self.message_id
295
+ 9
296
+ end
297
+ def send
298
+ OTLogger.debug(method_name)
299
+ ""
300
+ end # send
301
+ end # class HeartBeat
302
+
303
+ # OTRequestTickStreamEx request message
304
+ # Data format is { :exchange => string, :ticker => string, :mask => int }
305
+ class TickStreamExRequest < AbstractMessage
306
+ def self.message_id
307
+ 15
308
+ end
309
+ def send
310
+ OTLogger.debug(method_name)
311
+ [@data[:sessionId],@data[:exchange],@data[:ticker],@data[:mask]].to_a.pack("a64a15a15xxl")
312
+ end # send
313
+ end # class TickStreamEx
314
+
315
+ # OTRequestHistTicks request message
316
+ # Data format is { :exchange => string, :ticker => string, :from => int, :to => int, mask => int}
317
+ class HistTicksRequest < AbstractMessage
318
+ def self.message_id
319
+ 17
320
+ end
321
+ def send
322
+ OTLogger.debug(method_name)
323
+ [@sessionId,@data[:exchange],@data[:ticker],@data[:from],@data[:to],@data[:mask]].to_a.pack("a64a15a15iii")
324
+ end # send
325
+ end # class HistTicksRequest
326
+
327
+ # OTRequestTickSnapshot request message
328
+ # Data format is { :exchange => string, :ticker => string, :mask => int }
329
+ class TickSnapshotRequest < AbstractMessage
330
+ def self.message_id
331
+ 25
332
+ end
333
+ def send
334
+ OTLogger.debug(method_name)
335
+ [@data[:sessionId],@data[:exchange],@data[:ticker],@data[:mask]].to_a.pack("a64a15a15xxl")
336
+ end # send
337
+ end # class TickSnapshot
338
+ end # module OutgoiingMessage
339
+
340
+ module IncomingMessages
341
+
342
+ OT_MSG_SERVER_RESPONSE = 2
343
+ Classes = Array.new
344
+
345
+ class AbstractMessage
346
+ include Debug
347
+ attr_accessor :reqId
348
+
349
+ def self.message_id
350
+ raise "AbstractMessage.message_id called - you need to override this in a subclass."
351
+ end
352
+ def initialize(buf)
353
+ raise Exception.new("Don't use AbstractMessage directly; use the subclass for your specific message type") if self.class.name == "AbstractMessage"
354
+ self.load(buf)
355
+ end # initialize
356
+ def method_missing(m, *args)
357
+ instance_eval("@"+m.id2name)
358
+ end # method_missing
359
+ def AbstractMessage.inherited(by)
360
+ super(by)
361
+ Classes.push(by)
362
+ end # inherited
363
+ def load
364
+ raise Exception.new("Don't use AbstractMessage; override load() in a subclass.")
365
+ end # load
366
+ end # class AbstractMessage
367
+
368
+ class ErrorResponse < AbstractMessage
369
+ def self.message_id
370
+ -1
371
+ end
372
+ def load(buf)
373
+ end
374
+ end # class ErrorResponse
375
+
376
+ class LoginResponse < AbstractMessage
377
+ def self.message_id
378
+ 1
379
+ end
380
+ def load(buf)
381
+ OTLogger.debug(method_name)
382
+ @sessionId, @redirectFlag, @redirectHost, @redirectHost = buf.string.unpack("a64ca64s")
383
+ end # load
384
+ end # class LoginResponse
385
+
386
+ class LogoutReponse < AbstractMessage
387
+ def self.message_id
388
+ 2
389
+ end
390
+ def load
391
+ OTLogger.debug(method_name)
392
+ end # load
393
+ end # class LogoutResponse
394
+
395
+ class TickStreamResponse < AbstractMessage
396
+ def self.message_id
397
+ 3
398
+ end
399
+ def load(buf)
400
+ OTLogger.debug(method_name)
401
+ @tickType, @timeStamp, @rest = buf.string.unpack("lcla*")
402
+ case @tickType.to_i
403
+ when 3
404
+ @price, @size, @volume, @sn, @indicator, @tickindicator, @flags, @exchange = @rest.unpack("diqiccca2")
405
+ when 1
406
+ @bidSize, @askSize, @bidPrice, @askPrice, @askExchange, @indicator, @tickindicator, @bidExchange, @tickExchange = @rest.unpack("iidda2cca2a2")
407
+ when 2
408
+ when 4
409
+ end
410
+ end # load
411
+ end # class RequestTickStream
412
+
413
+ class HistoryStreamResponse < AbstractMessage
414
+ def self.message_id
415
+ 5
416
+ end
417
+ def load(buf)
418
+ OTLogger.debug(method_name)
419
+ @rowNum, @rest = buf.string.unpack("la*")
420
+ @data = Array.new
421
+ @rowNum.times {|i|
422
+ @data[i] = Hash.new
423
+ @data[i][:type], @data[i][:time], @rest = @rest.unpack("cia*")
424
+ case @data[i][:type]
425
+ when 50
426
+ @data[i][:openPrice], @data[i][:highPrice], @data[i][:lowPrice], @data[i][:closePrice], @data[i][:volume], @rest = @rest.unpack("ddddqa*")
427
+ when 3
428
+ @data[i][:price], @data[i][:size], @data[i][:volume], @data[i][:sn], @data[i][:indicator], @data[i][:tickInd], @data[i][:flags], @rest = @rest.unpack("diqiccca*")
429
+ end
430
+ }
431
+ end
432
+ end # class HistoryStreamResponse
433
+
434
+ class ListExchangesResponse < AbstractMessage
435
+ def self.message_id
436
+ 7
437
+ end
438
+ def load(buf)
439
+ OTLogger.debug(method_name)
440
+ urlLen, @rest = buf.string.unpack("sa*")
441
+ @url, @exNum, @rest = @rest.unpack("a#{urlLen}sa*")
442
+ @data = Array.new
443
+ @exNum.times {|i|
444
+ @data[i] = Hash.new
445
+ @data[i][:code], @data[i][:availFlag], titleLen, @rest = @rest.unpack("a15csa*")
446
+ @data[i][:title], descLen, @rest = @rest.unpack("a#{titleLen}sa*")
447
+ @data[i][:desc], @rest = @rest.unpack("a#{descLen}a*")
448
+ }
449
+ end
450
+ end # class ListExchangesResponse
451
+
452
+ Table = Hash.new
453
+ Classes.each { |msg_class|
454
+ Table[msg_class.message_id] = msg_class
455
+ }
456
+ end # module IncomingMessage
457
+
458
+ OTLogger = Logger.new(STDERR)
459
+ OTLogger.level = Logger::Severity::DEBUG
460
+
461
+ def self.get_historical_quotes( login, password, symbol, exchange, type, from, to )
462
+ # create connection
463
+ ot = OpenTick.new("feed1.opentick.com", 10010)
464
+ # history response listener
465
+ ot.subscribe(IncomingMessages::HistoryStreamResponse, lambda {|msg|
466
+ if msg.rowNum == 1 and msg.data[0][:type] == 0
467
+ # recieve end data flag
468
+ ot.sessionId = nil
469
+ else
470
+ msg.rowNum.times { |i|
471
+ case msg.data[i][:type]
472
+ when 50
473
+ yield [Time.at(msg.data[i][:time]), msg.data[i][:openPrice], msg.data[i][:highPrice], msg.data[i][:lowPrice], msg.data[i][:closePrice], msg.data[i][:volume]]
474
+ end
475
+ }
476
+ end
477
+ })
478
+ # sending login request
479
+ ot.login(login, password)
480
+ # send history request
481
+ ot.dispatch(OutgoingMessages::HistoryStreamRequest.new(:sessionId => sessionId, :exchange => exchange, :ticker => symbol, :from => from, :to => to, :type => type, :interval => 1))
482
+ # waiting for end data
483
+ while not sessionId.nil? ; end
484
+ ot.close
485
+ end # get_historical_quotes
486
+
487
+ def self.get_historical_ticks( login, password, symbol, exchange, mask, from, to )
488
+ # create connection
489
+ ot = OpenTick.new("feed1.opentick.com", 10010)
490
+ # history response listener
491
+ ot.subscribe(IncomingMessages::HistoryStreamResponse, lambda {|msg|
492
+ if msg.rowNum == 1 and msg.data[0][:type] == 0
493
+ # recieve end data flag
494
+ ot.sessionId = nil
495
+ else
496
+ msg.rowNum.times { |i|
497
+ case msg.data[i][:type]
498
+ when 3
499
+ yield [msg.data[i][:time], msg.data[i][:sn], msg.data[i][:price], msg.data[i][:volume]]#, msg.data[:data][i][:size], msg.data[:data][i][:indicator], msg.data[:data][i][:tickInd], msg.data[:data][i][:flags]]
500
+ end
501
+ }
502
+ end
503
+ })
504
+ # login response listener
505
+ ot.login(login, password)
506
+ # send history request
507
+ ot.dispatch(OutgoingMessages::HistTicksRequest.new(:exchange => exchange, :ticker => symbol, :from => from, :to => to, :mask => mask))
508
+ # waiting for end data
509
+ while not ot.sessionId.nil? ; end
510
+ ot.close
511
+ end # get_historical_ticks
512
+
513
+ def self.get_exchange_list(login, password)
514
+ # create connection
515
+ ot = OpenTick.new("feed1.opentick.com", 10010)
516
+ # history response listener
517
+ ot.subscribe(IncomingMessages::ListExchangesResponse, lambda {|msg|
518
+ (msg.data.select {|x| x[:availFlag] == 1}).each { |x| yield [x[:title].strip, x[:code].strip]}
519
+ ot.sessionId = nil
520
+ })
521
+ # sending login request
522
+ ot.login(login, password)
523
+ # send history request
524
+ ot.dispatch(OutgoingMessages::ListExchangesRequest.new)
525
+ # waiting for end data
526
+ while not ot.sessionId.nil? ; end
527
+ ot.close
528
+ end
529
+
530
+ end # module OpenTick
531
+
532
+ if $0 == __FILE__
533
+
534
+ login = 'test_opentick'
535
+ password = '123123'
536
+
537
+ #OpenTick.get_historical_quotes(login,password,"GOOG", "Q", 1, Time.now.to_i-24*3600, Time.now.to_i) { |msg| p msg}
538
+
539
+ #OpenTick.get_historical_ticks(login, password,"GOOG", "Q", 4, Time.now.to_i-24*3600, Time.now.to_i) { |msg| p msg}
540
+
541
+ OpenTick.get_exchange_list(login, password) {|x| p x}
542
+
543
+ end
@@ -0,0 +1,20 @@
1
+ require 'rubygems'
2
+
3
+ SPEC = Gem::Specification.new do |s|
4
+ s.name = "opentick-ruby"
5
+ s.version = "0.1.1"
6
+ s.platform = Gem::Platform::RUBY
7
+ s.summary = "Ruby implementation for Opentick API"
8
+ s.files = Dir["./*"] + Dir["*/**"]
9
+ s.require_path = 'lib'
10
+ s.extra_rdoc_files = ["README", "ChangeLog"]
11
+ s.author = "Timur Adigamov"
12
+ s.email = "timur at adigamov dot com"
13
+ s.homepage = "http://www.tradeindexfuture.com"
14
+ end
15
+
16
+ if $0==__FILE__
17
+ p SPEC
18
+ Gem.manage_gems
19
+ Gem::Builder.new(SPEC).build
20
+ end
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ rubygems_version: 0.9.4
3
+ specification_version: 1
4
+ name: opentick-ruby
5
+ version: !ruby/object:Gem::Version
6
+ version: 0.1.1
7
+ date: 2007-11-08 00:00:00 +03:00
8
+ summary: Ruby implementation for Opentick API
9
+ require_paths:
10
+ - lib
11
+ email: timur at adigamov dot com
12
+ homepage: http://www.tradeindexfuture.com
13
+ rubyforge_project:
14
+ description:
15
+ autorequire:
16
+ default_executable:
17
+ bindir: bin
18
+ has_rdoc: false
19
+ required_ruby_version: !ruby/object:Gem::Version::Requirement
20
+ requirements:
21
+ - - ">"
22
+ - !ruby/object:Gem::Version
23
+ version: 0.0.0
24
+ version:
25
+ platform: ruby
26
+ signing_key:
27
+ cert_chain:
28
+ post_install_message:
29
+ authors:
30
+ - Timur Adigamov
31
+ files:
32
+ - ./ChangeLog
33
+ - ./doc
34
+ - ./lib
35
+ - ./opentick.gemspec
36
+ - ./README
37
+ - doc/classes
38
+ - doc/created.rid
39
+ - doc/files
40
+ - doc/fr_class_index.html
41
+ - doc/fr_file_index.html
42
+ - doc/fr_method_index.html
43
+ - doc/index.html
44
+ - doc/rdoc-style.css
45
+ - lib/opentick.rb
46
+ - README
47
+ - ChangeLog
48
+ test_files: []
49
+
50
+ rdoc_options: []
51
+
52
+ extra_rdoc_files:
53
+ - README
54
+ - ChangeLog
55
+ executables: []
56
+
57
+ extensions: []
58
+
59
+ requirements: []
60
+
61
+ dependencies: []
62
+