shale 0.9.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/shale/mapper.rb CHANGED
@@ -151,7 +151,7 @@ module Shale
151
151
  # @raise [DefaultNotCallableError] when attribute's default is not callable
152
152
  #
153
153
  # @example
154
- # calss Person < Shale::Mapper
154
+ # class Person < Shale::Mapper
155
155
  # attribute :first_name, Shale::Type::String
156
156
  # attribute :last_name, Shale::Type::String
157
157
  # attribute :age, Shale::Type::Integer, default: -> { 1 }
@@ -200,7 +200,7 @@ module Shale
200
200
  # @param [Proc] block
201
201
  #
202
202
  # @example
203
- # calss Person < Shale::Mapper
203
+ # class Person < Shale::Mapper
204
204
  # attribute :first_name, Shale::Type::String
205
205
  # attribute :last_name, Shale::Type::String
206
206
  # attribute :age, Shale::Type::Integer
@@ -224,7 +224,7 @@ module Shale
224
224
  # @param [Proc] block
225
225
  #
226
226
  # @example
227
- # calss Person < Shale::Mapper
227
+ # class Person < Shale::Mapper
228
228
  # attribute :first_name, Shale::Type::String
229
229
  # attribute :last_name, Shale::Type::String
230
230
  # attribute :age, Shale::Type::Integer
@@ -248,7 +248,7 @@ module Shale
248
248
  # @param [Proc] block
249
249
  #
250
250
  # @example
251
- # calss Person < Shale::Mapper
251
+ # class Person < Shale::Mapper
252
252
  # attribute :first_name, Shale::Type::String
253
253
  # attribute :last_name, Shale::Type::String
254
254
  # attribute :age, Shale::Type::Integer
@@ -272,7 +272,7 @@ module Shale
272
272
  # @param [Proc] block
273
273
  #
274
274
  # @example
275
- # calss Person < Shale::Mapper
275
+ # class Person < Shale::Mapper
276
276
  # attribute :first_name, Shale::Type::String
277
277
  # attribute :last_name, Shale::Type::String
278
278
  # attribute :age, Shale::Type::Integer
@@ -296,7 +296,7 @@ module Shale
296
296
  # @param [Proc] block
297
297
  #
298
298
  # @example
299
- # calss Person < Shale::Mapper
299
+ # class Person < Shale::Mapper
300
300
  # attribute :first_name, Shale::Type::String
301
301
  # attribute :last_name, Shale::Type::String
302
302
  # attribute :age, Shale::Type::Integer
@@ -320,7 +320,7 @@ module Shale
320
320
  # @param [Proc] block
321
321
  #
322
322
  # @example
323
- # calss Person < Shale::Mapper
323
+ # class Person < Shale::Mapper
324
324
  # attribute :first_name, Shale::Type::String
325
325
  # attribute :last_name, Shale::Type::String
326
326
  # attribute :age, Shale::Type::Integer
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Shale
4
+ module Mapping
5
+ # Class for handling attribute delegation
6
+ #
7
+ # @api private
8
+ class Delegates
9
+ # Class representing individual delegation
10
+ #
11
+ # @api private
12
+ class Delegate
13
+ # Return receiver_attribute
14
+ #
15
+ # @return [Shale::Attribute]
16
+ #
17
+ # @api private
18
+ attr_reader :receiver_attribute
19
+
20
+ # Return attribute setter on a delegate
21
+ #
22
+ # @return [String]
23
+ #
24
+ # @api private
25
+ attr_reader :setter
26
+
27
+ # Return value to set on a delegate
28
+ #
29
+ # @return [any]
30
+ #
31
+ # @api private
32
+ attr_reader :value
33
+
34
+ # Initialize instance
35
+ #
36
+ # @param [Shale::Attribute] receiver_attribute
37
+ # @param [String] setter
38
+ # @param [any] value
39
+ #
40
+ # @api private
41
+ def initialize(receiver_attribute, setter, value)
42
+ @receiver_attribute = receiver_attribute
43
+ @setter = setter
44
+ @value = value
45
+ end
46
+ end
47
+
48
+ # Initialize instance
49
+ #
50
+ # @api private
51
+ def initialize
52
+ @delegates = []
53
+ end
54
+
55
+ # Add single value to delegate
56
+ #
57
+ # @param [Shale::Attribute] receiver_attribute
58
+ # @param [String] setter
59
+ # @param [any] value
60
+ #
61
+ # @api private
62
+ def add(receiver_attribute, setter, value)
63
+ @delegates << Delegate.new(receiver_attribute, setter, value)
64
+ end
65
+
66
+ # Add collection to delegate
67
+ #
68
+ # @param [Shale::Attribute] receiver_attribute
69
+ # @param [String] setter
70
+ # @param [any] value
71
+ #
72
+ # @api private
73
+ def add_collection(receiver_attribute, setter, value)
74
+ delegate = @delegates.find do |e|
75
+ e.receiver_attribute == receiver_attribute && e.setter == setter
76
+ end
77
+
78
+ if delegate
79
+ delegate.value << value
80
+ else
81
+ @delegates << Delegate.new(receiver_attribute, setter, [value])
82
+ end
83
+ end
84
+
85
+ # Iterate over delegates and yield a block
86
+ #
87
+ # @param [Proc] block
88
+ #
89
+ # @api private
90
+ def each(&block)
91
+ @delegates.each(&block)
92
+ end
93
+ end
94
+ end
95
+ end
@@ -21,6 +21,13 @@ module Shale
21
21
  # @api private
