shale 0.2.2 → 0.4.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +33 -0
- data/README.md +366 -8
- data/exe/shaleb +123 -0
- data/lib/shale/adapter/json.rb +7 -2
- data/lib/shale/adapter/nokogiri.rb +48 -12
- data/lib/shale/adapter/ox.rb +28 -4
- data/lib/shale/adapter/rexml.rb +56 -13
- data/lib/shale/attribute.rb +7 -1
- data/lib/shale/error.rb +12 -0
- data/lib/shale/mapper.rb +17 -15
- data/lib/shale/mapping/descriptor/dict.rb +57 -0
- data/lib/shale/mapping/descriptor/xml.rb +43 -0
- data/lib/shale/mapping/descriptor/xml_namespace.rb +37 -0
- data/lib/shale/mapping/{key_value.rb → dict.rb} +8 -6
- data/lib/shale/mapping/validator.rb +51 -0
- data/lib/shale/mapping/xml.rb +86 -15
- data/lib/shale/schema/json_compiler/boolean.rb +21 -0
- data/lib/shale/schema/json_compiler/date.rb +21 -0
- data/lib/shale/schema/json_compiler/float.rb +21 -0
- data/lib/shale/schema/json_compiler/integer.rb +21 -0
- data/lib/shale/schema/json_compiler/object.rb +85 -0
- data/lib/shale/schema/json_compiler/property.rb +70 -0
- data/lib/shale/schema/json_compiler/string.rb +21 -0
- data/lib/shale/schema/json_compiler/time.rb +21 -0
- data/lib/shale/schema/json_compiler/utils.rb +52 -0
- data/lib/shale/schema/json_compiler/value.rb +13 -0
- data/lib/shale/schema/json_compiler.rb +333 -0
- data/lib/shale/schema/json_generator/base.rb +41 -0
- data/lib/shale/schema/json_generator/boolean.rb +23 -0
- data/lib/shale/schema/json_generator/collection.rb +39 -0
- data/lib/shale/schema/json_generator/date.rb +23 -0
- data/lib/shale/schema/json_generator/float.rb +23 -0
- data/lib/shale/schema/json_generator/integer.rb +23 -0
- data/lib/shale/schema/json_generator/object.rb +40 -0
- data/lib/shale/schema/json_generator/ref.rb +28 -0
- data/lib/shale/schema/json_generator/schema.rb +59 -0
- data/lib/shale/schema/json_generator/string.rb +23 -0
- data/lib/shale/schema/json_generator/time.rb +23 -0
- data/lib/shale/schema/json_generator/value.rb +23 -0
- data/lib/shale/schema/json_generator.rb +165 -0
- data/lib/shale/schema/xml_generator/attribute.rb +41 -0
- data/lib/shale/schema/xml_generator/complex_type.rb +70 -0
- data/lib/shale/schema/xml_generator/element.rb +55 -0
- data/lib/shale/schema/xml_generator/import.rb +46 -0
- data/lib/shale/schema/xml_generator/ref_attribute.rb +37 -0
- data/lib/shale/schema/xml_generator/ref_element.rb +39 -0
- data/lib/shale/schema/xml_generator/schema.rb +121 -0
- data/lib/shale/schema/xml_generator/typed_attribute.rb +46 -0
- data/lib/shale/schema/xml_generator/typed_element.rb +46 -0
- data/lib/shale/schema/xml_generator.rb +315 -0
- data/lib/shale/schema.rb +70 -0
- data/lib/shale/type/boolean.rb +2 -2
- data/lib/shale/type/composite.rb +78 -72
- data/lib/shale/type/date.rb +35 -2
- data/lib/shale/type/float.rb +2 -2
- data/lib/shale/type/integer.rb +2 -2
- data/lib/shale/type/string.rb +2 -2
- data/lib/shale/type/time.rb +35 -2
- data/lib/shale/type/{base.rb → value.rb} +18 -7
- data/lib/shale/utils.rb +18 -2
- data/lib/shale/version.rb +1 -1
- data/lib/shale.rb +10 -10
- data/shale.gemspec +6 -2
- metadata +53 -13
- data/lib/shale/mapping/base.rb +0 -32
@@ -0,0 +1,315 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../../shale'
|
4
|
+
require_relative 'xml_generator/complex_type'
|
5
|
+
require_relative 'xml_generator/import'
|
6
|
+
require_relative 'xml_generator/ref_attribute'
|
7
|
+
require_relative 'xml_generator/ref_element'
|
8
|
+
require_relative 'xml_generator/schema'
|
9
|
+
require_relative 'xml_generator/typed_attribute'
|
10
|
+
require_relative 'xml_generator/typed_element'
|
11
|
+
|
12
|
+
module Shale
|
13
|
+
module Schema
|
14
|
+
# Class for generating XML schema
|
15
|
+
#
|
16
|
+
# @api public
|
17
|
+
class XMLGenerator
|
18
|
+
# XML Schema default name
|
19
|
+
# @api private
|
20
|
+
DEFAULT_SCHEMA_NAME = 'schema'
|
21
|
+
|
22
|
+
@xml_types = Hash.new('string')
|
23
|
+
|
24
|
+
# Register Shale to XML type mapping
|
25
|
+
#
|
26
|
+
# @param [Shale::Type::Value] shale_type
|
27
|
+
# @param [String] xml_type
|
28
|
+
#
|
29
|
+
# @example
|
30
|
+
# Shale::Schema::XMLGenerator.register_xml_type(Shale::Type::String, 'myType')
|
31
|
+
#
|
32
|
+
# @api public
|
33
|
+
def self.register_xml_type(shale_type, xml_type)
|
34
|
+
@xml_types[shale_type] = xml_type
|
35
|
+
end
|
36
|
+
|
37
|
+
# Return XML type for given Shale type
|
38
|
+
#
|
39
|
+
# @param [Shale::Type::Value] shale_type
|
40
|
+
#
|
41
|
+
# @return [String]
|
42
|
+
#
|
43
|
+
# @example
|
44
|
+
# Shale::Schema::XMLGenerator.get_xml_type(Shale::Type::String)
|
45
|
+
# # => 'string'
|
46
|
+
#
|
47
|
+
# @api private
|
48
|
+
def self.get_xml_type(shale_type)
|
49
|
+
@xml_types[shale_type]
|
50
|
+
end
|
51
|
+
|
52
|
+
register_xml_type(Shale::Type::Boolean, 'boolean')
|
53
|
+
register_xml_type(Shale::Type::Date, 'date')
|
54
|
+
register_xml_type(Shale::Type::Float, 'decimal')
|
55
|
+
register_xml_type(Shale::Type::Integer, 'integer')
|
56
|
+
register_xml_type(Shale::Type::Time, 'dateTime')
|
57
|
+
register_xml_type(Shale::Type::Value, 'anyType')
|
58
|
+
|
59
|
+
# Generate XML Schema from Shale model and return
|
60
|
+
# it as a Shale::Schema::XMLGenerator::Schema array
|
61
|
+
#
|
62
|
+
# @param [Shale::Mapper] klass
|
63
|
+
# @param [String, nil] base_name
|
64
|
+
#
|
65
|
+
# @raise [NotAShaleMapperError] when attribute is not a Shale model
|
66
|
+
#
|
67
|
+
# @return [Array<Shale::Schema::XMLGenerator::Schema>]
|
68
|
+
#
|
69
|
+
# @example
|
70
|
+
# Shale::Schema::XMLGenerator.new.as_schemas(Person)
|
71
|
+
#
|
72
|
+
# @api public
|
73
|
+
def as_schemas(klass, base_name = nil)
|
74
|
+
unless mapper_type?(klass)
|
75
|
+
raise NotAShaleMapperError, "XML Shema can't be generated for '#{klass}' type"
|
76
|
+
end
|
77
|
+
|
78
|
+
schemas = Hash.new do |hash, key|
|
79
|
+
name = "#{base_name || DEFAULT_SCHEMA_NAME}#{hash.keys.length}.xsd"
|
80
|
+
hash[key] = Schema.new(name, key)
|
81
|
+
end
|
82
|
+
|
83
|
+
default_namespace = klass.xml_mapping.default_namespace
|
84
|
+
|
85
|
+
root_element = TypedElement.new(
|
86
|
+
name: klass.xml_mapping.unprefixed_root,
|
87
|
+
type: [default_namespace.prefix, schematize(klass.name)].compact.join(':'),
|
88
|
+
required: true
|
89
|
+
)
|
90
|
+
|
91
|
+
schemas[default_namespace.name].add_namespace(
|
92
|
+
default_namespace.prefix,
|
93
|
+
default_namespace.name
|
94
|
+
)
|
95
|
+
schemas[default_namespace.name].add_child(root_element)
|
96
|
+
|
97
|
+
composites = []
|
98
|
+
collect_composite_types(composites, klass, klass.xml_mapping.default_namespace.name)
|
99
|
+
|
100
|
+
composites.each do |composite|
|
101
|
+
type = composite[:type]
|
102
|
+
namespace = composite[:namespace]
|
103
|
+
children = []
|
104
|
+
|
105
|
+
type.xml_mapping.elements.values.each do |mapping|
|
106
|
+
attribute = type.attributes[mapping.attribute]
|
107
|
+
next unless attribute
|
108
|
+
|
109
|
+
xml_type = get_xml_type_for_attribute(attribute.type, mapping.namespace)
|
110
|
+
default = get_default_for_attribute(attribute)
|
111
|
+
|
112
|
+
if namespace == mapping.namespace.name
|
113
|
+
children << TypedElement.new(
|
114
|
+
name: mapping.name,
|
115
|
+
type: xml_type,
|
116
|
+
collection: attribute.collection?,
|
117
|
+
default: default
|
118
|
+
)
|
119
|
+
else
|
120
|
+
target_namespace = mapping.namespace
|
121
|
+
target_schema = schemas[target_namespace.name]
|
122
|
+
|
123
|
+
children << RefElement.new(
|
124
|
+
ref: mapping.prefixed_name,
|
125
|
+
collection: attribute.collection?,
|
126
|
+
default: default
|
127
|
+
)
|
128
|
+
|
129
|
+
target_element = TypedElement.new(
|
130
|
+
name: mapping.name,
|
131
|
+
type: xml_type,
|
132
|
+
required: true
|
133
|
+
)
|
134
|
+
|
135
|
+
import_element = Import.new(
|
136
|
+
target_namespace.name,
|
137
|
+
target_schema.name
|
138
|
+
)
|
139
|
+
|
140
|
+
target_schema.add_namespace(
|
141
|
+
target_namespace.prefix,
|
142
|
+
target_namespace.name
|
143
|
+
)
|
144
|
+
target_schema.add_child(target_element)
|
145
|
+
|
146
|
+
schemas[namespace].add_namespace(
|
147
|
+
target_namespace.prefix,
|
148
|
+
target_namespace.name
|
149
|
+
)
|
150
|
+
schemas[namespace].add_child(import_element)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
type.xml_mapping.attributes.values.each do |mapping|
|
155
|
+
attribute = type.attributes[mapping.attribute]
|
156
|
+
next unless attribute
|
157
|
+
|
158
|
+
xml_type = get_xml_type_for_attribute(attribute.type, mapping.namespace)
|
159
|
+
default = get_default_for_attribute(attribute)
|
160
|
+
|
161
|
+
if namespace == mapping.namespace.name
|
162
|
+
children << TypedAttribute.new(
|
163
|
+
name: mapping.name,
|
164
|
+
type: xml_type,
|
165
|
+
default: default
|
166
|
+
)
|
167
|
+
else
|
168
|
+
target_namespace = mapping.namespace
|
169
|
+
target_schema = schemas[target_namespace.name]
|
170
|
+
|
171
|
+
children << RefAttribute.new(
|
172
|
+
ref: mapping.prefixed_name,
|
173
|
+
default: default
|
174
|
+
)
|
175
|
+
|
176
|
+
target_element = TypedAttribute.new(name: mapping.name, type: xml_type)
|
177
|
+
|
178
|
+
import_element = Import.new(
|
179
|
+
target_namespace.name,
|
180
|
+
target_schema.name
|
181
|
+
)
|
182
|
+
|
183
|
+
target_schema.add_namespace(
|
184
|
+
target_namespace.prefix,
|
185
|
+
target_namespace.name
|
186
|
+
)
|
187
|
+
target_schema.add_child(target_element)
|
188
|
+
|
189
|
+
schemas[namespace].add_namespace(
|
190
|
+
target_namespace.prefix,
|
191
|
+
target_namespace.name
|
192
|
+
)
|
193
|
+
schemas[namespace].add_child(import_element)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
complex = ComplexType.new(
|
198
|
+
schematize(type.name),
|
199
|
+
children,
|
200
|
+
mixed: !type.xml_mapping.content.nil?
|
201
|
+
)
|
202
|
+
|
203
|
+
schemas[namespace].add_child(complex)
|
204
|
+
end
|
205
|
+
|
206
|
+
schemas.values
|
207
|
+
end
|
208
|
+
|
209
|
+
# Generate XML Schema from Shale model
|
210
|
+
#
|
211
|
+
# @param [Shale::Mapper] klass
|
212
|
+
# @param [String, nil] base_name
|
213
|
+
# @param [true, false] pretty
|
214
|
+
# @param [true, false] declaration
|
215
|
+
#
|
216
|
+
# @return [Hash<String, String>]
|
217
|
+
#
|
218
|
+
# @example
|
219
|
+
# Shale::Schema::XMLGenerator.new.to_schemas(Person)
|
220
|
+
#
|
221
|
+
# @api public
|
222
|
+
def to_schemas(klass, base_name = nil, pretty: false, declaration: false)
|
223
|
+
schemas = as_schemas(klass, base_name)
|
224
|
+
|
225
|
+
options = [
|
226
|
+
pretty ? :pretty : nil,
|
227
|
+
declaration ? :declaration : nil,
|
228
|
+
]
|
229
|
+
|
230
|
+
schemas.to_h do |schema|
|
231
|
+
[schema.name, Shale.xml_adapter.dump(schema.as_xml, *options)]
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
private
|
236
|
+
|
237
|
+
# Check it type inherits from Shale::Mapper
|
238
|
+
#
|
239
|
+
# @param [Class] type
|
240
|
+
#
|
241
|
+
# @return [true, false]
|
242
|
+
#
|
243
|
+
# @api private
|
244
|
+
def mapper_type?(type)
|
245
|
+
type < Shale::Mapper
|
246
|
+
end
|
247
|
+
|
248
|
+
# Collect recursively Shale::Mapper types
|
249
|
+
#
|
250
|
+
# @param [Array<Shale::Mapper>] types
|
251
|
+
# @param [Shale::Mapper] type
|
252
|
+
# @param [String, nil] namespace
|
253
|
+
#
|
254
|
+
# @api private
|
255
|
+
def collect_composite_types(types, type, namespace)
|
256
|
+
types << { type: type, namespace: namespace }
|
257
|
+
|
258
|
+
type.xml_mapping.elements.values.each do |mapping|
|
259
|
+
attribute = type.attributes[mapping.attribute]
|
260
|
+
next unless attribute
|
261
|
+
|
262
|
+
is_mapper = mapper_type?(attribute.type)
|
263
|
+
is_included = types.include?({ type: attribute.type, namespace: namespace })
|
264
|
+
|
265
|
+
if is_mapper && !is_included
|
266
|
+
collect_composite_types(types, attribute.type, mapping.namespace.name)
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
# Convert Ruby class name to XML Schema name
|
272
|
+
#
|
273
|
+
# @param [String] word
|
274
|
+
#
|
275
|
+
# @return [String]
|
276
|
+
#
|
277
|
+
# @api private
|
278
|
+
def schematize(word)
|
279
|
+
word.gsub('::', '_')
|
280
|
+
end
|
281
|
+
|
282
|
+
# Return XML Schema type for given Shale::Type::Value
|
283
|
+
#
|
284
|
+
# @param [Shale::Type::Value] type
|
285
|
+
# @param [String] namespace
|
286
|
+
#
|
287
|
+
# @return [String]
|
288
|
+
#
|
289
|
+
# @api private
|
290
|
+
def get_xml_type_for_attribute(type, namespace)
|
291
|
+
if mapper_type?(type)
|
292
|
+
[namespace.prefix, schematize(type.name)].compact.join(':')
|
293
|
+
else
|
294
|
+
"xs:#{self.class.get_xml_type(type)}"
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
# Return default value for given Shale::Attribute
|
299
|
+
#
|
300
|
+
# @param [Shale::Attribute] attribute
|
301
|
+
#
|
302
|
+
# @return [String, nil]
|
303
|
+
#
|
304
|
+
# @api private
|
305
|
+
def get_default_for_attribute(attribute)
|
306
|
+
return unless attribute.default
|
307
|
+
return if attribute.collection?
|
308
|
+
return if mapper_type?(attribute.type)
|
309
|
+
|
310
|
+
value = attribute.type.cast(attribute.default.call)
|
311
|
+
attribute.type.as_xml_value(value)
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|
315
|
+
end
|
data/lib/shale/schema.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'schema/json_generator'
|
4
|
+
require_relative 'schema/json_compiler'
|
5
|
+
require_relative 'schema/xml_generator'
|
6
|
+
|
7
|
+
module Shale
|
8
|
+
# Module for handling JSON and XML schema
|
9
|
+
#
|
10
|
+
# @api private
|
11
|
+
module Schema
|
12
|
+
# Generate JSON Schema from Shale model
|
13
|
+
#
|
14
|
+
# @param [Shale::Mapper] klass
|
15
|
+
# @param [String, nil] id
|
16
|
+
# @param [String, nil] description
|
17
|
+
# @param [true, false] pretty
|
18
|
+
#
|
19
|
+
# @return [String]
|
20
|
+
#
|
21
|
+
# @example
|
22
|
+
# Shale::Schema.to_json(Person, id: 'My ID', description: 'My description', pretty: true)
|
23
|
+
# # => JSON schema
|
24
|
+
#
|
25
|
+
# @api public
|
26
|
+
def self.to_json(klass, id: nil, title: nil, description: nil, pretty: false)
|
27
|
+
JSONGenerator.new.to_schema(
|
28
|
+
klass,
|
29
|
+
id: id,
|
30
|
+
title: title,
|
31
|
+
description: description,
|
32
|
+
pretty: pretty
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Generate Shale model from JSON Schema
|
37
|
+
#
|
38
|
+
# @param [Array<String>] schemas
|
39
|
+
# @param [String, nil] root_name
|
40
|
+
#
|
41
|
+
# @return [Array<String>]
|
42
|
+
#
|
43
|
+
# @example
|
44
|
+
# Shale::Schema.from_json([json_schema1, json_schema2], root_name: 'foobar')
|
45
|
+
# # => [model1, model2, model3]
|
46
|
+
#
|
47
|
+
# @api public
|
48
|
+
def self.from_json(schemas, root_name: nil)
|
49
|
+
JSONCompiler.new.to_models(schemas, root_name: root_name)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Generate XML Schema from Shale model
|
53
|
+
#
|
54
|
+
# @param [Shale::Mapper] klass
|
55
|
+
# @param [String, nil] base_name
|
56
|
+
# @param [true, false] pretty
|
57
|
+
# @param [true, false] declaration
|
58
|
+
#
|
59
|
+
# @return [Hash<String, String>]
|
60
|
+
#
|
61
|
+
# @example
|
62
|
+
# Shale::Schema.to_xml(Person, pretty: true, declaration: true)
|
63
|
+
# # => { 'schema0.xsd' => <JSON schema>, 'schema1.xsd' => <JSON schema> }
|
64
|
+
#
|
65
|
+
# @api public
|
66
|
+
def self.to_xml(klass, base_name = nil, pretty: false, declaration: false)
|
67
|
+
XMLGenerator.new.to_schemas(klass, base_name, pretty: pretty, declaration: declaration)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
data/lib/shale/type/boolean.rb
CHANGED