garru-ruby_scribe_client 0.0.2
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/CHANGELOG +3 -0
- data/LICENSE +0 -0
- data/Manifest +39 -0
- data/README +0 -0
- data/Rakefile +8 -0
- data/etc/scribe_cat.rb +23 -0
- data/lib/ruby_scribe_client/FacebookService.rb +685 -0
- data/lib/ruby_scribe_client/fb303_types.rb +16 -0
- data/lib/ruby_scribe_client/reflection_limited_types.rb +148 -0
- data/lib/ruby_scribe_client/scribe.rb +82 -0
- data/lib/ruby_scribe_client/scribe_types.rb +32 -0
- data/lib/ruby_scribe_client.rb +33 -0
- data/ruby_scribe_client.gemspec +31 -0
- data/vendor/thrift/client.rb +44 -0
- data/vendor/thrift/deprecation.rb +155 -0
- data/vendor/thrift/exceptions.rb +65 -0
- data/vendor/thrift/processor.rb +39 -0
- data/vendor/thrift/protocol/binaryprotocol.rb +213 -0
- data/vendor/thrift/protocol/binaryprotocolaccelerated.rb +19 -0
- data/vendor/thrift/protocol/tbinaryprotocol.rb +2 -0
- data/vendor/thrift/protocol/tprotocol.rb +2 -0
- data/vendor/thrift/protocol.rb +270 -0
- data/vendor/thrift/serializer.rb +27 -0
- data/vendor/thrift/server/httpserver.rb +44 -0
- data/vendor/thrift/server/nonblockingserver.rb +278 -0
- data/vendor/thrift/server/thttpserver.rb +2 -0
- data/vendor/thrift/server/tserver.rb +2 -0
- data/vendor/thrift/server.rb +135 -0
- data/vendor/thrift/struct.rb +272 -0
- data/vendor/thrift/thrift.rb +14 -0
- data/vendor/thrift/transport/httpclient.rb +29 -0
- data/vendor/thrift/transport/socket.rb +153 -0
- data/vendor/thrift/transport/thttpclient.rb +2 -0
- data/vendor/thrift/transport/tsocket.rb +2 -0
- data/vendor/thrift/transport/ttransport.rb +2 -0
- data/vendor/thrift/transport/unixsocket.rb +58 -0
- data/vendor/thrift/transport.rb +319 -0
- data/vendor/thrift/types.rb +83 -0
- data/vendor/thrift.rb +28 -0
- metadata +104 -0
@@ -0,0 +1,278 @@
|
|
1
|
+
require 'thrift/server'
|
2
|
+
require 'logger'
|
3
|
+
require 'thread'
|
4
|
+
|
5
|
+
module Thrift
|
6
|
+
# this class expects to always use a FramedTransport for reading messages
|
7
|
+
class NonblockingServer < Server
|
8
|
+
def initialize(processor, serverTransport, transportFactory=nil, protocolFactory=nil, num=20, logger = nil)
|
9
|
+
super(processor, serverTransport, transportFactory, protocolFactory)
|
10
|
+
@num_threads = num
|
11
|
+
if logger.nil?
|
12
|
+
@logger = Logger.new(STDERR)
|
13
|
+
@logger.level = Logger::WARN
|
14
|
+
else
|
15
|
+
@logger = logger
|
16
|
+
end
|
17
|
+
@shutdown_semaphore = Mutex.new
|
18
|
+
@transport_semaphore = Mutex.new
|
19
|
+
end
|
20
|
+
|
21
|
+
def serve
|
22
|
+
@logger.info "Starting #{self}"
|
23
|
+
@serverTransport.listen
|
24
|
+
@io_manager = start_io_manager
|
25
|
+
|
26
|
+
begin
|
27
|
+
loop do
|
28
|
+
break if @serverTransport.closed?
|
29
|
+
rd, = select([@serverTransport], nil, nil, 0.1)
|
30
|
+
next if rd.nil?
|
31
|
+
socket = @serverTransport.accept
|
32
|
+
@logger.debug "Accepted socket: #{socket.inspect}"
|
33
|
+
@io_manager.add_connection socket
|
34
|
+
end
|
35
|
+
rescue IOError => e
|
36
|
+
end
|
37
|
+
# we must be shutting down
|
38
|
+
@logger.info "#{self} is shutting down, goodbye"
|
39
|
+
ensure
|
40
|
+
@transport_semaphore.synchronize do
|
41
|
+
@serverTransport.close
|
42
|
+
end
|
43
|
+
@io_manager.ensure_closed unless @io_manager.nil?
|
44
|
+
end
|
45
|
+
|
46
|
+
def shutdown(timeout = 0, block = true)
|
47
|
+
@shutdown_semaphore.synchronize do
|
48
|
+
return if @is_shutdown
|
49
|
+
@is_shutdown = true
|
50
|
+
end
|
51
|
+
# nonblocking is intended for calling from within a Handler
|
52
|
+
# but we can't change the order of operations here, so lets thread
|
53
|
+
shutdown_proc = lambda do
|
54
|
+
@io_manager.shutdown(timeout)
|
55
|
+
@transport_semaphore.synchronize do
|
56
|
+
@serverTransport.close # this will break the accept loop
|
57
|
+
end
|
58
|
+
end
|
59
|
+
if block
|
60
|
+
shutdown_proc.call
|
61
|
+
else
|
62
|
+
Thread.new &shutdown_proc
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def start_io_manager
|
69
|
+
iom = IOManager.new(@processor, @serverTransport, @transportFactory, @protocolFactory, @num_threads, @logger)
|
70
|
+
iom.spawn
|
71
|
+
iom
|
72
|
+
end
|
73
|
+
|
74
|
+
class IOManager # :nodoc:
|
75
|
+
DEFAULT_BUFFER = 2**20
|
76
|
+
|
77
|
+
def initialize(processor, serverTransport, transportFactory, protocolFactory, num, logger)
|
78
|
+
@processor = processor
|
79
|
+
@serverTransport = serverTransport
|
80
|
+
@transportFactory = transportFactory
|
81
|
+
@protocolFactory = protocolFactory
|
82
|
+
@num_threads = num
|
83
|
+
@logger = logger
|
84
|
+
@connections = []
|
85
|
+
@buffers = Hash.new { |h,k| h[k] = '' }
|
86
|
+
@signal_queue = Queue.new
|
87
|
+
@signal_pipes = IO.pipe
|
88
|
+
@signal_pipes[1].sync = true
|
89
|
+
@worker_queue = Queue.new
|
90
|
+
@shutdown_queue = Queue.new
|
91
|
+
end
|
92
|
+
|
93
|
+
def add_connection(socket)
|
94
|
+
signal [:connection, socket]
|
95
|
+
end
|
96
|
+
|
97
|
+
def spawn
|
98
|
+
@iom_thread = Thread.new do
|
99
|
+
@logger.debug "Starting #{self}"
|
100
|
+
run
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def shutdown(timeout = 0)
|
105
|
+
@logger.debug "#{self} is shutting down workers"
|
106
|
+
@worker_queue.clear
|
107
|
+
@num_threads.times { @worker_queue.push [:shutdown] }
|
108
|
+
signal [:shutdown, timeout]
|
109
|
+
@shutdown_queue.pop
|
110
|
+
@signal_pipes[0].close
|
111
|
+
@signal_pipes[1].close
|
112
|
+
@logger.debug "#{self} is shutting down, goodbye"
|
113
|
+
end
|
114
|
+
|
115
|
+
def ensure_closed
|
116
|
+
kill_worker_threads if @worker_threads
|
117
|
+
@iom_thread.kill
|
118
|
+
end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
def run
|
123
|
+
spin_worker_threads
|
124
|
+
|
125
|
+
loop do
|
126
|
+
rd, = select([@signal_pipes[0], *@connections])
|
127
|
+
if rd.delete @signal_pipes[0]
|
128
|
+
break if read_signals == :shutdown
|
129
|
+
end
|
130
|
+
rd.each do |fd|
|
131
|
+
if fd.handle.eof?
|
132
|
+
remove_connection fd
|
133
|
+
else
|
134
|
+
read_connection fd
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
join_worker_threads(@shutdown_timeout)
|
139
|
+
ensure
|
140
|
+
@shutdown_queue.push :shutdown
|
141
|
+
end
|
142
|
+
|
143
|
+
def read_connection(fd)
|
144
|
+
@buffers[fd] << fd.read(DEFAULT_BUFFER)
|
145
|
+
frame = slice_frame!(@buffers[fd])
|
146
|
+
if frame
|
147
|
+
@logger.debug "#{self} is processing a frame"
|
148
|
+
@worker_queue.push [:frame, fd, frame]
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def spin_worker_threads
|
153
|
+
@logger.debug "#{self} is spinning up worker threads"
|
154
|
+
@worker_threads = []
|
155
|
+
@num_threads.times do
|
156
|
+
@worker_threads << spin_thread
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def spin_thread
|
161
|
+
Worker.new(@processor, @transportFactory, @protocolFactory, @logger, @worker_queue).spawn
|
162
|
+
end
|
163
|
+
|
164
|
+
def signal(msg)
|
165
|
+
@signal_queue << msg
|
166
|
+
@signal_pipes[1].write " "
|
167
|
+
end
|
168
|
+
|
169
|
+
def read_signals
|
170
|
+
# clear the signal pipe
|
171
|
+
# note that since read_nonblock is broken in jruby,
|
172
|
+
# we can only read up to a set number of signals at once
|
173
|
+
sigstr = @signal_pipes[0].readpartial(1024)
|
174
|
+
# now read the signals
|
175
|
+
begin
|
176
|
+
sigstr.length.times do
|
177
|
+
signal, obj = @signal_queue.pop(true)
|
178
|
+
case signal
|
179
|
+
when :connection
|
180
|
+
@connections << obj
|
181
|
+
when :shutdown
|
182
|
+
@shutdown_timeout = obj
|
183
|
+
return :shutdown
|
184
|
+
end
|
185
|
+
end
|
186
|
+
rescue ThreadError
|
187
|
+
# out of signals
|
188
|
+
# note that in a perfect world this would never happen, since we're
|
189
|
+
# only reading the number of signals pushed on the pipe, but given the lack
|
190
|
+
# of locks, in theory we could clear the pipe/queue while a new signal is being
|
191
|
+
# placed on the pipe, at which point our next read_signals would hit this error
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def remove_connection(fd)
|
196
|
+
# don't explicitly close it, a thread may still be writing to it
|
197
|
+
@connections.delete fd
|
198
|
+
@buffers.delete fd
|
199
|
+
end
|
200
|
+
|
201
|
+
def join_worker_threads(shutdown_timeout)
|
202
|
+
start = Time.now
|
203
|
+
@worker_threads.each do |t|
|
204
|
+
if shutdown_timeout > 0
|
205
|
+
timeout = (start + shutdown_timeout) - Time.now
|
206
|
+
break if timeout <= 0
|
207
|
+
t.join(timeout)
|
208
|
+
else
|
209
|
+
t.join
|
210
|
+
end
|
211
|
+
end
|
212
|
+
kill_worker_threads
|
213
|
+
end
|
214
|
+
|
215
|
+
def kill_worker_threads
|
216
|
+
@worker_threads.each do |t|
|
217
|
+
t.kill if t.status
|
218
|
+
end
|
219
|
+
@worker_threads.clear
|
220
|
+
end
|
221
|
+
|
222
|
+
def slice_frame!(buf)
|
223
|
+
if buf.length >= 4
|
224
|
+
size = buf.unpack('N').first
|
225
|
+
if buf.length >= size + 4
|
226
|
+
buf.slice!(0, size + 4)
|
227
|
+
else
|
228
|
+
nil
|
229
|
+
end
|
230
|
+
else
|
231
|
+
nil
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
class Worker # :nodoc:
|
236
|
+
def initialize(processor, transportFactory, protocolFactory, logger, queue)
|
237
|
+
@processor = processor
|
238
|
+
@transportFactory = transportFactory
|
239
|
+
@protocolFactory = protocolFactory
|
240
|
+
@logger = logger
|
241
|
+
@queue = queue
|
242
|
+
end
|
243
|
+
|
244
|
+
def spawn
|
245
|
+
Thread.new do
|
246
|
+
@logger.debug "#{self} is spawning"
|
247
|
+
run
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
private
|
252
|
+
|
253
|
+
def run
|
254
|
+
loop do
|
255
|
+
cmd, *args = @queue.pop
|
256
|
+
case cmd
|
257
|
+
when :shutdown
|
258
|
+
@logger.debug "#{self} is shutting down, goodbye"
|
259
|
+
break
|
260
|
+
when :frame
|
261
|
+
fd, frame = args
|
262
|
+
begin
|
263
|
+
otrans = @transportFactory.get_transport(fd)
|
264
|
+
oprot = @protocolFactory.get_protocol(otrans)
|
265
|
+
membuf = MemoryBuffer.new(frame)
|
266
|
+
itrans = @transportFactory.get_transport(membuf)
|
267
|
+
iprot = @protocolFactory.get_protocol(itrans)
|
268
|
+
@processor.process(iprot, oprot)
|
269
|
+
rescue => e
|
270
|
+
@logger.error "#{Thread.current.inspect} raised error: #{e.inspect}\n#{e.backtrace.join("\n")}"
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
# Copyright (c) 2006- Facebook
|
2
|
+
# Distributed under the Apache Software License
|
3
|
+
#
|
4
|
+
# See accompanying file LICENSE or visit the Thrift site at:
|
5
|
+
# http://developers.facebook.com/thrift/
|
6
|
+
#
|
7
|
+
# Author: Mark Slee <mcslee@facebook.com>
|
8
|
+
#
|
9
|
+
require 'thrift/protocol'
|
10
|
+
require 'thrift/protocol/binaryprotocol'
|
11
|
+
require 'thrift/transport'
|
12
|
+
|
13
|
+
module Thrift
|
14
|
+
class Server
|
15
|
+
def initialize(processor, serverTransport, transportFactory=nil, protocolFactory=nil)
|
16
|
+
@processor = processor
|
17
|
+
@serverTransport = serverTransport
|
18
|
+
@transportFactory = transportFactory ? transportFactory : Thrift::TransportFactory.new
|
19
|
+
@protocolFactory = protocolFactory ? protocolFactory : Thrift::BinaryProtocolFactory.new
|
20
|
+
end
|
21
|
+
|
22
|
+
def serve; nil; end
|
23
|
+
end
|
24
|
+
deprecate_class! :TServer => Server
|
25
|
+
|
26
|
+
class SimpleServer < Server
|
27
|
+
def serve
|
28
|
+
begin
|
29
|
+
@serverTransport.listen
|
30
|
+
loop do
|
31
|
+
client = @serverTransport.accept
|
32
|
+
trans = @transportFactory.get_transport(client)
|
33
|
+
prot = @protocolFactory.get_protocol(trans)
|
34
|
+
begin
|
35
|
+
loop do
|
36
|
+
@processor.process(prot, prot)
|
37
|
+
end
|
38
|
+
rescue Thrift::TransportException, Thrift::ProtocolException
|
39
|
+
ensure
|
40
|
+
trans.close
|
41
|
+
end
|
42
|
+
end
|
43
|
+
ensure
|
44
|
+
@serverTransport.close
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
deprecate_class! :TSimpleServer => SimpleServer
|
49
|
+
end
|
50
|
+
|
51
|
+
# do *not* use fastthread
|
52
|
+
# it has a bug that triggers on NonblockingServer
|
53
|
+
require 'thread'
|
54
|
+
|
55
|
+
module Thrift
|
56
|
+
class ThreadedServer < Server
|
57
|
+
def serve
|
58
|
+
begin
|
59
|
+
@serverTransport.listen
|
60
|
+
loop do
|
61
|
+
client = @serverTransport.accept
|
62
|
+
trans = @transportFactory.get_transport(client)
|
63
|
+
prot = @protocolFactory.get_protocol(trans)
|
64
|
+
Thread.new(prot, trans) do |p, t|
|
65
|
+
begin
|
66
|
+
loop do
|
67
|
+
@processor.process(p, p)
|
68
|
+
end
|
69
|
+
rescue Thrift::TransportException, Thrift::ProtocolException
|
70
|
+
ensure
|
71
|
+
t.close
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
ensure
|
76
|
+
@serverTransport.close
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
deprecate_class! :TThreadedServer => ThreadedServer
|
81
|
+
|
82
|
+
class ThreadPoolServer < Server
|
83
|
+
def initialize(processor, serverTransport, transportFactory=nil, protocolFactory=nil, num=20)
|
84
|
+
super(processor, serverTransport, transportFactory, protocolFactory)
|
85
|
+
@thread_q = SizedQueue.new(num)
|
86
|
+
@exception_q = Queue.new
|
87
|
+
@running = false
|
88
|
+
end
|
89
|
+
|
90
|
+
## exceptions that happen in worker threads will be relayed here and
|
91
|
+
## must be caught. 'retry' can be used to continue. (threads will
|
92
|
+
## continue to run while the exception is being handled.)
|
93
|
+
def rescuable_serve
|
94
|
+
Thread.new { serve } unless @running
|
95
|
+
@running = true
|
96
|
+
raise @exception_q.pop
|
97
|
+
end
|
98
|
+
|
99
|
+
## exceptions that happen in worker threads simply cause that thread
|
100
|
+
## to die and another to be spawned in its place.
|
101
|
+
def serve
|
102
|
+
@serverTransport.listen
|
103
|
+
|
104
|
+
begin
|
105
|
+
loop do
|
106
|
+
@thread_q.push(:token)
|
107
|
+
Thread.new do
|
108
|
+
begin
|
109
|
+
loop do
|
110
|
+
client = @serverTransport.accept
|
111
|
+
trans = @transportFactory.get_transport(client)
|
112
|
+
prot = @protocolFactory.get_protocol(trans)
|
113
|
+
begin
|
114
|
+
loop do
|
115
|
+
@processor.process(prot, prot)
|
116
|
+
end
|
117
|
+
rescue Thrift::TransportException, Thrift::ProtocolException => e
|
118
|
+
ensure
|
119
|
+
trans.close
|
120
|
+
end
|
121
|
+
end
|
122
|
+
rescue => e
|
123
|
+
@exception_q.push(e)
|
124
|
+
ensure
|
125
|
+
@thread_q.pop # thread died!
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
ensure
|
130
|
+
@serverTransport.close
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
deprecate_class! :TThreadPoolServer => ThreadPoolServer
|
135
|
+
end
|
@@ -0,0 +1,272 @@
|
|
1
|
+
require 'thrift/types'
|
2
|
+
require 'set'
|
3
|
+
|
4
|
+
module Thrift
|
5
|
+
module Struct
|
6
|
+
def initialize(d={})
|
7
|
+
# get a copy of the default values to work on, removing defaults in favor of arguments
|
8
|
+
fields_with_defaults = fields_with_default_values.dup
|
9
|
+
|
10
|
+
# check if the defaults is empty, or if there are no parameters for this
|
11
|
+
# instantiation, and if so, don't bother overriding defaults.
|
12
|
+
unless fields_with_defaults.empty? || d.empty?
|
13
|
+
d.each_key do |name|
|
14
|
+
fields_with_defaults.delete(name.to_s)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# assign all the user-specified arguments
|
19
|
+
unless d.empty?
|
20
|
+
d.each do |name, value|
|
21
|
+
unless name_to_id(name.to_s)
|
22
|
+
raise Exception, "Unknown key given to #{self.class}.new: #{name}"
|
23
|
+
end
|
24
|
+
Thrift.check_type(value, struct_fields[name_to_id(name.to_s)], name) if Thrift.type_checking
|
25
|
+
instance_variable_set("@#{name}", value)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# assign all the default values
|
30
|
+
unless fields_with_defaults.empty?
|
31
|
+
fields_with_defaults.each do |name, default_value|
|
32
|
+
instance_variable_set("@#{name}", (default_value.dup rescue default_value))
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def fields_with_default_values
|
38
|
+
fields_with_default_values = self.class.instance_variable_get("@fields_with_default_values")
|
39
|
+
unless fields_with_default_values
|
40
|
+
fields_with_default_values = {}
|
41
|
+
struct_fields.each do |fid, field_def|
|
42
|
+
if field_def[:default]
|
43
|
+
fields_with_default_values[field_def[:name]] = field_def[:default]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
self.class.instance_variable_set("@fields_with_default_values", fields_with_default_values)
|
47
|
+
end
|
48
|
+
fields_with_default_values
|
49
|
+
end
|
50
|
+
|
51
|
+
def name_to_id(name)
|
52
|
+
names_to_ids = self.class.instance_variable_get("@names_to_ids")
|
53
|
+
unless names_to_ids
|
54
|
+
names_to_ids = {}
|
55
|
+
struct_fields.each do |fid, field_def|
|
56
|
+
names_to_ids[field_def[:name]] = fid
|
57
|
+
end
|
58
|
+
self.class.instance_variable_set("@names_to_ids", names_to_ids)
|
59
|
+
end
|
60
|
+
names_to_ids[name]
|
61
|
+
end
|
62
|
+
|
63
|
+
# Obsoleted by THRIFT-246, which generates this method inline
|
64
|
+
# TODO: Should be removed at some point. -- Kevin Clark
|
65
|
+
def struct_fields
|
66
|
+
self.class.const_get(:FIELDS)
|
67
|
+
end
|
68
|
+
|
69
|
+
def each_field
|
70
|
+
struct_fields.each do |fid, data|
|
71
|
+
yield fid, data[:type], data[:name], data[:default], data[:optional]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def inspect(skip_optional_nulls = true)
|
76
|
+
fields = []
|
77
|
+
each_field do |fid, type, name, default, optional|
|
78
|
+
value = instance_variable_get("@#{name}")
|
79
|
+
unless skip_optional_nulls && optional && value.nil?
|
80
|
+
fields << "#{name}:#{value.inspect}"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
"<#{self.class} #{fields.join(", ")}>"
|
84
|
+
end
|
85
|
+
|
86
|
+
def read(iprot)
|
87
|
+
# TODO(kevinclark): Make sure transport is C readable
|
88
|
+
if iprot.respond_to?(:decode_binary)
|
89
|
+
iprot.decode_binary(self, iprot.trans)
|
90
|
+
else
|
91
|
+
iprot.read_struct_begin
|
92
|
+
loop do
|
93
|
+
fname, ftype, fid = iprot.read_field_begin
|
94
|
+
break if (ftype == Types::STOP)
|
95
|
+
handle_message(iprot, fid, ftype)
|
96
|
+
iprot.read_field_end
|
97
|
+
end
|
98
|
+
iprot.read_struct_end
|
99
|
+
end
|
100
|
+
validate
|
101
|
+
end
|
102
|
+
|
103
|
+
def write(oprot)
|
104
|
+
validate
|
105
|
+
# if oprot.respond_to?(:encode_binary)
|
106
|
+
# # TODO(kevinclark): Clean this so I don't have to access the transport.
|
107
|
+
# oprot.trans.write oprot.encode_binary(self)
|
108
|
+
# else
|
109
|
+
oprot.write_struct_begin(self.class.name)
|
110
|
+
each_field do |fid, type, name|
|
111
|
+
unless (value = instance_variable_get("@#{name}")).nil?
|
112
|
+
if is_container? type
|
113
|
+
oprot.write_field_begin(name, type, fid)
|
114
|
+
write_container(oprot, value, struct_fields[fid])
|
115
|
+
oprot.write_field_end
|
116
|
+
else
|
117
|
+
oprot.write_field(name, type, fid, value)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
oprot.write_field_stop
|
122
|
+
oprot.write_struct_end
|
123
|
+
# end
|
124
|
+
end
|
125
|
+
|
126
|
+
def ==(other)
|
127
|
+
return false unless other.is_a?(self.class)
|
128
|
+
each_field do |fid, type, name, default|
|
129
|
+
return false unless self.instance_variable_get("@#{name}") == other.instance_variable_get("@#{name}")
|
130
|
+
end
|
131
|
+
true
|
132
|
+
end
|
133
|
+
|
134
|
+
def eql?(other)
|
135
|
+
self.class == other.class && self == other
|
136
|
+
end
|
137
|
+
|
138
|
+
# for the time being, we're ok with a naive hash. this could definitely be improved upon.
|
139
|
+
def hash
|
140
|
+
0
|
141
|
+
end
|
142
|
+
|
143
|
+
def self.field_accessor(klass, *fields)
|
144
|
+
fields.each do |field|
|
145
|
+
klass.send :attr_reader, field
|
146
|
+
klass.send :define_method, "#{field}=" do |value|
|
147
|
+
Thrift.check_type(value, klass::FIELDS.values.find { |f| f[:name].to_s == field.to_s }, field) if Thrift.type_checking
|
148
|
+
instance_variable_set("@#{field}", value)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
protected
|
154
|
+
|
155
|
+
def self.append_features(mod)
|
156
|
+
if mod.ancestors.include? ::Exception
|
157
|
+
mod.send :class_variable_set, :'@@__thrift_struct_real_initialize', mod.instance_method(:initialize)
|
158
|
+
super
|
159
|
+
# set up our custom initializer so `raise Xception, 'message'` works
|
160
|
+
mod.send :define_method, :struct_initialize, mod.instance_method(:initialize)
|
161
|
+
mod.send :define_method, :initialize, mod.instance_method(:exception_initialize)
|
162
|
+
else
|
163
|
+
super
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def exception_initialize(*args, &block)
|
168
|
+
if args.size == 1 and args.first.is_a? Hash
|
169
|
+
# looks like it's a regular Struct initialize
|
170
|
+
method(:struct_initialize).call(args.first)
|
171
|
+
else
|
172
|
+
# call the Struct initializer first with no args
|
173
|
+
# this will set our field default values
|
174
|
+
method(:struct_initialize).call()
|
175
|
+
# now give it to the exception
|
176
|
+
self.class.send(:class_variable_get, :'@@__thrift_struct_real_initialize').bind(self).call(*args, &block) if args.size > 0
|
177
|
+
# self.class.instance_method(:initialize).bind(self).call(*args, &block)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def handle_message(iprot, fid, ftype)
|
182
|
+
field = struct_fields[fid]
|
183
|
+
if field and field[:type] == ftype
|
184
|
+
value = read_field(iprot, field)
|
185
|
+
instance_variable_set("@#{field[:name]}", value)
|
186
|
+
else
|
187
|
+
iprot.skip(ftype)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def read_field(iprot, field = {})
|
192
|
+
case field[:type]
|
193
|
+
when Types::STRUCT
|
194
|
+
value = field[:class].new
|
195
|
+
value.read(iprot)
|
196
|
+
when Types::MAP
|
197
|
+
key_type, val_type, size = iprot.read_map_begin
|
198
|
+
value = {}
|
199
|
+
size.times do
|
200
|
+
k = read_field(iprot, field_info(field[:key]))
|
201
|
+
v = read_field(iprot, field_info(field[:value]))
|
202
|
+
value[k] = v
|
203
|
+
end
|
204
|
+
iprot.read_map_end
|
205
|
+
when Types::LIST
|
206
|
+
e_type, size = iprot.read_list_begin
|
207
|
+
value = Array.new(size) do |n|
|
208
|
+
read_field(iprot, field_info(field[:element]))
|
209
|
+
end
|
210
|
+
iprot.read_list_end
|
211
|
+
when Types::SET
|
212
|
+
e_type, size = iprot.read_set_begin
|
213
|
+
value = Set.new
|
214
|
+
size.times do
|
215
|
+
element = read_field(iprot, field_info(field[:element]))
|
216
|
+
value << element
|
217
|
+
end
|
218
|
+
iprot.read_set_end
|
219
|
+
else
|
220
|
+
value = iprot.read_type(field[:type])
|
221
|
+
end
|
222
|
+
value
|
223
|
+
end
|
224
|
+
|
225
|
+
def write_data(oprot, value, field)
|
226
|
+
if is_container? field[:type]
|
227
|
+
write_container(oprot, value, field)
|
228
|
+
else
|
229
|
+
oprot.write_type(field[:type], value)
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def write_container(oprot, value, field = {})
|
234
|
+
case field[:type]
|
235
|
+
when Types::MAP
|
236
|
+
oprot.write_map_begin(field[:key][:type], field[:value][:type], value.size)
|
237
|
+
value.each do |k, v|
|
238
|
+
write_data(oprot, k, field[:key])
|
239
|
+
write_data(oprot, v, field[:value])
|
240
|
+
end
|
241
|
+
oprot.write_map_end
|
242
|
+
when Types::LIST
|
243
|
+
oprot.write_list_begin(field[:element][:type], value.size)
|
244
|
+
value.each do |elem|
|
245
|
+
write_data(oprot, elem, field[:element])
|
246
|
+
end
|
247
|
+
oprot.write_list_end
|
248
|
+
when Types::SET
|
249
|
+
oprot.write_set_begin(field[:element][:type], value.size)
|
250
|
+
value.each do |v,| # the , is to preserve compatibility with the old Hash-style sets
|
251
|
+
write_data(oprot, v, field[:element])
|
252
|
+
end
|
253
|
+
oprot.write_set_end
|
254
|
+
else
|
255
|
+
raise "Not a container type: #{field[:type]}"
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
def is_container?(type)
|
260
|
+
[Types::LIST, Types::MAP, Types::SET].include? type
|
261
|
+
end
|
262
|
+
|
263
|
+
def field_info(field)
|
264
|
+
{ :type => field[:type],
|
265
|
+
:class => field[:class],
|
266
|
+
:key => field[:key],
|
267
|
+
:value => field[:value],
|
268
|
+
:element => field[:element] }
|
269
|
+
end
|
270
|
+
end
|
271
|
+
deprecate_module! :ThriftStruct => Struct
|
272
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# This file kept for backwards compatability
|
2
|
+
# require File.join(File.dirname(__FILE__), '../thrift')
|
3
|
+
$:.unshift File.dirname(File.dirname(__FILE__))
|
4
|
+
require 'thrift/deprecation'
|
5
|
+
require 'thrift/types'
|
6
|
+
require 'thrift/processor'
|
7
|
+
require 'thrift/exceptions'
|
8
|
+
require 'thrift/client'
|
9
|
+
require 'thrift/struct'
|
10
|
+
begin
|
11
|
+
require "thrift_native"
|
12
|
+
rescue
|
13
|
+
puts "Could not load thrift_native libraries. Using pure ruby version."
|
14
|
+
end
|