simcha-mappum 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/LICENSE +15 -0
- data/README +53 -0
- data/Rakefile +46 -0
- data/VERSION +1 -0
- data/bin/mapserver.rb +4 -0
- data/lib/mappum.rb +19 -0
- data/lib/mappum/dsl.rb +186 -0
- data/lib/mappum/map.rb +133 -0
- data/lib/mappum/mapserver/mapgraph.rb +193 -0
- data/lib/mappum/mapserver/mapserver.rb +197 -0
- data/lib/mappum/mapserver/views/transform-ws.wsdl.erb +50 -0
- data/lib/mappum/mapserver/views/ws-error.erb +10 -0
- data/lib/mappum/open_xml_object.rb +61 -0
- data/lib/mappum/ruby_transform.rb +129 -0
- data/lib/mappum/xml_transform.rb +298 -0
- data/mappum.gemspec +89 -0
- data/sample/address_fixture.xml +11 -0
- data/sample/crm.rb +9 -0
- data/sample/crm_client.xsd +28 -0
- data/sample/erp.rb +7 -0
- data/sample/erp_person.xsd +36 -0
- data/sample/example_map.rb +87 -0
- data/sample/example_notypes.rb +61 -0
- data/sample/person_fixture.xml +23 -0
- data/sample/person_fixture_any.xml +23 -0
- data/sample/server/map/example_any.rb +28 -0
- data/sample/server/map/example_soap4r.rb +59 -0
- data/sample/server/mapserver.sh +1 -0
- data/sample/server/schema/crm_client.xsd +29 -0
- data/sample/server/schema/erp/erp_person.xsd +38 -0
- data/test/test_example.rb +137 -0
- data/test/test_openstruct.rb +129 -0
- data/test/test_soap4r.rb +108 -0
- data/test/test_xml_any.rb +62 -0
- metadata +130 -0
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
== mappum
|
2
|
+
|
3
|
+
Copyright 2009 by Jan Topinski
|
4
|
+
|
5
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
you may not use this file except in compliance with the License.
|
7
|
+
You may obtain a copy of the License at
|
8
|
+
|
9
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
|
11
|
+
Unless required by applicable law or agreed to in writing, software
|
12
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
See the License for the specific language governing permissions and
|
15
|
+
limitations under the License.
|
data/README
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
== mappum
|
2
|
+
|
3
|
+
Mappum is the tree to tree (object, bean etc.) mapping DSL. The example of usage
|
4
|
+
is provided below. More documentation will follow.
|
5
|
+
|
6
|
+
Mappum.catalogue_add "CRM-ERP" do
|
7
|
+
|
8
|
+
map ERP::Person, CRM::Client do |p, c|
|
9
|
+
|
10
|
+
#simple mapping
|
11
|
+
map p.title <=> c.title
|
12
|
+
|
13
|
+
#map with simple function call
|
14
|
+
map p.person_id << c.key.downcase
|
15
|
+
map p.person_id.upcase >> c.key
|
16
|
+
|
17
|
+
#dictionary use
|
18
|
+
map p.sex <=> c.sex_id, :dict => {"F" => "1", "M" => "2"}
|
19
|
+
|
20
|
+
#submaps
|
21
|
+
map p.address(ERP::Address) <=> c.address(CRM::Address) do |a, b|
|
22
|
+
map a.street <=> b.street
|
23
|
+
#etc.
|
24
|
+
end
|
25
|
+
|
26
|
+
#subobject to fields
|
27
|
+
map p.main_phone(ERP::Phone) <=> c.self do |a, b|
|
28
|
+
map a.number <=> b.main_phone
|
29
|
+
map a.type <=> b.main_phone_type
|
30
|
+
end
|
31
|
+
|
32
|
+
#compilcated function call
|
33
|
+
map p.name >> c.surname do |name|
|
34
|
+
name + "ski"
|
35
|
+
end
|
36
|
+
map p.name << c.surname do |name|
|
37
|
+
if name =~ /ski/
|
38
|
+
name[0..-4]
|
39
|
+
else
|
40
|
+
name
|
41
|
+
end
|
42
|
+
end
|
43
|
+
#field to array and array to field
|
44
|
+
map p.email1 <=> c.emails[0]
|
45
|
+
map p.email2 <=> c.emails[1]
|
46
|
+
map p.email3 <=> c.emails[2]
|
47
|
+
|
48
|
+
map p.phones(ERP::Phone)[] <=> c.phones[] do |a, b|
|
49
|
+
map a.number <=> b.self
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
data/Rakefile
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
#
|
2
|
+
# To change this template, choose Tools | Templates
|
3
|
+
# and open the template in the editor.
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'rake'
|
7
|
+
require 'rake/clean'
|
8
|
+
require 'rake/rdoctask'
|
9
|
+
require 'rake/testtask'
|
10
|
+
require 'spec/rake/spectask'
|
11
|
+
|
12
|
+
Rake::RDocTask.new do |rdoc|
|
13
|
+
files =['README', 'LICENSE', 'lib/**/*.rb']
|
14
|
+
rdoc.rdoc_files.add(files)
|
15
|
+
rdoc.main = "README" # page to start on
|
16
|
+
rdoc.title = "rupper Docs"
|
17
|
+
rdoc.rdoc_dir = 'doc/rdoc' # rdoc output folder
|
18
|
+
rdoc.options << '--line-numbers'
|
19
|
+
end
|
20
|
+
|
21
|
+
Rake::TestTask.new do |t|
|
22
|
+
t.test_files = FileList['test/**/*.rb']
|
23
|
+
end
|
24
|
+
|
25
|
+
Spec::Rake::SpecTask.new do |t|
|
26
|
+
t.spec_files = FileList['spec/**/*.rb']
|
27
|
+
end
|
28
|
+
|
29
|
+
begin
|
30
|
+
require 'jeweler'
|
31
|
+
Jeweler::Tasks.new do |gemspec|
|
32
|
+
gemspec.name = "mappum"
|
33
|
+
gemspec.summary = "Mappum is the tree to tree (object, bean etc.) mapping DSL."
|
34
|
+
gemspec.email = "jtopinski@chatka.org"
|
35
|
+
gemspec.homepage = "http://wiki.github.com/simcha/mappum"
|
36
|
+
gemspec.description = ""
|
37
|
+
gemspec.authors = ["Jan Topiński"]
|
38
|
+
gemspec.add_dependency('facets', '>= 2.5.2')
|
39
|
+
gemspec.add_dependency('soap4r', '>= 1.5.8')
|
40
|
+
gemspec.add_dependency('sinatra', '>= 0.9.2')
|
41
|
+
gemspec.add_dependency('thin', '>= 1.2.2')
|
42
|
+
end
|
43
|
+
rescue LoadError
|
44
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
45
|
+
end
|
46
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/bin/mapserver.rb
ADDED
data/lib/mappum.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# TODO docs
|
2
|
+
require 'set'
|
3
|
+
require 'mappum/dsl'
|
4
|
+
require 'mappum/map'
|
5
|
+
|
6
|
+
module Mappum
|
7
|
+
def self.catalogue_add(name = "ROOT", &block)
|
8
|
+
@catalogue ||= {}
|
9
|
+
@catalogue[name] ||= RootMap.new(name)
|
10
|
+
definition = DSL::RootMap.new(name).make_definition(&block)
|
11
|
+
@catalogue[name].maps += definition.maps
|
12
|
+
@catalogue[name].bidi_maps += definition.bidi_maps
|
13
|
+
|
14
|
+
end
|
15
|
+
def self.catalogue(name = "ROOT")
|
16
|
+
name = "ROOT" if name.nil?
|
17
|
+
@catalogue[name]
|
18
|
+
end
|
19
|
+
end
|
data/lib/mappum/dsl.rb
ADDED
@@ -0,0 +1,186 @@
|
|
1
|
+
class Object
|
2
|
+
def >> field
|
3
|
+
return {Mappum::DSL::Constant.new(self) => field} if field.kind_of?(Mappum::DSL::Field)
|
4
|
+
throw "Constant can be mapped to field only"
|
5
|
+
end
|
6
|
+
end
|
7
|
+
module Mappum
|
8
|
+
module DSL
|
9
|
+
class Map
|
10
|
+
attr_accessor :def
|
11
|
+
def initialize
|
12
|
+
@def = Mappum::Map.new
|
13
|
+
end
|
14
|
+
def map(*attr, &block)
|
15
|
+
mapa = FieldMap.new(attr)
|
16
|
+
|
17
|
+
mapa.def.desc = @comment
|
18
|
+
@comment = nil
|
19
|
+
|
20
|
+
if (not mapa.def.normalized?) && block_given?
|
21
|
+
eval_right = mapa.mpun_right.clone
|
22
|
+
eval_right.mpun_definition.is_root = true
|
23
|
+
eval_left = mapa.mpun_left.clone
|
24
|
+
eval_left.mpun_definition.is_root = true
|
25
|
+
mapa.instance_exec(eval_left, eval_right, &block)
|
26
|
+
elsif block_given?
|
27
|
+
mapa.def.func = block
|
28
|
+
mapa.def.func_on_nil = true if mapa.mpun_left.kind_of?(Function)
|
29
|
+
end
|
30
|
+
@def.maps += mapa.def.normalize
|
31
|
+
@def.bidi_maps << mapa.def
|
32
|
+
return mapa.def
|
33
|
+
end
|
34
|
+
def `(str)
|
35
|
+
@comment ||= ""
|
36
|
+
@comment += str
|
37
|
+
end
|
38
|
+
|
39
|
+
def func
|
40
|
+
Mappum::DSL::Function.new
|
41
|
+
end
|
42
|
+
def tree(clazz)
|
43
|
+
return Field.new(nil, nil, clazz)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
class RootMap < Map
|
47
|
+
def initialize(name)
|
48
|
+
@def = Mappum::RootMap.new(name)
|
49
|
+
end
|
50
|
+
def make_definition &block
|
51
|
+
instance_eval(&block)
|
52
|
+
@def
|
53
|
+
end
|
54
|
+
end
|
55
|
+
class FieldMap < Map
|
56
|
+
attr_accessor :mpun_left, :mpun_right
|
57
|
+
def mpun_left=(left_map_dsl)
|
58
|
+
@mpun_left=left_map_dsl
|
59
|
+
@def.left=left_map_dsl.mpun_definition
|
60
|
+
end
|
61
|
+
def mpun_right=(right_map_dsl)
|
62
|
+
@mpun_right=right_map_dsl
|
63
|
+
@def.right=right_map_dsl.mpun_definition
|
64
|
+
end
|
65
|
+
def initialize(*attr)
|
66
|
+
@def = Mappum::FieldMap.new
|
67
|
+
type_size = 1
|
68
|
+
if attr == [[nil]]
|
69
|
+
raise """Can't make map for [[nil]] arguments.
|
70
|
+
Can be that You define top level class mapping with \"<=>\" please use \",\" instead (we know its a Bug). """
|
71
|
+
end
|
72
|
+
mapped = attr[0][0]
|
73
|
+
if attr[0][0].instance_of?(Class) or attr[0][0].instance_of?(Symbol) and
|
74
|
+
attr[0][1].instance_of?(Class) or attr[0][1].instance_of?(Symbol)
|
75
|
+
mapped = [attr[0][0], attr[0][1]]
|
76
|
+
type_size = 2
|
77
|
+
end
|
78
|
+
|
79
|
+
if mapped.instance_of?(Array) then
|
80
|
+
if(mapped[0]).instance_of?(Class) or (mapped[0]).instance_of?(Symbol)
|
81
|
+
@def.strip_empty = false
|
82
|
+
self.mpun_left = Field.new(nil,nil,mapped[0])
|
83
|
+
else
|
84
|
+
self.mpun_left = mapped[0]
|
85
|
+
end
|
86
|
+
if(mapped[1]).instance_of?(Class) or (mapped[1]).instance_of?(Symbol)
|
87
|
+
@def.strip_empty = false
|
88
|
+
self.mpun_right = Field.new(nil,nil,mapped[1])
|
89
|
+
else
|
90
|
+
self.mpun_right = mapped[1]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
if mapped.instance_of?(Hash) then
|
94
|
+
self.mpun_left = mapped.keys[0]
|
95
|
+
self.mpun_right = mapped.values[0]
|
96
|
+
end
|
97
|
+
|
98
|
+
if mapped.instance_of?(Hash) then
|
99
|
+
@def.from = @def.left
|
100
|
+
@def.to = @def.right
|
101
|
+
end
|
102
|
+
|
103
|
+
@def.dict = attr[0][1][:dict] if attr[0].size > type_size
|
104
|
+
@def.desc = attr[0][1][:desc] if attr[0].size > type_size
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|
108
|
+
#Base class for all mapped elements eg. fields, constants
|
109
|
+
class Mappet
|
110
|
+
def mpun_definition
|
111
|
+
@def
|
112
|
+
end
|
113
|
+
def <=> field
|
114
|
+
[self, field]
|
115
|
+
end
|
116
|
+
|
117
|
+
def << field
|
118
|
+
return {field => self} if field.kind_of?(Mappum::DSL::Mappet)
|
119
|
+
return {Constant.new(field) => self}
|
120
|
+
end
|
121
|
+
|
122
|
+
def >> field
|
123
|
+
return {self => field} if field.kind_of?(Mappum::DSL::Mappet)
|
124
|
+
throw "Must map to a field"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
class Constant < Mappet
|
128
|
+
def initialize(value)
|
129
|
+
@def = Mappum::Constant.new
|
130
|
+
@def.value = value
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
class Function < Mappet
|
135
|
+
def initialize
|
136
|
+
@def = Mappum::Function.new
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
class Field < Mappet
|
141
|
+
def initialize(parent, name, clazz)
|
142
|
+
@def = Mappum::Field.new
|
143
|
+
@def.parent = parent
|
144
|
+
@def.name = name
|
145
|
+
@def.clazz = clazz
|
146
|
+
@def.is_array = false
|
147
|
+
@def.is_root = false
|
148
|
+
end
|
149
|
+
|
150
|
+
def type(*attr)
|
151
|
+
method_missing(:type, *attr)
|
152
|
+
end
|
153
|
+
def id(*attr)
|
154
|
+
method_missing(:id, *attr)
|
155
|
+
end
|
156
|
+
|
157
|
+
def method_missing(symbol, *args)
|
158
|
+
if @def.is_root
|
159
|
+
if(symbol == :self)
|
160
|
+
return Field.new(@def, nil, args[0])
|
161
|
+
end
|
162
|
+
return Field.new(@def, symbol, args[0])
|
163
|
+
end
|
164
|
+
|
165
|
+
if symbol == :[]
|
166
|
+
#empty [] is just indication that field is an array not function
|
167
|
+
if args.size == 0
|
168
|
+
@def.is_array = true
|
169
|
+
return self
|
170
|
+
end
|
171
|
+
#[n] indicates both mapping function and array
|
172
|
+
if args.size == 1 and args[0].instance_of?(Fixnum)
|
173
|
+
@def.is_array = true
|
174
|
+
end
|
175
|
+
end
|
176
|
+
if @def.func.nil?
|
177
|
+
@def.func = "self.#{symbol}(#{args.join(", ")})"
|
178
|
+
else
|
179
|
+
@def.func += ".#{symbol}(#{args.join(", ")})"
|
180
|
+
end
|
181
|
+
|
182
|
+
return self
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
data/lib/mappum/map.rb
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
module Mappum
|
2
|
+
# Base Map class representing mapping betwean two or more types, properties etc.
|
3
|
+
class Map
|
4
|
+
|
5
|
+
attr_accessor :maps, :bidi_maps, :strip_empty
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@maps = []
|
9
|
+
@bidi_maps = []
|
10
|
+
@strip_empty = true
|
11
|
+
end
|
12
|
+
# When value of mapped property is null remove property.
|
13
|
+
def strip_empty?
|
14
|
+
@strip_empty
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
class RootMap < Map
|
20
|
+
attr_accessor :name
|
21
|
+
def initialize(name)
|
22
|
+
super()
|
23
|
+
@name = name
|
24
|
+
@strip_empty = false
|
25
|
+
end
|
26
|
+
def [](clazz)
|
27
|
+
#TODO optimize
|
28
|
+
mpa = @maps.find{|m| m.from.clazz == clazz or m.from.clazz.to_s == clazz.to_s }
|
29
|
+
return mpa unless mpa.nil?
|
30
|
+
return @maps.find{|m| "#{m.from.clazz}-to-#{m.to.clazz}" == clazz.to_s}
|
31
|
+
end
|
32
|
+
def get_bidi_map(name)
|
33
|
+
#TODO optimize
|
34
|
+
mpa = @bidi_maps.find{|m| m.right.clazz == name or m.right.clazz.to_s == name.to_s }
|
35
|
+
mpa ||= @bidi_maps.find{|m| m.left.clazz == name or m.left.clazz.to_s == name.to_s }
|
36
|
+
return mpa unless mpa.nil?
|
37
|
+
|
38
|
+
return @bidi_maps.find{|m| "#{m.left.clazz}-to-from-#{m.right.clazz}" == name.to_s}
|
39
|
+
end
|
40
|
+
def list_map_names(full_list = false)
|
41
|
+
list = []
|
42
|
+
list += @maps.collect{|m| "#{m.from.clazz}-to-#{m.to.clazz}"}
|
43
|
+
list += @maps.collect{|m|m.from.clazz} if full_list
|
44
|
+
return list
|
45
|
+
end
|
46
|
+
def list_bidi_map_names
|
47
|
+
list = []
|
48
|
+
list += @bidi_maps.collect{|m| "#{m.left.clazz}-to-from-#{m.right.clazz}"}
|
49
|
+
return list
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class FieldMap < Map
|
54
|
+
attr_accessor :dict, :desc, :left, :right, :func, :to, :from, :func_on_nil
|
55
|
+
# True if map is unidirectional. Map is unidirectional
|
56
|
+
# when maps one way only.
|
57
|
+
def normalized?
|
58
|
+
not @from.nil?
|
59
|
+
end
|
60
|
+
|
61
|
+
def normalize
|
62
|
+
#if bidirectional
|
63
|
+
if not normalized?
|
64
|
+
map_l = self.clone
|
65
|
+
map_l.to = self.left
|
66
|
+
map_l.from = self.right
|
67
|
+
map_l.maps = self.maps.select do |m|
|
68
|
+
m.to.parent == map_l.to
|
69
|
+
end
|
70
|
+
|
71
|
+
map_l.dict = self.dict.invert unless self.dict.nil?
|
72
|
+
|
73
|
+
map_r = self.clone
|
74
|
+
map_r.to = self.right
|
75
|
+
map_r.from = self.left
|
76
|
+
map_r.maps = self.maps.select do |m|
|
77
|
+
m.to.parent == map_r.to
|
78
|
+
end
|
79
|
+
|
80
|
+
[map_l, map_r]
|
81
|
+
else
|
82
|
+
[self]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
def simple?
|
86
|
+
@func.nil? && @dict.nil? && @desc.nil? &&
|
87
|
+
@maps.empty? && @bidi_maps.empty? && @right.func.nil? && @left.func.nil?
|
88
|
+
end
|
89
|
+
def func_on_nil?
|
90
|
+
@func_on_nil
|
91
|
+
end
|
92
|
+
end
|
93
|
+
class Tree
|
94
|
+
attr_accessor :parent
|
95
|
+
|
96
|
+
def initialize(parent)
|
97
|
+
@parent = parent
|
98
|
+
end
|
99
|
+
def method_missing(symbol, *args)
|
100
|
+
return Field.new(@parent, symbol, args[0])
|
101
|
+
end
|
102
|
+
end
|
103
|
+
class Field < Struct.new(:name, :clazz, :parent, :func, :is_root, :is_array)
|
104
|
+
def array?
|
105
|
+
@is_array
|
106
|
+
end
|
107
|
+
end
|
108
|
+
class Constant < Struct.new(:value)
|
109
|
+
def parent
|
110
|
+
nil
|
111
|
+
end
|
112
|
+
def is_array
|
113
|
+
@value.kind_of?(Array)
|
114
|
+
end
|
115
|
+
def func
|
116
|
+
nil
|
117
|
+
end
|
118
|
+
end
|
119
|
+
class Function
|
120
|
+
def parent
|
121
|
+
nil
|
122
|
+
end
|
123
|
+
def array?
|
124
|
+
false
|
125
|
+
end
|
126
|
+
def func
|
127
|
+
nil
|
128
|
+
end
|
129
|
+
def value
|
130
|
+
nil
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|