22
22
  attr_reader :attribute
23
23
 
24
+ # Return receiver name
25
+ #
26
+ # @return [Symbol]
27
+ #
28
+ # @api private
29
+ attr_reader :receiver
30
+
24
31
  # Return method symbol
25
32
  #
26
33
  # @return [Symbol]
@@ -42,20 +49,31 @@ module Shale
42
49
  # @api private
43
50
  attr_reader :group
44
51
 
52
+ # Return schema hash
53
+ #
54
+ # @return [Hash]
55
+ #
56
+ # @api private
57
+ attr_reader :schema
58
+
45
59
  # Initialize instance
46
60
  #
47
61
  # @param [String] name
48
62
  # @param [Symbol, nil] attribute
63
+ # @param [Symbol, nil] receiver
49
64
  # @param [Hash, nil] methods
50
65
  # @param [String, nil] group
51
66
  # @param [true, false] render_nil
67
+ # @param [Hash, nil] schema
52
68
  #
53
69
  # @api private
54
- def initialize(name:, attribute:, methods:, group:, render_nil:)
70
+ def initialize(name:, attribute:, receiver:, methods:, group:, render_nil:, schema: nil)
55
71
  @name = name
56
72
  @attribute = attribute
73
+ @receiver = receiver
57
74
  @group = group
58
75
  @render_nil = render_nil
76
+ @schema = schema
59
77
 
60
78
  if methods
61
79
  @method_from = methods[:from]
@@ -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,33 @@ 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
19
+ # @param [Hash, nil] schema
18
20
  #
19
21
  # @raise [IncorrectMappingArgumentsError] when arguments are incorrect
20
22
  #
23
+ # @api public
24
+ def map(key, to: nil, receiver: nil, using: nil, render_nil: nil, schema: nil)
25
+ super(key, to: to, receiver: receiver, using: using, render_nil: render_nil, schema: schema)
26
+ end
27
+
28
+ # Set render_nil default
29
+ #
30
+ # @param [true, false] val
31
+ #
21
32
  # @api private
22
- def map(key, to: nil, using: nil, render_nil: nil)
23
- super(key, to: to, using: using, render_nil: render_nil)
33
+ def render_nil(val)
34
+ @render_nil_default = val
24
35
  end
25
36
 
26
37
  # 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
@@ -16,6 +16,13 @@ module Shale
16
16
  # @api private
17
17
  attr_reader :keys
18
18
 
19
+ # Return hash for hash with properties for root Object
20
+ #
21
+ # @return [Hash]
22
+ #
23
+ # @api private
24
+ attr_reader :root
25
+
19
26
  # Initialize instance
20
27
  #
21
28
  # @param [true, false] render_nil_default
@@ -23,6 +30,7 @@ module Shale
23
30
  # @api private
24
31
  def initialize(render_nil_default: false)
25
32
  @keys = {}
33
+ @root = {}
26
34
  @finalized = false
27
35
  @render_nil_default = render_nil_default
28
36
  end
@@ -31,24 +39,44 @@ module Shale
31
39
  #
32
40
  # @param [String] key
33
41
  # @param [Symbol, nil] to
42
+ # @param [Symbol, nil] receiver
34
43
  # @param [Hash, nil] using
35
44
  # @param [String, nil] group
36
45
  # @param [true, false, nil] render_nil
46
+ # @param [Hash, nil] schema
37
47
  #
38
48
  # @raise [IncorrectMappingArgumentsError] when arguments are incorrect
39
49
  #
40
50
  # @api private
41
- def map(key, to: nil, using: nil, group: nil, render_nil: nil)
42
- Validator.validate_arguments(key, to, using)
51
+ def map(key, to: nil, receiver: nil, using: nil, group: nil, render_nil: nil, schema: nil)
52
+ Validator.validate_arguments(key, to, receiver, using)
53
+
43
54
  @keys[key] = Descriptor::Dict.new(
44
55
  name: key,
45
56
  attribute: to,
57
+ receiver: receiver,
46
58
  methods: using,
47
59
  group: group,
48
- render_nil: render_nil.nil? ? @render_nil_default : render_nil
60
+ render_nil: render_nil.nil? ? @render_nil_default : render_nil,
61
+ schema: schema
49
62
  )
50
63
  end
51
64
 
65
+ # Allow schema properties to be set on the object
66
+ #
67
+ # @param [Integer] min_properties
68
+ # @param [Integer] max_properties
69
+ # @param [Hash] dependent_required
70
+ #
71
+ # @api public
72
+ def properties(min_properties: nil, max_properties: nil, dependent_required: nil)
73
+ @root = {
74
+ min_properties: min_properties,
75
+ max_properties: max_properties,
76
+ dependent_required: dependent_required,
77
+ }
78
+ end
79
+
52
80
  # Set the "finalized" instance variable to true
53
81
  #
54
82
  # @api private
@@ -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