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/entity.rb
CHANGED
@@ -3,8 +3,10 @@ module Occi
|
|
3
3
|
class Entity
|
4
4
|
|
5
5
|
include Occi::Helpers::Inspect
|
6
|
+
include Occi::Helpers::Comparators::Entity
|
6
7
|
|
7
|
-
attr_accessor :mixins, :attributes, :actions, :id, :model, :
|
8
|
+
attr_accessor :mixins, :attributes, :actions, :id, :model, :location
|
9
|
+
attr_reader :kind
|
8
10
|
|
9
11
|
class_attribute :kind, :mixins, :attributes, :actions
|
10
12
|
|
@@ -64,27 +66,25 @@ module Occi
|
|
64
66
|
@kind = self.class.kind.clone
|
65
67
|
@mixins = Occi::Core::Mixins.new mixins
|
66
68
|
@mixins.entity = self
|
69
|
+
|
67
70
|
attributes = self.class.attribute_properties if attributes.blank?
|
68
71
|
if attributes.kind_of? Occi::Core::Attributes
|
69
72
|
@attributes = attributes.convert
|
70
73
|
else
|
71
74
|
@attributes = Occi::Core::Attributes.new attributes
|
72
75
|
end
|
76
|
+
@attributes['occi.core.id'] ||= UUIDTools::UUID.random_create.to_s
|
77
|
+
|
73
78
|
@actions = Occi::Core::Actions.new actions
|
74
79
|
@location = location
|
75
80
|
end
|
76
81
|
|
77
|
-
# @return [Occi::Core::Kind]
|
78
|
-
def kind
|
79
|
-
@kind
|
80
|
-
end
|
81
|
-
|
82
82
|
# @param [Occi::Core::Kind,String] kind
|
83
83
|
# @return [Occi::Core::Kind]
|
84
84
|
def kind=(kind)
|
85
85
|
if kind.kind_of? String
|
86
86
|
scheme, term = kind.split '#'
|
87
|
-
kind = Occi::Core::
|
87
|
+
kind = Occi::Core::Kind.get_class scheme, term
|
88
88
|
end
|
89
89
|
@kind = kind
|
90
90
|
end
|
@@ -115,7 +115,7 @@ module Occi
|
|
115
115
|
|
116
116
|
# @return [UUIDTools::UUID] id of the entity
|
117
117
|
def id
|
118
|
-
@id ||= @attributes.
|
118
|
+
@id ||= @attributes.occi_.core_.id
|
119
119
|
@id
|
120
120
|
end
|
121
121
|
|
@@ -127,17 +127,20 @@ module Occi
|
|
127
127
|
|
128
128
|
# @return [String] title attribute of entity
|
129
129
|
def title
|
130
|
-
@attributes.
|
130
|
+
@attributes.occi_.core_.title
|
131
131
|
end
|
132
132
|
|
133
133
|
# @param [Occi::Model] model
|
134
134
|
# @return [Occi::Model]
|
135
135
|
def model=(model)
|
136
136
|
@model = model
|
137
|
+
|
137
138
|
@kind = (model.get_by_id(@kind.type_identifier) || @kind)
|
138
139
|
@kind.entities << self
|
140
|
+
|
139
141
|
@mixins.model = model
|
140
142
|
@mixins.each { |mixin| mixin.entities << self }
|
143
|
+
|
141
144
|
@actions.model = model
|
142
145
|
end
|
143
146
|
|
@@ -150,56 +153,29 @@ module Occi
|
|
150
153
|
# @return [String] location of the entity
|
151
154
|
def location
|
152
155
|
return @location if @location
|
153
|
-
kind.location
|
156
|
+
"#{kind.location}#{id.gsub('urn:uuid:', '')}" if id
|
154
157
|
end
|
155
158
|
|
156
159
|
# check attributes against their definitions and set defaults
|
157
|
-
# @param [
|
158
|
-
def check
|
159
|
-
|
160
|
+
# @param [true,false] set default values for all empty attributes
|
161
|
+
def check(set_defaults = false)
|
162
|
+
|
163
|
+
raise ArgumentError, 'No model has been assigned to this entity' unless @model # XXX: Needs error type
|
164
|
+
|
165
|
+
kind = @model.get_by_id(@kind.to_s)
|
166
|
+
raise Occi::Errors::KindNotDefinedError, "Kind not found for entity #{self.to_s}!" unless kind # XXX: Needs error type
|
167
|
+
|
160
168
|
definitions = Occi::Core::Attributes.new
|
161
|
-
definitions.merge!
|
162
|
-
|
163
|
-
|
169
|
+
definitions.merge! kind.attributes
|
170
|
+
|
171
|
+
@mixins.each do |mxn|
|
172
|
+
mixin = @model.get_by_id(mxn.to_s)
|
164
173
|
next if mixin.nil?
|
174
|
+
|
165
175
|
definitions.merge!(mixin.attributes) if mixin.attributes
|
166
176
|
end if @mixins
|
167
|
-
|
168
|
-
@attributes
|
169
|
-
end
|
170
|
-
|
171
|
-
# @param [Occi::Core::Attributes] attributes
|
172
|
-
# @param [Occi::Core::Attributes] definitions
|
173
|
-
# @param [true,false] set_defaults
|
174
|
-
# @return [Occi::Core::Attributes] attributes with their defaults set
|
175
|
-
def self.check(attributes, definitions, set_defaults=false)
|
176
|
-
attributes = Occi::Core::Attributes.new(attributes)
|
177
|
-
definitions.each_key do |key|
|
178
|
-
next if definitions.key?(key[1..-1])
|
179
|
-
if definitions[key].kind_of? Occi::Core::Attributes
|
180
|
-
attributes[key] = check(attributes[key], definitions[key])
|
181
|
-
else
|
182
|
-
properties = definitions[key]
|
183
|
-
value = attributes[key]
|
184
|
-
value ||= properties.default if set_defaults or properties.required?
|
185
|
-
raise "required attribute #{key} not found" if value.nil? && properties.required?
|
186
|
-
next if value.blank? and not properties.required?
|
187
|
-
case properties.type
|
188
|
-
when 'number'
|
189
|
-
raise "attribute #{key} value #{value} from class #{value.class.name} does not match attribute property type #{properties.type}" unless value.kind_of?(Numeric)
|
190
|
-
when 'boolean'
|
191
|
-
raise "attribute #{key} value #{value} from class #{value.class.name} does not match attribute property type #{properties.type}" unless !!value == value
|
192
|
-
when 'string'
|
193
|
-
raise "attribute #{key} with value #{value} from class #{value.class.name} does not match attribute property type #{properties.type}" unless value.kind_of?(String)
|
194
|
-
else
|
195
|
-
raise "property type #{properties.type} is not one of the allowed types number, boolean or string"
|
196
|
-
end
|
197
|
-
Occi::Log.warn "attribute #{key} with value #{value} does not match pattern #{properties.pattern}" if value.to_s.scan(Regexp.new(properties.pattern)).blank? if properties.pattern
|
198
|
-
attributes[key] = value
|
199
|
-
end
|
200
|
-
end
|
201
|
-
attributes.delete_if { |_, v| v.blank? } # remove empty attributes
|
202
|
-
attributes
|
177
|
+
|
178
|
+
@attributes.check!(definitions, set_defaults)
|
203
179
|
end
|
204
180
|
|
205
181
|
# @param [Hash] options
|
@@ -208,7 +184,7 @@ module Occi
|
|
208
184
|
entity = Hashie::Mash.new
|
209
185
|
entity.kind = @kind.to_s if @kind
|
210
186
|
entity.mixins = @mixins.join(' ').split(' ') if @mixins.any?
|
211
|
-
entity.actions = @actions if @actions.any?
|
187
|
+
entity.actions = @actions.as_json if @actions.any?
|
212
188
|
entity.attributes = @attributes.as_json if @attributes.as_json.any?
|
213
189
|
entity.id = id.to_s if id
|
214
190
|
entity
|
@@ -216,42 +192,38 @@ module Occi
|
|
216
192
|
|
217
193
|
# @return [String] text representation
|
218
194
|
def to_text
|
219
|
-
text =
|
195
|
+
text = "Category: #{self.kind.term};scheme=#{self.kind.scheme.inspect};class=\"kind\""
|
220
196
|
@mixins.each do |mixin|
|
221
197
|
scheme, term = mixin.to_s.split('#')
|
222
198
|
scheme << '#'
|
223
|
-
text << "\
|
224
|
-
end
|
225
|
-
@attributes.names.each_pair do |name, value|
|
226
|
-
value = value.inspect
|
227
|
-
text << "\n" + 'X-OCCI-Attribute: ' + name + '=' + value
|
228
|
-
end
|
229
|
-
@actions.each do |action|
|
230
|
-
_, term = action.split('#')
|
231
|
-
text << "\n" + 'Link: <' + self.location + '?action=' + term + '>;rel=' + action.inspect
|
199
|
+
text << "\nCategory: #{term};scheme=#{scheme.inspect};class=\"mixin\""
|
232
200
|
end
|
201
|
+
|
202
|
+
text << @attributes.to_text
|
203
|
+
|
204
|
+
@actions.each { |action| text << "\nLink: <#{self.location}?action=#{action.term}>;rel=#{action.to_s}" }
|
205
|
+
|
233
206
|
text
|
234
207
|
end
|
235
208
|
|
236
209
|
# @return [Hash] hash containing the HTTP headers of the text/occi rendering
|
237
210
|
def to_header
|
238
211
|
header = Hashie::Mash.new
|
239
|
-
header['Category'] = self.kind.term
|
212
|
+
header['Category'] = "#{self.kind.term};scheme=#{self.kind.scheme.inspect};class=\"kind\""
|
213
|
+
|
240
214
|
@mixins.each do |mixin|
|
241
215
|
scheme, term = mixin.to_s.split('#')
|
242
216
|
scheme << '#'
|
243
|
-
header['Category']
|
244
|
-
end
|
245
|
-
attributes = []
|
246
|
-
@attributes.names.each_pair do |name, value|
|
247
|
-
attributes << name + '=' + value.to_s.inspect
|
217
|
+
header['Category'] << ",#{term};scheme=#{scheme.inspect};class=\"mixin\""
|
248
218
|
end
|
249
|
-
|
219
|
+
|
220
|
+
attributes = @attributes.to_header
|
221
|
+
header['X-OCCI-Attribute'] = attributes unless attributes.blank?
|
222
|
+
|
250
223
|
links = []
|
251
|
-
@actions.each
|
252
|
-
|
253
|
-
|
254
|
-
end
|
224
|
+
@actions.each { |action| links << "<#{self.location}?action=#{action.term}>;rel=#{action.to_s}" }
|
225
|
+
header['Link'] = links.join(',') if links.any?
|
226
|
+
|
255
227
|
header
|
256
228
|
end
|
257
229
|
|
@@ -260,6 +232,11 @@ module Occi
|
|
260
232
|
self.location
|
261
233
|
end
|
262
234
|
|
235
|
+
# @return [Bool] Indicating whether this entity is "empty", i.e. required attributes are blank
|
236
|
+
def empty?
|
237
|
+
kind.blank? || attributes['occi.core.id'].blank?
|
238
|
+
end
|
239
|
+
|
263
240
|
end
|
264
241
|
end
|
265
242
|
end
|
data/lib/occi/core/kind.rb
CHANGED
@@ -21,7 +21,7 @@ module Occi
|
|
21
21
|
@parent = [parent].flatten.first
|
22
22
|
@actions = Occi::Core::Actions.new(actions)
|
23
23
|
@entities = Occi::Core::Entities.new
|
24
|
-
location.blank? ? @location =
|
24
|
+
location.blank? ? @location = "/#{term}/" : @location = location
|
25
25
|
end
|
26
26
|
|
27
27
|
# @param scheme [String] The categorisation scheme.
|
@@ -30,9 +30,12 @@ module Occi
|
|
30
30
|
# @return [Class] Ruby class with scheme as namespace, term as name and parent kind as super class.
|
31
31
|
def self.get_class(scheme, term, parent=Occi::Core::Entity.kind)
|
32
32
|
parent ||= Occi::Core::Entity.kind
|
33
|
+
raise ArgumentError, 'Mandatory argument cannot be nil' unless scheme && term
|
34
|
+
|
33
35
|
if parent.kind_of? Array
|
34
36
|
parent = parent.first
|
35
37
|
end
|
38
|
+
|
36
39
|
if parent.to_s == 'http://schemas.ogf.org/occi/core#entity'
|
37
40
|
parent = Occi::Core::Entity.kind
|
38
41
|
elsif parent.kind_of? Occi::Core::Kind
|
@@ -41,8 +44,11 @@ module Occi
|
|
41
44
|
parent = self.get_class(*parent.to_s.split('#')).kind
|
42
45
|
end
|
43
46
|
|
47
|
+
term = self.sanitize_term(term) if Occi::Settings.compatibility
|
48
|
+
raise ArgumentError, "Invalid characters in term #{term}" unless Occi::Core::Category.valid_term?(term)
|
49
|
+
|
44
50
|
unless scheme.end_with? '#'
|
45
|
-
scheme
|
51
|
+
scheme << '#'
|
46
52
|
end
|
47
53
|
|
48
54
|
uri = URI.parse(scheme)
|
@@ -60,7 +66,7 @@ module Occi
|
|
60
66
|
end
|
61
67
|
end
|
62
68
|
|
63
|
-
class_name =
|
69
|
+
class_name = term.classify
|
64
70
|
if namespace.const_defined? class_name
|
65
71
|
klass = namespace.const_get class_name
|
66
72
|
unless klass.ancestors.include? Occi::Core::Entity
|
@@ -113,20 +119,18 @@ module Occi
|
|
113
119
|
# @return [String] string representation of the kind
|
114
120
|
def to_string
|
115
121
|
string = super
|
116
|
-
string <<
|
117
|
-
string <<
|
118
|
-
string <<
|
119
|
-
string <<
|
122
|
+
string << ";rel=#{self.related.first.to_s.inspect}" if self.related.any?
|
123
|
+
string << ";location=#{self.location.inspect}"
|
124
|
+
string << self.attributes.to_string_short
|
125
|
+
string << ";actions=#{self.actions.join(' ').inspect}" if self.actions.any?
|
120
126
|
string
|
121
127
|
end
|
122
128
|
|
123
129
|
private
|
124
130
|
|
125
131
|
# Relaxed parser rules require additional checks on terms.
|
126
|
-
|
127
|
-
|
128
|
-
def self.sanitize_term_before_classify(term)
|
129
|
-
sanitized = term.downcase.gsub(/[\s\(\)\.\{\}\-;,\\\/\?\!\|\*\<\>]/, '_').gsub(/_+/, '_').chomp('_').reverse.chomp('_').reverse
|
132
|
+
def self.sanitize_term(term)
|
133
|
+
sanitized = term.downcase.gsub(/[^a-z0-9-]/, '_').gsub(/_+/, '_').gsub(/^_|_$/, '')
|
130
134
|
sanitized = "uuid_#{sanitized}" if sanitized.match(/^[0-9]/)
|
131
135
|
|
132
136
|
sanitized
|
data/lib/occi/core/kinds.rb
CHANGED
data/lib/occi/core/link.rb
CHANGED
@@ -4,6 +4,7 @@ module Occi
|
|
4
4
|
|
5
5
|
attr_accessor :rel, :source, :target
|
6
6
|
|
7
|
+
self.attributes = Occi::Core::Attributes.new(Occi::Core::Entity.attributes)
|
7
8
|
self.attributes['occi.core.target'] = {:mutable => true}
|
8
9
|
self.attributes['occi.core.source'] = {:mutable => true}
|
9
10
|
|
@@ -30,7 +31,7 @@ module Occi
|
|
30
31
|
|
31
32
|
# @return [String] target attribute of the link
|
32
33
|
def target
|
33
|
-
@target ||= self.attributes.
|
34
|
+
@target ||= self.attributes.occi_.core_.target
|
34
35
|
@target
|
35
36
|
end
|
36
37
|
|
@@ -43,7 +44,7 @@ module Occi
|
|
43
44
|
|
44
45
|
# @return [String] source attribute of the link
|
45
46
|
def source
|
46
|
-
@source ||= self.attributes.
|
47
|
+
@source ||= self.attributes.occi_.core_.source
|
47
48
|
@source
|
48
49
|
end
|
49
50
|
|
@@ -54,9 +55,9 @@ module Occi
|
|
54
55
|
@source = source
|
55
56
|
end
|
56
57
|
|
57
|
-
#
|
58
|
+
# Runs check on attributes
|
58
59
|
def check
|
59
|
-
raise "rel
|
60
|
+
raise ArgumentError, "Cannot run check on #{self.to_s} without relation (@rel attribute) set" unless @rel
|
60
61
|
super
|
61
62
|
end
|
62
63
|
|
@@ -72,25 +73,23 @@ module Occi
|
|
72
73
|
|
73
74
|
# @return [String] text representation of link reference
|
74
75
|
def to_string
|
75
|
-
string =
|
76
|
-
string <<
|
77
|
-
string <<
|
76
|
+
string = "<#{self.target.to_s}>"
|
77
|
+
string << ";rel=#{@rel.to_s.inspect}"
|
78
|
+
string << ";self=#{self.location.inspect}" if self.location
|
79
|
+
|
78
80
|
categories = [@kind] + @mixins.join(',').split(',')
|
79
|
-
string <<
|
80
|
-
|
81
|
-
|
82
|
-
value = value.to_s.inspect
|
83
|
-
string << ';' + name + '=' + value
|
84
|
-
end
|
81
|
+
string << ";category=#{categories.join(' ').inspect}"
|
82
|
+
|
83
|
+
string << @attributes.to_string
|
85
84
|
|
86
85
|
string
|
87
86
|
end
|
88
87
|
|
89
88
|
# @return [String] text representation of link
|
90
89
|
def to_text_link
|
91
|
-
|
90
|
+
"Link: #{self.to_string}"
|
92
91
|
end
|
93
92
|
|
94
93
|
end
|
95
94
|
end
|
96
|
-
end
|
95
|
+
end
|
data/lib/occi/core/links.rb
CHANGED
data/lib/occi/core/mixin.rb
CHANGED
@@ -23,7 +23,7 @@ module Occi
|
|
23
23
|
@depends = Occi::Core::Dependencies.new depends
|
24
24
|
@actions = Occi::Core::Actions.new actions
|
25
25
|
@entities = Occi::Core::Entities.new
|
26
|
-
location.blank? ? @location =
|
26
|
+
location.blank? ? @location = "/mixins/#{term}/" : @location = location
|
27
27
|
@applies = Occi::Core::Kinds.new applies
|
28
28
|
end
|
29
29
|
|
@@ -59,10 +59,10 @@ module Occi
|
|
59
59
|
# @return [String] text representation
|
60
60
|
def to_string
|
61
61
|
string = super
|
62
|
-
string <<
|
63
|
-
string <<
|
64
|
-
string <<
|
65
|
-
string <<
|
62
|
+
string << ";rel=#{self.related.join(' ').inspect}" if self.related.any?
|
63
|
+
string << ";location=#{self.location.inspect}"
|
64
|
+
string << self.attributes.to_string_short
|
65
|
+
string << ";actions=#{self.actions.join(' ').inspect}" if self.actions.any?
|
66
66
|
string
|
67
67
|
end
|
68
68
|
|
data/lib/occi/core/mixins.rb
CHANGED
@@ -11,7 +11,7 @@ module Occi
|
|
11
11
|
|
12
12
|
def remove(mixin)
|
13
13
|
mixin = convert mixin
|
14
|
-
@entity.attributes.remove mixin.attributes
|
14
|
+
@entity.attributes.remove mixin.attributes if @entity
|
15
15
|
self.delete mixin
|
16
16
|
end
|
17
17
|
|
@@ -34,4 +34,4 @@ module Occi
|
|
34
34
|
|
35
35
|
end
|
36
36
|
end
|
37
|
-
end
|
37
|
+
end
|
data/lib/occi/core/properties.rb
CHANGED
@@ -3,33 +3,111 @@ module Occi
|
|
3
3
|
class Properties
|
4
4
|
|
5
5
|
include Occi::Helpers::Inspect
|
6
|
+
include Occi::Helpers::Comparators::Properties
|
6
7
|
|
7
|
-
|
8
|
+
PROPERTY_KEYS = [:type, :required, :mutable, :default, :description, :pattern]
|
9
|
+
attr_accessor :required, :mutable, :default, :description, :pattern
|
10
|
+
attr_reader :type
|
8
11
|
alias_method :required?, :required
|
9
12
|
alias_method :mutable?, :mutable
|
10
13
|
|
11
|
-
#
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
14
|
+
# Types supported in properties, and their mapping to Ruby Classes
|
15
|
+
SUPPORTED_TYPES = Hash.new
|
16
|
+
SUPPORTED_TYPES["string"] = [ String ]
|
17
|
+
SUPPORTED_TYPES["number"] = [ Numeric ]
|
18
|
+
SUPPORTED_TYPES["boolean"] = [ TrueClass, FalseClass ]
|
19
|
+
|
20
|
+
# @param source_hash [Hash]
|
21
|
+
def initialize(source_hash = {})
|
22
|
+
raise ArgumentError, 'Source_hash must be initialized from a hash-like structure!' unless source_hash.kind_of?(Hash)
|
23
|
+
raise ArgumentError, 'Source_hash must not be a Hashie::Mash instance!' if source_hash.kind_of?(Hashie::Mash)
|
24
|
+
source_hash = Occi::Core::Properties.normalize_props(source_hash)
|
25
|
+
|
26
|
+
self.type = source_hash[:type] ||= 'string'
|
27
|
+
raise Occi::Errors::AttributePropertyTypeError,
|
28
|
+
"Type \"#{self.type}\" unsupported in properties. " \
|
29
|
+
"Supported types are: #{Properties.supported_type_names}." unless SUPPORTED_TYPES.key?(self.type)
|
30
|
+
self.required = source_hash[:required] = source_hash[:required].nil? ? false : source_hash[:required]
|
31
|
+
self.mutable = source_hash[:mutable] = source_hash[:mutable].nil? ? false : source_hash[:mutable]
|
32
|
+
self.pattern = source_hash[:pattern] ||= '.*'
|
33
|
+
self.description = source_hash[:description]
|
34
|
+
self.default = source_hash[:default]
|
35
|
+
end
|
36
|
+
|
37
|
+
# @param type [String] Requested attribute type
|
38
|
+
def type=(type)
|
39
|
+
raise Occi::Errors::AttributePropertyTypeError,
|
40
|
+
"Type \"#{type}\" unsupported in properties. Supported " \
|
41
|
+
"types are: #{Properties.supported_type_names}." unless SUPPORTED_TYPES.key?(type)
|
42
|
+
@type = type
|
43
|
+
end
|
44
|
+
|
45
|
+
# @param value [Object] Object whose class will be checked against definition
|
46
|
+
def check_value_for_type(value, key_name = nil)
|
47
|
+
raise Occi::Errors::AttributePropertyTypeError,
|
48
|
+
"Property type #{@type} for #{key_name.inspect} is not one of the allowed " \
|
49
|
+
"types: #{Properties.supported_type_names}" unless SUPPORTED_TYPES.key?(@type)
|
50
|
+
raise Occi::Errors::AttributeTypeError,
|
51
|
+
"Attribute value #{value} for #{key_name.inspect} is class #{value.class.name}. " \
|
52
|
+
"It does not match attribute property type #{@type}" unless SUPPORTED_TYPES[@type].any? { |klasse| value.kind_of?(klasse) }
|
53
|
+
end
|
54
|
+
|
55
|
+
def to_hash
|
56
|
+
as_json.to_hash
|
57
|
+
end
|
58
|
+
|
59
|
+
def to_json(*a)
|
60
|
+
as_json(*a).to_json(*a)
|
20
61
|
end
|
21
62
|
|
22
63
|
def as_json(options={})
|
23
64
|
hash = Hashie::Mash.new
|
24
65
|
hash.default = self.default if self.default
|
25
66
|
hash.type = self.type if self.type
|
26
|
-
hash.required = self.required
|
27
|
-
hash.mutable = self.mutable
|
67
|
+
hash.required = self.required unless self.required.nil?
|
68
|
+
hash.mutable = self.mutable unless self.mutable.nil?
|
28
69
|
hash.pattern = self.pattern if self.pattern
|
29
70
|
hash.description = self.description if self.description
|
71
|
+
|
30
72
|
hash
|
31
73
|
end
|
32
74
|
|
75
|
+
# @return [Bool] Indicating whether this set of properties is "empty", i.e. no attributes are set
|
76
|
+
def empty?
|
77
|
+
as_json.empty?
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.normalize_props(hash)
|
81
|
+
props = {}
|
82
|
+
|
83
|
+
PROPERTY_KEYS.each do |key|
|
84
|
+
found = hash.keys.select { |k| k.to_s.downcase.to_sym == key }.first
|
85
|
+
props[key] = hash[found] if found
|
86
|
+
end
|
87
|
+
|
88
|
+
props
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.contains_props?(hash)
|
92
|
+
# Not a hash == doesn't contain Properties
|
93
|
+
return false unless hash.kind_of? Hash
|
94
|
+
hash = normalize_props(hash)
|
95
|
+
|
96
|
+
# Are there any Property keys?
|
97
|
+
return false if hash.empty?
|
98
|
+
|
99
|
+
# Do all Property keys point to simple values?
|
100
|
+
complx_keys = hash.keys.select { |k| hash[k].kind_of?(Hash) }
|
101
|
+
return false unless complx_keys.empty?
|
102
|
+
|
103
|
+
true
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
def self.supported_type_names()
|
109
|
+
SUPPORTED_TYPES.keys.join(', ')
|
110
|
+
end
|
33
111
|
end
|
34
112
|
end
|
35
113
|
end
|
data/lib/occi/core/resource.rb
CHANGED
@@ -4,7 +4,7 @@ module Occi
|
|
4
4
|
|
5
5
|
attr_accessor :links
|
6
6
|
|
7
|
-
self.attributes = Occi::Core::Attributes.new
|
7
|
+
self.attributes = Occi::Core::Attributes.new(Occi::Core::Entity.attributes)
|
8
8
|
|
9
9
|
self.attributes['occi.core.summary'] = {:mutable => true}
|
10
10
|
|
@@ -31,7 +31,7 @@ module Occi
|
|
31
31
|
|
32
32
|
# @return [String] summary attribute of the resource
|
33
33
|
def summary
|
34
|
-
self.attributes.
|
34
|
+
self.attributes.occi_.core_.summary
|
35
35
|
end
|
36
36
|
|
37
37
|
# set summary attribute of resource
|
@@ -42,19 +42,23 @@ module Occi
|
|
42
42
|
|
43
43
|
def link(target, kind=Occi::Core::Link.kind, mixins=[], attributes=Occi::Core::Attributes.new, rel=Occi::Core::Resource.type_identifier)
|
44
44
|
link = kind.entity_type.new
|
45
|
+
|
45
46
|
link.rel = rel
|
46
47
|
link.attributes = attributes
|
48
|
+
link.id ||= UUIDTools::UUID.random_create.to_s
|
47
49
|
link.target = target
|
48
50
|
link.source = self
|
49
51
|
link.mixins = mixins
|
52
|
+
|
50
53
|
@links << link
|
54
|
+
|
51
55
|
link
|
52
56
|
end
|
53
57
|
|
54
58
|
# @return [String] text representation
|
55
59
|
def to_text
|
56
60
|
text = super
|
57
|
-
@links.each { |link| text << "\n
|
61
|
+
@links.each { |link| text << "\n#{link.to_text_link}" }
|
58
62
|
text
|
59
63
|
end
|
60
64
|
|
data/lib/occi/core/resources.rb
CHANGED