hessian2 2.0.2 → 2.0.3
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.
- checksums.yaml +4 -4
- data/.gitignore +10 -10
- data/README.md +197 -197
- data/hessian2.gemspec +0 -2
- data/lib/hessian2/client.rb +57 -50
- data/lib/hessian2/constants.rb +164 -164
- data/lib/hessian2/fault.rb +3 -3
- data/lib/hessian2/handler.rb +18 -18
- data/lib/hessian2/hessian_client.rb +3 -3
- data/lib/hessian2/parser.rb +619 -619
- data/lib/hessian2/type_wrapper.rb +49 -49
- data/lib/hessian2/version.rb +3 -3
- data/lib/hessian2/writer.rb +499 -499
- data/lib/hessian2.rb +14 -14
- data/spec/binary_spec.rb +51 -51
- data/spec/boolean_spec.rb +26 -26
- data/spec/class_wrapper_spec.rb +52 -52
- data/spec/create_monkeys.rb +14 -14
- data/spec/date_spec.rb +45 -45
- data/spec/double_spec.rb +78 -78
- data/spec/int_spec.rb +54 -54
- data/spec/list_spec.rb +66 -66
- data/spec/long_spec.rb +68 -68
- data/spec/map_spec.rb +36 -36
- data/spec/null_spec.rb +17 -17
- data/spec/object_spec.rb +78 -78
- data/spec/ref_spec.rb +43 -43
- data/spec/spec_helper.rb +23 -23
- data/spec/string_spec.rb +61 -61
- data/spec/struct_wrapper_spec.rb +47 -47
- data/spec/type_wrapper_spec.rb +102 -102
- data/test/app.rb +24 -24
- data/test/async/em_http_asleep.rb +25 -25
- data/test/async/em_http_sleep.rb +25 -25
- data/test/async/monkey.asleep.rb +27 -27
- data/test/async/mysql2_aquery.rb +37 -37
- data/test/fault/monkey.undefined_method.rb +5 -5
- data/test/fault/monkey.wrong_arguments.rb +5 -5
- data/test/fiber_concurrency/em_http_asleep.rb +17 -17
- data/test/fiber_concurrency/em_http_sleep.rb +17 -17
- data/test/fiber_concurrency/monkey.asleep.fiber_aware.rb +18 -18
- data/test/fiber_concurrency/mysql2_query.rb +29 -29
- data/test/fiber_concurrency/net_http_asleep.rb +19 -19
- data/test/fiber_concurrency/net_http_sleep.rb +19 -19
- data/test/fibered_rainbows/Gemfile +15 -15
- data/test/fibered_rainbows/config.ru +11 -11
- data/test/fibered_rainbows/rainbows.rb +13 -13
- data/test/monkey_service.rb +16 -16
- data/test/prepare.rb +7 -7
- data/test/thread_concurrency/active_record_execute.rb +29 -29
- data/test/thread_concurrency/monkey.asleep.rb +22 -22
- data/test/thread_concurrency/net_http_asleep.rb +24 -24
- data/test/thread_concurrency/net_http_sleep.rb +24 -24
- data/test/threaded_rainbows/Gemfile +13 -13
- data/test/threaded_rainbows/config.ru +9 -9
- data/test/threaded_rainbows/rainbows.rb +13 -13
- metadata +4 -74
data/lib/hessian2/writer.rb
CHANGED
@@ -1,499 +1,499 @@
|
|
1
|
-
require 'hessian2/constants'
|
2
|
-
require 'bigdecimal'
|
3
|
-
require 'active_record'
|
4
|
-
|
5
|
-
module Hessian2
|
6
|
-
module Writer
|
7
|
-
include Constants
|
8
|
-
|
9
|
-
def call(method, args)
|
10
|
-
refs, crefs, trefs = {}, {}, {}
|
11
|
-
out = [ 'H', '2', '0', 'C' ].pack('ahha')
|
12
|
-
out << write_string(method)
|
13
|
-
out << write_int(args.size)
|
14
|
-
args.each{|arg| out << write(arg, refs, crefs, trefs) }
|
15
|
-
|
16
|
-
out
|
17
|
-
end
|
18
|
-
|
19
|
-
|
20
|
-
def reply(val)
|
21
|
-
[ 'H', '2', '0', 'R' ].pack('ahha') << write(val)
|
22
|
-
end
|
23
|
-
|
24
|
-
|
25
|
-
def write_fault(e)
|
26
|
-
val = {
|
27
|
-
code: e.class.to_s,
|
28
|
-
message: e.message,
|
29
|
-
detail: e.backtrace }
|
30
|
-
[ 'F' ].pack('a') << write_hash(val)
|
31
|
-
end
|
32
|
-
|
33
|
-
|
34
|
-
def write(val, refs = {}, crefs = {}, trefs = {})
|
35
|
-
case val
|
36
|
-
when StructWrapper # object to values-array
|
37
|
-
write_struct_wrapper(val, refs, crefs, trefs)
|
38
|
-
when ClassWrapper # class definition for statically typed languages
|
39
|
-
write_class_wrapper(val, refs, crefs, trefs)
|
40
|
-
when TypeWrapper
|
41
|
-
write_type_wrapper(val, refs, crefs, trefs)
|
42
|
-
when TrueClass
|
43
|
-
[ BC_TRUE ].pack('C')
|
44
|
-
when FalseClass
|
45
|
-
[ BC_FALSE ].pack('C')
|
46
|
-
when Time
|
47
|
-
if val.usec == 0 && val.sec == 0 # date in minutes
|
48
|
-
[ BC_DATE_MINUTE, val.to_i / 60 ].pack('CL>')
|
49
|
-
else
|
50
|
-
[ BC_DATE, val.to_i * 1000 + val.usec / 1000 ].pack('CQ>') # date
|
51
|
-
end
|
52
|
-
when Float, BigDecimal
|
53
|
-
write_float(val)
|
54
|
-
when Fixnum
|
55
|
-
write_int(val)
|
56
|
-
when Array, ActiveRecord::Relation
|
57
|
-
write_array(val, refs, crefs, trefs)
|
58
|
-
when Bignum
|
59
|
-
if val >= -0x80_000_000 && val <= 0x7f_fff_fff # four octet longs
|
60
|
-
[ BC_LONG_INT, val ].pack('Cl>')
|
61
|
-
else # long
|
62
|
-
[ BC_LONG, val ].pack('Cq>')
|
63
|
-
end
|
64
|
-
when Hash
|
65
|
-
write_hash(val, refs, crefs, trefs)
|
66
|
-
when NilClass
|
67
|
-
write_nil
|
68
|
-
when String
|
69
|
-
write_string(val)
|
70
|
-
when Symbol
|
71
|
-
write_string(val.to_s)
|
72
|
-
else
|
73
|
-
write_object(val, refs, crefs, trefs)
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
|
78
|
-
def print_string(str)
|
79
|
-
return str.b if String.method_defined?(:b)
|
80
|
-
|
81
|
-
arr, i = Array.new(str.bytesize), 0
|
82
|
-
str.unpack('U*').each do |c|
|
83
|
-
if c < 0x80 # 0xxxxxxx
|
84
|
-
arr[i] = c
|
85
|
-
elsif c < 0x800 # 110xxxxx 10xxxxxx
|
86
|
-
arr[i] = 0xc0 + ((c >> 6) & 0x1f)
|
87
|
-
arr[i += 1] = 0x80 + (c & 0x3f)
|
88
|
-
else # 1110xxxx 10xxxxxx 10xxxxxx
|
89
|
-
arr[i] = 0xe0 + ((c >> 12) & 0xf)
|
90
|
-
arr[i += 1] = 0x80 + ((c >> 6) & 0x3f)
|
91
|
-
arr[i += 1] = 0x80 + (c & 0x3f)
|
92
|
-
end
|
93
|
-
i += 1
|
94
|
-
end
|
95
|
-
|
96
|
-
arr.pack('C*')
|
97
|
-
end
|
98
|
-
|
99
|
-
|
100
|
-
def write_array(arr, refs = {}, crefs = {}, trefs = {})
|
101
|
-
idx = refs[arr.object_id]
|
102
|
-
return write_ref(idx) if idx
|
103
|
-
|
104
|
-
refs[arr.object_id] = refs.size
|
105
|
-
len = arr.size
|
106
|
-
if len <= LIST_DIRECT_MAX # [x78-7f] value*
|
107
|
-
str = [ BC_LIST_DIRECT_UNTYPED + len ].pack('C')
|
108
|
-
else # x58 int value*
|
109
|
-
str = [ BC_LIST_FIXED_UNTYPED ].pack('C') << write_int(len)
|
110
|
-
end
|
111
|
-
|
112
|
-
arr.each do |ele|
|
113
|
-
str << write(ele, refs, crefs, trefs)
|
114
|
-
end
|
115
|
-
|
116
|
-
str
|
117
|
-
end
|
118
|
-
|
119
|
-
|
120
|
-
def write_binary(str)
|
121
|
-
chunks, i, len = [], 0, str.size
|
122
|
-
while len > 0x8000
|
123
|
-
chunks << [ BC_BINARY_CHUNK, 0x8000 ].pack('Cn') << str[i...(i += 0x8000)]
|
124
|
-
len -= 0x8000
|
125
|
-
end
|
126
|
-
|
127
|
-
final = str[i..-1]
|
128
|
-
if len <= BINARY_DIRECT_MAX
|
129
|
-
chunks << [ BC_BINARY_DIRECT + len ].pack('C') << final
|
130
|
-
elsif len <= BINARY_SHORT_MAX
|
131
|
-
chunks << [ BC_BINARY_SHORT + (len >> 8), len ].pack('CC') << final
|
132
|
-
else
|
133
|
-
chunks << [ BC_BINARY, len ].pack('Cn') << final
|
134
|
-
end
|
135
|
-
|
136
|
-
chunks.join
|
137
|
-
end
|
138
|
-
|
139
|
-
|
140
|
-
def write_class_wrapper(val, refs, crefs, trefs)
|
141
|
-
return write_nil unless val.values
|
142
|
-
|
143
|
-
idx = refs[val.object_id]
|
144
|
-
return write_ref(idx) if idx
|
145
|
-
|
146
|
-
refs[val.object_id] = refs.size
|
147
|
-
|
148
|
-
if val.is_multi?
|
149
|
-
type = '[' << val.klass
|
150
|
-
if trefs.include?(type)
|
151
|
-
tstr = write_int(trefs[type])
|
152
|
-
else
|
153
|
-
trefs[type] = trefs.size # store a type
|
154
|
-
tstr = write_string(type)
|
155
|
-
end
|
156
|
-
return [ BC_LIST_DIRECT ].pack('C') << tstr if val.values.size == 0
|
157
|
-
end
|
158
|
-
|
159
|
-
cref = crefs[val.klass]
|
160
|
-
if cref
|
161
|
-
cidx = cref.first
|
162
|
-
fields = cref.last
|
163
|
-
str = ''
|
164
|
-
else
|
165
|
-
fstr = val.fields.map{|f| write_string(f) }.join
|
166
|
-
|
167
|
-
str = [ BC_OBJECT_DEF ].pack('C') << write_string(val.klass) << write_int(val.fields.size) << fstr
|
168
|
-
cidx = crefs.size
|
169
|
-
crefs[val.klass] = [cidx, val.fields] # store a class definition
|
170
|
-
end
|
171
|
-
|
172
|
-
if cidx <= OBJECT_DIRECT_MAX
|
173
|
-
cstr = [ BC_OBJECT_DIRECT + cidx ].pack('C')
|
174
|
-
else
|
175
|
-
cstr = [ BC_OBJECT ].pack('C') << write_int(cidx)
|
176
|
-
end
|
177
|
-
|
178
|
-
if val.is_multi?
|
179
|
-
len = val.values.size
|
180
|
-
if len <= LIST_DIRECT_MAX # [x70-77] type value*
|
181
|
-
str << [ BC_LIST_DIRECT + len ].pack('C') << tstr
|
182
|
-
else # 'V' type int value*
|
183
|
-
str << [ BC_LIST_FIXED ].pack('C') << tstr << write_int(len)
|
184
|
-
end
|
185
|
-
|
186
|
-
val.values.each do |ele|
|
187
|
-
if ele
|
188
|
-
ele_idx = refs[ele.object_id]
|
189
|
-
if ele_idx
|
190
|
-
str << (cstr + write_ref(ele_idx))
|
191
|
-
else
|
192
|
-
refs[ele.object_id] = refs.size
|
193
|
-
str << (cstr + ele.map{|v| write(v, refs, crefs, trefs)}.join)
|
194
|
-
end
|
195
|
-
else
|
196
|
-
str << write_nil
|
197
|
-
end
|
198
|
-
end
|
199
|
-
else
|
200
|
-
str << (cstr + val.values.map{|v| write(v, refs, crefs, trefs)}.join)
|
201
|
-
end
|
202
|
-
|
203
|
-
str
|
204
|
-
end
|
205
|
-
|
206
|
-
|
207
|
-
def write_float(val)
|
208
|
-
case val.infinite?
|
209
|
-
when 1
|
210
|
-
return [ BC_DOUBLE, Float::INFINITY ].pack('CG')
|
211
|
-
when -1
|
212
|
-
return [ BC_DOUBLE, -Float::INFINITY ].pack('CG')
|
213
|
-
else
|
214
|
-
return [ BC_DOUBLE, Float::NAN ].pack('CG') if val.nan?
|
215
|
-
return [ BC_DOUBLE_ZERO ].pack('C') if val.zero? # double zero
|
216
|
-
return [ BC_DOUBLE_ONE ].pack('C') if val == 1 # double one
|
217
|
-
|
218
|
-
ival = val.to_i
|
219
|
-
if ival == val
|
220
|
-
return [ BC_DOUBLE_BYTE, ival ].pack('Cc') if ival >= -0x80 && ival <= 0x7f # double octet
|
221
|
-
return [ BC_DOUBLE_SHORT, (ival >> 8), ival ].pack('Ccc') if ival >= -0x8000 && ival <= 0x7fff # double short
|
222
|
-
end
|
223
|
-
|
224
|
-
mval = val * 1000
|
225
|
-
if mval.finite?
|
226
|
-
mills = mval.to_i
|
227
|
-
if mills >= -0x80_000_000 && mills <= 0x7f_fff_fff && 0.001 * mills == val
|
228
|
-
return [ BC_DOUBLE_MILL, mills ].pack('Cl>') # double mill
|
229
|
-
end
|
230
|
-
end
|
231
|
-
|
232
|
-
[ BC_DOUBLE, val ].pack('CG') # double
|
233
|
-
end
|
234
|
-
end
|
235
|
-
|
236
|
-
|
237
|
-
def write_hash(hash, refs = {}, crefs = {}, trefs = {})
|
238
|
-
idx = refs[hash.object_id]
|
239
|
-
return write_ref(idx) if idx
|
240
|
-
|
241
|
-
refs[hash.object_id] = refs.size
|
242
|
-
str = [ BC_MAP_UNTYPED ].pack('C')
|
243
|
-
hash.each do |k, v|
|
244
|
-
str << write(k, refs, crefs, trefs)
|
245
|
-
str << write(v, refs, crefs, trefs)
|
246
|
-
end
|
247
|
-
|
248
|
-
str << [ BC_END ].pack('C')
|
249
|
-
end
|
250
|
-
|
251
|
-
|
252
|
-
def write_int(val)
|
253
|
-
if val >= INT_DIRECT_MIN && val <= INT_DIRECT_MAX # single octet integers
|
254
|
-
[ BC_INT_ZERO + val ].pack('c')
|
255
|
-
elsif val >= INT_BYTE_MIN && val <= INT_BYTE_MAX # two octet integers
|
256
|
-
[ BC_INT_BYTE_ZERO + (val >> 8), val ].pack('cc')
|
257
|
-
elsif val >= INT_SHORT_MIN && val <= INT_SHORT_MAX # three octet integers
|
258
|
-
[ BC_INT_SHORT_ZERO + (val >> 16), (val >> 8), val].pack('ccc')
|
259
|
-
elsif val >= -0x80_000_000 && val <= 0x7f_fff_fff # integer
|
260
|
-
[ BC_INT, val ].pack('Cl>')
|
261
|
-
else
|
262
|
-
[ BC_LONG, val ].pack('Cq>')
|
263
|
-
end
|
264
|
-
end
|
265
|
-
|
266
|
-
|
267
|
-
def write_long(val)
|
268
|
-
if val >= LONG_DIRECT_MIN && val <= LONG_DIRECT_MAX # single octet longs
|
269
|
-
[ BC_LONG_ZERO + val ].pack('c')
|
270
|
-
elsif val >= LONG_BYTE_MIN && val <= LONG_BYTE_MAX # two octet longs
|
271
|
-
[ BC_LONG_BYTE_ZERO + (val >> 8), val ].pack('cc')
|
272
|
-
elsif val >= LONG_SHORT_MIN && val <= LONG_SHORT_MAX # three octet longs
|
273
|
-
[ BC_LONG_SHORT_ZERO + (val >> 16), (val >> 8), val ].pack('ccc')
|
274
|
-
elsif val >= -0x80_000_000 && val <= 0x7f_fff_fff # four octet longs
|
275
|
-
[ BC_LONG_INT, val ].pack('Cl>')
|
276
|
-
else
|
277
|
-
[ BC_LONG, val ].pack('Cq>')
|
278
|
-
end
|
279
|
-
end
|
280
|
-
|
281
|
-
|
282
|
-
def write_nil
|
283
|
-
[ BC_NULL ].pack('C')
|
284
|
-
end
|
285
|
-
|
286
|
-
|
287
|
-
def write_object(object, refs = {}, crefs = {}, trefs = {})
|
288
|
-
return write_nil unless object
|
289
|
-
|
290
|
-
idx = refs[object.object_id]
|
291
|
-
return write_ref(idx) if idx
|
292
|
-
|
293
|
-
refs[object.object_id] = refs.size
|
294
|
-
|
295
|
-
klass = object.class.to_s
|
296
|
-
cref = crefs[klass]
|
297
|
-
if cref
|
298
|
-
cidx = cref.first
|
299
|
-
fields = cref.last
|
300
|
-
str = ''
|
301
|
-
else
|
302
|
-
fields = get_fields(object)
|
303
|
-
fstr = fields.map{|f| write_string(f) }.join
|
304
|
-
cidx = crefs.size
|
305
|
-
crefs[klass] = [cidx, fields]
|
306
|
-
|
307
|
-
str = [ BC_OBJECT_DEF ].pack('C') << write_string(klass) << write_int(fields.size) << fstr
|
308
|
-
end
|
309
|
-
|
310
|
-
if cidx <= OBJECT_DIRECT_MAX
|
311
|
-
cstr = [ BC_OBJECT_DIRECT + cidx ].pack('C')
|
312
|
-
else
|
313
|
-
cstr = [ BC_OBJECT ].pack('C') << write_int(cidx)
|
314
|
-
end
|
315
|
-
|
316
|
-
str << write_values(object, cstr, fields, refs, crefs, trefs)
|
317
|
-
|
318
|
-
str
|
319
|
-
end
|
320
|
-
|
321
|
-
|
322
|
-
def write_ref(val)
|
323
|
-
[ BC_REF ].pack('C') << write_int(val)
|
324
|
-
end
|
325
|
-
|
326
|
-
|
327
|
-
def write_struct_wrapper(val, refs, crefs, trefs)
|
328
|
-
return write_nil unless val.values
|
329
|
-
|
330
|
-
idx = refs[val.object_id]
|
331
|
-
return write_ref(idx) if idx
|
332
|
-
|
333
|
-
refs[val.object_id] = refs.size
|
334
|
-
|
335
|
-
write_array(val.values, refs, crefs, trefs)
|
336
|
-
end
|
337
|
-
|
338
|
-
|
339
|
-
def write_string(str)
|
340
|
-
chunks, i, len = '', 0, str.size
|
341
|
-
while len > 0x8000
|
342
|
-
chunks << [ BC_STRING_CHUNK, 0x8000 ].pack('Cn') << print_string(str[i...(i += 0x8000)])
|
343
|
-
len -= 0x8000
|
344
|
-
end
|
345
|
-
|
346
|
-
final = str[i..-1]
|
347
|
-
chunks << if len <= STRING_DIRECT_MAX
|
348
|
-
[ BC_STRING_DIRECT + len ].pack('C')
|
349
|
-
elsif len <= STRING_SHORT_MAX
|
350
|
-
[ BC_STRING_SHORT + (len >> 8), len ].pack('CC')
|
351
|
-
else
|
352
|
-
[ BC_STRING, len ].pack('Cn')
|
353
|
-
end
|
354
|
-
|
355
|
-
chunks << print_string(final)
|
356
|
-
end
|
357
|
-
|
358
|
-
|
359
|
-
def write_type_wrapped_array(arr, tstr, eletype, refs = {}, crefs = {}, trefs = {})
|
360
|
-
len = arr.size
|
361
|
-
return [ BC_LIST_DIRECT ].pack('C') << tstr if len == 0
|
362
|
-
|
363
|
-
if len <= LIST_DIRECT_MAX # [x70-77] type value*
|
364
|
-
str = [ BC_LIST_DIRECT + len ].pack('C') << tstr
|
365
|
-
else # 'V' type int value*
|
366
|
-
str = [ BC_LIST_FIXED ].pack('C') << tstr << write_int(len)
|
367
|
-
end
|
368
|
-
|
369
|
-
case eletype
|
370
|
-
when 'L'
|
371
|
-
arr.each do |ele|
|
372
|
-
str << write_long(Integer(ele))
|
373
|
-
end
|
374
|
-
when 'I'
|
375
|
-
arr.each do |ele|
|
376
|
-
str << write_int(Integer(ele))
|
377
|
-
end
|
378
|
-
when 'B'
|
379
|
-
arr.each do |ele|
|
380
|
-
str << write_binary(ele)
|
381
|
-
end
|
382
|
-
else
|
383
|
-
arr.each do |ele|
|
384
|
-
idx = refs[ele.object_id]
|
385
|
-
if idx
|
386
|
-
str << write_ref(idx)
|
387
|
-
else
|
388
|
-
refs[ele.object_id] = refs.size
|
389
|
-
str << write_type_wrapped_object(ele, tstr, refs, crefs, trefs)
|
390
|
-
end
|
391
|
-
end
|
392
|
-
end
|
393
|
-
|
394
|
-
str
|
395
|
-
end
|
396
|
-
|
397
|
-
|
398
|
-
def write_type_wrapped_object(object, tstr, refs = {}, crefs = {}, trefs = {})
|
399
|
-
return write_nil unless object
|
400
|
-
|
401
|
-
str = [ BC_MAP ].pack('C') << tstr
|
402
|
-
|
403
|
-
if object.is_a?(Hash)
|
404
|
-
object.each do |k, v|
|
405
|
-
str << write(k, refs, crefs, trefs)
|
406
|
-
str << write(v, refs, crefs, trefs)
|
407
|
-
end
|
408
|
-
elsif object.instance_variable_get(:@attributes).is_a?(Hash)
|
409
|
-
object.attributes.each do |k, v|
|
410
|
-
str << write(k, refs, crefs, trefs)
|
411
|
-
str << write(v, refs, crefs, trefs)
|
412
|
-
end
|
413
|
-
elsif object.is_a?(ClassWrapper)
|
414
|
-
object.fields.each_with_index do |f, i|
|
415
|
-
str << write(f, refs, crefs, trefs)
|
416
|
-
str << write(object.values[i], refs, crefs, trefs)
|
417
|
-
end
|
418
|
-
elsif object.is_a?(TypeWrapper)
|
419
|
-
object.object.each do |k, v|
|
420
|
-
str << write(k, refs, crefs, trefs)
|
421
|
-
str << write(v, refs, crefs, trefs)
|
422
|
-
end
|
423
|
-
else
|
424
|
-
object.instance_variables.each do |var|
|
425
|
-
str << write(var[1..-1], refs, crefs, trefs)
|
426
|
-
str << write(object.instance_variable_get(var), refs, crefs, trefs)
|
427
|
-
end
|
428
|
-
end
|
429
|
-
|
430
|
-
str << [ BC_END ].pack('C')
|
431
|
-
end
|
432
|
-
|
433
|
-
|
434
|
-
def write_type_wrapper(val, refs, crefs, trefs)
|
435
|
-
return write_nil unless val.object
|
436
|
-
|
437
|
-
idx = refs[val.object_id]
|
438
|
-
return write_ref(idx) if idx
|
439
|
-
|
440
|
-
refs[val.object_id] = refs.size
|
441
|
-
|
442
|
-
type = val.is_multi? ? ('[' << val.hessian_type) : val.hessian_type
|
443
|
-
if trefs.include?(type)
|
444
|
-
tstr = write_int(trefs[type])
|
445
|
-
else
|
446
|
-
trefs[type] = trefs.size
|
447
|
-
tstr = write_string(type)
|
448
|
-
end
|
449
|
-
|
450
|
-
if val.is_multi?
|
451
|
-
write_type_wrapped_array(val.object, tstr, val.hessian_type, refs, crefs, trefs)
|
452
|
-
else
|
453
|
-
case val.hessian_type
|
454
|
-
when 'L'
|
455
|
-
write_long(Integer(val.object))
|
456
|
-
when 'I'
|
457
|
-
write_int(Integer(val.object))
|
458
|
-
when 'B'
|
459
|
-
write_binary(val.object)
|
460
|
-
else
|
461
|
-
write_type_wrapped_object(val.object, tstr, refs, crefs, trefs)
|
462
|
-
end
|
463
|
-
end
|
464
|
-
end
|
465
|
-
|
466
|
-
|
467
|
-
private
|
468
|
-
|
469
|
-
def get_fields(object)
|
470
|
-
fields = if object.is_a?(Hash)
|
471
|
-
object.keys.map{|k| k.to_sym }
|
472
|
-
elsif object.instance_variable_get(:@attributes).is_a?(Hash)
|
473
|
-
object.attributes.keys.map{|k| k.to_sym }
|
474
|
-
else
|
475
|
-
object.instance_variables.map{|k| k[1..-1].to_sym }
|
476
|
-
end
|
477
|
-
|
478
|
-
raise "fields should not be empty: #{object.inspect}" if fields.empty?
|
479
|
-
|
480
|
-
fields
|
481
|
-
end
|
482
|
-
|
483
|
-
|
484
|
-
def write_values(object, cstr, fields, refs, crefs, trefs)
|
485
|
-
return write_nil unless object
|
486
|
-
|
487
|
-
vstr = if object.is_a?(Hash)
|
488
|
-
fields.map{|f| write(object[f] || object[f.to_s], refs, crefs, trefs) }.join
|
489
|
-
elsif object.instance_variable_get(:@attributes).is_a?(Hash)
|
490
|
-
fields.map{|f| write(object.attributes[f.to_s], refs, crefs, trefs) }.join
|
491
|
-
else
|
492
|
-
fields.map{|f| write(object.instance_variable_get(f.to_s.prepend('@')), refs, crefs, trefs) }.join
|
493
|
-
end
|
494
|
-
|
495
|
-
cstr + vstr
|
496
|
-
end
|
497
|
-
|
498
|
-
end
|
499
|
-
end
|
1
|
+
require 'hessian2/constants'
|
2
|
+
require 'bigdecimal'
|
3
|
+
require 'active_record'
|
4
|
+
|
5
|
+
module Hessian2
|
6
|
+
module Writer
|
7
|
+
include Constants
|
8
|
+
|
9
|
+
def call(method, args)
|
10
|
+
refs, crefs, trefs = {}, {}, {}
|
11
|
+
out = [ 'H', '2', '0', 'C' ].pack('ahha')
|
12
|
+
out << write_string(method)
|
13
|
+
out << write_int(args.size)
|
14
|
+
args.each{|arg| out << write(arg, refs, crefs, trefs) }
|
15
|
+
|
16
|
+
out
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
def reply(val)
|
21
|
+
[ 'H', '2', '0', 'R' ].pack('ahha') << write(val)
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
def write_fault(e)
|
26
|
+
val = {
|
27
|
+
code: e.class.to_s,
|
28
|
+
message: e.message,
|
29
|
+
detail: e.backtrace }
|
30
|
+
[ 'F' ].pack('a') << write_hash(val)
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
def write(val, refs = {}, crefs = {}, trefs = {})
|
35
|
+
case val
|
36
|
+
when StructWrapper # object to values-array
|
37
|
+
write_struct_wrapper(val, refs, crefs, trefs)
|
38
|
+
when ClassWrapper # class definition for statically typed languages
|
39
|
+
write_class_wrapper(val, refs, crefs, trefs)
|
40
|
+
when TypeWrapper
|
41
|
+
write_type_wrapper(val, refs, crefs, trefs)
|
42
|
+
when TrueClass
|
43
|
+
[ BC_TRUE ].pack('C')
|
44
|
+
when FalseClass
|
45
|
+
[ BC_FALSE ].pack('C')
|
46
|
+
when Time
|
47
|
+
if val.usec == 0 && val.sec == 0 # date in minutes
|
48
|
+
[ BC_DATE_MINUTE, val.to_i / 60 ].pack('CL>')
|
49
|
+
else
|
50
|
+
[ BC_DATE, val.to_i * 1000 + val.usec / 1000 ].pack('CQ>') # date
|
51
|
+
end
|
52
|
+
when Float, BigDecimal
|
53
|
+
write_float(val)
|
54
|
+
when Fixnum
|
55
|
+
write_int(val)
|
56
|
+
when Array, ActiveRecord::Relation
|
57
|
+
write_array(val, refs, crefs, trefs)
|
58
|
+
when Bignum
|
59
|
+
if val >= -0x80_000_000 && val <= 0x7f_fff_fff # four octet longs
|
60
|
+
[ BC_LONG_INT, val ].pack('Cl>')
|
61
|
+
else # long
|
62
|
+
[ BC_LONG, val ].pack('Cq>')
|
63
|
+
end
|
64
|
+
when Hash
|
65
|
+
write_hash(val, refs, crefs, trefs)
|
66
|
+
when NilClass
|
67
|
+
write_nil
|
68
|
+
when String
|
69
|
+
write_string(val)
|
70
|
+
when Symbol
|
71
|
+
write_string(val.to_s)
|
72
|
+
else
|
73
|
+
write_object(val, refs, crefs, trefs)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
def print_string(str)
|
79
|
+
return str.b if String.method_defined?(:b)
|
80
|
+
|
81
|
+
arr, i = Array.new(str.bytesize), 0
|
82
|
+
str.unpack('U*').each do |c|
|
83
|
+
if c < 0x80 # 0xxxxxxx
|
84
|
+
arr[i] = c
|
85
|
+
elsif c < 0x800 # 110xxxxx 10xxxxxx
|
86
|
+
arr[i] = 0xc0 + ((c >> 6) & 0x1f)
|
87
|
+
arr[i += 1] = 0x80 + (c & 0x3f)
|
88
|
+
else # 1110xxxx 10xxxxxx 10xxxxxx
|
89
|
+
arr[i] = 0xe0 + ((c >> 12) & 0xf)
|
90
|
+
arr[i += 1] = 0x80 + ((c >> 6) & 0x3f)
|
91
|
+
arr[i += 1] = 0x80 + (c & 0x3f)
|
92
|
+
end
|
93
|
+
i += 1
|
94
|
+
end
|
95
|
+
|
96
|
+
arr.pack('C*')
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
def write_array(arr, refs = {}, crefs = {}, trefs = {})
|
101
|
+
idx = refs[arr.object_id]
|
102
|
+
return write_ref(idx) if idx
|
103
|
+
|
104
|
+
refs[arr.object_id] = refs.size
|
105
|
+
len = arr.size
|
106
|
+
if len <= LIST_DIRECT_MAX # [x78-7f] value*
|
107
|
+
str = [ BC_LIST_DIRECT_UNTYPED + len ].pack('C')
|
108
|
+
else # x58 int value*
|
109
|
+
str = [ BC_LIST_FIXED_UNTYPED ].pack('C') << write_int(len)
|
110
|
+
end
|
111
|
+
|
112
|
+
arr.each do |ele|
|
113
|
+
str << write(ele, refs, crefs, trefs)
|
114
|
+
end
|
115
|
+
|
116
|
+
str
|
117
|
+
end
|
118
|
+
|
119
|
+
|
120
|
+
def write_binary(str)
|
121
|
+
chunks, i, len = [], 0, str.size
|
122
|
+
while len > 0x8000
|
123
|
+
chunks << [ BC_BINARY_CHUNK, 0x8000 ].pack('Cn') << str[i...(i += 0x8000)]
|
124
|
+
len -= 0x8000
|
125
|
+
end
|
126
|
+
|
127
|
+
final = str[i..-1]
|
128
|
+
if len <= BINARY_DIRECT_MAX
|
129
|
+
chunks << [ BC_BINARY_DIRECT + len ].pack('C') << final
|
130
|
+
elsif len <= BINARY_SHORT_MAX
|
131
|
+
chunks << [ BC_BINARY_SHORT + (len >> 8), len ].pack('CC') << final
|
132
|
+
else
|
133
|
+
chunks << [ BC_BINARY, len ].pack('Cn') << final
|
134
|
+
end
|
135
|
+
|
136
|
+
chunks.join
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
def write_class_wrapper(val, refs, crefs, trefs)
|
141
|
+
return write_nil unless val.values
|
142
|
+
|
143
|
+
idx = refs[val.object_id]
|
144
|
+
return write_ref(idx) if idx
|
145
|
+
|
146
|
+
refs[val.object_id] = refs.size
|
147
|
+
|
148
|
+
if val.is_multi?
|
149
|
+
type = '[' << val.klass
|
150
|
+
if trefs.include?(type)
|
151
|
+
tstr = write_int(trefs[type])
|
152
|
+
else
|
153
|
+
trefs[type] = trefs.size # store a type
|
154
|
+
tstr = write_string(type)
|
155
|
+
end
|
156
|
+
return [ BC_LIST_DIRECT ].pack('C') << tstr if val.values.size == 0
|
157
|
+
end
|
158
|
+
|
159
|
+
cref = crefs[val.klass]
|
160
|
+
if cref
|
161
|
+
cidx = cref.first
|
162
|
+
fields = cref.last
|
163
|
+
str = ''
|
164
|
+
else
|
165
|
+
fstr = val.fields.map{|f| write_string(f) }.join
|
166
|
+
|
167
|
+
str = [ BC_OBJECT_DEF ].pack('C') << write_string(val.klass) << write_int(val.fields.size) << fstr
|
168
|
+
cidx = crefs.size
|
169
|
+
crefs[val.klass] = [cidx, val.fields] # store a class definition
|
170
|
+
end
|
171
|
+
|
172
|
+
if cidx <= OBJECT_DIRECT_MAX
|
173
|
+
cstr = [ BC_OBJECT_DIRECT + cidx ].pack('C')
|
174
|
+
else
|
175
|
+
cstr = [ BC_OBJECT ].pack('C') << write_int(cidx)
|
176
|
+
end
|
177
|
+
|
178
|
+
if val.is_multi?
|
179
|
+
len = val.values.size
|
180
|
+
if len <= LIST_DIRECT_MAX # [x70-77] type value*
|
181
|
+
str << [ BC_LIST_DIRECT + len ].pack('C') << tstr
|
182
|
+
else # 'V' type int value*
|
183
|
+
str << [ BC_LIST_FIXED ].pack('C') << tstr << write_int(len)
|
184
|
+
end
|
185
|
+
|
186
|
+
val.values.each do |ele|
|
187
|
+
if ele
|
188
|
+
ele_idx = refs[ele.object_id]
|
189
|
+
if ele_idx
|
190
|
+
str << (cstr + write_ref(ele_idx))
|
191
|
+
else
|
192
|
+
refs[ele.object_id] = refs.size
|
193
|
+
str << (cstr + ele.map{|v| write(v, refs, crefs, trefs)}.join)
|
194
|
+
end
|
195
|
+
else
|
196
|
+
str << write_nil
|
197
|
+
end
|
198
|
+
end
|
199
|
+
else
|
200
|
+
str << (cstr + val.values.map{|v| write(v, refs, crefs, trefs)}.join)
|
201
|
+
end
|
202
|
+
|
203
|
+
str
|
204
|
+
end
|
205
|
+
|
206
|
+
|
207
|
+
def write_float(val)
|
208
|
+
case val.infinite?
|
209
|
+
when 1
|
210
|
+
return [ BC_DOUBLE, Float::INFINITY ].pack('CG')
|
211
|
+
when -1
|
212
|
+
return [ BC_DOUBLE, -Float::INFINITY ].pack('CG')
|
213
|
+
else
|
214
|
+
return [ BC_DOUBLE, Float::NAN ].pack('CG') if val.nan?
|
215
|
+
return [ BC_DOUBLE_ZERO ].pack('C') if val.zero? # double zero
|
216
|
+
return [ BC_DOUBLE_ONE ].pack('C') if val == 1 # double one
|
217
|
+
|
218
|
+
ival = val.to_i
|
219
|
+
if ival == val
|
220
|
+
return [ BC_DOUBLE_BYTE, ival ].pack('Cc') if ival >= -0x80 && ival <= 0x7f # double octet
|
221
|
+
return [ BC_DOUBLE_SHORT, (ival >> 8), ival ].pack('Ccc') if ival >= -0x8000 && ival <= 0x7fff # double short
|
222
|
+
end
|
223
|
+
|
224
|
+
mval = val * 1000
|
225
|
+
if mval.finite?
|
226
|
+
mills = mval.to_i
|
227
|
+
if mills >= -0x80_000_000 && mills <= 0x7f_fff_fff && 0.001 * mills == val
|
228
|
+
return [ BC_DOUBLE_MILL, mills ].pack('Cl>') # double mill
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
[ BC_DOUBLE, val ].pack('CG') # double
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
|
237
|
+
def write_hash(hash, refs = {}, crefs = {}, trefs = {})
|
238
|
+
idx = refs[hash.object_id]
|
239
|
+
return write_ref(idx) if idx
|
240
|
+
|
241
|
+
refs[hash.object_id] = refs.size
|
242
|
+
str = [ BC_MAP_UNTYPED ].pack('C')
|
243
|
+
hash.each do |k, v|
|
244
|
+
str << write(k, refs, crefs, trefs)
|
245
|
+
str << write(v, refs, crefs, trefs)
|
246
|
+
end
|
247
|
+
|
248
|
+
str << [ BC_END ].pack('C')
|
249
|
+
end
|
250
|
+
|
251
|
+
|
252
|
+
def write_int(val)
|
253
|
+
if val >= INT_DIRECT_MIN && val <= INT_DIRECT_MAX # single octet integers
|
254
|
+
[ BC_INT_ZERO + val ].pack('c')
|
255
|
+
elsif val >= INT_BYTE_MIN && val <= INT_BYTE_MAX # two octet integers
|
256
|
+
[ BC_INT_BYTE_ZERO + (val >> 8), val ].pack('cc')
|
257
|
+
elsif val >= INT_SHORT_MIN && val <= INT_SHORT_MAX # three octet integers
|
258
|
+
[ BC_INT_SHORT_ZERO + (val >> 16), (val >> 8), val].pack('ccc')
|
259
|
+
elsif val >= -0x80_000_000 && val <= 0x7f_fff_fff # integer
|
260
|
+
[ BC_INT, val ].pack('Cl>')
|
261
|
+
else
|
262
|
+
[ BC_LONG, val ].pack('Cq>')
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
|
267
|
+
def write_long(val)
|
268
|
+
if val >= LONG_DIRECT_MIN && val <= LONG_DIRECT_MAX # single octet longs
|
269
|
+
[ BC_LONG_ZERO + val ].pack('c')
|
270
|
+
elsif val >= LONG_BYTE_MIN && val <= LONG_BYTE_MAX # two octet longs
|
271
|
+
[ BC_LONG_BYTE_ZERO + (val >> 8), val ].pack('cc')
|
272
|
+
elsif val >= LONG_SHORT_MIN && val <= LONG_SHORT_MAX # three octet longs
|
273
|
+
[ BC_LONG_SHORT_ZERO + (val >> 16), (val >> 8), val ].pack('ccc')
|
274
|
+
elsif val >= -0x80_000_000 && val <= 0x7f_fff_fff # four octet longs
|
275
|
+
[ BC_LONG_INT, val ].pack('Cl>')
|
276
|
+
else
|
277
|
+
[ BC_LONG, val ].pack('Cq>')
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
|
282
|
+
def write_nil
|
283
|
+
[ BC_NULL ].pack('C')
|
284
|
+
end
|
285
|
+
|
286
|
+
|
287
|
+
def write_object(object, refs = {}, crefs = {}, trefs = {})
|
288
|
+
return write_nil unless object
|
289
|
+
|
290
|
+
idx = refs[object.object_id]
|
291
|
+
return write_ref(idx) if idx
|
292
|
+
|
293
|
+
refs[object.object_id] = refs.size
|
294
|
+
|
295
|
+
klass = object.class.to_s
|
296
|
+
cref = crefs[klass]
|
297
|
+
if cref
|
298
|
+
cidx = cref.first
|
299
|
+
fields = cref.last
|
300
|
+
str = ''
|
301
|
+
else
|
302
|
+
fields = get_fields(object)
|
303
|
+
fstr = fields.map{|f| write_string(f) }.join
|
304
|
+
cidx = crefs.size
|
305
|
+
crefs[klass] = [cidx, fields]
|
306
|
+
|
307
|
+
str = [ BC_OBJECT_DEF ].pack('C') << write_string(klass) << write_int(fields.size) << fstr
|
308
|
+
end
|
309
|
+
|
310
|
+
if cidx <= OBJECT_DIRECT_MAX
|
311
|
+
cstr = [ BC_OBJECT_DIRECT + cidx ].pack('C')
|
312
|
+
else
|
313
|
+
cstr = [ BC_OBJECT ].pack('C') << write_int(cidx)
|
314
|
+
end
|
315
|
+
|
316
|
+
str << write_values(object, cstr, fields, refs, crefs, trefs)
|
317
|
+
|
318
|
+
str
|
319
|
+
end
|
320
|
+
|
321
|
+
|
322
|
+
def write_ref(val)
|
323
|
+
[ BC_REF ].pack('C') << write_int(val)
|
324
|
+
end
|
325
|
+
|
326
|
+
|
327
|
+
def write_struct_wrapper(val, refs, crefs, trefs)
|
328
|
+
return write_nil unless val.values
|
329
|
+
|
330
|
+
idx = refs[val.object_id]
|
331
|
+
return write_ref(idx) if idx
|
332
|
+
|
333
|
+
refs[val.object_id] = refs.size
|
334
|
+
|
335
|
+
write_array(val.values, refs, crefs, trefs)
|
336
|
+
end
|
337
|
+
|
338
|
+
|
339
|
+
def write_string(str)
|
340
|
+
chunks, i, len = '', 0, str.size
|
341
|
+
while len > 0x8000
|
342
|
+
chunks << [ BC_STRING_CHUNK, 0x8000 ].pack('Cn') << print_string(str[i...(i += 0x8000)])
|
343
|
+
len -= 0x8000
|
344
|
+
end
|
345
|
+
|
346
|
+
final = str[i..-1]
|
347
|
+
chunks << if len <= STRING_DIRECT_MAX
|
348
|
+
[ BC_STRING_DIRECT + len ].pack('C')
|
349
|
+
elsif len <= STRING_SHORT_MAX
|
350
|
+
[ BC_STRING_SHORT + (len >> 8), len ].pack('CC')
|
351
|
+
else
|
352
|
+
[ BC_STRING, len ].pack('Cn')
|
353
|
+
end
|
354
|
+
|
355
|
+
chunks << print_string(final)
|
356
|
+
end
|
357
|
+
|
358
|
+
|
359
|
+
def write_type_wrapped_array(arr, tstr, eletype, refs = {}, crefs = {}, trefs = {})
|
360
|
+
len = arr.size
|
361
|
+
return [ BC_LIST_DIRECT ].pack('C') << tstr if len == 0
|
362
|
+
|
363
|
+
if len <= LIST_DIRECT_MAX # [x70-77] type value*
|
364
|
+
str = [ BC_LIST_DIRECT + len ].pack('C') << tstr
|
365
|
+
else # 'V' type int value*
|
366
|
+
str = [ BC_LIST_FIXED ].pack('C') << tstr << write_int(len)
|
367
|
+
end
|
368
|
+
|
369
|
+
case eletype
|
370
|
+
when 'L'
|
371
|
+
arr.each do |ele|
|
372
|
+
str << write_long(Integer(ele))
|
373
|
+
end
|
374
|
+
when 'I'
|
375
|
+
arr.each do |ele|
|
376
|
+
str << write_int(Integer(ele))
|
377
|
+
end
|
378
|
+
when 'B'
|
379
|
+
arr.each do |ele|
|
380
|
+
str << write_binary(ele)
|
381
|
+
end
|
382
|
+
else
|
383
|
+
arr.each do |ele|
|
384
|
+
idx = refs[ele.object_id]
|
385
|
+
if idx
|
386
|
+
str << write_ref(idx)
|
387
|
+
else
|
388
|
+
refs[ele.object_id] = refs.size
|
389
|
+
str << write_type_wrapped_object(ele, tstr, refs, crefs, trefs)
|
390
|
+
end
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
str
|
395
|
+
end
|
396
|
+
|
397
|
+
|
398
|
+
def write_type_wrapped_object(object, tstr, refs = {}, crefs = {}, trefs = {})
|
399
|
+
return write_nil unless object
|
400
|
+
|
401
|
+
str = [ BC_MAP ].pack('C') << tstr
|
402
|
+
|
403
|
+
if object.is_a?(Hash)
|
404
|
+
object.each do |k, v|
|
405
|
+
str << write(k, refs, crefs, trefs)
|
406
|
+
str << write(v, refs, crefs, trefs)
|
407
|
+
end
|
408
|
+
elsif object.instance_variable_get(:@attributes).is_a?(Hash)
|
409
|
+
object.attributes.each do |k, v|
|
410
|
+
str << write(k, refs, crefs, trefs)
|
411
|
+
str << write(v, refs, crefs, trefs)
|
412
|
+
end
|
413
|
+
elsif object.is_a?(ClassWrapper)
|
414
|
+
object.fields.each_with_index do |f, i|
|
415
|
+
str << write(f, refs, crefs, trefs)
|
416
|
+
str << write(object.values[i], refs, crefs, trefs)
|
417
|
+
end
|
418
|
+
elsif object.is_a?(TypeWrapper)
|
419
|
+
object.object.each do |k, v|
|
420
|
+
str << write(k, refs, crefs, trefs)
|
421
|
+
str << write(v, refs, crefs, trefs)
|
422
|
+
end
|
423
|
+
else
|
424
|
+
object.instance_variables.each do |var|
|
425
|
+
str << write(var[1..-1], refs, crefs, trefs)
|
426
|
+
str << write(object.instance_variable_get(var), refs, crefs, trefs)
|
427
|
+
end
|
428
|
+
end
|
429
|
+
|
430
|
+
str << [ BC_END ].pack('C')
|
431
|
+
end
|
432
|
+
|
433
|
+
|
434
|
+
def write_type_wrapper(val, refs, crefs, trefs)
|
435
|
+
return write_nil unless val.object
|
436
|
+
|
437
|
+
idx = refs[val.object_id]
|
438
|
+
return write_ref(idx) if idx
|
439
|
+
|
440
|
+
refs[val.object_id] = refs.size
|
441
|
+
|
442
|
+
type = val.is_multi? ? ('[' << val.hessian_type) : val.hessian_type
|
443
|
+
if trefs.include?(type)
|
444
|
+
tstr = write_int(trefs[type])
|
445
|
+
else
|
446
|
+
trefs[type] = trefs.size
|
447
|
+
tstr = write_string(type)
|
448
|
+
end
|
449
|
+
|
450
|
+
if val.is_multi?
|
451
|
+
write_type_wrapped_array(val.object, tstr, val.hessian_type, refs, crefs, trefs)
|
452
|
+
else
|
453
|
+
case val.hessian_type
|
454
|
+
when 'L'
|
455
|
+
write_long(Integer(val.object))
|
456
|
+
when 'I'
|
457
|
+
write_int(Integer(val.object))
|
458
|
+
when 'B'
|
459
|
+
write_binary(val.object)
|
460
|
+
else
|
461
|
+
write_type_wrapped_object(val.object, tstr, refs, crefs, trefs)
|
462
|
+
end
|
463
|
+
end
|
464
|
+
end
|
465
|
+
|
466
|
+
|
467
|
+
private
|
468
|
+
|
469
|
+
def get_fields(object)
|
470
|
+
fields = if object.is_a?(Hash)
|
471
|
+
object.keys.map{|k| k.to_sym }
|
472
|
+
elsif object.instance_variable_get(:@attributes).is_a?(Hash)
|
473
|
+
object.attributes.keys.map{|k| k.to_sym }
|
474
|
+
else
|
475
|
+
object.instance_variables.map{|k| k[1..-1].to_sym }
|
476
|
+
end
|
477
|
+
|
478
|
+
raise "fields should not be empty: #{object.inspect}" if fields.empty?
|
479
|
+
|
480
|
+
fields
|
481
|
+
end
|
482
|
+
|
483
|
+
|
484
|
+
def write_values(object, cstr, fields, refs, crefs, trefs)
|
485
|
+
return write_nil unless object
|
486
|
+
|
487
|
+
vstr = if object.is_a?(Hash)
|
488
|
+
fields.map{|f| write(object[f] || object[f.to_s], refs, crefs, trefs) }.join
|
489
|
+
elsif object.instance_variable_get(:@attributes).is_a?(Hash)
|
490
|
+
fields.map{|f| write(object.attributes[f.to_s], refs, crefs, trefs) }.join
|
491
|
+
else
|
492
|
+
fields.map{|f| write(object.instance_variable_get(f.to_s.prepend('@')), refs, crefs, trefs) }.join
|
493
|
+
end
|
494
|
+
|
495
|
+
cstr + vstr
|
496
|
+
end
|
497
|
+
|
498
|
+
end
|
499
|
+
end
|