shale 0.9.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -26,7 +26,8 @@ module Shale
26
26
  # Initialize instance
27
27
  #
28
28
  # @param [String] name
29
- # @param [Symbol, String] attribute
29
+ # @param [Symbol, nil] attribute
30
+ # @param [Symbol, nil] receiver
30
31
  # @param [Hash, nil] methods
31
32
  # @param [String, nil] group
32
33
  # @param [Shale::Mapping::XmlNamespace] namespace
@@ -34,10 +35,20 @@ module Shale
34
35
  # @param [true, false] render_nil
35
36
  #
36
37
  # @api private
37
- def initialize(name:, attribute:, methods:, group:, namespace:, cdata:, render_nil:)
38
+ def initialize(
39
+ name:,
40
+ attribute:,
41
+ receiver:,
42
+ methods:,
43
+ group:,
44
+ namespace:,
45
+ cdata:,
46
+ render_nil:
47
+ )
38
48
  super(
39
49
  name: name,
40
50
  attribute: attribute,
51
+ receiver: receiver,
41
52
  methods: methods,
42
53
  group: group,
43
54
  render_nil: render_nil
@@ -5,22 +5,32 @@ require_relative 'dict_group'
5
5
 
6
6
  module Shale
7
7
  module Mapping
8
- # Mapping for dictionary serialization formats (Hash/JSON/YAML)
8
+ # Mapping for dictionary serialization formats (Hash/JSON/YAML/TOML/CSV)
9
9
  #
10
10
  # @api private
11
11
  class Dict < DictBase
12
12
  # Map key to attribute
13
13
  #
14
14
  # @param [String] key Document's key
15
- # @param [Symbol, nil] to Object's attribute
15
+ # @param [Symbol, nil] to
16
+ # @param [Symbol, nil] receiver
16
17
  # @param [Hash, nil] using
17
18
  # @param [true, false, nil] render_nil
18
19
  #
19
20
  # @raise [IncorrectMappingArgumentsError] when arguments are incorrect
20
21
  #
21
22
  # @api private
22
- def map(key, to: nil, using: nil, render_nil: nil)
23
- super(key, to: to, using: using, render_nil: render_nil)
23
+ def map(key, to: nil, receiver: nil, using: nil, render_nil: nil)
24
+ super(key, to: to, receiver: receiver, using: using, render_nil: render_nil)
25
+ end
26
+
27
+ # Set render_nil default
28
+ #
29
+ # @param [true, false] val
30
+ #
31
+ # @api private
32
+ def render_nil(val)
33
+ @render_nil_default = val
24
34
  end
25
35
 
26
36
  # Map group of keys to mapping methods
@@ -5,7 +5,7 @@ require_relative 'validator'
5
5
 
6
6
  module Shale
7
7
  module Mapping
8
- # Base class for Mapping dictionary serialization formats (Hash/JSON/YAML)
8
+ # Base class for Mapping dictionary serialization formats (Hash/JSON/YAML/TOML/CSV)
9
9
  #
10
10
  # @api private
11
11
  class DictBase
@@ -31,6 +31,7 @@ module Shale
31
31
  #
32
32
  # @param [String] key
33
33
  # @param [Symbol, nil] to
34
+ # @param [Symbol, nil] receiver
34
35
  # @param [Hash, nil] using
35
36
  # @param [String, nil] group
36
37
  # @param [true, false, nil] render_nil
@@ -38,11 +39,13 @@ module Shale
38
39
  # @raise [IncorrectMappingArgumentsError] when arguments are incorrect
39
40
  #
40
41
  # @api private
41
- def map(key, to: nil, using: nil, group: nil, render_nil: nil)
42
- Validator.validate_arguments(key, to, using)
42
+ def map(key, to: nil, receiver: nil, using: nil, group: nil, render_nil: nil)
43
+ Validator.validate_arguments(key, to, receiver, using)
44
+
43
45
  @keys[key] = Descriptor::Dict.new(
44
46
  name: key,
45
47
  attribute: to,
48
+ receiver: receiver,
46
49
  methods: using,
47
50
  group: group,
48
51
  render_nil: render_nil.nil? ? @render_nil_default : render_nil
@@ -4,7 +4,7 @@ require_relative 'dict_base'
4
4
 
5
5
  module Shale
6
6
  module Mapping
7
- # Group for dictionary serialization formats (Hash/JSON/YAML)
7
+ # Group for dictionary serialization formats (Hash/JSON/YAML/TOML/CSV)
8
8
  #
9
9
  # @api private
10
10
  class DictGroup < DictBase
@@ -8,18 +8,25 @@ module Shale
8
8
  # Validate correctness of argument passed to map functions
9
9
  #
10
10
  # @param [String] key
11
- # @param [Symbol] to
12
- # @param [Hash] using
11
+ # @param [Symbol, nil] to
12
+ # @param [Symbol, nil] receiver
13
+ # @param [Hash, nil] using
13
14
  #
14
15
  # @raise [IncorrectMappingArgumentsError] when arguments are incorrect
15
16
  #
16
17
  # @api private
17
- def self.validate_arguments(key, to, using)
18
+ def self.validate_arguments(key, to, receiver, using)
18
19
  if to.nil? && using.nil?
19
20
  msg = ":to or :using argument is required for mapping '#{key}'"
20
21
  raise IncorrectMappingArgumentsError, msg
21
22
  end
22
23
 
24
+ if to.nil? && !receiver.nil?
25
+ msg = ":receiver argument for mapping '#{key}' " \
26
+ 'can only be used together with :to argument'
27
+ raise IncorrectMappingArgumentsError, msg
28
+ end
29
+
23
30
  if !using.nil? && (using[:from].nil? || using[:to].nil?)
24
31
  msg = ":using argument for mapping '#{key}' requires :to and :from keys"
25
32
  raise IncorrectMappingArgumentsError, msg
@@ -13,25 +13,28 @@ module Shale
13
13
  #
14
14
  # @param [String] element
15
15
  # @param [Symbol, nil] to
16
+ # @param [Symbol, nil] receiver
16
17
  # @param [Hash, nil] using
17
18
  # @param [String, nil] namespace
18
19
  # @param [String, nil] prefix
19
20
  # @param [true, false] cdata
20
- # @param [true, false] render_nil
21
+ # @param [true, false, nil] render_nil
21
22
  #
22
23
  # @api private
23
24
  def map_element(
24
25
  element,
25
26
  to: nil,
27
+ receiver: nil,
26
28
  using: nil,
27
29
  namespace: :undefined,
28
30
  prefix: :undefined,
29
31
  cdata: false,
30
- render_nil: false
32
+ render_nil: nil
31
33
  )
32
34
  super(
33
35
  element,
34
36
  to: to,
37
+ receiver: receiver,
35
38
  using: using,
36
39
  namespace: namespace,
37
40
  prefix: prefix,
@@ -44,23 +47,26 @@ module Shale
44
47
  #
45
48
  # @param [String] attribute
46
49
  # @param [Symbol, nil] to
50
+ # @param [Symbol, nil] receiver
47
51
  # @param [Hash, nil] using
48
52
  # @param [String, nil] namespace
49
53
  # @param [String, nil] prefix
50
- # @param [true, false] render_nil
54
+ # @param [true, false, nil] render_nil
51
55
  #
52
56
  # @api private
53
57
  def map_attribute(
54
58
  attribute,
55
59
  to: nil,
60
+ receiver: nil,
56
61
  using: nil,
57
62
  namespace: nil,
58
63
  prefix: nil,
59
- render_nil: false
64
+ render_nil: nil
60
65
  )
61
66
  super(
62
67
  attribute,
63
68
  to: to,
69
+ receiver: receiver,
64
70
  using: using,
65
71
  namespace: namespace,
66
72
  prefix: prefix,
@@ -71,12 +77,22 @@ module Shale
71
77
  # Map document's content to object's attribute
72
78
  #
73
79
  # @param [Symbol] to
80
+ # @param [Symbol, nil] receiver
74
81
  # @param [Hash, nil] using
75
82
  # @param [true, false] cdata
76
83
  #
77
84
  # @api private
78
- def map_content(to: nil, using: nil, cdata: false)
79
- super(to: to, using: using, cdata: cdata)
85
+ def map_content(to: nil, receiver: nil, using: nil, cdata: false)
86
+ super(to: to, receiver: receiver, using: using, cdata: cdata)
87
+ end
88
+
89
+ # Set render_nil default
90
+ #
91
+ # @param [true, false] val
92
+ #
93
+ # @api private
94
+ def render_nil(val)
95
+ @render_nil_default = val
80
96
  end
81
97
 
82
98
  # Map group of nodes to mapping methods
@@ -67,18 +67,20 @@ module Shale
67
67
  @root = ''
68
68
  @default_namespace = Descriptor::XmlNamespace.new
69
69
  @finalized = false
70
+ @render_nil_default = false
70
71
  end
71
72
 
72
73
  # Map element to attribute
73
74
  #
74
75
  # @param [String] element
75
76
  # @param [Symbol, nil] to
77
+ # @param [Symbol, nil] receiver
76
78
  # @param [Hash, nil] using
77
79
  # @param [String, nil] group
78
80
  # @param [String, nil] namespace
79
81
  # @param [String, nil] prefix
80
82
  # @param [true, false] cdata
81
- # @param [true, false] render_nil
83
+ # @param [true, false, nil] render_nil
82
84
  #
83
85
  # @raise [IncorrectMappingArgumentsError] when arguments are incorrect
84
86
  #
@@ -86,14 +88,15 @@ module Shale
86
88
  def map_element(
87
89
  element,
88
90
  to: nil,
91
+ receiver: nil,
89
92
  using: nil,
90
93
  group: nil,
91
94
  namespace: :undefined,
92
95
  prefix: :undefined,
93
96
  cdata: false,
94
- render_nil: false
97
+ render_nil: nil
95
98
  )
96
- Validator.validate_arguments(element, to, using)
99
+ Validator.validate_arguments(element, to, receiver, using)
97
100
  Validator.validate_namespace(element, namespace, prefix)
98
101
 
99
102
  if namespace == :undefined && prefix == :undefined
@@ -109,11 +112,12 @@ module Shale
109
112
  @elements[namespaced_element] = Descriptor::Xml.new(
110
113
  name: element,
111
114
  attribute: to,
115
+ receiver: receiver,
112
116
  methods: using,
113
117
  group: group,
114
118
  namespace: Descriptor::XmlNamespace.new(nsp, pfx),
115
119
  cdata: cdata,
116
- render_nil: render_nil
120
+ render_nil: render_nil.nil? ? @render_nil_default : render_nil
117
121
  )
118
122
  end
119
123
 
@@ -121,10 +125,12 @@ module Shale
121
125
  #
122
126
  # @param [String] attribute
123
127
  # @param [Symbol, nil] to
128
+ # @param [Symbol, nil] receiver
124
129
  # @param [Hash, nil] using
130
+ # @param [String, nil] group
125
131
  # @param [String, nil] namespace
126
132
  # @param [String, nil] prefix
127
- # @param [true, false] render_nil
133
+ # @param [true, false, nil] render_nil
128
134
  #
129
135
  # @raise [IncorrectMappingArgumentsError] when arguments are incorrect
130
136
  #
@@ -132,13 +138,14 @@ module Shale
132
138
  def map_attribute(
133
139
  attribute,
134
140
  to: nil,
141
+ receiver: nil,
135
142
  using: nil,
136
143
  group: nil,
137
144
  namespace: nil,
138
145
  prefix: nil,
139
- render_nil: false
146
+ render_nil: nil
140
147
  )
141
- Validator.validate_arguments(attribute, to, using)
148
+ Validator.validate_arguments(attribute, to, receiver, using)
142
149
  Validator.validate_namespace(attribute, namespace, prefix)
143
150
 
144
151
  namespaced_attribute = [namespace, attribute].compact.join(':')
@@ -146,27 +153,31 @@ module Shale
146
153
  @attributes[namespaced_attribute] = Descriptor::Xml.new(
147
154
  name: attribute,
148
155
  attribute: to,
156
+ receiver: receiver,
149
157
  methods: using,
150
158
  namespace: Descriptor::XmlNamespace.new(namespace, prefix),
151
159
  cdata: false,
152
160
  group: group,
153
- render_nil: render_nil
161
+ render_nil: render_nil.nil? ? @render_nil_default : render_nil
154
162
  )
155
163
  end
156
164
 
157
165
  # Map document's content to object's attribute
158
166
  #
159
167
  # @param [Symbol] to
168
+ # @param [Symbol, nil] receiver
160
169
  # @param [Hash, nil] using
170
+ # @param [String, nil] group
161
171
  # @param [true, false] cdata
162
172
  #
163
173
  # @api private
164
- def map_content(to: nil, using: nil, group: nil, cdata: false)
165
- Validator.validate_arguments('content', to, using)
174
+ def map_content(to: nil, receiver: nil, using: nil, group: nil, cdata: false)
175
+ Validator.validate_arguments('content', to, receiver, using)
166
176
 
167
177
  @content = Descriptor::Xml.new(
168
178
  name: nil,
169
179
  attribute: to,
180
+ receiver: receiver,
170
181
  methods: using,
171
182
  namespace: Descriptor::XmlNamespace.new(nil, nil),
172
183
  cdata: cdata,
@@ -215,8 +226,6 @@ module Shale
215
226
  def initialize_dup(other)
216
227
  @elements = other.instance_variable_get('@elements').dup
217
228
  @attributes = other.instance_variable_get('@attributes').dup
218
- @content = other.instance_variable_get('@content').dup
219
- @root = other.instance_variable_get('@root').dup
220
229
  @default_namespace = other.instance_variable_get('@default_namespace').dup
221
230
  @finalized = false
222
231
 
@@ -23,32 +23,52 @@ module Shale
23
23
  # @api private
24
24
  attr_reader :properties
25
25
 
26
- # Name setter
26
+ # Root name setter
27
27
  #
28
28
  # @param [String] val
29
29
  #
30
30
  # @api private
31
- attr_writer :name
31
+ attr_writer :root_name
32
32
 
33
33
  # Initialize object
34
34
  #
35
35
  # @param [String] id
36
- # @param [String] name
36
+ # @param [String] root_name
37
+ # @param [String, nil] package
37
38
  #
38
39
  # @api private
39
- def initialize(id, name)
40
+ def initialize(id, root_name, package)
40
41
  @id = id
41
- @name = name
42
+ @root_name = root_name
43
+ @package = package ? Utils.classify(package) : nil
42
44
  @properties = []
43
45
  end
44
46
 
45
- # Return name
47
+ # Return base name
48
+ #
49
+ # @return [String]
50
+ #
51
+ # @api private
52
+ def root_name
53
+ Utils.classify(@root_name)
54
+ end
55
+
56
+ # Return namespaced name
46
57
  #
47
58
  # @return [String]
48
59
  #
49
60
  # @api private
50
61
  def name
51
- Utils.classify(@name)
62
+ Utils.classify([@package, @root_name].compact.join('::'))
63
+ end
64
+
65
+ # Return module names
66
+ #
67
+ # @return [Array<String>]
68
+ #
69
+ # @api private
70
+ def modules
71
+ (@package || '').split('::')
52
72
  end
53
73
 
54
74
  # Return file name
@@ -57,7 +77,31 @@ module Shale
57
77
  #
58
78
  # @api private
59
79
  def file_name
60
- Utils.snake_case(@name)
80
+ Utils.snake_case(name)
81
+ end
82
+
83
+ # Return relative path to target
84
+ #
85
+ # @param [String] target
86
+ #
87
+ # @return [String]
88
+ #
89
+ # @api private
90
+ def relative_path(target)
91
+ base_paths = file_name.split('/')
92
+ target_paths = target.split('/')
93
+
94
+ common_paths_length = 0
95
+
96
+ base_paths.length.times do |i|
97
+ break if base_paths[i] != target_paths[i]
98
+ common_paths_length += 1
99
+ end
100
+
101
+ unique_base_paths = base_paths[common_paths_length..-1]
102
+ unique_target_paths = target_paths[common_paths_length..-1]
103
+
104
+ ((0...unique_base_paths.length - 1).map { '..' } + unique_target_paths).join('/')
61
105
  end
62
106
 
63
107
  # Return references
@@ -33,14 +33,15 @@ module Shale
33
33
  # Initialize object
34
34
  #
35
35
  # @param [String] id
36
- # @param [String] name
36
+ # @param [String] root_name
37
37
  # @param [String] prefix
38
38
  # @param [String] namespace
39
+ # @param [String, nil] package
39
40
  #
40
41
  # @api private
41
- def initialize(id, name, prefix, namespace)
42
- super(id, name)
43
- @root = name
42
+ def initialize(id, root_name, prefix, namespace, package)
43
+ super(id, root_name, package)
44
+ @root = root_name
44
45
  @prefix = prefix
45
46
  @namespace = namespace
46
47
  end
@@ -4,6 +4,7 @@ require 'erb'
4
4
  require 'uri'
5
5
 
6
6
  require_relative '../../shale'
7
+ require_relative '../utils'
7
8
  require_relative 'compiler/boolean'
8
9
  require_relative 'compiler/complex'
9
10
  require_relative 'compiler/date'
@@ -31,29 +32,37 @@ module Shale
31
32
  <%- unless type.references.empty? -%>
32
33
 
33
34
  <%- type.references.each do |property| -%>
34
- require_relative '<%= property.type.file_name %>'
35
+ require_relative '<%= type.relative_path(property.type.file_name) %>'
35
36
  <%- end -%>
36
37
  <%- end -%>
37
38
 
38
- class <%= type.name %> < Shale::Mapper
39
+ <%- type.modules.each_with_index do |name, i| -%>
40
+ <%= ' ' * i %>module <%= name %>
41
+ <%- end -%>
42
+ <%- indent = ' ' * type.modules.length -%>
43
+ <%= indent %>class <%= type.root_name %> < Shale::Mapper
39
44
  <%- type.properties.each do |property| -%>
40
- attribute :<%= property.attribute_name %>, <%= property.type.name -%>
45
+ <%= indent %>attribute :<%= property.attribute_name %>, <%= property.type.name -%>
41
46
  <%- if property.collection? %>, collection: true<% end -%>
42
47
  <%- unless property.default.nil? %>, default: -> { <%= property.default %> }<% end %>
43
48
  <%- end -%>
44
49
 
45
- json do
50
+ <%= indent %>json do
46
51
  <%- type.properties.each do |property| -%>
47
- map '<%= property.mapping_name %>', to: :<%= property.attribute_name %>
52
+ <%= indent %>map '<%= property.mapping_name %>', to: :<%= property.attribute_name %>
48
53
  <%- end -%>
49
- end
50
- end
54
+ <%= indent %>end
55
+ <%= indent %>end
56
+ <%- type.modules.length.times do |i| -%>
57
+ <%= ' ' * (type.modules.length - i - 1) %>end
58
+ <%- end -%>
51
59
  TEMPLATE
52
60
 
53
61
  # Generate Shale models from JSON Schema and return them as a Ruby Array of objects
54
62
  #
55
63
  # @param [Array<String>] schemas
56
64
  # @param [String, nil] root_name
65
+ # @param [Hash<String, String>, nil] namespace_mapping
57
66
  #
58
67
  # @raise [SchemaError] when JSON Schema has errors
59
68
  #
@@ -63,12 +72,13 @@ module Shale
63
72
  # Shale::Schema::JSONCompiler.new.as_models([schema1, schema2])
64
73
  #
65
74
  # @api public
66
- def as_models(schemas, root_name: nil)
75
+ def as_models(schemas, root_name: nil, namespace_mapping: nil)
67
76
  schemas = schemas.map do |schema|
68
77
  Shale.json_adapter.load(schema)
69
78
  end
70
79
 
71
80
  @root_name = root_name
81
+ @namespace_mapping = namespace_mapping || {}
72
82
  @schema_repository = {}
73
83
  @types = []
74
84
 
@@ -89,7 +99,7 @@ module Shale
89
99
  duplicates[type.name] += 1
90
100
 
91
101
  if total_duplicates[type.name] > 1
92
- type.name = format("#{type.name}%d", duplicates[type.name])
102
+ type.root_name = format("#{type.root_name}%d", duplicates[type.name])
93
103
  end
94
104
  end
95
105
 
@@ -100,6 +110,7 @@ module Shale
100
110
  #
101
111
  # @param [Array<String>] schemas
102
112
  # @param [String, nil] root_name
113
+ # @param [Hash<String, String>, nil] namespace_mapping
103
114
  #
104
115
  # @raise [SchemaError] when JSON Schema has errors
105
116
  #
@@ -109,8 +120,8 @@ module Shale
109
120
  # Shale::Schema::JSONCompiler.new.to_models([schema1, schema2])
110
121
  #
111
122
  # @api public
112
- def to_models(schemas, root_name: nil)
113
- types = as_models(schemas, root_name: root_name)
123
+ def to_models(schemas, root_name: nil, namespace_mapping: nil)
124
+ types = as_models(schemas, root_name: root_name, namespace_mapping: namespace_mapping)
114
125
 
115
126
  types.to_h do |type|
116
127
  [type.file_name, MODEL_TEMPLATE.result(binding)]
@@ -207,7 +218,8 @@ module Shale
207
218
  end
208
219
 
209
220
  if type == 'object'
210
- Compiler::Complex.new(id, name)
221
+ base_id = Utils.presence(id.split('#')[0])
222
+ Compiler::Complex.new(id, name, @namespace_mapping[base_id])
211
223
  elsif type == 'string' && format == 'date'
212
224
  Compiler::Date.new
213
225
  elsif type == 'string' && format == 'date-time'
@@ -297,7 +309,9 @@ module Shale
297
309
  if schema.is_a?(Hash) && schema.key?('$ref')
298
310
  entry = resolve_ref(id, schema['$ref'])
299
311
  schema = entry[:schema]
300
- fragment = entry[:id].split('/') - ['#']
312
+ entry_id, entry_fragment = entry[:id].split('#')
313
+ id = build_id(id, entry_id)
314
+ fragment = (entry_fragment || '').split('/') - ['']
301
315
  end
302
316
 
303
317
  pointer = entry[:id] || build_pointer(id, fragment)
@@ -87,7 +87,7 @@ module Shale
87
87
  next unless attribute
88
88
 
89
89
  if mapper_type?(attribute.type)
90
- json_type = Ref.new(mapping.name, attribute.type.name)
90
+ json_type = Ref.new(mapping.name, attribute.type.model.name)
91
91
  else
92
92
  json_klass = self.class.get_json_type(attribute.type)
93
93
 
@@ -103,7 +103,7 @@ module Shale
103
103
  properties << json_type
104
104
  end
105
105
 
106
- objects << Object.new(type.name, properties)
106
+ objects << Object.new(type.model.name, properties)
107
107
  end
108
108
 
109
109
  Schema.new(objects, id: id, title: title, description: description).as_json