mappum 0.2.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 (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
+