mappum 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. data/.gitignore +4 -0
  2. data/LICENSE +15 -0
  3. data/README +53 -0
  4. data/Rakefile +48 -0
  5. data/VERSION +1 -0
  6. data/bin/mapserver.rb +4 -0
  7. data/java-api/pom.xml +63 -0
  8. data/java-api/src/main/java/pl/ivmx/mappum/JavaTransform.java +12 -0
  9. data/java-api/src/main/java/pl/ivmx/mappum/MappumApi.java +83 -0
  10. data/java-api/src/main/java/pl/ivmx/mappum/TreeElement.java +23 -0
  11. data/java-api/src/main/java/pl/ivmx/mappum/WorkdirLoader.java +12 -0
  12. data/java-api/src/test/java/iv/Client.java +237 -0
  13. data/java-api/src/test/java/iv/Person.java +261 -0
  14. data/java-api/src/test/java/pl/ivmx/mappum/MappumTest.java +122 -0
  15. data/java-api/src/test/resources/map/example_map.rb +88 -0
  16. data/lib/mappum.rb +46 -0
  17. data/lib/mappum/autoconv_catalogue.rb +43 -0
  18. data/lib/mappum/dsl.rb +255 -0
  19. data/lib/mappum/java_transform.rb +107 -0
  20. data/lib/mappum/map.rb +194 -0
  21. data/lib/mappum/mapserver/mapgraph.rb +192 -0
  22. data/lib/mappum/mapserver/mapserver.rb +213 -0
  23. data/lib/mappum/mapserver/maptable.rb +80 -0
  24. data/lib/mappum/mapserver/views/doc.erb +15 -0
  25. data/lib/mappum/mapserver/views/main.erb +39 -0
  26. data/lib/mappum/mapserver/views/maptable.erb +16 -0
  27. data/lib/mappum/mapserver/views/rubysource.erb +25 -0
  28. data/lib/mappum/mapserver/views/transform-ws.wsdl.erb +50 -0
  29. data/lib/mappum/mapserver/views/ws-error.erb +10 -0
  30. data/lib/mappum/open_xml_object.rb +68 -0
  31. data/lib/mappum/ruby_transform.rb +199 -0
  32. data/lib/mappum/xml_transform.rb +382 -0
  33. data/mappum.gemspec +117 -0
  34. data/sample/address_fixture.xml +11 -0
  35. data/sample/crm.rb +9 -0
  36. data/sample/crm_client.xsd +28 -0
  37. data/sample/erp.rb +7 -0
  38. data/sample/erp_person.xsd +44 -0
  39. data/sample/example_conversions.rb +12 -0
  40. data/sample/example_map.rb +92 -0
  41. data/sample/example_notypes.rb +77 -0
  42. data/sample/example_when.rb +13 -0
  43. data/sample/person_fixture.xml +23 -0
  44. data/sample/person_fixture_any.xml +26 -0
  45. data/sample/server/map/example_any.rb +28 -0
  46. data/sample/server/map/example_soap4r.rb +59 -0
  47. data/sample/server/mapserver.sh +1 -0
  48. data/sample/server/schema/crm_client.xsd +29 -0
  49. data/sample/server/schema/erp/erp_person.xsd +38 -0
  50. data/test/test_conversions.rb +24 -0
  51. data/test/test_example.rb +175 -0
  52. data/test/test_openstruct.rb +129 -0
  53. data/test/test_soap4r.rb +108 -0
  54. data/test/test_when.rb +35 -0
  55. data/test/test_xml_any.rb +62 -0
  56. metadata +164 -0
