rcap 0.1

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.
@@ -0,0 +1,20 @@
1
+ module RCAP
2
+ class EventCode < Parameter
3
+
4
+ XML_ELEMENT_NAME = 'eventCode' # :nodoc:
5
+
6
+ XPATH = "cap:#{ XML_ELEMENT_NAME }" # :nodoc:
7
+
8
+ def to_xml_element # :nodoc:
9
+ xml_element = REXML::Element.new( XML_ELEMENT_NAME )
10
+ xml_element.add_element( NAME_ELEMENT_NAME ).add_text( self.name )
11
+ xml_element.add_element( VALUE_ELEMENT_NAME ).add_text( self.value )
12
+ xml_element
13
+ end
14
+
15
+ def self.from_xml_element( event_code_xml_element ) # :nodoc:
16
+ EventCode.new( :name => RCAP.xpath_text( event_code_xml_element, NAME_XPATH ),
17
+ :value => RCAP.xpath_text( event_code_xml_element, VALUE_XPATH ))
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,20 @@
1
+ module RCAP
2
+ class Geocode < Parameter
3
+
4
+ XML_ELEMENT_NAME = 'geocode' # :nodoc:
5
+
6
+ XPATH = "cap:#{ XML_ELEMENT_NAME }" # :nodoc:
7
+
8
+ def to_xml_element # :nodoc:
9
+ xml_element = REXML::Element.new( XML_ELEMENT_NAME )
10
+ xml_element.add_element( NAME_ELEMENT_NAME ).add_text( @name )
11
+ xml_element.add_element( VALUE_ELEMENT_NAME ).add_text( @value )
12
+ xml_element
13
+ end
14
+
15
+ def self.from_xml_element( geocode_xml_element ) # :nodoc:
16
+ self.new( :name => RCAP.xpath_text( geocode_xml_element, NAME_XPATH ),
17
+ :value => RCAP.xpath_text( geocode_xml_element, VALUE_XPATH ))
18
+ end
19
+ end
20
+ end
data/lib/rcap/info.rb ADDED
@@ -0,0 +1,239 @@
1
+ module RCAP
2
+ # In Info object is valid if
3
+ # * it has an event
4
+ # * it has an urgency with a valid value
5
+ # * it has a severity with a valid value
6
+ # * it has a certainty with a valid value
7
+ # * all categories are valid and categories has at minimum 1 entry
8
+ # * all Resource objects in the resources collection are valid
9
+ # * all Area objects in the areas collection are valid
10
+ #
11
+ class Info
12
+ include Validation
13
+
14
+ CATEGORY_GEO = "Geo"
15
+ CATEGORY_MET = "Met"
16
+ CATEGORY_SAFETY = "Safety"
17
+ CATEGORY_SECURITY = "Security"
18
+ CATEGORY_RESCUE = "Rescue"
19
+ CATEGORY_FIRE = "Fire"
20
+ CATEGORY_HEALTH = "Health"
21
+ CATEGORY_ENV = "Env"
22
+ CATEGORY_TRANSPORT = "Transport"
23
+ CATEGORY_INFRA = "Infra"
24
+ CATEGORY_CBRNE = "CBRNE"
25
+ CATEGORY_OTHER = "Other"
26
+ ALL_CATEGORIES = [ CATEGORY_GEO, CATEGORY_MET, CATEGORY_SAFETY,
27
+ CATEGORY_SECURITY, CATEGORY_RESCUE, CATEGORY_FIRE, CATEGORY_HEALTH,
28
+ CATEGORY_ENV, CATEGORY_TRANSPORT, CATEGORY_INFRA, CATEGORY_CBRNE,
29
+ CATEGORY_OTHER ] # :nodoc:
30
+
31
+ RESPONSE_TYPE_SHELTER = "Shelter"
32
+ RESPONSE_TYPE_EVACUATE = "Evacuate"
33
+ RESPONSE_TYPE_PREPARE = "Prepare"
34
+ RESPONSE_TYPE_EXECUTE = "Execute"
35
+ RESPONSE_TYPE_MONITOR = "Monitor"
36
+ RESPONSE_TYPE_ASSESS = "Assess"
37
+ RESPONSE_TYPE_NONE = "None"
38
+ ALL_RESPONSE_TYPES = [ RESPONSE_TYPE_SHELTER, RESPONSE_TYPE_EVACUATE,
39
+ RESPONSE_TYPE_PREPARE, RESPONSE_TYPE_EXECUTE, RESPONSE_TYPE_MONITOR,
40
+ RESPONSE_TYPE_ASSESS, RESPONSE_TYPE_NONE ] # :nodoc:
41
+
42
+ URGENCY_IMMEDIATE = "Immediate"
43
+ URGENCY_EXPECTED = "Expected"
44
+ URGENCY_FUTURE = "Future"
45
+ URGENCY_PAST = "Past"
46
+ URGENCY_UNKNOWN = "Unknown"
47
+ ALL_URGENCIES = [ URGENCY_IMMEDIATE, URGENCY_EXPECTED, URGENCY_FUTURE,
48
+ URGENCY_PAST, URGENCY_UNKNOWN ] # :nodoc:
49
+
50
+ SEVERITY_EXTREME = "Extreme"
51
+ SEVERITY_SEVERE = "Severe"
52
+ SEVERITY_MODERATE = "Moderate"
53
+ SEVERITY_MINOR = "Minor"
54
+ SEVERITY_UNKNOWN = "Unknown"
55
+ ALL_SEVERITIES = [ SEVERITY_EXTREME, SEVERITY_SEVERE, SEVERITY_MODERATE,
56
+ SEVERITY_MINOR, SEVERITY_UNKNOWN ] # :nodoc:
57
+
58
+ CERTAINTY_OBSERVED = "Observed"
59
+ CERTAINTY_LIKELY = "Likely"
60
+ CERTAINTY_POSSIBLE = "Possible"
61
+ CERTAINTY_UNLIKELY = "Unlikely"
62
+ CERTAINTY_UNKNOWN = "Unknown"
63
+ ALL_CERTAINTIES = [ CERTAINTY_OBSERVED, CERTAINTY_LIKELY,
64
+ CERTAINTY_POSSIBLE, CERTAINTY_UNLIKELY, CERTAINTY_UNKNOWN ] # :nodoc:
65
+
66
+ XML_ELEMENT_NAME = 'info' # :nodoc:
67
+ LANGUAGE_ELEMENT_NAME = 'language' # :nodoc:
68
+ CATEGORY_ELEMENT_NAME = 'category' # :nodoc:
69
+ EVENT_ELEMENT_NAME = 'event' # :nodoc:
70
+ RESPONSE_TYPE_ELEMENT_NAME = 'responseType' # :nodoc:
71
+ URGENCY_ELEMENT_NAME = 'urgency' # :nodoc:
72
+ SEVERITY_ELEMENT_NAME = 'severity' # :nodoc:
73
+ CERTAINTY_ELEMENT_NAME = 'certainty' # :nodoc:
74
+ AUDIENCE_ELEMENT_NAME = 'audience' # :nodoc:
75
+ EVENT_CODE_ELEMENT_NAME = 'eventCode' # :nodoc:
76
+ EFFECTIVE_ELEMENT_NAME = 'effective' # :nodoc:
77
+ ONSET_ELEMENT_NAME = 'onset' # :nodoc:
78
+ EXPIRES_ELEMENT_NAME = 'expires' # :nodoc:
79
+ SENDER_NAME_ELEMENT_NAME = 'sernderName' # :nodoc:
80
+ HEADLINE_ELEMENT_NAME = 'headline' # :nodoc:
81
+ DESCRIPTION_ELEMENT_NAME = 'description' # :nodoc:
82
+ INSTRUCTION_ELEMENT_NAME = 'instruction' # :nodoc:
83
+ WEB_ELEMENT_NAME = 'web' # :nodoc:
84
+ CONTACT_ELEMENT_NAME = 'contact' # :nodoc:
85
+
86
+ XPATH = "cap:#{ XML_ELEMENT_NAME }" # :nodoc:
87
+ LANGUAGE_XPATH = "cap:#{ LANGUAGE_ELEMENT_NAME }" # :nodoc:
88
+ EVENT_XPATH = "cap:#{ EVENT_ELEMENT_NAME }" # :nodoc:
89
+ URGENCY_XPATH = "cap:#{ URGENCY_ELEMENT_NAME }" # :nodoc:
90
+ RESPONSE_TYPE_XPATH = "cap:#{ RESPONSE_TYPE_ELEMENT_NAME }" # :nodoc:
91
+ CATEGORY_XPATH = "cap:#{ CATEGORY_ELEMENT_NAME }" # :nodoc:
92
+ SEVERITY_XPATH = "cap:#{ SEVERITY_ELEMENT_NAME }" # :nodoc:
93
+ CERTAINTY_XPATH = "cap:#{ CERTAINTY_ELEMENT_NAME }" # :nodoc:
94
+ AUDIENCE_XPATH = "cap:#{ AUDIENCE_ELEMENT_NAME }" # :nodoc:
95
+ EVENT_CODE_XPATH = "cap:#{ EVENT_CODE_ELEMENT_NAME }" # :nodoc:
96
+ EFFECTIVE_XPATH = "cap:#{ EFFECTIVE_ELEMENT_NAME }" # :nodoc:
97
+ ONSET_XPATH = "cap:#{ ONSET_ELEMENT_NAME }" # :nodoc:
98
+ EXPIRES_XPATH = "cap:#{ EXPIRES_ELEMENT_NAME }" # :nodoc:
99
+ SENDER_NAME_XPATH = "cap:#{ SENDER_NAME_ELEMENT_NAME }" # :nodoc:
100
+ HEADLINE_XPATH = "cap:#{ HEADLINE_ELEMENT_NAME }" # :nodoc:
101
+ DESCRIPTION_XPATH = "cap:#{ DESCRIPTION_ELEMENT_NAME }" # :nodoc:
102
+ INSTRUCTION_XPATH = "cap:#{ INSTRUCTION_ELEMENT_NAME }" # :nodoc:
103
+ WEB_XPATH = "cap:#{ WEB_ELEMENT_NAME }" # :nodoc:
104
+ CONTACT_XPATH = "cap:#{ CONTACT_ELEMENT_NAME }" # :nodoc:
105
+
106
+ DEFAULT_LANGUAGE = 'en-US'
107
+
108
+ validates_presence_of( :event, :urgency, :severity, :certainty )
109
+ validates_length_of( :categories, :minimum => 1 )
110
+ validates_inclusion_of( :certainty, :allow_nil => true, :in => ALL_CERTAINTIES, :message => "can only be assigned the following values: #{ ALL_CERTAINTIES.join(', ') }")
111
+ validates_inclusion_of( :severity, :allow_nil => true, :in => ALL_SEVERITIES, :message => "can only be assigned the following values: #{ ALL_SEVERITIES.join(', ') }" )
112
+ validates_inclusion_of( :urgency, :allow_nil => true, :in => ALL_URGENCIES, :message => "can only be assigned the following values: #{ ALL_URGENCIES.join(', ') }" )
113
+ validates_inclusion_of_members_of( :response_types, :in => ALL_RESPONSE_TYPES, :allow_blank => true )
114
+ validates_inclusion_of_members_of( :categories, :in => ALL_CATEGORIES, :allow_blank => true )
115
+ validates_collection_of( :resources, :areas )
116
+
117
+ attr_accessor( :event )
118
+ attr_accessor( :urgency )
119
+ attr_accessor( :severity )
120
+ attr_accessor( :certainty )
121
+ attr_accessor( :language )
122
+ attr_accessor( :audience )
123
+ # Effective start time of information
124
+ attr_accessor( :effective )
125
+ # Expected start of event
126
+ attr_accessor( :onset )
127
+ # Effected expiry time of information
128
+ attr_accessor( :expires )
129
+ attr_accessor( :sender_name )
130
+ attr_accessor( :headline )
131
+ attr_accessor( :description )
132
+ attr_accessor( :instruction )
133
+ attr_accessor( :web )
134
+ attr_accessor( :contact )
135
+
136
+ # Collection of textual categories
137
+ attr_reader( :categories )
138
+ # Collection of textual response types
139
+ attr_reader( :response_types )
140
+ # Collectoin of EventCode objects
141
+ attr_reader( :event_codes )
142
+ # Collection of Parameter objects
143
+ attr_reader( :parameters )
144
+ # Collection of Resource objects
145
+ attr_reader( :resources )
146
+ # Collection of Area objects
147
+ attr_reader( :areas )
148
+
149
+ def initialize( attributes = {} )
150
+ @language = attributes[ :language ] || DEFAULT_LANGUAGE
151
+ @categories = Array( attributes[ :categories ])
152
+ @event = attributes [ :event ]
153
+ @response_types = Array( attributes[ :response_types ])
154
+ @urgency = attributes[ :urgency ]
155
+ @severity = attributes[ :severity ]
156
+ @certainty = attributes[ :certainty ]
157
+ @effective = attributes[ :effective ]
158
+ @onset = attributes[ :onset ]
159
+ @expires = attributes[ :expires ]
160
+ @event_codes = Array( attributes[ :event_codes ])
161
+ @sender_name = attributes[ :sender_name ]
162
+ @headline = attributes[ :headline ]
163
+ @description = attributes[ :description ]
164
+ @instruction = attributes[ :instruction ]
165
+ @web = attributes[ :web ]
166
+ @contact = attributes[ :contact ]
167
+ @parameters = Array( attributes[ :parameters ])
168
+ @resources = Array( attributes[ :resources ])
169
+ @areas = Array( attributes[ :areas ])
170
+ end
171
+
172
+ def to_xml_element # :nodoc:
173
+ xml_element = REXML::Element.new( XML_ELEMENT_NAME )
174
+ xml_element.add_element( LANGUAGE_ELEMENT_NAME ).add_text( self.language ) if self.language
175
+ @categories.each do |category|
176
+ xml_element.add_element( CATEGORY_ELEMENT_NAME ).add_text( category )
177
+ end
178
+ xml_element.add_element( EVENT_ELEMENT_NAME ).add_text( self.event )
179
+ @response_types.each do |response_type|
180
+ xml_element.add_element( RESPONSE_TYPE_ELEMENT_NAME ).add_text( response_type )
181
+ end
182
+ xml_element.add_element( URGENCY_ELEMENT_NAME ).add_text( self.urgency )
183
+ xml_element.add_element( SEVERITY_ELEMENT_NAME ).add_text( self.severity )
184
+ xml_element.add_element( CERTAINTY_ELEMENT_NAME ).add_text( self.certainty )
185
+ xml_element.add_element( AUDIENCE_ELEMENT_NAME ).add_text( self.audience ) if self.audience
186
+ @event_codes.each do |event_code|
187
+ xml_element.add_element( event_code.to_xml_element )
188
+ end
189
+ xml_element.add_element( EFFECTIVE_ELEMENT_NAME ).add_text( self.effective.to_s ) if self.effective
190
+ xml_element.add_element( ONSET_ELEMENT_NAME ).add_text( self.onset.to_s ) if self.onset
191
+ xml_element.add_element( EXPIRES_ELEMENT_NAME ).add_text( self.expires.to_s ) if self.expires
192
+ xml_element.add_element( SENDER_NAME_ELEMENT_NAME ).add_text( self.sender_name ) if self.sender_name
193
+ xml_element.add_element( HEADLINE_ELEMENT_NAME ).add_text( self.headline ) if self.headline
194
+ xml_element.add_element( DESCRIPTION_ELEMENT_NAME ).add_text( self.description ) if self.description
195
+ xml_element.add_element( INSTRUCTION_ELEMENT_NAME ).add_text( self.instruction ) if self.instruction
196
+ xml_element.add_element( WEB_ELEMENT_NAME ).add_text( self.web ) if self.web
197
+ xml_element.add_element( CONTACT_ELEMENT_NAME ).add_text( self.contact ) if self.contact
198
+ @parameters.each do |parameter|
199
+ xml_element.add_element( parameter.to_xml_element )
200
+ end
201
+ @resources.each do |resource|
202
+ xml_element.add_element( resource.to_xml_element )
203
+ end
204
+ @areas.each do |area|
205
+ xml_element.add_element( area.to_xml_element )
206
+ end
207
+ xml_element
208
+ end
209
+
210
+ def to_xml # :nodoc:
211
+ self.to_xml_element.to_s
212
+ end
213
+
214
+ def self.from_xml_element( info_xml_element ) # :nodoc:
215
+ self.new(
216
+ :language => RCAP.xpath_text( info_xml_element, LANGUAGE_XPATH ) || DEFAULT_LANGUAGE,
217
+ :categories => RCAP.xpath_match( info_xml_element, CATEGORY_XPATH ).map{ |element| element.text },
218
+ :event => RCAP.xpath_text( info_xml_element, EVENT_XPATH ),
219
+ :response_types => RCAP.xpath_match( info_xml_element, RESPONSE_TYPE_XPATH ).map{ |element| element.text },
220
+ :urgency => RCAP.xpath_text( info_xml_element, URGENCY_XPATH ),
221
+ :severity => RCAP.xpath_text( info_xml_element, SEVERITY_XPATH ),
222
+ :certainty => RCAP.xpath_text( info_xml_element, CERTAINTY_XPATH ),
223
+ :audience => RCAP.xpath_text( info_xml_element, AUDIENCE_XPATH ),
224
+ :effective => (( effective = RCAP.xpath_first( info_xml_element, EFFECTIVE_XPATH )) ? DateTime.parse( effective.text ) : nil ),
225
+ :onset => (( onset = RCAP.xpath_first( info_xml_element, ONSET_XPATH )) ? DateTime.parse( onset.text ) : nil ),
226
+ :expires => (( expires = RCAP.xpath_first( info_xml_element, EXPIRES_XPATH )) ? DateTime.parse( expires.text ) : nil ),
227
+ :sender_name => RCAP.xpath_text( info_xml_element, SENDER_NAME_XPATH ),
228
+ :headline => RCAP.xpath_text( info_xml_element, HEADLINE_XPATH ),
229
+ :description => RCAP.xpath_text( info_xml_element, DESCRIPTION_XPATH ),
230
+ :instruction => RCAP.xpath_text( info_xml_element, INSTRUCTION_XPATH ),
231
+ :web => RCAP.xpath_text( info_xml_element, WEB_XPATH ),
232
+ :contact => RCAP.xpath_text( info_xml_element, CONTACT_XPATH ),
233
+ :event_codes => RCAP.xpath_match( info_xml_element, RCAP::EventCode::XPATH ).map{ |element| RCAP::EventCode.from_xml_element( element )},
234
+ :parameters => RCAP.xpath_match( info_xml_element, RCAP::Parameter::XPATH ).map{ |element| RCAP::Parameter.from_xml_element( element )},
235
+ :areas => RCAP.xpath_match( info_xml_element, RCAP::Area::XPATH ).map{ |element| RCAP::Area.from_xml_element( element )}
236
+ )
237
+ end
238
+ end
239
+ end
@@ -0,0 +1,54 @@
1
+ module RCAP
2
+ # A Parameter object is valid if
3
+ # * it has a name
4
+ # * it has a value
5
+ class Parameter
6
+ include Validation
7
+
8
+ validates_presence_of( :name, :value )
9
+
10
+ attr_accessor( :name, :value )
11
+
12
+ XML_ELEMENT_NAME = "parameter" # :nodoc:
13
+ NAME_ELEMENT_NAME = "valueName" # :nodoc:
14
+ VALUE_ELEMENT_NAME = "value" # :nodoc:
15
+
16
+ XPATH = "cap:#{ XML_ELEMENT_NAME }" # :nodoc:
17
+ NAME_XPATH = "cap:#{ NAME_ELEMENT_NAME }" # :nodoc:
18
+ VALUE_XPATH = "cap:#{ VALUE_ELEMENT_NAME }" # :nodoc:
19
+
20
+ def initialize( attributes = {} )
21
+ @name = attributes[ :name ]
22
+ @value = attributes[ :value ]
23
+ end
24
+
25
+ def to_xml_element # :nodoc:
26
+ xml_element = REXML::Element.new( XML_ELEMENT_NAME )
27
+ xml_element.add_element( NAME_ELEMENT_NAME ).add_text( self.name )
28
+ xml_element.add_element( VALUE_ELEMENT_NAME ).add_text( self.value )
29
+ xml_element
30
+ end
31
+
32
+ def to_xml # :nodoc:
33
+ self.to_xml_element.to_s
34
+ end
35
+
36
+ def inspect # :nodoc:
37
+ "#{ self.name }: #{ self.value }"
38
+ end
39
+
40
+ def to_s # :nodoc:
41
+ self.inspect
42
+ end
43
+
44
+ def self.from_xml_element( parameter_xml_element ) # :nodoc:
45
+ Parameter.new( :name => RCAP.xpath_text( parameter_xml_element, NAME_XPATH ),
46
+ :value => RCAP.xpath_text( parameter_xml_element, VALUE_XPATH ))
47
+ end
48
+
49
+ # Two parameters are equivalent if they have the same name and value.
50
+ def ==( other )
51
+ [ self.name, self.value ] == [ other.name, other.value ]
52
+ end
53
+ end
54
+ end
data/lib/rcap/point.rb ADDED
@@ -0,0 +1,39 @@
1
+ module RCAP
2
+ # A Point object is valid if
3
+ # * it has a lattitude within the minimum and maximum lattitude values
4
+ # * it has a longitude within the minimum and maximum longitude values
5
+ class Point
6
+ include Validation
7
+
8
+ MAX_LONGITUDE = 180
9
+ MIN_LONGITUDE = -180
10
+ MAX_LATTITUDE = 90
11
+ MIN_LATTITUDE= -90
12
+
13
+ attr_accessor( :lattitude )
14
+ attr_accessor( :longitude )
15
+
16
+ validates_numericality_of( :lattitude, :longitude )
17
+ validates_inclusion_of( :lattitude, :in => MIN_LATTITUDE..MAX_LATTITUDE )
18
+ validates_inclusion_of( :longitude, :in => MIN_LONGITUDE..MAX_LONGITUDE)
19
+
20
+ def initialize( attributes = {} )
21
+ @lattitude = attributes[ :lattitude ]
22
+ @longitude = attributes[ :longitude ]
23
+ end
24
+
25
+ def to_s # :nodoc:
26
+ "#{ self.lattitude },#{ self.longitude }"
27
+ end
28
+
29
+ def inspect # :nodoc:
30
+ '('+self.to_s+')'
31
+ end
32
+
33
+ # Two points are equivalent if they have the same lattitude and longitude
34
+ def ==( other )
35
+ self.lattitude == other.lattitude &&
36
+ self.longitude == other.longitude
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,50 @@
1
+ module RCAP
2
+ # A Polygon object is valid if
3
+ # * it has a minimum of three points
4
+ # * each Point object in the points collection is valid
5
+ class Polygon
6
+ include Validation
7
+
8
+ # Collection of Point objects.
9
+ attr_reader( :points )
10
+
11
+ validates_length_of( :points, :minimum => 3 )
12
+ validates_collection_of( :points )
13
+
14
+ XML_ELEMENT_NAME = 'polygon' # :nodoc:
15
+ XPATH = "cap:#{ XML_ELEMENT_NAME }" # :nodoc:
16
+
17
+ def initialize( attributes = {})
18
+ @points = Array( attributes[ :points ])
19
+ end
20
+
21
+ def to_s # :nodoc:
22
+ (@points.map{ |point| point.to_s } + [ @points.first ]).join( ' ' )
23
+ end
24
+
25
+ def inspect # :nodoc:
26
+ "(#{ @points.map{|point| point.inspect}.join(', ')})"
27
+ end
28
+
29
+ def to_xml_element # :nodoc:
30
+ xml_element = REXML::Element.new( XML_ELEMENT_NAME )
31
+ xml_element.add_text( self.to_s )
32
+ xml_element
33
+ end
34
+
35
+ # Two polygons are equivalent if their collection of points is equivalent.
36
+ def ==( other )
37
+ self.points == other.points
38
+ end
39
+
40
+ def self.parse_polygon_string( polygon_string ) # :nodoc:
41
+ polygon_string.split( ' ' ).map{ |coordinate_string| coordinate_string.split( ',' ).map{|coordinate| coordinate.to_f }}
42
+ end
43
+
44
+ def self.from_xml_element( polygon_xml_element ) # :nodoc:
45
+ coordinates = self.parse_polygon_string( polygon_xml_element.text )
46
+ points = coordinates.map{ |lattitude, longitude| RCAP::Point.new( :lattitude => lattitude, :longitude => longitude )}[0..-2]
47
+ polygon = self.new( :points => points )
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,81 @@
1
+ module RCAP
2
+ # A Resourse object is valid if
3
+ # * it has a resource description
4
+ class Resource
5
+ include Validation
6
+
7
+ # Resource Description
8
+ attr_accessor( :resource_desc )
9
+ attr_accessor( :mime_type )
10
+ # Expressed in bytes
11
+ attr_accessor( :size )
12
+ # Resource location
13
+ attr_accessor( :uri )
14
+ # Dereferenced URI - contents of URI Base64 encoded
15
+ attr_accessor( :deref_uri )
16
+ # SHA-1 hash of contents of resource
17
+ attr_accessor( :digest )
18
+
19
+ validates_presence_of( :resource_desc )
20
+
21
+ XML_ELEMENT_NAME = 'resource' # :nodoc:
22
+ MIME_TYPE_ELEMENT_NAME = 'mimeType' # :nodoc:
23
+ SIZE_ELEMENT_NAME = 'size' # :nodoc:
24
+ URI_ELEMENT_NAME = 'uri' # :nodoc:
25
+ DEREF_URI_ELEMENT_NAME = 'derefUri' # :nodoc:
26
+ DIGEST_ELEMENT_NAME = 'digest' # :nodoc:
27
+ RESOURCE_DESC_ELEMENT_NAME = 'resourceDesc' # :nodoc:
28
+
29
+ XPATH = "cap:#{ XML_ELEMENT_NAME }" # :nodoc:
30
+ MIME_TYPE_XPATH = "cap:#{ MIME_TYPE_ELEMENT_NAME }" # :nodoc:
31
+ SIZE_XPATH = "cap:#{ SIZE_ELEMENT_NAME }" # :nodoc:
32
+ URI_XPATH = "cap:#{ URI_ELEMENT_NAME }" # :nodoc:
33
+ DEREF_URI_XPATH = "cap:#{ DEREF_URI_ELEMENT_NAME }" # :nodoc:
34
+ DIGEST_XPATH = "cap:#{ DIGEST_ELEMENT_NAME }" # :nodoc:
35
+ RESOURCE_DESC_XPATH = "cap:#{ RESOURCE_DESC_ELEMENT_NAME }" # :nodoc:
36
+
37
+ def initialize( attributes = {} )
38
+ @mime_type = attributes[ :mime_type ]
39
+ @size = attributes[ :size ]
40
+ @uri = attributes[ :uri ]
41
+ @deref_uri = attributes[ :deref_uri ]
42
+ @digest = attributes[ :digest ]
43
+ @resource_desc = attributes[ :resource_desc ]
44
+ end
45
+
46
+ def to_xml_element # :nodoc:
47
+ xml_element = REXML::Element.new( XML_ELEMENT_NAME )
48
+ xml_element.add_element( RESOURCE_DESC_ELEMENT_NAME ).add_text( self.resource_desc )
49
+ xml_element.add_element( MIME_TYPE_ELEMENT_NAME ).add_text( self.mime_type ) if self.mime_type
50
+ xml_element.add_element( SIZE_ELEMENT_NAME ).add_text( self.size ) if self.size
51
+ xml_element.add_element( URI_ELEMENT_NAME ).add_text( self.uri ) if self.uri
52
+ xml_element.add_element( DEREF_URI_ELEMENT_NAME ).add_text( self.deref_uri ) if self.deref_uri
53
+ xml_element.add_element( DIGEST_ELEMENT_NAME ).add_text( self.digest ) if self.digest
54
+ xml_element
55
+ end
56
+
57
+ # If size is defined returns the size in kilobytes
58
+ def size_in_kb
59
+ if self.size
60
+ self.size.to_f/1024
61
+ end
62
+ end
63
+
64
+ def to_xml # :nodoc:
65
+ self.to_xml_element.to_s
66
+ end
67
+
68
+ def inspect # :nodoc:
69
+ [ self.resource_desc, self.uri, self.mime_type, self.size ? format( "%.1fKB", self.size_in_kb ) : nil ].compact.join(' - ')
70
+ end
71
+
72
+ def self.from_xml_element( resource_xml_element ) # :nodoc:
73
+ resource = self.new( :resource_desc => RCAP.xpath_text( resource_xml_element, RESOURCE_DESC_XPATH ),
74
+ :uri => RCAP.xpath_text( resource_xml_element, URI_XPATH ),
75
+ :mime_type => RCAP.xpath_text( resource_xml_element, MIME_TYPE_XPATH ),
76
+ :deref_uri => RCAP.xpath_text( resource_xml_element, DEREF_URI_XPATH ),
77
+ :size => RCAP.xpath_text( resource_xml_element, SIZE_XPATH ),
78
+ :digest => RCAP.xpath_text( resource_xml_element, DIGEST_XPATH ))
79
+ end
80
+ end
81
+ end