shale 0.5.0 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -30,19 +30,20 @@ module Shale
30
30
  # Serialize Ox document into XML
31
31
  #
32
32
  # @param [::Ox::Document, ::Ox::Element] doc Ox document
33
- # @param [Array<Symbol>] options
33
+ # @param [true, false] pretty
34
+ # @param [true, false] declaration
34
35
  #
35
36
  # @return [String]
36
37
  #
37
38
  # @api private
38
- def self.dump(doc, *options)
39
+ def self.dump(doc, pretty: false, declaration: false)
39
40
  opts = { indent: -1, with_xml: false }
40
41
 
41
- if options.include?(:pretty)
42
+ if pretty
42
43
  opts[:indent] = 2
43
44
  end
44
45
 
45
- if options.include?(:declaration)
46
+ if declaration
46
47
  doc[:version] = '1.0'
47
48
  opts[:with_xml] = true
48
49
  end
@@ -42,6 +42,16 @@ module Shale
42
42
  ::REXML::Element.new(name, nil, attribute_quote: :quote)
43
43
  end
44
44
 
45
+ # Create CDATA node and add it to parent
46
+ #
47
+ # @param [String] text
48
+ # @param [::REXML::Element] parent
49
+ #
50
+ # @api private
51
+ def create_cdata(text, parent)
52
+ ::REXML::CData.new(text, true, parent)
53
+ end
54
+
45
55
  # Add XML namespace to document
46
56
  #
47
57
  # @param [String] prefix
@@ -60,7 +70,7 @@ module Shale
60
70
  #
61
71
  # @api private
62
72
  def add_attribute(element, name, value)
63
- element.add_attribute(name, value)
73
+ element.add_attribute(name, value || '')
64
74
  end
65
75
 
66
76
  # Add child element to REXML element
@@ -31,19 +31,20 @@ module Shale
31
31
  # Serialize REXML document into XML
32
32
  #
33
33
  # @param [::REXML::Document] doc REXML document
34
- # @param [Array<Symbol>] options
34
+ # @param [true, false] pretty
35
+ # @param [true, false] declaration
35
36
  #
36
37
  # @return [String]
37
38
  #
38
39
  # @api private
39
- def self.dump(doc, *options)
40
- if options.include?(:declaration)
40
+ def self.dump(doc, pretty: false, declaration: false)
41
+ if declaration
41
42
  doc.add(::REXML::XMLDecl.new)
42
43
  end
43
44
 
44
45
  io = StringIO.new
45
46
 
46
- if options.include?(:pretty)
47
+ if pretty
47
48
  formatter = ::REXML::Formatters::Pretty.new
48
49
  formatter.compact = true
49
50
  else
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'toml-rb'
4
+
5
+ module Shale
6
+ module Adapter
7
+ # TOML adapter
8
+ #
9
+ # @api public
10
+ class TomlRB
11
+ # Parse TOML into Hash
12
+ #
13
+ # @param [String] toml TOML document
14
+ #
15
+ # @return [Hash]
16
+ #
17
+ # @api private
18
+ def self.load(toml)
19
+ ::TomlRB.parse(toml)
20
+ end
21
+
22
+ # Serialize Hash into TOML
23
+ #
24
+ # @param [Hash] obj Hash object
25
+ #
26
+ # @return [String]
27
+ #
28
+ # @api private
29
+ def self.dump(obj)
30
+ ::TomlRB.dump(obj)
31
+ end
32
+ end
33
+ end
34
+ end
data/lib/shale/error.rb CHANGED
@@ -1,22 +1,38 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Shale
4
- # Error message displayed when adapter is not set
4
+ # Error message displayed when TOML adapter is not set
5
5
  # @api private
6
- ADAPTER_NOT_SET_MESSAGE = <<~MSG
6
+ TOML_ADAPTER_NOT_SET_MESSAGE = <<~MSG
7
+ TOML Adapter is not set.
8
+ To use Shale with TOML documents you have to install parser and set adapter.
9
+
10
+ # To use Tomlib:
11
+ # Make sure tomlib is installed eg. execute: gem install tomlib
12
+ Shale.toml_adapter = Tomlib
13
+
14
+ # To use toml-rb:
15
+ # Make sure toml-rb is installed eg. execute: gem install toml-rb
16
+ require 'shale/adapter/toml_rb'
17
+ Shale.toml_adapter = Shale::Adapter::TomlRB
18
+ MSG
19
+
20
+ # Error message displayed when XML adapter is not set
21
+ # @api private
22
+ XML_ADAPTER_NOT_SET_MESSAGE = <<~MSG
7
23
  XML Adapter is not set.