@@ -0,0 +1,382 @@
1
+ require 'mappum/ruby_transform'
2
+ require 'rubygems'
3
+ gem 'soap4r'
4
+ require 'soap/marshal'
5
+ require 'xsd/mapping'
6
+ require 'wsdl/xmlSchema/xsd2ruby'
7
+ require 'mappum/open_xml_object'
8
+ require 'tmpdir'
9
+ require 'fileutils'
10
+
11
+ module SOAP::Mapping::RegistrySupport
12
+ def class_schema_definition
13
+ @class_schema_definition
14
+ end
15
+ def class_elename_schema_definition
16
+ @class_elename_schema_definition
17
+ end
18
+ end
19
+
20
+ class XSD::Mapping::Mapper
21
+ attr_reader :registry
22
+ def self.inherited(klass)
23
+ @@mapper_classes ||= []
24
+ @@mapper_classes << klass
25
+ end
26
+ def self.find_mapper_for_class(klass)
27
+ klass = const_get(klass) if klass.instance_of?(Symbol)
28
+ ret_maper=nil
29
+ #FIXME add cache
30
+ mappers.each do |mapper|
31
+ begin
32
+ sch = mapper.registry.schema_definition_from_class klass
33
+ ret_maper = mapper unless sch.nil?
34
+ rescue NoMethodError
35
+ end
36
+ end
37
+ return ret_maper
38
+ end
39
+ def self.get_qname_from_class(klass)
40
+ begin
41
+ klass = const_get(klass) if klass.instance_of?(Symbol)
42
+ rescue NameError
43
+ return nil
44
+ end
45
+ ret_qname = nil
46
+ #FIXME add cache
47
+ mappers.each do |mapper|
48
+ begin
49
+ sch = mapper.registry.schema_definition_from_class klass
50
+ ret_qname = sch.elename unless sch.nil?
51
+ rescue NoMethodError
52
+ end
53
+ end
54
+ return ret_qname
55
+
56
+ end
57
+ def self.find_mapper_for_type(qname)
58
+ ret_maper=nil
59
+ #FIXME add cache
60
+ mappers.each do |mapper|
61
+ begin
62
+ sch = mapper.registry.schema_definition_from_elename qname
63
+ ret_maper = mapper unless sch.nil?
64
+ rescue NoMethodError
65
+ end
66
+ end
67
+ return ret_maper
68
+ end
69
+ def obj2soap(obj, elename = nil, io = nil)
70
+ opt = MAPPING_OPT.dup
71
+ unless elename
72
+ if definition = @registry.elename_schema_definition_from_class(obj.class)
73
+ elename = definition.elename
74
+ opt[:root_type_hint] = false
75
+ end
76
+ end
77
+ elename = SOAP::Mapping.to_qname(elename) if elename
78
+ soap = SOAP::Mapping.obj2soap(obj, @registry, elename, opt)
79
+ if soap.elename.nil? or soap.elename == XSD::QName::EMPTY
80
+ soap.elename =
81
+ XSD::QName.new(nil, SOAP::Mapping.name2elename(obj.class.to_s))
82
+ end
83
+ return soap
84
+ end
85
+
86
+ private
87
+ def self.mappers
88
+ @@mappers ||= []
89
+ @@mapper_classes ||= []
90
+ if @@mapper_classes.size > @@mappers.size
91
+ @@mappers = @@mapper_classes.collect{|k|k.new()}
92
+ end
93
+ @@mappers
94
+ end
95
+ end
96
+
97
+ module Mappum
98
+ #
99
+ # SOAP4r based xml to xml transforming class.
100
+ #
101
+ class XmlTransform
102
+ def initialize(map_catalogue = nil, force_open_struct=false)
103
+ @default_mapper = XSD::Mapping::Mapper.new(SOAP::Mapping::LiteralRegistry.new)
104
+ @ruby_transform = RubyXmlTransform.new(map_catalogue, OpenXmlObject, force_open_struct)
105
+ end
106
+ #
107
+ # Transforms given from_xml using map xml transformation from and to Ruby objects can
108
+ # be controled via from_qname and to_qname. And soap envelope will be striped when handle_soap=true.
109
+ #
110
+ def transform(from_xml, map=nil, from_qname=nil, to_qname=nil, handle_soap=true)
111
+ soap = false
112
+
113
+ parser = SOAP::Parser.new(XSD::Mapping::Mapper::MAPPING_OPT)
114
+ preparsed = parser.parse(from_xml)
115
+
116
+ if from_qname.nil?
117
+ from_qname = preparsed.elename
118
+ end
119
+
120
+ if handle_soap and from_qname == XSD::QName.new("http://schemas.xmlsoap.org/soap/envelope/","Envelope")
121
+ soap = true
122
+ #for soap remove envelope
123
+ preparsed = preparsed.body.root_node
124
+ from_qname = preparsed.elename
125
+ end
126
+
127
+
128
+ from_mapper = XSD::Mapping::Mapper.find_mapper_for_type(from_qname)
129
+ if from_mapper.nil?
130
+ from_mapper = @default_mapper
131
+ end
132
+
133
+ begin
134
+ parsed =SOAP::Mapping.soap2obj(preparsed, from_mapper.registry, nil)
135
+ rescue NoMethodError => e
136
+ raise ParsingFailedException.new("Parsing failed for xml with root element: #{from_qname}")
137
+ end
138
+
139
+ map ||= @ruby_transform.map_catalogue[from_qname]
140
+ map ||= @ruby_transform.map_catalogue[from_qname.name.to_sym]
141
+ if not map.nil? and not map.kind_of?(Map)
142
+ map = @ruby_transform.map_catalogue[map.to_sym]
143
+ end
144
+
145
+ begin
146
+ transformed = @ruby_transform.transform(parsed, map)
147
+ rescue MapMissingException => e
148
+ if e.from == parsed
149
+ raise MapMissingException.new(e.from,"Map for element \"#{from_qname}\" not found!")
150
+ else
151
+ raise e
152
+ end
153
+ end
154
+
155
+ to_mapper = XSD::Mapping::Mapper.find_mapper_for_class(transformed.class)
156
+ if to_mapper.nil?
157
+ to_mapper = @default_mapper
158
+ end
159
+
160
+ if transformed.kind_of?(OpenXmlObject) and to_qname.nil? and not map.nil?
161
+ to = map.to
162
+ if to.clazz.kind_of?(XSD::QName)
163
+ to_qname = to.clazz
164
+ else
165
+ to_qname = XSD::QName.new(nil, XSD::CodeGen::GenSupport.safeconstname(to.clazz.to_s))
166
+ end
167
+ end
168
+ to_preparsed = to_mapper.obj2soap(transformed,to_qname)
169
+ if soap == true
170
+ to_preparsed = SOAP::SOAPEnvelope.new(SOAP::SOAPHeader.new, SOAP::SOAPBody.new(to_preparsed))
171
+ end
172
+ generator = SOAP::Generator.new(XSD::Mapping::Mapper::MAPPING_OPT)
173
+ to_xml = generator.generate(to_preparsed, nil)
174
+ return to_xml
175
+ end
176
+ end
177
+ class RubyXmlTransform < RubyTransform
178
+ def initialize(*args)
179
+ super(*args)
180
+ end
181
+ def get(object, field)
182
+ begin
183
+ super(object, field)
184
+ rescue NoMethodError
185
+ begin
186
+ super(object, XSD::CodeGen::GenSupport.safemethodname(field.to_s).to_sym)
187
+ rescue NoMethodError
188
+ #for dynamic xml nil value == no methond
189
+ if object.kind_of?(SOAP::Mapping::Object)
190
+ return nil
191
+ else
192
+ raise
193
+ end
194
+ end
195
+ end
196
+ end
197
+ end
198
+ class TreeElement < Struct.new(:name, :elements, :is_array, :clazz)
199
+ end
200
+ # Class supporting loading working directory of the layout:
201
+ #
202
+ # schema/ - Directory containing xsd files
203
+ #
204
+ # map/ - directory containing Mappum maps
205
+ #
206
+ class WorkdirLoader
207
+ def initialize(schema_path = "schema", map_dir="map", basedir=nil)
208
+ schema_path = "schema" if schema_path.nil?
209
+ @schema_path = schema_path
210
+ @basedir = basedir
211
+ @basedir ||= mktmpdir
212
+ map_dir = "map" if map_dir.nil?
213
+ @map_dir = map_dir
214
+ @mapper_scripts = []
215
+ end
216
+ def generate_and_require
217
+ generate_classes
218
+ require_all
219
+ end
220
+ def require_all
221
+ require_schemas
222
+ require_maps
223
+ end
224
+ # Generate classes from xsd files in shema_path (defaults to schema).
225
+ # Files containt in subdirectories are generated to modules where module name is
226
+ # optaind from folder name.
227
+ # Generated classes are saved to basedir (defaults to tmp)
228
+ def generate_classes(schema_path=nil, module_path=nil)
229
+ class_dir = @basedir
230
+ modname = module_path
231
+ unless module_path.nil?
232
+ class_dir = File.join(class_dir, module_path)
233
+ modname = modname.gsub(File::SEPARATOR, "::")
234
+ modname = modname.gsub(/^[a-z]|\s+[a-z]|\:\:+[a-z]/) { |a| a.upcase }
235
+ end
236
+
237
+ Dir.mkdir(class_dir) unless File.exist? class_dir
238
+
239
+
240
+
241
+ schema_path ||= @schema_path
242
+
243
+ Dir.foreach(schema_path) do |file_name|
244
+ full_name = File.join(schema_path,file_name)
245
+ #when file is a directory
246
+ #generate classes in module (recursive)
247
+ if File.directory?(full_name) and not file_name[0..0] == "."
248
+ #make directory for future class files
249
+ module_pth = file_name
250
+ module_pth = File.join(module_path, module_pth) unless module_path.nil?
251
+
252
+ generate_classes(full_name, module_pth)
253
+ # for XSD files generate classes using XSD2Ruby
254
+ elsif /.*.xsd/ =~ file_name
255
+ run_xsd2ruby(full_name, file_name, module_path, modname)
256
+ @mapper_scripts << class_dir + File::SEPARATOR + file_name[0..-5] + "_mapper.rb"
257
+ end
258
+ end
259
+ end
260
+ def require_schemas
261
+ $:.unshift @basedir
262
+ @mapper_scripts.each do |script|
263
+ require script
264
+ end
265
+ end
266
+ def require_maps
267
+ $:.unshift @map_dir
268
+ Dir.foreach(@map_dir) do |file_name|
269
+ if /.*.rb/ =~ file_name
270
+ Mappum.source = File.join(@map_dir, file_name)
271
+ require File.join(@map_dir, file_name)
272
+ Mappum.source = nil
273
+ end
274
+ end
275
+ end
276
+ #
277
+ # Returns simple tree structure representing all elements defined in schemas
278
+ #
279
+ #
280
+ def defined_element_trees(schema_definition = nil)
281
+ returning = []
282
+ if schema_definition == nil then
283
+ XSD::Mapping::Mapper.mappers.each do |mapper|
284
+ mapper.registry.class_schema_definition.each do |k,v|
285
+ returning << defined_element_trees(v)
286
+ end
287
+ end
288
+ return returning
289
+ end
290
+ name = schema_definition.varname if schema_definition.respond_to?(:varname)
291
+ name ||= schema_definition.class_for
292
+ is_array = false
293
+ is_array = schema_definition.as_array? if schema_definition.respond_to?(:as_array?)
294
+ if schema_definition.respond_to?(:elements) and not schema_definition.elements.nil?
295
+ subelems = []
296
+ schema_definition.elements.each do |element|
297
+ subelems << defined_element_trees(element)
298
+ end
299
+ return TreeElement.new(name, subelems, is_array, nil)
300
+ end
301
+
302
+ return TreeElement.new(name, nil,is_array,schema_definition.mapped_class)
303
+ end
304
+ #
305
+ # Remove tmpdir
306
+ #
307
+ def cleanup
308
+ FileUtils.rm_rf(@basedir) if @cleanup
309
+ end
310
+ private
311
+ def run_xsd2ruby(full_name, file_name, module_path, modname)
312
+ worker = WSDL::XMLSchema::XSD2Ruby.new
313
+ worker.location = full_name
314
+ worker.basedir = @basedir
315
+
316
+ classdef = file_name[0..-5]
317
+ classdef = File.join(module_path,classdef) unless module_path.nil?
318
+
319
+ opt = {"classdef" => classdef, "mapping_registry" => "", "mapper" => "", "force" => ""}
320
+ opt["module_path"] = modname unless modname.nil?
321
+ worker.opt.update(opt)
322
+ worker.run
323
+ end
324
+ def mktmpdir
325
+ @cleanup = true
326
+ prefix = "d"
327
+ tmpdir ||= Dir.tmpdir
328
+ t = Time.now.strftime("%Y%m%d")
329
+ n = nil
330
+ begin
331
+ path = "#{tmpdir}/#{prefix}#{t}-#{$$}-#{rand(0x100000000).to_s(36)}"
332
+ path << "-#{n}" if n
333
+ Dir.mkdir(path, 0700)
334
+ rescue Errno::EEXIST
335
+ n ||= 0
336
+ n += 1
337
+ retry
338
+ end
339
+ return path
340
+ end
341
+ end
342
+ class XmlSupport
343
+ #choose parser
344
+ begin
345
+ gem 'libxml-ruby'
346
+ require 'libxml'
347
+ @@parser = :libxml
348
+ rescue Gem::LoadError
349
+ require 'rexml/parsers/sax2parser'
350
+ require 'rexml/element'
351
+ @@parser = :rexml
352
+ end
353
+ #
354
+ # Get target namespace from given xsd file
355
+ #
356
+ def self.get_target_ns(xsd_file)
357
+ return get_target_ns_libxml(xsd_file) if @@parser == :libxml
358
+ return get_target_ns_rexml(xsd_file)
359
+ end
360
+ def self.get_target_ns_rexml(xsd_file)
361
+ reader = REXML::Parsers::SAX2Parser.new(File.new(xsd_file))
362
+ namespace = nil
363
+ #FIXME: quit after root
364
+ reader.listen(:start_element) do |uri, localname, qname, attributes|
365
+ namespace ||= attributes["targetNamespace"]
366
+ end
367
+
368
+ reader.parse
369
+ return namespace
370
+ end
371
+ def self.get_target_ns_libxml(xsd_file)
372
+ reader = LibXML::XML::Reader.file(xsd_file)
373
+ reader.read
374
+ reader.move_to_attribute("targetNamespace")
375
+ namespace = reader.value
376
+ reader.close
377
+ return namespace
378
+ end
379
+ end
380
+ class ParsingFailedException < RuntimeError
381
+ end
382
+ end
data/mappum.gemspec ADDED
@@ -0,0 +1,117 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{mappum}
8
+ s.version = "0.2.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Jan Topi\305\204ski"]
12
+ s.date = %q{2009-11-08}
13
+ s.default_executable = %q{mapserver.rb}
14
+ s.description = %q{}
15
+ s.email = %q{jtopinski@chatka.org}
16
+ s.executables = ["mapserver.rb"]
17
+ s.extra_rdoc_files = [
18
+ "LICENSE",
19
+ "README"
20
+ ]
21
+ s.files = [
22
+ ".gitignore",
23
+ "LICENSE",
24
+ "README",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "bin/mapserver.rb",
28
+ "java-api/pom.xml",
29
+ "java-api/src/main/java/pl/ivmx/mappum/JavaTransform.java",
30
+ "java-api/src/main/java/pl/ivmx/mappum/MappumApi.java",
31
+ "java-api/src/main/java/pl/ivmx/mappum/TreeElement.java",
32
+ "java-api/src/main/java/pl/ivmx/mappum/WorkdirLoader.java",
33
+ "java-api/src/test/java/iv/Client.java",
34
+ "java-api/src/test/java/iv/Person.java",
35
+ "java-api/src/test/java/pl/ivmx/mappum/MappumTest.java",
36
+ "java-api/src/test/resources/map/example_map.rb",
37
+ "lib/mappum.rb",
38
+ "lib/mappum/autoconv_catalogue.rb",
39
+ "lib/mappum/dsl.rb",
40
+ "lib/mappum/java_transform.rb",
41
+ "lib/mappum/map.rb",
42
+ "lib/mappum/mapserver/mapgraph.rb",
43
+ "lib/mappum/mapserver/mapserver.rb",
44
+ "lib/mappum/mapserver/maptable.rb",
45
+ "lib/mappum/mapserver/views/doc.erb",
46
+ "lib/mappum/mapserver/views/main.erb",
47
+ "lib/mappum/mapserver/views/maptable.erb",
48
+ "lib/mappum/mapserver/views/rubysource.erb",
49
+ "lib/mappum/mapserver/views/transform-ws.wsdl.erb",
50
+ "lib/mappum/mapserver/views/ws-error.erb",
51
+ "lib/mappum/open_xml_object.rb",
52
+ "lib/mappum/ruby_transform.rb",
53
+ "lib/mappum/xml_transform.rb",
54
+ "mappum.gemspec",
55
+ "sample/address_fixture.xml",
56
+ "sample/crm.rb",
57
+ "sample/crm_client.xsd",
58
+ "sample/erp.rb",
59
+ "sample/erp_person.xsd",
60
+ "sample/example_conversions.rb",
61
+ "sample/example_map.rb",
62
+ "sample/example_notypes.rb",
63
+ "sample/example_when.rb",
64
+ "sample/person_fixture.xml",
65
+ "sample/person_fixture_any.xml",
66
+ "sample/server/map/example_any.rb",
67
+ "sample/server/map/example_soap4r.rb",
68
+ "sample/server/mapserver.sh",
69
+ "sample/server/schema/crm_client.xsd",
70
+ "sample/server/schema/erp/erp_person.xsd",
71
+ "test/test_conversions.rb",
72
+ "test/test_example.rb",
73
+ "test/test_openstruct.rb",
74
+ "test/test_soap4r.rb",
75
+ "test/test_when.rb",
76
+ "test/test_xml_any.rb"
77
+ ]
78
+ s.homepage = %q{http://wiki.github.com/simcha/mappum}
79
+ s.rdoc_options = ["--charset=UTF-8"]
80
+ s.require_paths = ["lib"]
81
+ s.rubygems_version = %q{1.3.5}
82
+ s.summary = %q{Mappum is the tree to tree (object, bean etc.) mapping DSL.}
83
+ s.test_files = [
84
+ "test/test_example.rb",
85
+ "test/test_openstruct.rb",
86
+ "test/test_when.rb",
87
+ "test/test_conversions.rb",
88
+ "test/test_xml_any.rb",
89
+ "test/test_soap4r.rb"
90
+ ]
91
+
92
+ if s.respond_to? :specification_version then
93
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
94
+ s.specification_version = 3
95
+
96
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
97
+ s.add_runtime_dependency(%q<facets>, [">= 2.5.2"])
98
+ s.add_runtime_dependency(%q<soap4r>, [">= 1.5.8"])
99
+ s.add_runtime_dependency(%q<sinatra>, [">= 0.9.2"])
100
+ s.add_runtime_dependency(%q<thin>, [">= 1.2.2"])
101
+ s.add_runtime_dependency(%q<syntax>, [">= 1.0.0"])
102
+ else
103
+ s.add_dependency(%q<facets>, [">= 2.5.2"])
104
+ s.add_dependency(%q<soap4r>, [">= 1.5.8"])
105
+ s.add_dependency(%q<sinatra>, [">= 0.9.2"])
106
+ s.add_dependency(%q<thin>, [">= 1.2.2"])
107
+ s.add_dependency(%q<syntax>, [">= 1.0.0"])
108
+ end
109
+ else
110
+ s.add_dependency(%q<facets>, [">= 2.5.2"])
111
+ s.add_dependency(%q<soap4r>, [">= 1.5.8"])
112
+ s.add_dependency(%q<sinatra>, [">= 0.9.2"])
113
+ s.add_dependency(%q<thin>, [">= 1.2.2"])
114
+ s.add_dependency(%q<syntax>, [">= 1.0.0"])
115
+ end
116
+ end
117
+