occi-core 4.1.3 → 4.2.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.
- 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
|