shale 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +3 -0
- data/LICENSE.txt +21 -0
- data/README.md +392 -0
- data/lib/shale/adapter/json.rb +34 -0
- data/lib/shale/adapter/nokogiri.rb +176 -0
- data/lib/shale/adapter/ox.rb +162 -0
- data/lib/shale/adapter/rexml.rb +163 -0
- data/lib/shale/attribute.rb +47 -0
- data/lib/shale/error.rb +33 -0
- data/lib/shale/mapper.rb +292 -0
- data/lib/shale/mapping/key_value.rb +40 -0
- data/lib/shale/mapping/xml.rb +87 -0
- data/lib/shale/type/base.rb +118 -0
- data/lib/shale/type/boolean.rb +33 -0
- data/lib/shale/type/complex.rb +427 -0
- data/lib/shale/type/date.rb +29 -0
- data/lib/shale/type/float.rb +29 -0
- data/lib/shale/type/integer.rb +21 -0
- data/lib/shale/type/string.rb +21 -0
- data/lib/shale/type/time.rb +29 -0
- data/lib/shale/utils.rb +25 -0
- data/lib/shale/version.rb +6 -0
- data/lib/shale.rb +127 -0
- data/shale.gemspec +26 -0
- metadata +72 -0
@@ -0,0 +1,162 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ox'
|
4
|
+
|
5
|
+
module Shale
|
6
|
+
module Adapter
|
7
|
+
# Ox adapter
|
8
|
+
#
|
9
|
+
# @api public
|
10
|
+
module Ox
|
11
|
+
# Parse XML into Ox document
|
12
|
+
#
|
13
|
+
# @param [String] xml XML document
|
14
|
+
#
|
15
|
+
# @return [::Ox::Document, ::Ox::Element]
|
16
|
+
#
|
17
|
+
# @api private
|
18
|
+
def self.load(xml)
|
19
|
+
Node.new(::Ox.parse(xml))
|
20
|
+
end
|
21
|
+
|
22
|
+
# Serialize Ox document into XML
|
23
|
+
#
|
24
|
+
# @param [::Ox::Document, ::Ox::Element] doc Ox document
|
25
|
+
#
|
26
|
+
# @return [String]
|
27
|
+
#
|
28
|
+
# @api private
|
29
|
+
def self.dump(doc)
|
30
|
+
::Ox.dump(doc)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Create Shale::Adapter::Ox::Document instance
|
34
|
+
#
|
35
|
+
# @api private
|
36
|
+
def self.create_document
|
37
|
+
Document.new
|
38
|
+
end
|
39
|
+
|
40
|
+
# Wrapper around Ox API
|
41
|
+
#
|
42
|
+
# @api private
|
43
|
+
class Document
|
44
|
+
# Return Ox document
|
45
|
+
#
|
46
|
+
# @return [::Ox::Document]
|
47
|
+
#
|
48
|
+
# @api private
|
49
|
+
attr_reader :doc
|
50
|
+
|
51
|
+
# Initialize object
|
52
|
+
#
|
53
|
+
# @api private
|
54
|
+
def initialize
|
55
|
+
@doc = ::Ox::Document.new
|
56
|
+
end
|
57
|
+
|
58
|
+
# Create Ox element
|
59
|
+
#
|
60
|
+
# @param [String] name Name of the XML element
|
61
|
+
#
|
62
|
+
# @return [::Ox::Element]
|
63
|
+
#
|
64
|
+
# @api private
|
65
|
+
def create_element(name)
|
66
|
+
::Ox::Element.new(name)
|
67
|
+
end
|
68
|
+
|
69
|
+
# Add attribute to Ox element
|
70
|
+
#
|
71
|
+
# @param [::Ox::Element] element Ox element
|
72
|
+
# @param [String] name Name of the XML attribute
|
73
|
+
# @param [String] value Value of the XML attribute
|
74
|
+
#
|
75
|
+
# @api private
|
76
|
+
def add_attribute(element, name, value)
|
77
|
+
element[name] = value
|
78
|
+
end
|
79
|
+
|
80
|
+
# Add child element to Ox element
|
81
|
+
#
|
82
|
+
# @param [::Ox::Element] element Ox parent element
|
83
|
+
# @param [::Ox::Element] child Ox child element
|
84
|
+
#
|
85
|
+
# @api private
|
86
|
+
def add_element(element, child)
|
87
|
+
element << child
|
88
|
+
end
|
89
|
+
|
90
|
+
# Add text node to Ox element
|
91
|
+
#
|
92
|
+
# @param [::Ox::Element] element Ox element
|
93
|
+
# @param [String] text Text to add
|
94
|
+
#
|
95
|
+
# @api private
|
96
|
+
def add_text(element, text)
|
97
|
+
element << text
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Wrapper around Ox::Element API
|
102
|
+
#
|
103
|
+
# @api private
|
104
|
+
class Node
|
105
|
+
# Initialize object with Ox element
|
106
|
+
#
|
107
|
+
# @param [::Ox::Element] node Ox element
|
108
|
+
#
|
109
|
+
# @api private
|
110
|
+
def initialize(node)
|
111
|
+
@node = node
|
112
|
+
end
|
113
|
+
|
114
|
+
# Return fully qualified name of the node in the format of
|
115
|
+
# namespace:name when the node is namespaced or just name when it's not
|
116
|
+
#
|
117
|
+
# @return [String]
|
118
|
+
#
|
119
|
+
# @example without namespace
|
120
|
+
# node.name # => Bar
|
121
|
+
#
|
122
|
+
# @example with namespace
|
123
|
+
# node.name # => foo:Bar
|
124
|
+
#
|
125
|
+
# @api private
|
126
|
+
def name
|
127
|
+
@node.name
|
128
|
+
end
|
129
|
+
|
130
|
+
# Return all attributes associated with the node
|
131
|
+
#
|
132
|
+
# @return [Hash]
|
133
|
+
#
|
134
|
+
# @api private
|
135
|
+
def attributes
|
136
|
+
@node.attributes
|
137
|
+
end
|
138
|
+
|
139
|
+
# Return node's element children
|
140
|
+
#
|
141
|
+
# @return [Array<Shale::Adapter::Ox::Node>]
|
142
|
+
#
|
143
|
+
# @api private
|
144
|
+
def children
|
145
|
+
@node
|
146
|
+
.nodes
|
147
|
+
.filter { |e| e.is_a?(::Ox::Element) }
|
148
|
+
.map { |e| self.class.new(e) }
|
149
|
+
end
|
150
|
+
|
151
|
+
# Return first text child of a node
|
152
|
+
#
|
153
|
+
# @return [String]
|
154
|
+
#
|
155
|
+
# @api private
|
156
|
+
def text
|
157
|
+
@node.text
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
@@ -0,0 +1,163 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rexml/document'
|
4
|
+
|
5
|
+
module Shale
|
6
|
+
module Adapter
|
7
|
+
# REXML adapter
|
8
|
+
#
|
9
|
+
# @api public
|
10
|
+
module REXML
|
11
|
+
# Parse XML into REXML document
|
12
|
+
#
|
13
|
+
# @param [String] xml XML document
|
14
|
+
#
|
15
|
+
# @return [::REXML::Document]
|
16
|
+
#
|
17
|
+
# @api private
|
18
|
+
def self.load(xml)
|
19
|
+
doc = ::REXML::Document.new(xml, ignore_whitespace_nodes: :all)
|
20
|
+
Node.new(doc.root)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Serialize REXML document into XML
|
24
|
+
#
|
25
|
+
# @param [::REXML::Document] doc REXML document
|
26
|
+
#
|
27
|
+
# @return [String]
|
28
|
+
#
|
29
|
+
# @api private
|
30
|
+
def self.dump(doc)
|
31
|
+
doc.to_s
|
32
|
+
end
|
33
|
+
|
34
|
+
# Create Shale::Adapter::REXML::Document instance
|
35
|
+
#
|
36
|
+
# @api private
|
37
|
+
def self.create_document
|
38
|
+
Document.new
|
39
|
+
end
|
40
|
+
|
41
|
+
# Wrapper around REXML API
|
42
|
+
#
|
43
|
+
# @api private
|
44
|
+
class Document
|
45
|
+
# Return REXML document
|
46
|
+
#
|
47
|
+
# @return [::REXML::Document]
|
48
|
+
#
|
49
|
+
# @api private
|
50
|
+
attr_reader :doc
|
51
|
+
|
52
|
+
# Initialize object
|
53
|
+
#
|
54
|
+
# @api private
|
55
|
+
def initialize
|
56
|
+
@doc = ::REXML::Document.new
|
57
|
+
end
|
58
|
+
|
59
|
+
# Create REXML element
|
60
|
+
#
|
61
|
+
# @param [String] name Name of the XML element
|
62
|
+
#
|
63
|
+
# @return [::REXML::Element]
|
64
|
+
#
|
65
|
+
# @api private
|
66
|
+
def create_element(name)
|
67
|
+
::REXML::Element.new(name)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Add attribute to REXML element
|
71
|
+
#
|
72
|
+
# @param [::REXML::Element] element REXML element
|
73
|
+
# @param [String] name Name of the XML attribute
|
74
|
+
# @param [String] value Value of the XML attribute
|
75
|
+
#
|
76
|
+
# @api private
|
77
|
+
def add_attribute(element, name, value)
|
78
|
+
element.add_attribute(name, value)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Add child element to REXML element
|
82
|
+
#
|
83
|
+
# @param [::REXML::Element] element REXML parent element
|
84
|
+
# @param [::REXML::Element] child REXML child element
|
85
|
+
#
|
86
|
+
# @api private
|
87
|
+
def add_element(element, child)
|
88
|
+
element.add_element(child)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Add text node to REXML element
|
92
|
+
#
|
93
|
+
# @param [::REXML::Element] element REXML element
|
94
|
+
# @param [String] text Text to add
|
95
|
+
#
|
96
|
+
# @api private
|
97
|
+
def add_text(element, text)
|
98
|
+
element.add_text(text)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Wrapper around REXML::Element API
|
103
|
+
#
|
104
|
+
# @api private
|
105
|
+
class Node
|
106
|
+
# Initialize object with REXML element
|
107
|
+
#
|
108
|
+
# @param [::REXML::Element] node REXML element
|
109
|
+
#
|
110
|
+
# @api private
|
111
|
+
def initialize(node)
|
112
|
+
@node = node
|
113
|
+
end
|
114
|
+
|
115
|
+
# Return fully qualified name of the node in the format of
|
116
|
+
# namespace:name when the node is namespaced or just name when it's not
|
117
|
+
#
|
118
|
+
# @return [String]
|
119
|
+
#
|
120
|
+
# @example without namespace
|
121
|
+
# node.name # => Bar
|
122
|
+
#
|
123
|
+
# @example with namespace
|
124
|
+
# node.name # => foo:Bar
|
125
|
+
#
|
126
|
+
# @api private
|
127
|
+
def name
|
128
|
+
@node.expanded_name
|
129
|
+
end
|
130
|
+
|
131
|
+
# Return all attributes associated with the node
|
132
|
+
#
|
133
|
+
# @return [Hash]
|
134
|
+
#
|
135
|
+
# @api private
|
136
|
+
def attributes
|
137
|
+
@node.attributes
|
138
|
+
end
|
139
|
+
|
140
|
+
# Return node's element children
|
141
|
+
#
|
142
|
+
# @return [Array<Shale::Adapter::REXML::Node>]
|
143
|
+
#
|
144
|
+
# @api private
|
145
|
+
def children
|
146
|
+
@node
|
147
|
+
.children
|
148
|
+
.filter { |e| e.node_type == :element }
|
149
|
+
.map { |e| self.class.new(e) }
|
150
|
+
end
|
151
|
+
|
152
|
+
# Return first text child of a node
|
153
|
+
#
|
154
|
+
# @return [String]
|
155
|
+
#
|
156
|
+
# @api private
|
157
|
+
def text
|
158
|
+
@node.text
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Shale
|
4
|
+
# Class representing object's attribute
|
5
|
+
#
|
6
|
+
# @api private
|
7
|
+
class Attribute
|
8
|
+
# Return name
|
9
|
+
#
|
10
|
+
# @api private
|
11
|
+
attr_reader :name
|
12
|
+
|
13
|
+
# Return type
|
14
|
+
#
|
15
|
+
# @api private
|
16
|
+
attr_reader :type
|
17
|
+
|
18
|
+
# Return default
|
19
|
+
#
|
20
|
+
# @api private
|
21
|
+
attr_reader :default
|
22
|
+
|
23
|
+
# Initialize Attribute object
|
24
|
+
#
|
25
|
+
# @param [Symbol] name Name of the attribute
|
26
|
+
# @param [Shale::Type::Base] type Type of the attribute
|
27
|
+
# @param [Boolean] collection Is this attribute a collection
|
28
|
+
# @param [Proc] default Default value
|
29
|
+
#
|
30
|
+
# @api private
|
31
|
+
def initialize(name, type, collection, default)
|
32
|
+
@name = name
|
33
|
+
@type = type
|
34
|
+
@collection = collection
|
35
|
+
@default = collection ? -> { [] } : default
|
36
|
+
end
|
37
|
+
|
38
|
+
# Return wheter attribute is collection or not
|
39
|
+
#
|
40
|
+
# @return [Boolean]
|
41
|
+
#
|
42
|
+
# @api private
|
43
|
+
def collection?
|
44
|
+
@collection == true
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/shale/error.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Shale
|
4
|
+
# Error for assigning value to not existing attribute
|
5
|
+
#
|
6
|
+
# @api private
|
7
|
+
class UnknownAttributeError < NoMethodError
|
8
|
+
# Initialize error object
|
9
|
+
#
|
10
|
+
# @param [String] record
|
11
|
+
# @param [String] attribute
|
12
|
+
#
|
13
|
+
# @api private
|
14
|
+
def initialize(record, attribute)
|
15
|
+
super("unknown attribute '#{attribute}' for #{record}.")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Error for trying to assign not callable object as an attribute's default
|
20
|
+
#
|
21
|
+
# @api private
|
22
|
+
class DefaultNotCallableError < StandardError
|
23
|
+
# Initialize error object
|
24
|
+
#
|
25
|
+
# @param [String] record
|
26
|
+
# @param [String] attribute
|
27
|
+
#
|
28
|
+
# @api private
|
29
|
+
def initialize(record, attribute)
|
30
|
+
super("'#{attribute}' default is not callable for #{record}.")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/shale/mapper.rb
ADDED
@@ -0,0 +1,292 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'attribute'
|
4
|
+
require_relative 'error'
|
5
|
+
require_relative 'utils'
|
6
|
+
require_relative 'mapping/key_value'
|
7
|
+
require_relative 'mapping/xml'
|
8
|
+
require_relative 'type/complex'
|
9
|
+
|
10
|
+
module Shale
|
11
|
+
# Base class used for mapping
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
# class Address < Shale::Mapper
|
15
|
+
# attribute :city, Shale::Type::String
|
16
|
+
# attribute :street, Shale::Type::String
|
17
|
+
# attribute :state, Shale::Type::Integer
|
18
|
+
# attribute :zip, Shale::Type::String
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# class Person < Shale::Mapper
|
22
|
+
# attribute :first_name, Shale::Type::String
|
23
|
+
# attribute :last_name, Shale::Type::String
|
24
|
+
# attribute :age, Shale::Type::Integer
|
25
|
+
# attribute :address, Address
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# person = Person.from_json(%{
|
29
|
+
# {
|
30
|
+
# "first_name": "John",
|
31
|
+
# "last_name": "Doe",
|
32
|
+
# "age": 55,
|
33
|
+
# "address": {
|
34
|
+
# "city": "London",
|
35
|
+
# "street": "Oxford Street",
|
36
|
+
# "state": "London",
|
37
|
+
# "zip": "E1 6AN"
|
38
|
+
# }
|
39
|
+
# }
|
40
|
+
# })
|
41
|
+
#
|
42
|
+
# person.to_json
|
43
|
+
#
|
44
|
+
# @api public
|
45
|
+
class Mapper < Type::Complex
|
46
|
+
@attributes = {}
|
47
|
+
@hash_mapping = Mapping::KeyValue.new
|
48
|
+
@json_mapping = Mapping::KeyValue.new
|
49
|
+
@yaml_mapping = Mapping::KeyValue.new
|
50
|
+
@xml_mapping = Mapping::Xml.new
|
51
|
+
|
52
|
+
class << self
|
53
|
+
# Return attributes Hash
|
54
|
+
#
|
55
|
+
# @return [Hash<Symbol, Shale::Attribute>]
|
56
|
+
#
|
57
|
+
# @api public
|
58
|
+
attr_reader :attributes
|
59
|
+
|
60
|
+
# Return Hash mapping object
|
61
|
+
#
|
62
|
+
# @return [Shale::Mapping::KeyValue]
|
63
|
+
#
|
64
|
+
# @api public
|
65
|
+
attr_reader :hash_mapping
|
66
|
+
|
67
|
+
# Return JSON mapping object
|
68
|
+
#
|
69
|
+
# @return [Shale::Mapping::KeyValue]
|
70
|
+
#
|
71
|
+
# @api public
|
72
|
+
attr_reader :json_mapping
|
73
|
+
|
74
|
+
# Return YAML mapping object
|
75
|
+
#
|
76
|
+
# @return [Shale::Mapping::KeyValue]
|
77
|
+
#
|
78
|
+
# @api public
|
79
|
+
attr_reader :yaml_mapping
|
80
|
+
|
81
|
+
# Return XML mapping object
|
82
|
+
#
|
83
|
+
# @return [Shale::Mapping::XML]
|
84
|
+
#
|
85
|
+
# @api public
|
86
|
+
attr_reader :xml_mapping
|
87
|
+
|
88
|
+
# @api private
|
89
|
+
def inherited(subclass)
|
90
|
+
super
|
91
|
+
subclass.instance_variable_set('@attributes', @attributes.dup)
|
92
|
+
|
93
|
+
subclass.instance_variable_set('@__hash_mapping_init', @hash_mapping.dup)
|
94
|
+
subclass.instance_variable_set('@__json_mapping_init', @json_mapping.dup)
|
95
|
+
subclass.instance_variable_set('@__yaml_mapping_init', @yaml_mapping.dup)
|
96
|
+
subclass.instance_variable_set('@__xml_mapping_init', @xml_mapping.dup)
|
97
|
+
|
98
|
+
subclass.instance_variable_set('@hash_mapping', @hash_mapping.dup)
|
99
|
+
subclass.instance_variable_set('@json_mapping', @json_mapping.dup)
|
100
|
+
subclass.instance_variable_set('@yaml_mapping', @yaml_mapping.dup)
|
101
|
+
|
102
|
+
xml_mapping = @xml_mapping.dup
|
103
|
+
xml_mapping.root(Utils.underscore(subclass.name || ''))
|
104
|
+
|
105
|
+
subclass.instance_variable_set('@xml_mapping', xml_mapping.dup)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Define attribute on class
|
109
|
+
#
|
110
|
+
# @param [Symbol] name Name of the attribute
|
111
|
+
# @param [Shale::Type::Base] type Type of the attribute
|
112
|
+
# @param [Boolean] collection Is the attribute a collection
|
113
|
+
# @param [Proc] default Default value for the attribute
|
114
|
+
#
|
115
|
+
# @raise [DefaultNotCallableError] when attribute's default is not callable
|
116
|
+
#
|
117
|
+
# @example
|
118
|
+
# calss Person < Shale::Mapper
|
119
|
+
# attribute :first_name, Shale::Type::String
|
120
|
+
# attribute :last_name, Shale::Type::String
|
121
|
+
# attribute :age, Shale::Type::Integer, default: -> { 1 }
|
122
|
+
# attribute :hobbies, Shale::Type::String, collection: true
|
123
|
+
# end
|
124
|
+
#
|
125
|
+
# person = Person.new
|
126
|
+
#
|
127
|
+
# person.first_name # => nil
|
128
|
+
# person.first_name = 'John'
|
129
|
+
# person.first_name # => 'John'
|
130
|
+
#
|
131
|
+
# person.age # => 1
|
132
|
+
#
|
133
|
+
# person.hobbies << 'Dancing'
|
134
|
+
# person.hobbies # => ['Dancing']
|
135
|
+
#
|
136
|
+
# @api public
|
137
|
+
def attribute(name, type, collection: false, default: nil)
|
138
|
+
name = name.to_sym
|
139
|
+
|
140
|
+
unless default.nil? || default.respond_to?(:call)
|
141
|
+
raise DefaultNotCallableError.new(to_s, name)
|
142
|
+
end
|
143
|
+
|
144
|
+
@attributes[name] = Attribute.new(name, type, collection, default)
|
145
|
+
|
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)
|
150
|
+
|
151
|
+
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
152
|
+
attr_reader name
|
153
|
+
|
154
|
+
def #{name}=(val)
|
155
|
+
@#{name} = #{collection} ? val : #{type}.cast(val)
|
156
|
+
end
|
157
|
+
RUBY
|
158
|
+
end
|
159
|
+
|
160
|
+
# Define Hash mapping
|
161
|
+
#
|
162
|
+
# @param [Proc] block
|
163
|
+
#
|
164
|
+
# @example
|
165
|
+
# calss Person < Shale::Mapper
|
166
|
+
# attribute :first_name, Shale::Type::String
|
167
|
+
# attribute :last_name, Shale::Type::String
|
168
|
+
# attribute :age, Shale::Type::Integer
|
169
|
+
#
|
170
|
+
# yaml do
|
171
|
+
# map 'firatName', to: :first_name
|
172
|
+
# map 'lastName', to: :last_name
|
173
|
+
# map 'age', to: :age
|
174
|
+
# end
|
175
|
+
# end
|
176
|
+
#
|
177
|
+
# @api public
|
178
|
+
def hash(&block)
|
179
|
+
@hash_mapping = @__hash_mapping_init.dup
|
180
|
+
@hash_mapping.instance_eval(&block)
|
181
|
+
end
|
182
|
+
|
183
|
+
# Define JSON mapping
|
184
|
+
#
|
185
|
+
# @param [Proc] block
|
186
|
+
#
|
187
|
+
# @example
|
188
|
+
# calss Person < Shale::Mapper
|
189
|
+
# attribute :first_name, Shale::Type::String
|
190
|
+
# attribute :last_name, Shale::Type::String
|
191
|
+
# attribute :age, Shale::Type::Integer
|
192
|
+
#
|
193
|
+
# yaml do
|
194
|
+
# map 'firatName', to: :first_name
|
195
|
+
# map 'lastName', to: :last_name
|
196
|
+
# map 'age', to: :age
|
197
|
+
# end
|
198
|
+
# end
|
199
|
+
#
|
200
|
+
# @api public
|
201
|
+
def json(&block)
|
202
|
+
@json_mapping = @__json_mapping_init.dup
|
203
|
+
@json_mapping.instance_eval(&block)
|
204
|
+
end
|
205
|
+
|
206
|
+
# Define YAML mapping
|
207
|
+
#
|
208
|
+
# @param [Proc] block
|
209
|
+
#
|
210
|
+
# @example
|
211
|
+
# calss Person < Shale::Mapper
|
212
|
+
# attribute :first_name, Shale::Type::String
|
213
|
+
# attribute :last_name, Shale::Type::String
|
214
|
+
# attribute :age, Shale::Type::Integer
|
215
|
+
#
|
216
|
+
# yaml do
|
217
|
+
# map 'firat_name', to: :first_name
|
218
|
+
# map 'last_name', to: :last_name
|
219
|
+
# map 'age', to: :age
|
220
|
+
# end
|
221
|
+
# end
|
222
|
+
#
|
223
|
+
# @api public
|
224
|
+
def yaml(&block)
|
225
|
+
@yaml_mapping = @__yaml_mapping_init.dup
|
226
|
+
@yaml_mapping.instance_eval(&block)
|
227
|
+
end
|
228
|
+
|
229
|
+
# Define XML mapping
|
230
|
+
#
|
231
|
+
# @param [Proc] block
|
232
|
+
#
|
233
|
+
# @example
|
234
|
+
# calss Person < Shale::Mapper
|
235
|
+
# attribute :first_name, Shale::Type::String
|
236
|
+
# attribute :last_name, Shale::Type::String
|
237
|
+
# attribute :age, Shale::Type::Integer
|
238
|
+
#
|
239
|
+
# xml do
|
240
|
+
# root 'Person'
|
241
|
+
# map_content to: :first_name
|
242
|
+
# map_element 'LastName', to: :last_name
|
243
|
+
# map_attribute 'age', to: :age
|
244
|
+
# end
|
245
|
+
# end
|
246
|
+
#
|
247
|
+
# @api public
|
248
|
+
def xml(&block)
|
249
|
+
@xml_mapping = @__xml_mapping_init.dup
|
250
|
+
@xml_mapping.instance_eval(&block)
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
# Initialize instance with properties
|
255
|
+
#
|
256
|
+
# @param [Hash] props Properties
|
257
|
+
#
|
258
|
+
# @raise [UnknownAttributeError] when attribute is not defined on the class
|
259
|
+
#
|
260
|
+
# @example
|
261
|
+
# Person.new(
|
262
|
+
# first_name: 'John',
|
263
|
+
# last_name: 'Doe',
|
264
|
+
# address: Address.new(city: 'London')
|
265
|
+
# )
|
266
|
+
# # => #<Person:0x00007f82768a2370
|
267
|
+
# @first_name="John",
|
268
|
+
# @last_name="Doe"
|
269
|
+
# @address=#<Address:0x00007fe9cf0f57d8 @city="London">>
|
270
|
+
#
|
271
|
+
# @api public
|
272
|
+
def initialize(**props)
|
273
|
+
super()
|
274
|
+
|
275
|
+
props.each_key do |name|
|
276
|
+
unless self.class.attributes.keys.include?(name)
|
277
|
+
raise UnknownAttributeError.new(self.class.to_s, name.to_s)
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
self.class.attributes.each do |name, attribute|
|
282
|
+
if props.key?(name)
|
283
|
+
value = props[name]
|
284
|
+
elsif attribute.default
|
285
|
+
value = attribute.default.call
|
286
|
+
end
|
287
|
+
|
288
|
+
public_send("#{name}=", value)
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|