DistelliServiceMarshallers 1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. data/lib/distelli/servicemarshallers.rb +431 -0
  2. metadata +77 -0
@@ -0,0 +1,431 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'json'
4
+ require 'date'
5
+ require 'nokogiri'
6
+ require 'distelli/serviceinterface'
7
+
8
+ module Distelli
9
+ DATA_TYPES = ["int", "string", "boolean", "long", "double"]
10
+ class Marshaller
11
+ def initialize()
12
+ @object_map = Hash.new
13
+ end
14
+
15
+ def add_object(obj)
16
+ @object_map[obj.name] = obj
17
+ end
18
+ end
19
+
20
+ class MarshallException < StandardError
21
+ end
22
+
23
+ ################################
24
+ # JSON Marshaller
25
+ ################################
26
+
27
+ class JsonMarshaller < Marshaller
28
+ def initialize()
29
+ super
30
+ end
31
+
32
+ def encode(obj)
33
+ data = Hash.new
34
+ if obj.is_a?(Integer) or obj.is_a?(Float) or obj.is_a?(String) or obj.is_a?(TrueClass) or obj.is_a?(FalseClass)
35
+ return obj
36
+ elsif obj.is_a?(DateTime)
37
+ return obj.strftime("%Y-%m-%dT%H:%M:%S%z")
38
+ end
39
+
40
+ type_map_name = '_'+obj.class.name.split('::').last+'__type_map'
41
+ obj.instance_variables.each do |var|
42
+ var_name = var.to_s.delete('@')
43
+ if var_name == type_map_name
44
+ next
45
+ end
46
+ val = obj.instance_variable_get(var)
47
+ if val.is_a?(Integer) or val.is_a?(Float) or val.is_a?(String) or val.is_a?(TrueClass) or val.is_a?(FalseClass)
48
+ data[var_name] = val
49
+ elsif val.is_a?(DateTime)
50
+ data[var_name] = val.strftime("%Y-%m-%dT%H:%M:%S%z")
51
+ elsif val.instance_of?(Array)
52
+ array = Array.new
53
+ val.each do |elem|
54
+ encoded = encode(elem)
55
+ array.push(encoded)
56
+ end
57
+ data[var_name] = array
58
+ else val.kind_of?(Object)
59
+ encoded = encode(val)
60
+ data[var_name] = encoded
61
+ end
62
+ end
63
+ return data
64
+ end
65
+
66
+ def marshall(request)
67
+ data = encode(request)
68
+ request_wrapper = Hash.new
69
+ request_wrapper[request.class.name.split('::').last] = data
70
+ return request_wrapper.to_json
71
+ end
72
+
73
+ def marshall_error(error)
74
+ if not error.is_a?(Distelli::BaseException)
75
+ raise StandardError.new("Cannot marshall error: "+error.inspect)
76
+ end
77
+
78
+ err_msg = error.err_msg
79
+ err_code = error.err_code
80
+ return '{"Error":{"code":"'+err_code.to_s+'", "message":"'+err_msg.to_s+'"}}'
81
+ end
82
+
83
+ def unmarshall_error(json_data)
84
+ data_hash = JSON.load(json_data)
85
+ err_obj = data_hash[ServiceConstants::ERROR_KEY]
86
+ if err_obj == nil
87
+ return nil
88
+ end
89
+ err_code = err_obj[ServiceConstants::ERR_CODE_KEY]
90
+ err_msg = err_obj[ServiceConstants::ERR_MSG_KEY]
91
+ return [err_code, err_msg]
92
+ end
93
+
94
+ def unmarshall(json_data)
95
+ data_hash = JSON.load(json_data)
96
+ data_hash.each_pair do |k,v|
97
+ if v.is_a?(Hash)
98
+ return unmarshall_obj(k, v)
99
+ else
100
+ raise MarshallException.new("Invalid data "+json_data)
101
+ end
102
+ end
103
+ end
104
+
105
+ private
106
+ def to_obj_list(obj_name, list_data)
107
+ obj_list = Array.new
108
+ # If its a list type then its a list of complex objects
109
+ if list_data.is_a?(Array)
110
+ list_data.each do |v|
111
+ obj = unmarshall_obj(obj_name, v)
112
+ obj_list.push(obj)
113
+ end
114
+ else # Else its a map (hopefully) and its only a single complex object
115
+ obj = unmarshall_obj(obj_name, list_data)
116
+ obj_list.push(obj)
117
+ end
118
+ return obj_list
119
+ end
120
+
121
+ private
122
+ def to_date_list(list_data)
123
+ date_list = Array.new
124
+ list_data.each do |v|
125
+ date_val = DateTime.strptime(v, "%Y-%m-%dT%H:%M:%S%z")
126
+ date_list.push(date_val)
127
+ end
128
+ return date_list
129
+ end
130
+
131
+ private
132
+ def unmarshall_list(list_data, data_type)
133
+ if list_data.length == 0
134
+ return Array.new
135
+ end
136
+ actual_list = list_data
137
+ if data_type == "date"
138
+ return to_date_list(actual_list)
139
+ elsif DATA_TYPES.include?(data_type)
140
+ return actual_list
141
+ else
142
+ return to_obj_list(data_type, actual_list)
143
+ end
144
+ end
145
+
146
+ private
147
+ def unmarshall_obj(obj_name, data)
148
+ # puts "Unmarshalling object: "+obj_name
149
+ if !@object_map.has_key?(obj_name)
150
+ raise MarshallException.new("Unknown object: "+obj_name.to_s)
151
+ end
152
+ class_obj = @object_map[obj_name]
153
+ instance = class_obj.new
154
+ type_map_name = '@_'+obj_name+'__type_map'
155
+ type_map = instance.instance_variable_get(type_map_name)
156
+ data.each_pair do |k,v|
157
+ if !type_map.include?(k)
158
+ raise MarshallException.new("Unknown type for field: "+k)
159
+ end
160
+ field_type = type_map[k]
161
+ if field_type.is_a?(Array)
162
+ field_type = field_type[0]
163
+ end
164
+ if v.is_a?(Hash)
165
+ um_obj = unmarshall_obj(field_type, v)
166
+ instance.instance_variable_set('@'+k, um_obj)
167
+ elsif v.is_a?(Array)
168
+ um_list = unmarshall_list(v, field_type)
169
+ instance.instance_variable_set('@'+k, um_list)
170
+ else
171
+ if field_type == "date"
172
+ date_val = DateTime.strptime(v, "%Y-%m-%dT%H:%M:%S%z")
173
+ instance.instance_variable_set('@'+k, date_val)
174
+ elsif field_type == "int"
175
+ instance.instance_variable_set('@'+k, v.to_i)
176
+ elsif field_type == "double"
177
+ instance.instance_variable_set('@'+k, v.to_f)
178
+ elsif field_type == "long"
179
+ instance.instance_variable_set('@'+k, v.to_i)
180
+ else
181
+ instance.instance_variable_set('@'+k, v)
182
+ end
183
+ end
184
+ end
185
+ return instance
186
+ end
187
+ end
188
+
189
+ ################################
190
+ # XML Marshaller
191
+ ################################
192
+
193
+ class XmlMarshaller < Marshaller
194
+ def initialize()
195
+ super
196
+ end
197
+
198
+ def marshall(request)
199
+ root_tag_name = request.class.name.split('::').last
200
+ return ['<', root_tag_name,'>', encode_obj_xml(request), '</', root_tag_name, '>'].join('')
201
+ end
202
+
203
+ def unmarshall(xml_data)
204
+ xml_doc = Nokogiri::XML(xml_data)
205
+ root_node = xml_doc.root
206
+ return unmarshall_obj(root_node)
207
+ end
208
+
209
+ def marshall_error(error)
210
+ if not error.is_a?(Distelli::BaseException)
211
+ raise StandardError.new("Cannot marshall error: "+error.inspect)
212
+ end
213
+
214
+ err_msg = error.err_msg
215
+ err_code = error.err_code
216
+ return "<Error><code>"+err_code.to_s+"</code><message>"+err_msg.to_s+"</message></Error>"
217
+ end
218
+
219
+ ##########################################################
220
+ # Unmarshalls an xml error response and returns the error
221
+ # code and message.
222
+ #
223
+ # This is what an xml error looks like:
224
+ #
225
+ # <Error>
226
+ # <code>MalformedRequest</code>
227
+ # <message>The request is malformed</message>
228
+ # </Error>
229
+ ##########################################################
230
+ def unmarshall_error(xml_data)
231
+ xml_doc = Nokogiri::XML(xml_data)
232
+ err_code_elem = xml_doc.xpath('//'+ServiceConstants::ERROR_KEY+'//'+ServiceConstants::ERR_CODE_KEY)
233
+ err_msg_elem = xml_doc.xpath('//'+ServiceConstants::ERROR_KEY+'//'+ServiceConstants::ERR_MSG_KEY)
234
+ err_code = nil
235
+ err_msg = nil
236
+ if err_code_elem != nil
237
+ err_code = err_code_elem.text
238
+ end
239
+ if err_msg_elem != nil
240
+ err_msg = err_msg_elem.text
241
+ end
242
+ return [err_code, err_msg]
243
+ end
244
+
245
+ private
246
+ def get_node_text(node)
247
+ children = node.children
248
+ if children.length == 0
249
+ return nil
250
+ end
251
+ if children.length != 1
252
+ raise MarshallException.new(node.to_s+" is not a text node")
253
+ end
254
+
255
+ text_node = children[0]
256
+ return text_node.content
257
+ end
258
+
259
+ private
260
+ def unmarshall_list(obj_node, list_type)
261
+ # puts "Unmarshalling list at "+obj_node.to_s
262
+ list_elements = obj_node.children
263
+ list_data = Array.new
264
+ list_elements.each do |elem|
265
+ # If its a simple type list
266
+ # puts "Unmarshalling list elemnt "+elem.name+" "+list_type
267
+ if list_type == "date"
268
+ obj_node_content = get_node_text(elem)
269
+ date_val = DateTime.strptime(obj_node.content, "%Y-%m-%dT%H:%M:%S%z")
270
+ list_data.push(date_val)
271
+ elsif DATA_TYPES.include?(list_type)
272
+ # puts "Unmarshalling primitive list element "+elem.name+" "+list_type
273
+ elem_text = get_node_text(elem)
274
+ if list_type == "int"
275
+ list_data.push(elem_text.to_i)
276
+ elsif list_type == "long"
277
+ list_data.push(elem_text.to_i)
278
+ elsif list_type == "boolean"
279
+ if elem_text.downcase == "true"
280
+ list_data.push(true)
281
+ else
282
+ list_data.push(false)
283
+ end
284
+ elsif list_type == "double"
285
+ list_data.push(elem_text.to_f)
286
+ else
287
+ list_data.push(elem_text)
288
+ end
289
+ else
290
+ # puts "Unmarshalling complex list element"+elem.name+" "+list_type
291
+ # Else its a list of complex types
292
+ unmarshalled_obj = unmarshall_obj(elem)
293
+ list_data.push(unmarshalled_obj)
294
+ end
295
+ end
296
+ return list_data
297
+ end
298
+
299
+ private
300
+ def unmarshall_obj(obj_node, parent_obj=nil)
301
+ # puts "Unmarshalling "+obj_node.name+" "+obj_node.inspect+" member of "+parent_obj.to_s
302
+ obj_name = obj_node.name
303
+ obj_instance = nil
304
+ if parent_obj == nil
305
+ if !@object_map.has_key?(obj_name)
306
+ raise MarshallException.new("Unknown object: "+obj_name.to_s)
307
+ end
308
+ class_obj = @object_map[obj_name]
309
+ obj_instance = class_obj.new
310
+ else
311
+ # Parent object is not none. So obj_node is a member of
312
+ # obj_instance and we can get the type from the parent obj
313
+ # First get the type map
314
+ # puts "Processing "+obj_name
315
+ type_map_name = '@_'+parent_obj.class.name.split('::').last+'__type_map'
316
+ type_map = parent_obj.instance_variable_get(type_map_name)
317
+ if type_map == nil
318
+ raise MarshallException.new(parent_obj.class.name+" cannot be unmarshalled. Missing type_map: "+type_map_name)
319
+ end
320
+
321
+ if !type_map.include?(obj_name)
322
+ raise MarshallException.new("Unknown type for field: "+obj_name+" in obj "+obj_instance.to_s)
323
+ end
324
+ obj_type = type_map[obj_name]
325
+ # puts "Unmarshalling "+obj_name+" "+obj_type.to_s
326
+ # Obj type can be either a list or one of the defined
327
+ # primitive types or a complex type
328
+ if obj_type.is_a?(Array)
329
+ list_type = obj_type[0]
330
+ # puts obj_name+" is a list in "+parent_obj.to_s+" of type "+list_type
331
+ unmarshalled_list = unmarshall_list(obj_node, list_type)
332
+ # puts "Unmarshalled list"+unmarshalled_list.to_s
333
+ parent_obj.instance_variable_set('@'+obj_name, unmarshalled_list)
334
+ return obj_instance
335
+ elsif obj_type == "date"
336
+ date_val = DateTime.strptime(obj_node.content, "%Y-%m-%dT%H:%M:%S%z")
337
+ parent_obj.instance_variable_set('@'+obj_name, date_val)
338
+ return obj_instance
339
+ elsif DATA_TYPES.include?(obj_type)
340
+ # puts obj_name+" is a primitive "+obj_type+" "+obj_node+" in "+parent_obj.to_s
341
+ if obj_type == "int" or obj_type == "long"
342
+ parent_obj.instance_variable_set('@'+obj_name, get_node_text(obj_node).to_i)
343
+ elsif obj_type == "boolean"
344
+ obj_text = get_node_text(obj_node)
345
+ if obj_text.downcase == "true"
346
+ parent_obj.instance_variable_set('@'+obj_name, true)
347
+ else
348
+ parent_obj.instance_variable_set('@'+obj_name, false)
349
+ end
350
+ elsif obj_type == "double"
351
+ parent_obj.instance_variable_set('@'+obj_name, get_node_text(obj_node).to_f)
352
+ else
353
+ parent_obj.instance_variable_set('@'+obj_name, get_node_text(obj_node))
354
+ end
355
+ return obj_instance
356
+ else
357
+ # puts obj_name+" is a complex type "+obj_type+" in "+parent_obj.to_s
358
+ if !@object_map.has_key?(obj_type)
359
+ raise MarshallException.new("Unknown object: "+obj_type.to_s)
360
+ end
361
+
362
+ class_obj = @object_map[obj_type]
363
+ obj_instance = class_obj.new
364
+ parent_obj.instance_variable_set('@'+obj_name, obj_instance)
365
+ end
366
+ end
367
+ # puts "Processing children of "+obj_node.name
368
+ children = obj_node.children
369
+ children.each do |child|
370
+ unmarshall_obj(child, parent_obj=obj_instance)
371
+ end
372
+ return obj_instance
373
+ end
374
+
375
+ private
376
+ def encode_primitive_xml(tag_name, value)
377
+ if value.is_a?(DateTime)
378
+ return ['<', tag_name, '>', value.strftime("%Y-%m-%dT%H:%M:%S%z"), '</', tag_name, '>'].join('')
379
+ else
380
+ ['<', tag_name, '>', value.to_s, '</', tag_name, '>'].join('')
381
+ end
382
+ end
383
+
384
+ private
385
+ def list_to_xml(list_obj, list_type)
386
+ xml_data = Array.new
387
+ list_obj.each do |item|
388
+ if item.is_a?(DateTime) or item.is_a?(Integer) or item.is_a?(Float) or item.is_a?(String) or item.is_a?(TrueClass) or item.is_a?(FalseClass)
389
+ xml_data.push(encode_primitive_xml(list_type, item))
390
+ else
391
+ xml_data.push(['<', list_type, '>', encode_obj_xml(item), '</', list_type, '>'].join(''))
392
+ end
393
+ end
394
+ return xml_data.join('')
395
+ end
396
+
397
+ private
398
+ def encode_obj_xml(obj)
399
+ type_map_name = '_'+obj.class.name.split('::').last+'__type_map'
400
+ type_map = obj.instance_variable_get('@'+type_map_name)
401
+ xml_data = Array.new
402
+
403
+ obj.instance_variables.each do |var|
404
+ k = var.to_s.delete('@')
405
+ # puts "Var: "+var.to_s+" TMN: "+type_map_name.to_s
406
+ if k == type_map_name
407
+ next
408
+ end
409
+ v = obj.instance_variable_get(var)
410
+ data_type = type_map[k]
411
+
412
+ if data_type.is_a?(Array)
413
+ data_type = data_type[0]
414
+ end
415
+
416
+ # print "Encoding "+k.to_s+" "+v.to_s
417
+ key = k
418
+ if v.is_a?(Array)
419
+ xml_data.push(['<', key, '>', list_to_xml(v, data_type), '</', key, '>'].join(''))
420
+ elsif v.is_a?(DateTime)
421
+ xml_data.push(encode_primitive_xml(key, v))
422
+ elsif DATA_TYPES.include?(data_type)
423
+ xml_data.push(encode_primitive_xml(key, v))
424
+ else
425
+ xml_data.push(['<', key, '>', encode_obj_xml(v), '</', key, '>'].join(''))
426
+ end
427
+ end
428
+ return xml_data.join('')
429
+ end
430
+ end
431
+ end
metadata ADDED
@@ -0,0 +1,77 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: DistelliServiceMarshallers
3
+ version: !ruby/object:Gem::Version
4
+ version: '1.0'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Rahul Singh
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-10-04 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: DistelliServiceInterface
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: nokogiri
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description: Distelli Service Marshallers for Ruby Servers and Clients
47
+ email: rsingh@distelli.com
48
+ executables: []
49
+ extensions: []
50
+ extra_rdoc_files: []
51
+ files:
52
+ - lib/distelli/servicemarshallers.rb
53
+ homepage: http://www.distelli.com/
54
+ licenses: []
55
+ post_install_message:
56
+ rdoc_options: []
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ none: false
61
+ requirements:
62
+ - - ! '>='
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ! '>='
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ requirements: []
72
+ rubyforge_project:
73
+ rubygems_version: 1.8.23
74
+ signing_key:
75
+ specification_version: 3
76
+ summary: Distelli Service Marshaller classes
77
+ test_files: []