8
24
  To use Shale with XML documents you have to install parser and set adapter.
9
25
 
10
- To use REXML:
26
+ # To use REXML:
11
27
  require 'shale/adapter/rexml'
12
28
  Shale.xml_adapter = Shale::Adapter::REXML
13
29
 
14
- To use Nokogiri:
30
+ # To use Nokogiri:
15
31
  # Make sure Nokogiri is installed eg. execute: gem install nokogiri
16
32
  require 'shale/adapter/nokogiri'
17
33
  Shale.xml_adapter = Shale::Adapter::Nokogiri
18
34
 
19
- To use OX:
35
+ # To use OX:
20
36
  # Make sure Ox is installed eg. execute: gem install ox
21
37
  require 'shale/adapter/ox'
22
38
  Shale.xml_adapter = Shale::Adapter::Ox
@@ -52,6 +68,12 @@ module Shale
52
68
  end
53
69
  end
54
70
 
71
+ # Error for passing incorrect model type
72
+ #
73
+ # @api private
74
+ class IncorrectModelError < StandardError
75
+ end
76
+
55
77
  # Error for passing incorrect arguments to map functions
56
78
  #
57
79
  # @api private
data/lib/shale/mapper.rb CHANGED
@@ -43,10 +43,12 @@ module Shale
43
43
  #
44
44
  # @api public
45
45
  class Mapper < Type::Complex
46
+ @model = nil
46
47
  @attributes = {}
47
48
  @hash_mapping = Mapping::Dict.new
48
49
  @json_mapping = Mapping::Dict.new
49
50
  @yaml_mapping = Mapping::Dict.new
51
+ @toml_mapping = Mapping::Dict.new
50
52
  @xml_mapping = Mapping::Xml.new
51
53
 
52
54
  class << self
@@ -78,6 +80,13 @@ module Shale
78
80
  # @api public
79
81
  attr_reader :yaml_mapping
80
82
 
83
+ # Return TOML mapping object
84
+ #
85
+ # @return [Shale::Mapping::Dict]
86
+ #
87
+ # @api public
88
+ attr_reader :toml_mapping
89
+
81
90
  # Return XML mapping object
82
91
  #
83
92
  # @return [Shale::Mapping::XML]
@@ -88,16 +97,20 @@ module Shale
88
97
  # @api private
89
98
  def inherited(subclass)
90
99
  super
100
+
101
+ subclass.instance_variable_set('@model', subclass)
91
102
  subclass.instance_variable_set('@attributes', @attributes.dup)
92
103
 
93
104
  subclass.instance_variable_set('@__hash_mapping_init', @hash_mapping.dup)
94
105
  subclass.instance_variable_set('@__json_mapping_init', @json_mapping.dup)
95
106
  subclass.instance_variable_set('@__yaml_mapping_init', @yaml_mapping.dup)
107
+ subclass.instance_variable_set('@__toml_mapping_init', @toml_mapping.dup)
96
108
  subclass.instance_variable_set('@__xml_mapping_init', @xml_mapping.dup)
97
109
 
98
110
  subclass.instance_variable_set('@hash_mapping', @hash_mapping.dup)
99
111
  subclass.instance_variable_set('@json_mapping', @json_mapping.dup)
100
112
  subclass.instance_variable_set('@yaml_mapping', @yaml_mapping.dup)
113
+ subclass.instance_variable_set('@toml_mapping', @toml_mapping.dup)
101
114
 
102
115
  xml_mapping = @xml_mapping.dup
103
116
  xml_mapping.root(Utils.underscore(subclass.name || ''))
@@ -105,6 +118,15 @@ module Shale
105
118
  subclass.instance_variable_set('@xml_mapping', xml_mapping.dup)
106
119
  end
107
120
 
121
+ def model(klass = nil)
122
+ if klass
123
+ @model = klass
124
+ xml_mapping.root(Utils.underscore(@model.name))
125
+ else
126
+ @model
127
+ end
128
+ end
129
+
108
130
  # Define attribute on class
109
131
  #
110
132
  # @param [Symbol] name Name of the attribute
@@ -143,10 +165,11 @@ module Shale
143
165
 
144
166
  @attributes[name] = Attribute.new(name, type, collection, default)
145
167
 
