occi-core 4.1.3 → 4.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/Gemfile +8 -0
- data/README.md +49 -17
- data/lib/occi/collection.rb +37 -21
- data/lib/occi/core/action.rb +5 -5
- data/lib/occi/core/action_instance.rb +45 -3
- data/lib/occi/core/actions.rb +2 -1
- data/lib/occi/core/attributes.rb +253 -73
- data/lib/occi/core/categories.rb +1 -0
- data/lib/occi/core/category.rb +25 -8
- data/lib/occi/core/entities.rb +1 -0
- data/lib/occi/core/entity.rb +51 -74
- data/lib/occi/core/kind.rb +15 -11
- data/lib/occi/core/kinds.rb +1 -1
- data/lib/occi/core/link.rb +14 -15
- data/lib/occi/core/links.rb +1 -1
- data/lib/occi/core/mixin.rb +5 -5
- data/lib/occi/core/mixins.rb +2 -2
- data/lib/occi/core/properties.rb +90 -12
- data/lib/occi/core/resource.rb +7 -3
- data/lib/occi/core/resources.rb +2 -2
- data/lib/occi/errors/attribute_definitions_converted_error.rb +5 -0
- data/lib/occi/errors/attribute_missing_error.rb +5 -0
- data/lib/occi/errors/attribute_name_invalid_error.rb +5 -0
- data/lib/occi/errors/attribute_not_defined_error.rb +5 -0
- data/lib/occi/errors/attribute_property_type_error.rb +5 -0
- data/lib/occi/errors/attribute_type_error.rb +5 -0
- data/lib/occi/errors/kind_not_defined_error.rb +5 -0
- data/lib/occi/errors/parser_input_error.rb +5 -0
- data/lib/occi/errors/parser_type_error.rb +5 -0
- data/lib/occi/errors.rb +1 -0
- data/lib/occi/extensions/hashie.rb +25 -0
- data/lib/occi/helpers/comparators/action_instance.rb +22 -0
- data/lib/occi/helpers/comparators/attributes.rb +22 -0
- data/lib/occi/helpers/comparators/categories.rb +22 -0
- data/lib/occi/helpers/comparators/category.rb +22 -0
- data/lib/occi/helpers/comparators/collection.rb +40 -0
- data/lib/occi/helpers/comparators/entities.rb +22 -0
- data/lib/occi/helpers/comparators/entity.rb +22 -0
- data/lib/occi/helpers/comparators/properties.rb +26 -0
- data/lib/occi/helpers/comparators.rb +1 -0
- data/lib/occi/infrastructure/compute.rb +11 -9
- data/lib/occi/infrastructure/network.rb +27 -27
- data/lib/occi/infrastructure/networkinterface.rb +22 -23
- data/lib/occi/infrastructure/os_tpl.rb +1 -1
- data/lib/occi/infrastructure/resource_tpl.rb +1 -1
- data/lib/occi/infrastructure/storage.rb +7 -6
- data/lib/occi/infrastructure/storagelink.rb +4 -4
- data/lib/occi/log.rb +13 -10
- data/lib/occi/model.rb +9 -8
- data/lib/occi/parser/json.rb +11 -9
- data/lib/occi/parser/ova.rb +12 -6
- data/lib/occi/parser/ovf.rb +173 -116
- data/lib/occi/parser/text/constants.rb +87 -0
- data/lib/occi/parser/text.rb +161 -200
- data/lib/occi/parser/xml.rb +10 -8
- data/lib/occi/parser.rb +100 -50
- data/lib/occi/settings.rb +2 -1
- data/lib/occi/version.rb +1 -1
- data/lib/occi-core.rb +6 -4
- data/occi-core.gemspec +0 -7
- data/spec/occi/collection_samples/collection1.json +1 -0
- data/spec/occi/collection_samples/directory2/collection2.json +1 -0
- data/spec/occi/collection_spec.rb +961 -31
- data/spec/occi/core/action_instance_spec.rb +317 -0
- data/spec/occi/core/action_spec.rb +71 -0
- data/spec/occi/core/attributes_spec.rb +582 -27
- data/spec/occi/core/category_spec.rb +194 -18
- data/spec/occi/core/entities_spec.rb +96 -0
- data/spec/occi/core/entity_spec.rb +317 -28
- data/spec/occi/core/kind_spec.rb +127 -16
- data/spec/occi/core/link_spec.rb +35 -0
- data/spec/occi/core/links_spec.rb +130 -0
- data/spec/occi/core/mixins_spec.rb +107 -0
- data/spec/occi/core/properties_spec.rb +167 -0
- data/spec/occi/core/resource_spec.rb +23 -9
- data/spec/occi/core_spec.rb +12 -0
- data/spec/occi/infrastructure/compute_spec.rb +218 -18
- data/spec/occi/infrastructure/network_spec.rb +96 -0
- data/spec/occi/infrastructure/networkinterface_spec.rb +96 -0
- data/spec/occi/infrastructure/storage_spec.rb +33 -0
- data/spec/occi/infrastructure/storagelink_spec.rb +45 -0
- data/spec/occi/log_spec.rb +104 -1
- data/spec/occi/model_spec.rb +251 -39
- data/spec/occi/{test.json → parser/json_samples/test.json} +0 -0
- data/spec/occi/parser/ova_samples/test.dump +0 -0
- data/spec/occi/{test.ova → parser/ova_samples/test.ova} +0 -0
- data/spec/occi/parser/ovf_samples/test.dump +0 -0
- data/spec/occi/{test.ovf → parser/ovf_samples/test.ovf} +0 -0
- data/spec/occi/parser/text_samples/occi_categories.dump +0 -0
- data/spec/occi/parser/text_samples/occi_categories.text +2 -0
- data/spec/occi/parser/text_samples/occi_compute_rocci_server.dump +0 -0
- data/spec/occi/parser/text_samples/occi_compute_rocci_server.resource.dump +0 -0
- data/spec/occi/parser/text_samples/occi_compute_rocci_server.text +10 -0
- data/spec/occi/parser/text_samples/occi_link_resource_instance.dump +0 -0
- data/spec/occi/parser/text_samples/occi_link_resource_instance.text +7 -0
- data/spec/occi/parser/text_samples/occi_link_simple.dump +0 -0
- data/spec/occi/parser/text_samples/occi_link_simple.link_string.dump +0 -0
- data/spec/occi/parser/text_samples/occi_link_simple.text +1 -0
- data/spec/occi/parser/text_samples/occi_link_w_attributes.dump +0 -0
- data/spec/occi/parser/text_samples/occi_link_w_attributes.text +7 -0
- data/spec/occi/parser/text_samples/occi_link_w_category.dump +0 -0
- data/spec/occi/parser/text_samples/occi_link_w_category.text +3 -0
- data/spec/occi/parser/text_samples/occi_model_rocci_server.dump +0 -0
- data/spec/occi/parser/text_samples/occi_model_rocci_server.text +51 -0
- data/spec/occi/parser/text_samples/occi_network_rocci_server.dump +0 -0
- data/spec/occi/parser/text_samples/occi_network_rocci_server.resource.dump +0 -0
- data/spec/occi/parser/text_samples/occi_network_rocci_server.text +11 -0
- data/spec/occi/parser/text_samples/occi_resource_w_attributes.dump +0 -0
- data/spec/occi/parser/text_samples/occi_resource_w_attributes.text +11 -0
- data/spec/occi/parser/text_samples/occi_resource_w_inline_links.dump +0 -0
- data/spec/occi/parser/text_samples/occi_resource_w_inline_links.text +16 -0
- data/spec/occi/parser/text_samples/occi_resource_w_inline_links_only.dump +0 -0
- data/spec/occi/parser/text_samples/occi_resource_w_inline_links_only.text +13 -0
- data/spec/occi/parser/text_samples/occi_storage_rocci_server.dump +0 -0
- data/spec/occi/parser/text_samples/occi_storage_rocci_server.resource.dump +0 -0
- data/spec/occi/parser/text_samples/occi_storage_rocci_server.text +9 -0
- data/spec/occi/parser/text_spec.rb +274 -78
- data/spec/occi/parser/xml_samples/test.xml +352 -0
- data/spec/occi/parser_spec.rb +255 -104
- data/spec/occi-core_spec.rb +31 -0
- data/spec/spec_helper.rb +6 -2
- metadata +110 -111
- checksums.yaml +0 -7
- data/spec/occi/core/attribute_spec.rb +0 -0
data/lib/occi/core/attributes.rb
CHANGED
@@ -5,6 +5,18 @@ module Occi
|
|
5
5
|
attr_accessor :converted
|
6
6
|
|
7
7
|
include Occi::Helpers::Inspect
|
8
|
+
include Occi::Helpers::Comparators::Attributes
|
9
|
+
|
10
|
+
def initialize(source_hash = {}, default = nil, &blk)
|
11
|
+
raise ArgumentError, 'Source_hash is a mandatory argument!' unless source_hash
|
12
|
+
raise ArgumentError, 'Source_hash must be a hash-like structure!' unless source_hash.kind_of?(Hash)
|
13
|
+
|
14
|
+
# All internal Hashie::Mash elements in source_hash have to be re-typed
|
15
|
+
# to Occi::Core::Attributes, so we have to rebuild the object from scratch
|
16
|
+
source_hash = source_hash.to_hash unless source_hash.kind_of?(Occi::Core::Attributes)
|
17
|
+
|
18
|
+
super(source_hash, default, &blk)
|
19
|
+
end
|
8
20
|
|
9
21
|
def converted?
|
10
22
|
@converted||=false
|
@@ -14,7 +26,8 @@ module Occi
|
|
14
26
|
if key.to_s.include? '.'
|
15
27
|
key, string = key.to_s.split('.', 2)
|
16
28
|
attributes = super(key)
|
17
|
-
raise
|
29
|
+
raise Occi::Errors::AttributeMissingError,
|
30
|
+
"Attribute with key #{key} not found" unless attributes
|
18
31
|
attributes[string]
|
19
32
|
else
|
20
33
|
super(key)
|
@@ -27,34 +40,8 @@ module Occi
|
|
27
40
|
super(key, Occi::Core::Attributes.new) unless self[key].kind_of? Occi::Core::Attributes
|
28
41
|
self[key][string] = value
|
29
42
|
else
|
30
|
-
property_key =
|
31
|
-
|
32
|
-
when Occi::Core::Attributes
|
33
|
-
super(key, value)
|
34
|
-
when Occi::Core::Properties
|
35
|
-
super(key, value.clone)
|
36
|
-
super(property_key, value.clone)
|
37
|
-
when Hash
|
38
|
-
properties = Occi::Core::Properties.new(value)
|
39
|
-
super(key, properties.clone)
|
40
|
-
super(property_key, properties.clone)
|
41
|
-
when Occi::Core::Entity
|
42
|
-
raise "value #{value} derived from Occi::Core::Entity assigned but attribute of type #{self[property_key].type} required" unless self[property_key].type == 'string' if self[property_key]
|
43
|
-
raise "value #{value} does not match pattern" unless value.to_s.match '^' + self[property_key].pattern + '$' if Occi::Settings.verify_attribute_pattern if self[property_key]
|
44
|
-
super(key, value)
|
45
|
-
when String
|
46
|
-
raise "value #{value} of type String assigned but attribute of type #{self[property_key].type} required" unless self[property_key].type == 'string' if self[property_key]
|
47
|
-
raise "value #{value} does not match pattern" unless value.match '^' + self[property_key].pattern + '$' if Occi::Settings.verify_attribute_pattern if self[property_key]
|
48
|
-
super(key, value)
|
49
|
-
when Numeric
|
50
|
-
raise "value #{value} of type String assigned but attribute of type #{self[property_key].type} required" unless self[property_key].type == 'number' if self[property_key]
|
51
|
-
raise "value #{value} does not match pattern" unless value.to_s.match '^' + self[property_key].pattern + '$' if Occi::Settings.verify_attribute_pattern if self[property_key]
|
52
|
-
super(key, value)
|
53
|
-
when NilClass
|
54
|
-
super(key, value)
|
55
|
-
else
|
56
|
-
raise "value #{value} of type #{value.class} not supported as attribute"
|
57
|
-
end
|
43
|
+
property_key = "_#{key.to_s}"
|
44
|
+
validate_and_assign(key, value, property_key)
|
58
45
|
end
|
59
46
|
end
|
60
47
|
|
@@ -62,10 +49,10 @@ module Occi
|
|
62
49
|
attributes.keys.each do |key|
|
63
50
|
if self.keys.include? key
|
64
51
|
case self[key]
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
52
|
+
when Occi::Core::Attributes
|
53
|
+
self[key].remove attributes[key]
|
54
|
+
else
|
55
|
+
self.delete(key)
|
69
56
|
end
|
70
57
|
end
|
71
58
|
end
|
@@ -74,12 +61,12 @@ module Occi
|
|
74
61
|
|
75
62
|
def convert(attributes=Occi::Core::Attributes.new(self))
|
76
63
|
attributes.each_pair do |key, value|
|
77
|
-
next if
|
64
|
+
next if key =~ /^_/
|
78
65
|
case value
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
66
|
+
when Occi::Core::Attributes
|
67
|
+
value.convert!
|
68
|
+
else
|
69
|
+
attributes[key] = nil
|
83
70
|
end
|
84
71
|
end
|
85
72
|
attributes.converted = true
|
@@ -90,13 +77,13 @@ module Occi
|
|
90
77
|
convert self
|
91
78
|
end
|
92
79
|
|
93
|
-
# @return [
|
80
|
+
# @return [Hash] key value pair of full attribute names with their corresponding values
|
94
81
|
def names
|
95
82
|
hash = {}
|
96
83
|
self.each_key do |key|
|
97
|
-
next if
|
84
|
+
next if key =~ /^_/
|
98
85
|
if self[key].kind_of? Occi::Core::Attributes
|
99
|
-
self[key].names.each_pair { |k, v| hash[key
|
86
|
+
self[key].names.each_pair { |k, v| hash["#{key}.#{k}"] = v unless v.nil? }
|
100
87
|
else
|
101
88
|
hash[key] = self[key]
|
102
89
|
end
|
@@ -105,45 +92,89 @@ module Occi
|
|
105
92
|
end
|
106
93
|
|
107
94
|
# @param [Hash] attributes
|
108
|
-
# @return [Occi::Core::
|
109
|
-
def self.
|
95
|
+
# @return [Occi::Core::Attributes] parsed attributes with properties
|
96
|
+
def self.parse_properties(hash)
|
110
97
|
hash ||= {}
|
98
|
+
raise Occi::Errors::ParserInputError,
|
99
|
+
'Hash must be a hash-like structure!' unless hash.respond_to?(:each_pair)
|
100
|
+
|
111
101
|
attributes = Occi::Core::Attributes.new
|
112
102
|
hash.each_pair do |key, value|
|
113
|
-
if
|
114
|
-
|
115
|
-
value[:required] ||= value[:Required] ||= false
|
116
|
-
value[:mutable] ||= value[:Mutable] ||= false
|
117
|
-
value[:default] = value[:Default] if value[:Default]
|
118
|
-
value[:description] = value[:Description] if value[:Description]
|
119
|
-
value[:pattern] ||= value[:Pattern] ||= ".*"
|
120
|
-
value.delete :Type
|
121
|
-
value.delete :Required
|
122
|
-
value.delete :Mutable
|
123
|
-
value.delete :Default
|
124
|
-
value.delete :Description
|
125
|
-
value.delete :Pattern
|
126
|
-
attributes[key] = Occi::Core::Properties.new value
|
103
|
+
if Occi::Core::Properties.contains_props?(value)
|
104
|
+
attributes[key] = Occi::Core::Properties.new(value)
|
127
105
|
else
|
128
|
-
attributes[key] = self.
|
106
|
+
attributes[key] = self.parse_properties(attributes[key])
|
129
107
|
end
|
130
108
|
end
|
109
|
+
|
131
110
|
attributes
|
132
111
|
end
|
133
112
|
|
134
113
|
# @param [Hash] attributes key value pair of full attribute names with their corresponding values
|
135
|
-
# @return [Occi::Core::
|
114
|
+
# @return [Occi::Core::Attributes]
|
136
115
|
def self.split(attributes)
|
137
116
|
attribute = Attributes.new
|
138
117
|
attributes.each do |name, value|
|
139
118
|
key, _, rest = name.partition('.')
|
140
|
-
if rest.
|
119
|
+
if rest.blank?
|
141
120
|
attribute[key] = value
|
142
121
|
else
|
143
122
|
attribute.merge! Attributes.new(key => self.split(rest => value))
|
144
123
|
end
|
145
124
|
end
|
146
|
-
|
125
|
+
|
126
|
+
attribute
|
127
|
+
end
|
128
|
+
|
129
|
+
# @return [String]
|
130
|
+
def to_string
|
131
|
+
attributes = ';'
|
132
|
+
attributes << to_header.gsub(',', ';')
|
133
|
+
|
134
|
+
attributes == ';' ? '' : attributes
|
135
|
+
end
|
136
|
+
|
137
|
+
# @return [String]
|
138
|
+
def to_string_short
|
139
|
+
any? ? ";attributes=#{names.keys.join(' ').inspect}" : ""
|
140
|
+
end
|
141
|
+
|
142
|
+
# @return [String]
|
143
|
+
def to_text
|
144
|
+
text = ""
|
145
|
+
names.each_pair do |name, value|
|
146
|
+
# TODO: find a better way to skip properties
|
147
|
+
next if name.include? '._'
|
148
|
+
case value
|
149
|
+
when Occi::Core::Entity
|
150
|
+
text << "\nX-OCCI-Attribute: #{name}=\"#{value.location}\""
|
151
|
+
when Occi::Core::Category
|
152
|
+
text << "\nX-OCCI-Attribute: #{name}=\"#{value.type_identifier}\""
|
153
|
+
else
|
154
|
+
text << "\nX-OCCI-Attribute: #{name}=#{value.inspect}"
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
text
|
159
|
+
end
|
160
|
+
|
161
|
+
# @return [String] of attributes put in an array and then concatenated into a string
|
162
|
+
def to_header
|
163
|
+
attributes = []
|
164
|
+
names.each_pair do |name, value|
|
165
|
+
# TODO: find a better way to skip properties
|
166
|
+
next if name.include? '._'
|
167
|
+
case value
|
168
|
+
when Occi::Core::Entity
|
169
|
+
attributes << "#{name}=\"#{value.location}\""
|
170
|
+
when Occi::Core::Category
|
171
|
+
attributes << "#{name}=\"#{value.type_identifier}\""
|
172
|
+
else
|
173
|
+
attributes << "#{name}=#{value.inspect}"
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
attributes.join(',')
|
147
178
|
end
|
148
179
|
|
149
180
|
def to_json(*a)
|
@@ -153,24 +184,173 @@ module Occi
|
|
153
184
|
# @param [Hash] options
|
154
185
|
# @return [Hashie::Mash] json representation
|
155
186
|
def as_json(options={})
|
156
|
-
hash =
|
187
|
+
hash = Hashie::Mash.new
|
157
188
|
self.each_pair do |key, value|
|
158
|
-
next if
|
189
|
+
next if key =~ /^_/
|
190
|
+
# TODO: find a better way to skip properties
|
191
|
+
next if key.start_with? '_'
|
192
|
+
|
159
193
|
case value
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
194
|
+
when Occi::Core::Attributes
|
195
|
+
hash[key] = value.as_json if value && value.as_json.size > 0
|
196
|
+
when Occi::Core::Entity
|
197
|
+
hash[key] = value.to_s unless value.blank?
|
198
|
+
when Occi::Core::Category
|
199
|
+
hash[key] = value.to_s
|
200
|
+
else
|
201
|
+
hash[key] = value.as_json unless value.nil?
|
168
202
|
end
|
169
203
|
end
|
204
|
+
|
170
205
|
hash
|
171
206
|
end
|
172
207
|
|
173
|
-
|
208
|
+
# @param [Occi::Core::Attributes] definitions
|
209
|
+
# @param [true,false] set_defaults
|
210
|
+
# @return [Occi::Core::Attributes] attributes with their defaults set
|
211
|
+
def check(definitions, set_defaults = false)
|
212
|
+
attributes = Occi::Core::Attributes.new(self)
|
213
|
+
attributes.check!(definitions, set_defaults)
|
214
|
+
attributes
|
215
|
+
end
|
174
216
|
|
217
|
+
# @param [Occi::Core::Attributes] definitions
|
218
|
+
# @param [true,false] set_defaults
|
219
|
+
# Assigns default values to attributes
|
220
|
+
def check!(definitions, set_defaults = false)
|
221
|
+
raise Occi::Errors::AttributeDefinitionsConvrertedError,
|
222
|
+
"Definition attributes must not be converted" if definitions.converted?
|
223
|
+
|
224
|
+
# Start with checking for missing attributes
|
225
|
+
add_missing_attributes(self, definitions, set_defaults)
|
226
|
+
|
227
|
+
# Then check all attributes against definitions
|
228
|
+
check_wrt_definitions(self, definitions, set_defaults)
|
229
|
+
|
230
|
+
# Delete remaining empty attributes
|
231
|
+
delete_empty(self)
|
232
|
+
end
|
233
|
+
|
234
|
+
private
|
235
|
+
|
236
|
+
def validate_and_assign(key, value, property_key)
|
237
|
+
raise Occi::Errors::AttributeNameInvalidError,
|
238
|
+
"Attribute names (as in \"#{key}\") must not begin with underscores" if key =~ /^_/
|
239
|
+
case value
|
240
|
+
when Occi::Core::Attributes
|
241
|
+
add_to_hashie(key, value)
|
242
|
+
when Occi::Core::Properties
|
243
|
+
add_to_hashie(key, value.clone)
|
244
|
+
add_to_hashie(property_key, value.clone)
|
245
|
+
when Hash
|
246
|
+
properties = Occi::Core::Properties.new(value)
|
247
|
+
add_to_hashie(key, properties.clone)
|
248
|
+
add_to_hashie(property_key, properties.clone)
|
249
|
+
when Occi::Core::Entity
|
250
|
+
match_type(value, self[property_key], 'string') if self[property_key]
|
251
|
+
add_to_hashie(key, value)
|
252
|
+
when Occi::Core::Category
|
253
|
+
match_type(value, self[property_key], 'string') if self[property_key]
|
254
|
+
add_to_hashie(key, value)
|
255
|
+
when String
|
256
|
+
match_type(value, self[property_key], 'string') if self[property_key]
|
257
|
+
add_to_hashie(key, value)
|
258
|
+
when Numeric
|
259
|
+
match_type(value, self[property_key], 'number') if self[property_key]
|
260
|
+
add_to_hashie(key, value)
|
261
|
+
when FalseClass, TrueClass
|
262
|
+
match_type(value, self[property_key], 'boolean') if self[property_key]
|
263
|
+
add_to_hashie(key, value)
|
264
|
+
when NilClass
|
265
|
+
add_to_hashie(key, value)
|
266
|
+
else
|
267
|
+
raise Occi::Errors::AttributeTypeError,
|
268
|
+
"Value #{value} of type #{value.class} not supported as attribute"
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
def add_to_hashie(*args)
|
273
|
+
Hashie::Mash.instance_method(:[]=).bind(self).call(*args)
|
274
|
+
end
|
275
|
+
|
276
|
+
def match_type(value, property, expected_type)
|
277
|
+
raise Occi::Errors::AttributeTypeError,
|
278
|
+
"Value #{value} derived from #{value.class} assigned " \
|
279
|
+
"but attribute of type #{property.type} required" unless property.type == expected_type
|
280
|
+
match_pattern(property.pattern, value)
|
281
|
+
end
|
282
|
+
|
283
|
+
def match_pattern(pattern, value)
|
284
|
+
return if pattern.blank?
|
285
|
+
|
286
|
+
if Occi::Settings.verify_attribute_pattern && !Occi::Settings.compatibility
|
287
|
+
raise Occi::Errors::AttributeTypeError,
|
288
|
+
"Value #{value.to_s} does not match pattern #{pattern}" unless value.to_s.match "^#{pattern}$"
|
289
|
+
else
|
290
|
+
Occi::Log.warn "[#{self.class}] Skipping pattern checks on attributes, turn off " \
|
291
|
+
"the compatibility mode and enable the attribute pattern check in settings!"
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
def add_missing_attributes(attributes, definitions, set_defaults)
|
296
|
+
definitions.each_key do |key|
|
297
|
+
next if key =~ /^_/
|
298
|
+
|
299
|
+
if definitions[key].kind_of? Occi::Core::Attributes
|
300
|
+
add_missing_attributes(attributes[key], definitions[key], set_defaults)
|
301
|
+
elsif attributes[key].nil?
|
302
|
+
|
303
|
+
if definitions[key].default.nil?
|
304
|
+
raise Occi::Errors::AttributeMissingError,
|
305
|
+
"Required attribute #{key} not specified" if definitions[key].required
|
306
|
+
else
|
307
|
+
attributes[key] = definitions[key].default if definitions[key].required || set_defaults
|
308
|
+
end
|
309
|
+
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
def check_wrt_definitions(attributes, definitions, set_defaults)
|
315
|
+
attributes.each_key do |key|
|
316
|
+
next if key =~ /^_/
|
317
|
+
|
318
|
+
#Raise exception for attributes not defined at all
|
319
|
+
raise Occi::Errors::AttributeNotDefinedError,
|
320
|
+
"Attribute #{key} not found in definitions" unless definitions.key?(key)
|
321
|
+
|
322
|
+
if attributes[key].kind_of? Occi::Core::Attributes
|
323
|
+
check_wrt_definitions(attributes[key], definitions[key], set_defaults)
|
324
|
+
else
|
325
|
+
next if attributes[key].nil? # I will be removed in the next step
|
326
|
+
|
327
|
+
#Check value types
|
328
|
+
definitions[key].check_value_for_type(attributes[key], key)
|
329
|
+
|
330
|
+
# Check patterns
|
331
|
+
if definitions[key].pattern
|
332
|
+
if Occi::Settings.verify_attribute_pattern && !Occi::Settings.compatibility
|
333
|
+
raise Occi::Errors::AttributeTypeError,
|
334
|
+
"Attribute #{key} with value #{attributes[key]} does not " \
|
335
|
+
"match pattern #{definitions[key].pattern}" unless attributes[key].to_s.match "^#{definitions[key].pattern}$"
|
336
|
+
else
|
337
|
+
Occi::Log.warn "[#{self.class}] [#{key}] Skipping pattern checks on attributes, turn off " \
|
338
|
+
"the compatibility mode and enable the attribute pattern check in settings!"
|
339
|
+
end
|
340
|
+
end
|
341
|
+
end
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
def delete_empty(attributes)
|
346
|
+
attributes.each_key do |key|
|
347
|
+
if attributes[key].kind_of? Occi::Core::Attributes
|
348
|
+
delete_empty(attributes[key])
|
349
|
+
else
|
350
|
+
attributes.delete(key) if attributes[key].nil?
|
351
|
+
end
|
352
|
+
end
|
353
|
+
end
|
354
|
+
end
|
175
355
|
end
|
176
|
-
end
|
356
|
+
end
|
data/lib/occi/core/categories.rb
CHANGED
data/lib/occi/core/category.rb
CHANGED
@@ -3,6 +3,7 @@ module Occi
|
|
3
3
|
class Category
|
4
4
|
|
5
5
|
include Occi::Helpers::Inspect
|
6
|
+
include Occi::Helpers::Comparators::Category
|
6
7
|
|
7
8
|
attr_accessor :scheme, :term, :title, :attributes, :model
|
8
9
|
|
@@ -14,21 +15,26 @@ module Occi
|
|
14
15
|
term='category',
|
15
16
|
title=nil,
|
16
17
|
attributes=Occi::Core::Attributes.new)
|
17
|
-
scheme
|
18
|
+
raise ArgumentError, 'scheme and term cannot be nil' unless scheme && term
|
19
|
+
raise ArgumentError, 'scheme and term cannot be empty' if scheme.empty? || term.empty?
|
20
|
+
attributes ||= Occi::Core::Attributes.new
|
21
|
+
|
22
|
+
scheme << '#' unless scheme.end_with? '#'
|
18
23
|
@scheme = scheme
|
19
24
|
@term = term
|
20
25
|
@title = title
|
26
|
+
|
21
27
|
case attributes
|
22
28
|
when Occi::Core::Attributes
|
23
|
-
@attributes = attributes
|
29
|
+
@attributes = Occi::Core::Attributes.new(attributes)
|
24
30
|
else
|
25
|
-
@attributes = Occi::Core::Attributes.
|
31
|
+
@attributes = Occi::Core::Attributes.parse_properties attributes
|
26
32
|
end
|
27
33
|
end
|
28
34
|
|
29
35
|
# @return [String] Type identifier of the Category.
|
30
36
|
def type_identifier
|
31
|
-
self.scheme
|
37
|
+
"#{self.scheme}#{self.term}"
|
32
38
|
end
|
33
39
|
|
34
40
|
# @param options [Hash]
|
@@ -38,25 +44,25 @@ module Occi
|
|
38
44
|
category.scheme = self.scheme
|
39
45
|
category.term = self.term
|
40
46
|
category.title = self.title if self.title
|
41
|
-
category.attributes = self.attributes
|
47
|
+
category.attributes = self.attributes.any? ? self.attributes.as_json : Occi::Core::Attributes.new.as_json
|
42
48
|
category
|
43
49
|
end
|
44
50
|
|
45
51
|
# @return [String] Short text representation of the Category.
|
46
52
|
def to_string_short
|
47
|
-
self.term
|
53
|
+
"#{self.term};scheme=#{self.scheme.inspect};class=#{self.class.name.demodulize.downcase.inspect}"
|
48
54
|
end
|
49
55
|
|
50
56
|
# @return [String] Full text representation of the Category.
|
51
57
|
def to_string
|
52
58
|
string = self.to_string_short
|
53
|
-
string <<
|
59
|
+
string << ";title=#{self.title.inspect}" if self.title
|
54
60
|
string
|
55
61
|
end
|
56
62
|
|
57
63
|
# @return [String] Text representation of the Category.
|
58
64
|
def to_text
|
59
|
-
|
65
|
+
"Category: #{self.to_string}"
|
60
66
|
end
|
61
67
|
|
62
68
|
# @return [Hash] Hash containing the HTTP headers of the text/occi rendering.
|
@@ -74,6 +80,17 @@ module Occi
|
|
74
80
|
self.type_identifier
|
75
81
|
end
|
76
82
|
|
83
|
+
# @return [Bool] Indicating whether this category is "empty", i.e. required attributes are blank
|
84
|
+
def empty?
|
85
|
+
term.blank? || scheme.blank?
|
86
|
+
end
|
87
|
+
|
88
|
+
# @param term [String] Term to check.
|
89
|
+
# @return [Bool] Indicating whether term consists exclusively of valid characters.
|
90
|
+
def self.valid_term?(term)
|
91
|
+
term =~ /^[a-z][a-z0-9_-]*$/
|
92
|
+
end
|
93
|
+
|
77
94
|
end
|
78
95
|
end
|
79
96
|
end
|