shale 0.9.0 → 1.0.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.
@@ -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