146
- @hash_mapping.map(name.to_s, to: name)
147
- @json_mapping.map(name.to_s, to: name)
148
- @yaml_mapping.map(name.to_s, to: name)
149
- @xml_mapping.map_element(name.to_s, to: name)
168
+ @hash_mapping.map(name.to_s, to: name) unless @hash_mapping.finalized?
169
+ @json_mapping.map(name.to_s, to: name) unless @json_mapping.finalized?
170
+ @yaml_mapping.map(name.to_s, to: name) unless @yaml_mapping.finalized?
171
+ @toml_mapping.map(name.to_s, to: name) unless @toml_mapping.finalized?
172
+ @xml_mapping.map_element(name.to_s, to: name) unless @xml_mapping.finalized?
150
173
 
151
174
  class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
152
175
  attr_reader :#{name}
@@ -168,7 +191,7 @@ module Shale
168
191
  # attribute :age, Shale::Type::Integer
169
192
  #
170
193
  # hsh do
171
- # map 'firatName', to: :first_name
194
+ # map 'firstName', to: :first_name
172
195
  # map 'lastName', to: :last_name
173
196
  # map 'age', to: :age
174
197
  # end
@@ -177,6 +200,7 @@ module Shale
177
200
  # @api public
178
201
  def hsh(&block)
179
202
  @hash_mapping = @__hash_mapping_init.dup
203
+ @hash_mapping.finalize!
180
204
  @hash_mapping.instance_eval(&block)
181
205
  end
182
206
 
@@ -191,7 +215,7 @@ module Shale
191
215
  # attribute :age, Shale::Type::Integer
192
216
  #
193
217
  # json do
194
- # map 'firatName', to: :first_name
218
+ # map 'firstName', to: :first_name
195
219
  # map 'lastName', to: :last_name
196
220
  # map 'age', to: :age
197
221
  # end
@@ -200,6 +224,7 @@ module Shale
200
224
  # @api public
201
225
  def json(&block)
202
226
  @json_mapping = @__json_mapping_init.dup
227
+ @json_mapping.finalize!
203
228
  @json_mapping.instance_eval(&block)
204
229
  end
205
230
 
@@ -214,7 +239,7 @@ module Shale
214
239
  # attribute :age, Shale::Type::Integer
215
240
  #
216
241
  # yaml do
217
- # map 'firat_name', to: :first_name
242
+ # map 'first_name', to: :first_name
218
243
  # map 'last_name', to: :last_name
219
244
  # map 'age', to: :age
220
245
  # end
@@ -223,9 +248,34 @@ module Shale
223
248
  # @api public
224
249
  def yaml(&block)
225
250
  @yaml_mapping = @__yaml_mapping_init.dup
251
+ @yaml_mapping.finalize!
226
252
  @yaml_mapping.instance_eval(&block)
227
253
  end
228
254
 
255
+ # Define TOML mapping
256
+ #
257
+ # @param [Proc] block
258
+ #
259
+ # @example
260
+ # calss Person < Shale::Mapper
261
+ # attribute :first_name, Shale::Type::String
262
+ # attribute :last_name, Shale::Type::String
263
+ # attribute :age, Shale::Type::Integer
264
+ #
265
+ # toml do
266
+ # map 'first_name', to: :first_name
267
+ # map 'last_name', to: :last_name
268
+ # map 'age', to: :age
269
+ # end
270
+ # end
271
+ #
272
+ # @api public
273
+ def toml(&block)
274
+ @toml_mapping = @__toml_mapping_init.dup
275
+ @toml_mapping.finalize!
276
+ @toml_mapping.instance_eval(&block)
277
+ end
278
+
229
279
  # Define XML mapping
230
280
  #
231
281
  # @param [Proc] block
@@ -247,6 +297,8 @@ module Shale
247
297
  # @api public
248
298
  def xml(&block)
249
299
  @xml_mapping = @__xml_mapping_init.dup
300
+ @xml_mapping.finalize!
301
+ @xml_mapping.root('')
250
302
  @xml_mapping.instance_eval(&block)
251
303
  end
252
304
  end
@@ -40,17 +40,28 @@ module Shale
40
40
  # @param [String] name
41
41
  # @param [Symbol, nil] attribute
42
42
  # @param [Hash, nil] methods
43
+ # @param [true, false] render_nil
43
44
  #
44
45
  # @api private
45
- def initialize(name:, attribute:, methods:)
46
+ def initialize(name:, attribute:, methods:, render_nil:)
46
47
  @name = name
47
48
  @attribute = attribute
49
+ @render_nil = render_nil
48
50
 
49
51
  if methods
50
52
  @method_from = methods[:from]
51
53
  @method_to = methods[:to]
52
54
  end
53
55
  end
