shale 0.1.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 +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
|