56
+
57
+ # Check render_nil
58
+ #
59
+ # @return [true, false]
60
+ #
61
+ # @api private
62
+ def render_nil?
63
+ @render_nil == true
64
+ end
54
65
  end
55
66
  end
56
67
  end
@@ -16,17 +16,27 @@ module Shale
16
16
  # @api private
17
17
  attr_reader :namespace
18
18
 
19
+ # Return cdata
20
+ #
21
+ # @return [true, false]
22
+ #
23
+ # @api private
24
+ attr_reader :cdata
25
+
19
26
  # Initialize instance
20
27
  #
21
28
  # @param [String] name
22
29
  # @param [Symbol, String] attribute
23
30
  # @param [Hash, nil] methods
24
31
  # @param [Shale::Mapping::XmlNamespace] namespace
32
+ # @param [true, false] cdata
33
+ # @param [true, false] render_nil
25
34
  #
26
35
  # @api private
27
- def initialize(name:, attribute:, methods:, namespace:)
28
- super(name: name, attribute: attribute, methods: methods)
36
+ def initialize(name:, attribute:, methods:, namespace:, cdata:, render_nil:)
37
+ super(name: name, attribute: attribute, methods: methods, render_nil: render_nil)
29
38
  @namespace = namespace
39
+ @cdata = cdata
30
40
  end
31
41
 
32
42
  # Return name with XML prefix
@@ -22,6 +22,7 @@ module Shale
22
22
  def initialize
23
23
  super
24
24
  @keys = {}
25
+ @finalized = false
25
26
  end
26
27
 
27
28
  # Map key to attribute
@@ -29,18 +30,41 @@ module Shale
29
30
  # @param [String] key Document's key
30
31
  # @param [Symbol, nil] to Object's attribute
31
32
  # @param [Hash, nil] using
33
+ # @param [true, false] render_nil
32
34
  #
33
35
  # @raise [IncorrectMappingArgumentsError] when arguments are incorrect
34
36
  #
35
37
  # @api private
36
- def map(key, to: nil, using: nil)
38
+ def map(key, to: nil, using: nil, render_nil: false)
37
39
  Validator.validate_arguments(key, to, using)
38
- @keys[key] = Descriptor::Dict.new(name: key, attribute: to, methods: using)
40
+ @keys[key] = Descriptor::Dict.new(
41
+ name: key,
42
+ attribute: to,
43
+ methods: using,
44
+ render_nil: render_nil
45
+ )
46
+ end
47
+
48
+ # Set the "finalized" instance variable to true
49
+ #
50
+ # @api private
51
+ def finalize!
52
+ @finalized = true
53
+ end
54
+
55
+ # Query the "finalized" instance variable
56
+ #
57
+ # @return [truem false]
58
+ #
59
+ # @api private
60
+ def finalized?
61
+ @finalized
39
62
  end
40
63
 
41
64
  # @api private
42
65
  def initialize_dup(other)
43
66
  @keys = other.instance_variable_get('@keys').dup
67
+ @finalized = false
44
68
  super
45
69
  end
46
70
  end
@@ -63,6 +63,7 @@ module Shale
63
63
  @content = nil
64
64
  @root = ''
65
65
  @default_namespace = Descriptor::XmlNamespace.new
66
+ @finalized = false
66
67
  end
67
68
 
68
69
  # Map element to attribute
@@ -76,7 +77,15 @@ module Shale
76
77
  # @raise [IncorrectMappingArgumentsError] when arguments are incorrect
77
78
  #
78
79
  # @api private
79
- def map_element(element, to: nil, using: nil, namespace: :undefined, prefix: :undefined)
80
+ def map_element(
81
+ element,
82
+ to: nil,
83
+ using: nil,
84
+ namespace: :undefined,
85
+ prefix: :undefined,
86
+ cdata: false,
87
+ render_nil: false
88
+ )
80
89
  Validator.validate_arguments(element, to, using)
81
90
  Validator.validate_namespace(element, namespace, prefix)
82
91
 
@@ -94,7 +103,9 @@ module Shale
94
103
  name: element,
95
104
  attribute: to,
96
105
  methods: using,
97
- namespace: Descriptor::XmlNamespace.new(nsp, pfx)
106
+ namespace: Descriptor::XmlNamespace.new(nsp, pfx),
107
+ cdata: cdata,
108
+ render_nil: render_nil
98
109
  )
99
110
  end
100
111
 
@@ -109,7 +120,14 @@ module Shale
109
120
  # @raise [IncorrectMappingArgumentsError] when arguments are incorrect
110
121
  #
111
122
  # @api private
112
- def map_attribute(attribute, to: nil, using: nil, namespace: nil, prefix: nil)
123
+ def map_attribute(
124
+ attribute,
125
+ to: nil,
126
+ using: nil,
127
+ namespace: nil,
128
+ prefix: nil,
129
+ render_nil: false
130
+ )
113
131
  Validator.validate_arguments(attribute, to, using)
114
132
  Validator.validate_namespace(attribute, namespace, prefix)
115
133
 
@@ -119,7 +137,9 @@ module Shale
119
137
  name: attribute,
120
138
  attribute: to,
121
139
  methods: using,
122
- namespace: Descriptor::XmlNamespace.new(namespace, prefix)
140
+ namespace: Descriptor::XmlNamespace.new(namespace, prefix),
141
+ cdata: false,
142
+ render_nil: render_nil
123
143
  )
124
144
  end
125
145
 
@@ -128,8 +148,17 @@ module Shale
128
148
  # @param [Symbol] to Object's attribute
129
149
  #
130
150
  # @api private
131
- def map_content(to:)
132
- @content = to
151
+ def map_content(to: nil, using: nil, cdata: false)
152
+ Validator.validate_arguments('content', to, using)
153
+
154
+ @content = Descriptor::Xml.new(
155
+ name: nil,
156
+ attribute: to,
157
+ methods: using,
158
+ namespace: nil,
159
+ cdata: cdata,
160
+ render_nil: false
161
+ )
133
162
  end
134
163
 
135
164
  # Set the name for root element
@@ -152,6 +181,22 @@ module Shale
152
181
  @default_namespace.prefix = prefix
153
182
  end
154
183
 
184
+ # Set the "finalized" instance variable to true
185
+ #
186
+ # @api private
187
+ def finalize!
188
+ @finalized = true
189
+ end
190
+
191
+ # Query the "finalized" instance variable
192
+ #
193
+ # @return [truem false]
194
+ #
195
+ # @api private
196
+ def finalized?
197
+ @finalized
198
+ end
199
+
155
200
  # @api private
156
201
  def initialize_dup(other)
157
202
  @elements = other.instance_variable_get('@elements').dup
@@ -159,6 +204,7 @@ module Shale
159
204
  @content = other.instance_variable_get('@content').dup
160
205
  @root = other.instance_variable_get('@root').dup
161
206
  @default_namespace = other.instance_variable_get('@default_namespace').dup
207
+ @finalized = false
162
208
 
163
209
  super
164
210
  end
@@ -124,9 +124,7 @@ module Shale
124
124
  # @api public
125
125
  def to_schema(klass, id: nil, title: nil, description: nil, pretty: false)
126
126
  schema = as_schema(klass, id: id, title: title, description: description)
127
- options = pretty ? :pretty : nil
128
-
129
- Shale.json_adapter.dump(schema, options)
127
+ Shale.json_adapter.dump(schema, pretty: pretty)
130
128
  end
131
129
 
132
130
  private
@@ -200,7 +200,7 @@ module Shale
200
200
  # @api public
201
201
  def as_models(schemas)
202
202
  unless Shale.xml_adapter
203
- raise AdapterError, ADAPTER_NOT_SET_MESSAGE
203
+ raise AdapterError, XML_ADAPTER_NOT_SET_MESSAGE
204
204
  end
205
205
 
206
206
  if Shale.xml_adapter.name == 'Shale::Adapter::Ox'
@@ -35,8 +35,8 @@ module Shale
35
35
  def as_xml(doc)
36
36
  import = doc.create_element('xs:import')
37
37
 
38
- doc.add_attribute(import, 'namespace', @namespace)
39
- doc.add_attribute(import, 'schemaLocation', @location)
38
+ doc.add_attribute(import, 'namespace', @namespace) if @namespace
39
+ doc.add_attribute(import, 'schemaLocation', @location) if @location
40
40
 
41
41
  import
42
42
  end
@@ -222,13 +222,11 @@ module Shale
222
222
  def to_schemas(klass, base_name = nil, pretty: false, declaration: false)
223
223
  schemas = as_schemas(klass, base_name)
224
224
 
225
- options = [
226
- pretty ? :pretty : nil,
227
- declaration ? :declaration : nil,
228
- ]
229
-
230
225
  schemas.to_h do |schema|
231
- [schema.name, Shale.xml_adapter.dump(schema.as_xml, *options)]
226
+ [
227
+ schema.name,
228
+ Shale.xml_adapter.dump(schema.as_xml, pretty: pretty, declaration: declaration),
229
+ ]
232
230
  end
233
231
  end
234
232