rcap 0.1 → 0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +11 -0
- data/{README → README.rdoc} +85 -44
- data/lib/rcap/alert.rb +134 -41
- data/lib/rcap/area.rb +56 -8
- data/lib/rcap/circle.rb +8 -0
- data/lib/rcap/geocode.rb +2 -2
- data/lib/rcap/info.rb +195 -67
- data/lib/rcap/parameter.rb +4 -2
- data/lib/rcap/point.rb +4 -3
- data/lib/rcap/polygon.rb +18 -1
- data/lib/rcap/resource.rb +40 -5
- data/lib/rcap/utilities.rb +17 -1
- data/lib/rcap/validations.rb +3 -3
- data/lib/rcap.rb +1 -1
- data/spec/alert_spec.rb +41 -17
- metadata +6 -6
- data/CHANGELOG +0 -2
data/CHANGELOG.rdoc
ADDED
data/{README → README.rdoc}
RENAMED
@@ -4,29 +4,49 @@
|
|
4
4
|
|
5
5
|
The Common Alerting Protocol is a lightweight standard to facilitate the distribution of alerting data. RCAP is an implementation of the CAP in Ruby. It allows for the creation of RCAP messages from Ruby applications and the parsing of external messages.
|
6
6
|
|
7
|
-
RCAP supports CAP Version 1.1.
|
7
|
+
RCAP currently supports only CAP Version 1.1.
|
8
8
|
|
9
|
-
==
|
9
|
+
== Version
|
10
10
|
|
11
|
-
|
11
|
+
0.2
|
12
12
|
|
13
|
-
|
13
|
+
== Dependencies
|
14
14
|
|
15
|
-
|
15
|
+
RCAP depends on the following gems
|
16
16
|
|
17
|
-
|
17
|
+
* {Assistance}[http://assistance.rubyforge.org]
|
18
|
+
* {UUIDTools}[http://uuidtools.rubyforge.org]
|
18
19
|
|
19
|
-
RCAP
|
20
|
+
RCAP uses the REXML API, included in Ruby, to parse and generate XML.
|
20
21
|
|
21
|
-
|
22
|
+
== Installation
|
22
23
|
|
23
|
-
|
24
|
+
RCAP is distributed as a Ruby gem and is available from {Gemcutter}[http://gemcutter.org]. If you have Gemcutter set as a source of your gems then RCAP can be installed from the command line
|
25
|
+
|
26
|
+
gem install rcap
|
27
|
+
|
28
|
+
The gem is also available for download and manual installtion at http://www.aimred.com/gems .
|
24
29
|
|
25
|
-
|
30
|
+
== Web resources
|
26
31
|
|
27
|
-
|
32
|
+
* The RCAP project page can be found at http://www.aimred.com/projects/rcap
|
33
|
+
* The RCAP API docs can be fount at http://www.aimred.com/projects/rcap/api
|
34
|
+
* A public git repository can be found at http:///www.aimred.com/git/rcap.git
|
35
|
+
|
36
|
+
== Usage
|
37
|
+
|
38
|
+
To include RCAP into your application add the following require
|
39
|
+
|
40
|
+
require 'rcap'
|
41
|
+
|
42
|
+
All RCAP classes reside in the RCAP namespace but including the RCAP module makes the classes available at the top level without the RCAP prefix.
|
28
43
|
|
44
|
+
alert = RCAP::Alert.new(...
|
45
|
+
|
29
46
|
include RCAP # Include RCAP module into namespace
|
47
|
+
alert = Alert.new(...
|
48
|
+
|
49
|
+
=== Creating an Alert
|
30
50
|
|
31
51
|
alert = Alert.new( :sender => 'cape_town_disaster_relief@capetown.municipal.za',
|
32
52
|
:status => Alert::STATUS_ACTUAL,
|
@@ -49,6 +69,12 @@ Alerts can also be created programatically by initialising the various RCAP clas
|
|
49
69
|
puts alert.infos[0].language # Print out "en-ZA"
|
50
70
|
puts alert.infos[0].categories.join( ' ' ) # Print out "Transport Fire"
|
51
71
|
|
72
|
+
=== Exporting an Alert
|
73
|
+
|
74
|
+
==== To XML
|
75
|
+
|
76
|
+
Using the alert message created above
|
77
|
+
|
52
78
|
puts alert.to_xml # Print out CAP XML message
|
53
79
|
|
54
80
|
Will print the following CAP XML
|
@@ -57,7 +83,7 @@ Will print the following CAP XML
|
|
57
83
|
<alert xmlns='urn:oasis:names:tc:emergency:cap:1.1'>
|
58
84
|
<identifier>494207a7-f86b-4060-8318-a4b2a3ce565e</identifier>
|
59
85
|
<sender>cape_town_disaster_relief@capetown.municipal.za</sender>
|
60
|
-
<sent>2009-10-26T21:04:51+
|
86
|
+
<sent>2009-10-26T21:04:51+02:00</sent>
|
61
87
|
<status>Actual</status>
|
62
88
|
<msgType>Alert</msgType>
|
63
89
|
<scope>Public</scope>
|
@@ -79,36 +105,56 @@ Will print the following CAP XML
|
|
79
105
|
</info>
|
80
106
|
</alert>
|
81
107
|
|
108
|
+
==== To YAML
|
82
109
|
|
83
|
-
|
110
|
+
YAML is a plain text serialization format designed to be easily readable and editable by both human and machine. RCAP has custom YAML generation and parsing methods to produce a YAML document that is as human friednly as possible. The following code
|
84
111
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
:msg_type => Alert::MSG_TYPE_ALERT,
|
89
|
-
:scope => Alert::SCOPE_PUBLIC )
|
90
|
-
|
91
|
-
# Setting attributes after initialisation
|
92
|
-
info = Info.new
|
93
|
-
info.event = 'Liquid Petroleoum Tanker Fire'
|
94
|
-
info.language = 'en-ZA'
|
95
|
-
info.categories = [ Info::CATEGORY_TRANSPORT, Info::CATEGORY_FIRE ]
|
96
|
-
info.urgency = Info::URGENCY_IMMEDIATE
|
97
|
-
info.severity = Info::SEVERITY_SEVERE
|
98
|
-
info.certainty = Info::CERTAINTY_OBSERVED
|
112
|
+
alert.to_yaml
|
113
|
+
|
114
|
+
will produce the following YAML document
|
99
115
|
|
116
|
+
---
|
117
|
+
Identifier: 2a1ba96d-16e4-4f52-85ea-0258c1440bd5
|
118
|
+
Sender: cape_town_disaster_relief@capetown.municipal.za
|
119
|
+
Sent: 2009-11-19T02:41:29+02:00
|
120
|
+
Status: Actual
|
121
|
+
Message Type: Alert
|
122
|
+
Scope: Public
|
123
|
+
Information:
|
124
|
+
- Language: en-ZA
|
125
|
+
Categories: [Transport, Fire]
|
126
|
+
Event: Liquid Petroleoum Tanker Fire
|
127
|
+
Urgency: Immediate
|
128
|
+
Severity: Severe
|
129
|
+
Certainty: Observed
|
130
|
+
Headline: LIQUID PETROLEOUM TANKER FIRE ON N2 INCOMING FREEWAY
|
131
|
+
Description: |-
|
132
|
+
A liquid petroleoum tanker has caught fire on the N2 incoming freeway 1km
|
133
|
+
after the R300 interchange. Municipal fire fighting crews have been dispatched.
|
134
|
+
Traffic control officers are on the scene and have diverted traffic onto
|
135
|
+
alternate routes.
|
100
136
|
|
101
|
-
|
102
|
-
:uri => 'http://capetown.municipal.za/traffic/management_guidelines.pdf',
|
103
|
-
:mime_type => 'application/pdf')
|
137
|
+
Note: If you use Ruby 1.8 the order of the attributes is jumbled due to hashes being unorderd (Ruby 1.9 implements ordered hashes). This does not affect the ability to parse documents generated from RCAP::Alert#to_yaml, it just makes things the output slightly messy.
|
104
138
|
|
105
|
-
|
106
|
-
|
107
|
-
|
139
|
+
=== Parsing an Alert From An External Source
|
140
|
+
|
141
|
+
==== From XML
|
142
|
+
|
143
|
+
RCAP allows for the parsing of a CAP XML string
|
144
|
+
|
145
|
+
alert = RCAP::Alert.from_xml( xml_string )
|
146
|
+
|
147
|
+
Currently RCAP only supports version 1.1 of the CAP standard and the parser is as strict as possible when parsing data.
|
148
|
+
|
149
|
+
==== From YAML
|
150
|
+
|
151
|
+
Alert messgaes can be read in from text files containing data formatted in YAML as generated by Alert#to_yaml.
|
152
|
+
|
153
|
+
alert = RCAP::Alert.from_yaml( yaml_string )
|
108
154
|
|
109
155
|
=== Validating an alert
|
110
156
|
|
111
|
-
The RCAP API aims to codify as many of the rules of the CAP XML format into validation rules that can be checked using the Assistance API
|
157
|
+
The RCAP API aims to codify as many of the rules of the CAP XML format into validation rules that can be checked using the Assistance API. The following Info object has two attributes ('severity' and 'certainty') set to incorrect values.
|
112
158
|
|
113
159
|
info = Info.new( :event => 'Liquid Petroleoum Tanker Fire',
|
114
160
|
:language => 'en-ZA',
|
@@ -125,19 +171,14 @@ Will produce the folling output:
|
|
125
171
|
severity is not present
|
126
172
|
certainty can only be assigned the following values: Observed, Likely, Possible, Unlikely, Unknown
|
127
173
|
|
128
|
-
|
174
|
+
All RCAP classes include the Validation module.
|
129
175
|
|
130
|
-
|
176
|
+
A full spec suite using {RSpec}[http://www.rspec.info] was used to test the validations and currently numbers over 150 tests.
|
131
177
|
|
132
|
-
|
178
|
+
=== DateTime and Time
|
133
179
|
|
134
|
-
|
180
|
+
It is highly recommended that when dealing with date and time fields (onset, expires etc) that the DateTime class is used to ensure the correct formatting of dates. The Time class can be used when generating a CAP alert XML message however any CAP alert that is parsed from an external XML source will use DateTime by default.
|
135
181
|
|
136
|
-
RCAP depends on the following gems
|
137
|
-
|
138
|
-
* Assistance - http://assistance.rubyforge.org
|
139
|
-
* UUIDTools - http://uuidtools.rubyforge.org
|
140
|
-
|
141
182
|
== Authors
|
142
183
|
|
143
184
|
Farrel Lifson - farrel.lifson@aimred.com
|
@@ -148,4 +189,4 @@ RCAP is released under the BSD License.
|
|
148
189
|
|
149
190
|
== Copyright
|
150
191
|
|
151
|
-
2009 Aimred CC
|
192
|
+
2009-2010 Aimred CC
|
data/lib/rcap/alert.rb
CHANGED
@@ -10,24 +10,27 @@ module RCAP
|
|
10
10
|
class Alert
|
11
11
|
include Validation
|
12
12
|
|
13
|
-
STATUS_ACTUAL = "Actual"
|
14
|
-
STATUS_EXERCISE = "Exercise"
|
15
|
-
STATUS_SYSTEM = "System"
|
16
|
-
STATUS_TEST = "Test"
|
17
|
-
STATUS_DRAFT = "Draft"
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
13
|
+
STATUS_ACTUAL = "Actual" # :nodoc:
|
14
|
+
STATUS_EXERCISE = "Exercise" # :nodoc:
|
15
|
+
STATUS_SYSTEM = "System" # :nodoc:
|
16
|
+
STATUS_TEST = "Test" # :nodoc:
|
17
|
+
STATUS_DRAFT = "Draft" # :nodoc:
|
18
|
+
# Valid values for status
|
19
|
+
VALID_STATUSES = [ STATUS_ACTUAL, STATUS_EXERCISE, STATUS_SYSTEM, STATUS_TEST, STATUS_DRAFT ]
|
20
|
+
|
21
|
+
MSG_TYPE_ALERT = "Alert" # :nodoc:
|
22
|
+
MSG_TYPE_UPDATE = "Update" # :nodoc:
|
23
|
+
MSG_TYPE_CANCEL = "Cancel" # :nodoc:
|
24
|
+
MSG_TYPE_ACK = "Ack" # :nodoc:
|
25
|
+
MSG_TYPE_ERROR = "Error" # :nodoc:
|
26
|
+
# Valid values for msg_type
|
27
|
+
VALID_MSG_TYPES = [ MSG_TYPE_ALERT, MSG_TYPE_UPDATE, MSG_TYPE_CANCEL, MSG_TYPE_ACK, MSG_TYPE_ERROR ]
|
28
|
+
|
29
|
+
SCOPE_PUBLIC = "Public" # :nodoc:
|
30
|
+
SCOPE_RESTRICTED = "Restricted" # :nodoc:
|
31
|
+
SCOPE_PRIVATE = "Private" # :nodoc:
|
32
|
+
# Valid values for scope
|
33
|
+
VALID_SCOPES = [ SCOPE_PUBLIC, SCOPE_PRIVATE, SCOPE_RESTRICTED ]
|
31
34
|
|
32
35
|
XML_ELEMENT_NAME = 'alert' # :nodoc:
|
33
36
|
IDENTIFIER_ELEMENT_NAME = 'identifier' # :nodoc:
|
@@ -64,9 +67,11 @@ module RCAP
|
|
64
67
|
attr_accessor( :sender )
|
65
68
|
# Sent Time - If not set will value will be time of creation.
|
66
69
|
attr_accessor( :sent )
|
70
|
+
# Value can only be one of VALID_STATUSES
|
67
71
|
attr_accessor( :status )
|
68
|
-
#
|
72
|
+
# Value can only be one of VALID_MSG_TYPES
|
69
73
|
attr_accessor( :msg_type )
|
74
|
+
# Value can only be one of VALID_SCOPES
|
70
75
|
attr_accessor( :scope )
|
71
76
|
attr_accessor( :source )
|
72
77
|
# Depends on scope being SCOPE_RESTRICTED.
|
@@ -85,9 +90,9 @@ module RCAP
|
|
85
90
|
|
86
91
|
validates_presence_of( :identifier, :sender, :sent, :status, :msg_type, :scope )
|
87
92
|
|
88
|
-
validates_inclusion_of( :status, :in =>
|
89
|
-
validates_inclusion_of( :msg_type, :in =>
|
90
|
-
validates_inclusion_of( :scope, :in =>
|
93
|
+
validates_inclusion_of( :status, :in => VALID_STATUSES )
|
94
|
+
validates_inclusion_of( :msg_type, :in => VALID_MSG_TYPES )
|
95
|
+
validates_inclusion_of( :scope, :in => VALID_SCOPES )
|
91
96
|
|
92
97
|
validates_format_of( :identifier, :with => ALLOWED_CHARACTERS )
|
93
98
|
validates_format_of( :sender , :with => ALLOWED_CHARACTERS )
|
@@ -152,35 +157,123 @@ module RCAP
|
|
152
157
|
self.to_xml_document.to_s
|
153
158
|
end
|
154
159
|
|
155
|
-
# Returns a string of the
|
160
|
+
# Returns a string representation of the alert suitable for usage as a reference in a CAP message of the form
|
161
|
+
# sender,identifier,sent
|
156
162
|
def to_reference
|
157
163
|
"#{ self.sender },#{ self.identifier },#{ self.sent }"
|
158
164
|
end
|
159
165
|
|
166
|
+
def inspect # :nodoc:
|
167
|
+
alert_inspect = <<EOF
|
168
|
+
Identifier: #{ self.identifier }
|
169
|
+
Sender: #{ self.sender }
|
170
|
+
Sent: #{ self.sent }
|
171
|
+
Status: #{ self.status }
|
172
|
+
Message Type: #{ self.msg_type }
|
173
|
+
Source: #{ self.source }
|
174
|
+
Scope: #{ self.scope }
|
175
|
+
Restriction: #{ self.restriction }
|
176
|
+
Addresses: #{ self.addresses.to_s_for_cap }
|
177
|
+
Code: #{ self.code }
|
178
|
+
Note: #{ self.note }
|
179
|
+
References: #{ self.references.join( ' ' )}
|
180
|
+
Incidents: #{ self.incidents.join( ' ')}
|
181
|
+
Information:
|
182
|
+
#{ self.infos.map{ |info| " " + info.to_s }.join( "\n" )}
|
183
|
+
EOF
|
184
|
+
RCAP.format_lines_for_inspect( 'ALERT', alert_inspect )
|
185
|
+
end
|
186
|
+
|
187
|
+
# Returns a string representation of the alert of the form
|
188
|
+
# sender/identifier/sent
|
189
|
+
# See Alert#to_reference for another string representation suitable as a CAP reference.
|
190
|
+
def to_s
|
191
|
+
"#{ self.sender }/#{ self.identifier }/#{ self.sent }"
|
192
|
+
end
|
193
|
+
|
160
194
|
def self.from_xml_element( alert_xml_element ) # :nodoc:
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
195
|
+
RCAP::Alert.new( :identifier => RCAP.xpath_text( alert_xml_element, RCAP::Alert::IDENTIFIER_XPATH ),
|
196
|
+
:sender => RCAP.xpath_text( alert_xml_element, SENDER_XPATH ),
|
197
|
+
:sent => (( sent = RCAP.xpath_first( alert_xml_element, SENT_XPATH )) ? DateTime.parse( sent.text ) : nil ),
|
198
|
+
:status => RCAP.xpath_text( alert_xml_element, STATUS_XPATH ),
|
199
|
+
:msg_type => RCAP.xpath_text( alert_xml_element, MSG_TYPE_XPATH ),
|
200
|
+
:source => RCAP.xpath_text( alert_xml_element, SOURCE_XPATH ),
|
201
|
+
:scope => RCAP.xpath_text( alert_xml_element, SCOPE_XPATH ),
|
202
|
+
:restriction => RCAP.xpath_text( alert_xml_element, RESTRICTION_XPATH ),
|
203
|
+
:addresses => (( address = RCAP.xpath_text( alert_xml_element, ADDRESSES_XPATH )) ? address.unpack_cap_list : nil ),
|
204
|
+
:code => RCAP.xpath_text( alert_xml_element, CODE_XPATH ),
|
205
|
+
:note => RCAP.xpath_text( alert_xml_element, NOTE_XPATH ),
|
206
|
+
:references => (( references = RCAP.xpath_text( alert_xml_element, REFERENCES_XPATH )) ? references.split( ' ' ) : nil ),
|
207
|
+
:incidents => (( incidents = RCAP.xpath_text( alert_xml_element, INCIDENTS_XPATH )) ? incidents.split( ' ' ) : nil ),
|
208
|
+
:infos => RCAP.xpath_match( alert_xml_element, RCAP::Info::XPATH ).map{ |element| RCAP::Info.from_xml_element( element )})
|
175
209
|
end
|
176
210
|
|
177
211
|
def self.from_xml_document( xml_document ) # :nodoc:
|
178
212
|
self.from_xml_element( xml_document.root )
|
179
213
|
end
|
180
214
|
|
181
|
-
#
|
182
|
-
def self.from_xml(
|
183
|
-
self.from_xml_document( REXML::Document.new(
|
215
|
+
# Initialise an Alert object from an XML string. Any object that is a subclass of IO (e.g. File) can be passed in.
|
216
|
+
def self.from_xml( xml )
|
217
|
+
self.from_xml_document( REXML::Document.new( xml ))
|
184
218
|
end
|
185
|
-
|
219
|
+
|
220
|
+
IDENTIFIER_YAML = "Identifier" # :nodoc:
|
221
|
+
SENDER_YAML = "Sender" # :nodoc:
|
222
|
+
SENT_YAML = "Sent" # :nodoc:
|
223
|
+
STATUS_YAML = "Status" # :nodoc:
|
224
|
+
MSG_TYPE_YAML = "Message Type" # :nodoc:
|
225
|
+
SOURCE_YAML = "Source" # :nodoc:
|
226
|
+
SCOPE_YAML = "Scope" # :nodoc:
|
227
|
+
RESTRICTION_YAML = "Restriction" # :nodoc:
|
228
|
+
ADDRESSES_YAML = "Addresses" # :nodoc:
|
229
|
+
CODE_YAML = "Code" # :nodoc:
|
230
|
+
NOTE_YAML = "Note" # :nodoc:
|
231
|
+
REFERENCES_YAML = "References" # :nodoc:
|
232
|
+
INCIDENTS_YAML = "Incidents" # :nodoc:
|
233
|
+
INFOS_YAML = "Information" # :nodoc:
|
234
|
+
|
235
|
+
# Returns a string containing the YAML representation of the alert.
|
236
|
+
def to_yaml( options = {} )
|
237
|
+
RCAP.attribute_values_to_yaml_hash(
|
238
|
+
[ IDENTIFIER_YAML, self.identifier ],
|
239
|
+
[ SENDER_YAML, self.sender ],
|
240
|
+
[ SENT_YAML, self.sent ],
|
241
|
+
[ STATUS_YAML, self.status ],
|
242
|
+
[ MSG_TYPE_YAML, self.msg_type ],
|
243
|
+
[ SOURCE_YAML, self.source ],
|
244
|
+
[ SCOPE_YAML, self.scope ],
|
245
|
+
[ RESTRICTION_YAML, self.restriction ],
|
246
|
+
[ ADDRESSES_YAML, self.addresses ],
|
247
|
+
[ CODE_YAML, self.code ],
|
248
|
+
[ NOTE_YAML, self.note ],
|
249
|
+
[ REFERENCES_YAML, self.references ],
|
250
|
+
[ INCIDENTS_YAML, self.incidents ],
|
251
|
+
[ INFOS_YAML, self.infos ]
|
252
|
+
).to_yaml( options )
|
253
|
+
end
|
254
|
+
|
255
|
+
# Initialise an Alert object from a YAML string. Any object that is a subclass of IO (e.g. File) can be passed in.
|
256
|
+
def self.from_yaml( yaml )
|
257
|
+
self.from_yaml_data( YAML.load( yaml ))
|
258
|
+
end
|
259
|
+
|
260
|
+
def self.from_yaml_data( alert_yaml_data ) # :nodoc:
|
261
|
+
Alert.new(
|
262
|
+
:identifier => alert_yaml_data[ IDENTIFIER_YAML ],
|
263
|
+
:sender => alert_yaml_data[ SENDER_YAML ],
|
264
|
+
:sent => ( sent = alert_yaml_data[ SENT_YAML ]).blank? ? nil : DateTime.parse( sent.to_s ),
|
265
|
+
:status => alert_yaml_data[ STATUS_YAML ],
|
266
|
+
:msg_type => alert_yaml_data[ MSG_TYPE_YAML ],
|
267
|
+
:source => alert_yaml_data[ SOURCE_YAML ],
|
268
|
+
:scope => alert_yaml_data[ SCOPE_YAML ],
|
269
|
+
:restriction => alert_yaml_data[ RESTRICTION_YAML ],
|
270
|
+
:addresses => alert_yaml_data[ ADDRESSES_YAML ],
|
271
|
+
:code => alert_yaml_data[ CODE_YAML ],
|
272
|
+
:note => alert_yaml_data[ NOTE_YAML ],
|
273
|
+
:references => alert_yaml_data[ REFERENCES_YAML ],
|
274
|
+
:incidents => alert_yaml_data[ INCIDENTS_YAML ],
|
275
|
+
:infos => Array( alert_yaml_data[ INFOS_YAML ]).map{ |info_yaml_data| RCAP::Info.from_yaml_data( info_yaml_data )}
|
276
|
+
)
|
277
|
+
end
|
278
|
+
end
|
186
279
|
end
|
data/lib/rcap/area.rb
CHANGED
@@ -55,7 +55,7 @@ module RCAP
|
|
55
55
|
@circles.inject( xml_element, &add_to_xml_element )
|
56
56
|
@geocodes.inject( xml_element, &add_to_xml_element )
|
57
57
|
xml_element.add_element( ALTITUDE_ELEMENT_NAME ).add_text( @altitude.to_s ) unless self.altitude.blank?
|
58
|
-
xml_element.add_element( CEILING_ELEMENT_NAME ).add_text( @ceiling.to_s )
|
58
|
+
xml_element.add_element( CEILING_ELEMENT_NAME ).add_text( @ceiling.to_s ) unless self.altitude.blank?
|
59
59
|
xml_element
|
60
60
|
end
|
61
61
|
|
@@ -69,14 +69,62 @@ module RCAP
|
|
69
69
|
comparison_attributes.call( self ) == comparison_attributes.call( other )
|
70
70
|
end
|
71
71
|
|
72
|
+
def inspect # :nodoc:
|
73
|
+
area_inspect = <<EOF
|
74
|
+
Area Description: #{ self.area_desc }
|
75
|
+
Polygons:
|
76
|
+
#{ self.polygons.map{ |polygon| " " + polygon.inspect }.join("\n" )}
|
77
|
+
Circles: #{ self.circles.inspect }
|
78
|
+
Geocodes: #{ self.geocodes.inspect }
|
79
|
+
EOF
|
80
|
+
RCAP.format_lines_for_inspect( 'AREA', area_inspect )
|
81
|
+
end
|
82
|
+
|
83
|
+
# Returns a string representation of the area of the form
|
84
|
+
# area_desc
|
85
|
+
def to_s
|
86
|
+
self.area_desc
|
87
|
+
end
|
88
|
+
|
72
89
|
def self.from_xml_element( area_xml_element ) # :nodoc:
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
area
|
90
|
+
RCAP::Area.new( :area_desc => RCAP.xpath_text( area_xml_element, AREA_DESC_XPATH ),
|
91
|
+
:altitude => (( alt = RCAP.xpath_text( area_xml_element, ALTITUDE_XPATH )) ? alt.to_f : nil ),
|
92
|
+
:ceiling => (( ceil = RCAP.xpath_text( area_xml_element, CEILING_XPATH )) ? ceil.to_f : nil ),
|
93
|
+
:circles => RCAP.xpath_match( area_xml_element, RCAP::Circle::XPATH ).map{ |circle_element| RCAP::Circle.from_xml_element( circle_element )},
|
94
|
+
:geocodes => RCAP.xpath_match( area_xml_element, RCAP::Geocode::XPATH ).map{ |geocode_element| RCAP::Geocode.from_xml_element( geocode_element )},
|
95
|
+
:polygons => RCAP.xpath_match( area_xml_element, RCAP::Polygon::XPATH ).map{ |polygon_element| RCAP::Polygon.from_xml_element( polygon_element )})
|
80
96
|
end
|
97
|
+
|
98
|
+
AREA_DESC_YAML = 'Area Description' # :nodoc:
|
99
|
+
ALTITUDE_YAML = 'Altitude' # :nodoc:
|
100
|
+
CEILING_YAML = 'Ceiling' # :nodoc:
|
101
|
+
CIRCLES_YAML = 'Circles' # :nodoc:
|
102
|
+
GEOCODES_YAML = 'Geocodes' # :nodoc:
|
103
|
+
POLYGONS_YAML = 'Polygons' # :nodoc:
|
104
|
+
|
105
|
+
def to_yaml( options = {} ) # :nodoc:
|
106
|
+
circles_yaml = self.circles.map{ |circle| [[ circle.point.lattitude, circle.point.longitude ], circle.radius ]}
|
107
|
+
def circles_yaml.to_yaml_style; :inline; end
|
108
|
+
|
109
|
+
RCAP.attribute_values_to_yaml_hash(
|
110
|
+
[ AREA_DESC_YAML, self.area_desc ],
|
111
|
+
[ ALTITUDE_YAML, self.altitude ],
|
112
|
+
[ CEILING_YAML, self.ceiling ],
|
113
|
+
[ CIRCLES_YAML, circles_yaml ],
|
114
|
+
[ GEOCODES_YAML, self.geocodes.inject({}){|h,geocode| h.merge( geocode.name => geocode.value )}],
|
115
|
+
[ POLYGONS_YAML, self.polygons ]
|
116
|
+
).to_yaml( options )
|
117
|
+
end
|
118
|
+
|
119
|
+
def from_yaml_data( area_yaml_data ) # :nodoc:
|
120
|
+
Area.new(
|
121
|
+
:area_desc => area_yaml_data[ AREA_DESC_YAML ],
|
122
|
+
:altitude => area_yaml_data[ ALTITUDE_YAML ],
|
123
|
+
:ceiling => area_yaml_data[ CEILING_YAML ],
|
124
|
+
:circles => Array( area_yaml_data[ CIRCLES_YAML ]).map{ |circle_yaml_data| RCAP::Circle.from_yaml_data( circle_yaml_data )},
|
125
|
+
:geocodes => Array( area_yaml_data[ GEOCODES_YAML ]).map{ |name, value| RCAP::Geocode.new( :name => name, :value => value )},
|
126
|
+
:polygons => Array( area_yaml_data[ POLYGONS_YAML ]).map{ |polyon_yaml_data| RCAP::Polygon.from_yaml_data( polyon_yaml_data )}
|
127
|
+
)
|
128
|
+
end
|
81
129
|
end
|
82
130
|
end
|
data/lib/rcap/circle.rb
CHANGED
@@ -23,6 +23,8 @@ module RCAP
|
|
23
23
|
@radius = attributes[ :radius ]
|
24
24
|
end
|
25
25
|
|
26
|
+
# Returns a string representation of the circle of the form
|
27
|
+
# point radius
|
26
28
|
def to_s # :nodoc:
|
27
29
|
"#{ self.point.to_s } #{ self.radius }"
|
28
30
|
end
|
@@ -58,5 +60,11 @@ module RCAP
|
|
58
60
|
def ==( other )
|
59
61
|
self.point == other.point && self.radius == other.radius
|
60
62
|
end
|
63
|
+
|
64
|
+
def self.from_yaml_data( circle_yaml_data ) # :nodoc:
|
65
|
+
point_yaml_data, radius = circle_yaml_data
|
66
|
+
self.new( :point => RCAP::Point.new( :lattitude => point_yaml_data[ 0 ], longitude => point_yaml_data[ 1 ]),
|
67
|
+
:radius => radius )
|
68
|
+
end
|
61
69
|
end
|
62
70
|
end
|
data/lib/rcap/geocode.rb
CHANGED
@@ -13,8 +13,8 @@ module RCAP
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def self.from_xml_element( geocode_xml_element ) # :nodoc:
|
16
|
-
self.new( :name
|
17
|
-
|
16
|
+
self.new( :name => RCAP.xpath_text( geocode_xml_element, NAME_XPATH ),
|
17
|
+
:value => RCAP.xpath_text( geocode_xml_element, VALUE_XPATH ))
|
18
18
|
end
|
19
19
|
end
|
20
20
|
end
|
data/lib/rcap/info.rb
CHANGED
@@ -11,57 +11,62 @@ module RCAP
|
|
11
11
|
class Info
|
12
12
|
include Validation
|
13
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
|
-
|
14
|
+
CATEGORY_GEO = "Geo" # :nodoc:
|
15
|
+
CATEGORY_MET = "Met" # :nodoc:
|
16
|
+
CATEGORY_SAFETY = "Safety" # :nodoc:
|
17
|
+
CATEGORY_SECURITY = "Security" # :nodoc:
|
18
|
+
CATEGORY_RESCUE = "Rescue" # :nodoc:
|
19
|
+
CATEGORY_FIRE = "Fire" # :nodoc:
|
20
|
+
CATEGORY_HEALTH = "Health" # :nodoc:
|
21
|
+
CATEGORY_ENV = "Env" # :nodoc:
|
22
|
+
CATEGORY_TRANSPORT = "Transport" # :nodoc:
|
23
|
+
CATEGORY_INFRA = "Infra" # :nodoc:
|
24
|
+
CATEGORY_CBRNE = "CBRNE" # :nodoc:
|
25
|
+
CATEGORY_OTHER = "Other" # :nodoc:
|
26
|
+
# Valid values for categories
|
27
|
+
VALID_CATEGORIES = [ CATEGORY_GEO, CATEGORY_MET, CATEGORY_SAFETY,
|
27
28
|
CATEGORY_SECURITY, CATEGORY_RESCUE, CATEGORY_FIRE, CATEGORY_HEALTH,
|
28
29
|
CATEGORY_ENV, CATEGORY_TRANSPORT, CATEGORY_INFRA, CATEGORY_CBRNE,
|
29
|
-
CATEGORY_OTHER ]
|
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
|
-
|
30
|
+
CATEGORY_OTHER ]
|
31
|
+
|
32
|
+
RESPONSE_TYPE_SHELTER = "Shelter" # :nodoc:
|
33
|
+
RESPONSE_TYPE_EVACUATE = "Evacuate" # :nodoc:
|
34
|
+
RESPONSE_TYPE_PREPARE = "Prepare" # :nodoc:
|
35
|
+
RESPONSE_TYPE_EXECUTE = "Execute" # :nodoc:
|
36
|
+
RESPONSE_TYPE_MONITOR = "Monitor" # :nodoc:
|
37
|
+
RESPONSE_TYPE_ASSESS = "Assess" # :nodoc:
|
38
|
+
RESPONSE_TYPE_NONE = "None" # :nodoc:
|
39
|
+
# Valid values for response_type
|
40
|
+
VALID_RESPONSE_TYPES = [ RESPONSE_TYPE_SHELTER, RESPONSE_TYPE_EVACUATE,
|
39
41
|
RESPONSE_TYPE_PREPARE, RESPONSE_TYPE_EXECUTE, RESPONSE_TYPE_MONITOR,
|
40
|
-
RESPONSE_TYPE_ASSESS, RESPONSE_TYPE_NONE ]
|
41
|
-
|
42
|
-
URGENCY_IMMEDIATE = "Immediate"
|
43
|
-
URGENCY_EXPECTED = "Expected"
|
44
|
-
URGENCY_FUTURE = "Future"
|
45
|
-
URGENCY_PAST = "Past"
|
46
|
-
URGENCY_UNKNOWN = "Unknown"
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
42
|
+
RESPONSE_TYPE_ASSESS, RESPONSE_TYPE_NONE ]
|
43
|
+
|
44
|
+
URGENCY_IMMEDIATE = "Immediate" # :nodoc:
|
45
|
+
URGENCY_EXPECTED = "Expected" # :nodoc:
|
46
|
+
URGENCY_FUTURE = "Future" # :nodoc:
|
47
|
+
URGENCY_PAST = "Past" # :nodoc:
|
48
|
+
URGENCY_UNKNOWN = "Unknown" # :nodoc:
|
49
|
+
# Valid values for urgency
|
50
|
+
VALID_URGENCIES = [ URGENCY_IMMEDIATE, URGENCY_EXPECTED, URGENCY_FUTURE,
|
51
|
+
URGENCY_PAST, URGENCY_UNKNOWN ]
|
52
|
+
|
53
|
+
SEVERITY_EXTREME = "Extreme" # :nodoc:
|
54
|
+
SEVERITY_SEVERE = "Severe" # :nodoc:
|
55
|
+
SEVERITY_MODERATE = "Moderate" # :nodoc:
|
56
|
+
SEVERITY_MINOR = "Minor" # :nodoc:
|
57
|
+
SEVERITY_UNKNOWN = "Unknown" # :nodoc:
|
58
|
+
# Valid values for severity
|
59
|
+
VALID_SEVERITIES = [ SEVERITY_EXTREME, SEVERITY_SEVERE, SEVERITY_MODERATE,
|
60
|
+
SEVERITY_MINOR, SEVERITY_UNKNOWN ]
|
61
|
+
|
62
|
+
CERTAINTY_OBSERVED = "Observed" # :nodoc:
|
63
|
+
CERTAINTY_LIKELY = "Likely" # :nodoc:
|
64
|
+
CERTAINTY_POSSIBLE = "Possible" # :nodoc:
|
65
|
+
CERTAINTY_UNLIKELY = "Unlikely" # :nodoc:
|
66
|
+
CERTAINTY_UNKNOWN = "Unknown" # :nodoc:
|
67
|
+
# Valid valies for certainty
|
68
|
+
VALID_CERTAINTIES = [ CERTAINTY_OBSERVED, CERTAINTY_LIKELY,
|
69
|
+
CERTAINTY_POSSIBLE, CERTAINTY_UNLIKELY, CERTAINTY_UNKNOWN ]
|
65
70
|
|
66
71
|
XML_ELEMENT_NAME = 'info' # :nodoc:
|
67
72
|
LANGUAGE_ELEMENT_NAME = 'language' # :nodoc:
|
@@ -107,16 +112,19 @@ module RCAP
|
|
107
112
|
|
108
113
|
validates_presence_of( :event, :urgency, :severity, :certainty )
|
109
114
|
validates_length_of( :categories, :minimum => 1 )
|
110
|
-
validates_inclusion_of( :certainty, :allow_nil => true, :in =>
|
111
|
-
validates_inclusion_of( :severity, :allow_nil => true, :in =>
|
112
|
-
validates_inclusion_of( :urgency, :allow_nil => true, :in =>
|
113
|
-
validates_inclusion_of_members_of( :response_types, :in
|
114
|
-
|
115
|
+
validates_inclusion_of( :certainty, :allow_nil => true, :in => VALID_CERTAINTIES, :message => "can only be assigned the following values: #{ VALID_CERTAINTIES.join(', ') }")
|
116
|
+
validates_inclusion_of( :severity, :allow_nil => true, :in => VALID_SEVERITIES, :message => "can only be assigned the following values: #{ VALID_SEVERITIES.join(', ') }" )
|
117
|
+
validates_inclusion_of( :urgency, :allow_nil => true, :in => VALID_URGENCIES, :message => "can only be assigned the following values: #{ VALID_URGENCIES.join(', ') }" )
|
118
|
+
validates_inclusion_of_members_of( :response_types, :in => VALID_RESPONSE_TYPES, :allow_blank => true )
|
119
|
+
validates_inclusion_of_members_of( :categories, :in => VALID_CATEGORIES, :allow_blank => true )
|
115
120
|
validates_collection_of( :resources, :areas )
|
116
121
|
|
117
122
|
attr_accessor( :event )
|
123
|
+
# Value can only be one of VALID_URGENCIES
|
118
124
|
attr_accessor( :urgency )
|
125
|
+
# Value can only be one of VALID_SEVERITIES
|
119
126
|
attr_accessor( :severity )
|
127
|
+
# Value can only be one of VALID_CERTAINTIES
|
120
128
|
attr_accessor( :certainty )
|
121
129
|
attr_accessor( :language )
|
122
130
|
attr_accessor( :audience )
|
@@ -124,7 +132,7 @@ module RCAP
|
|
124
132
|
attr_accessor( :effective )
|
125
133
|
# Expected start of event
|
126
134
|
attr_accessor( :onset )
|
127
|
-
#
|
135
|
+
# Effective expiry time of information
|
128
136
|
attr_accessor( :expires )
|
129
137
|
attr_accessor( :sender_name )
|
130
138
|
attr_accessor( :headline )
|
@@ -133,7 +141,7 @@ module RCAP
|
|
133
141
|
attr_accessor( :web )
|
134
142
|
attr_accessor( :contact )
|
135
143
|
|
136
|
-
# Collection of textual categories
|
144
|
+
# Collection of textual categories; elements can be one of VALID_CATEGORIES
|
137
145
|
attr_reader( :categories )
|
138
146
|
# Collection of textual response types
|
139
147
|
attr_reader( :response_types )
|
@@ -186,15 +194,15 @@ module RCAP
|
|
186
194
|
@event_codes.each do |event_code|
|
187
195
|
xml_element.add_element( event_code.to_xml_element )
|
188
196
|
end
|
189
|
-
xml_element.add_element( EFFECTIVE_ELEMENT_NAME ).add_text( self.effective.
|
190
|
-
xml_element.add_element( ONSET_ELEMENT_NAME ).add_text( self.onset.
|
191
|
-
xml_element.add_element( EXPIRES_ELEMENT_NAME ).add_text( self.expires.
|
192
|
-
xml_element.add_element( SENDER_NAME_ELEMENT_NAME ).add_text( self.sender_name )
|
193
|
-
xml_element.add_element( HEADLINE_ELEMENT_NAME ).add_text( self.headline )
|
194
|
-
xml_element.add_element( DESCRIPTION_ELEMENT_NAME ).add_text( self.description )
|
195
|
-
xml_element.add_element( INSTRUCTION_ELEMENT_NAME ).add_text( self.instruction )
|
196
|
-
xml_element.add_element( WEB_ELEMENT_NAME ).add_text( self.web )
|
197
|
-
xml_element.add_element( CONTACT_ELEMENT_NAME ).add_text( self.contact )
|
197
|
+
xml_element.add_element( EFFECTIVE_ELEMENT_NAME ).add_text( self.effective.to_s_for_cap ) if self.effective
|
198
|
+
xml_element.add_element( ONSET_ELEMENT_NAME ).add_text( self.onset.to_s_for_cap ) if self.onset
|
199
|
+
xml_element.add_element( EXPIRES_ELEMENT_NAME ).add_text( self.expires.to_s_for_cap ) if self.expires
|
200
|
+
xml_element.add_element( SENDER_NAME_ELEMENT_NAME ).add_text( self.sender_name ) if self.sender_name
|
201
|
+
xml_element.add_element( HEADLINE_ELEMENT_NAME ).add_text( self.headline ) if self.headline
|
202
|
+
xml_element.add_element( DESCRIPTION_ELEMENT_NAME ).add_text( self.description ) if self.description
|
203
|
+
xml_element.add_element( INSTRUCTION_ELEMENT_NAME ).add_text( self.instruction ) if self.instruction
|
204
|
+
xml_element.add_element( WEB_ELEMENT_NAME ).add_text( self.web ) if self.web
|
205
|
+
xml_element.add_element( CONTACT_ELEMENT_NAME ).add_text( self.contact ) if self.contact
|
198
206
|
@parameters.each do |parameter|
|
199
207
|
xml_element.add_element( parameter.to_xml_element )
|
200
208
|
end
|
@@ -211,6 +219,43 @@ module RCAP
|
|
211
219
|
self.to_xml_element.to_s
|
212
220
|
end
|
213
221
|
|
222
|
+
def inspect # :nodoc:
|
223
|
+
info_inspect = <<EOF
|
224
|
+
Language: #{ self.language }
|
225
|
+
Categories: #{ self.categories.to_s_for_cap }
|
226
|
+
Event: #{ self.event }
|
227
|
+
Response Types: #{ self.response_types.to_s_for_cap }
|
228
|
+
Urgency: #{ self.urgency }
|
229
|
+
Severity: #{ self.severity }
|
230
|
+
Certainty: #{ self.certainty }
|
231
|
+
Audience: #{ self.audience }
|
232
|
+
Event Codes: #{ self.event_codes.inspect }
|
233
|
+
Effective: #{ self.effective }
|
234
|
+
Onset: #{ self.onset }
|
235
|
+
Expires: #{ self.expires }
|
236
|
+
Sender Name: #{ self.sender_name }
|
237
|
+
Headline: #{ self.headline }
|
238
|
+
Description:
|
239
|
+
#{ self.description.to_s.lines.map{ |line| " " + line }.join }
|
240
|
+
Instruction: #{ self.instruction }
|
241
|
+
Web: #{ self.web }
|
242
|
+
Contact: #{ self.contact }
|
243
|
+
Parameters:
|
244
|
+
#{ self.parameters.map{ |parameter| parameter.inspect }.join( "\n" )}
|
245
|
+
Resources:
|
246
|
+
#{ self.resources.map{ |resource| " " + resource.inspect }.join( "\n" )}
|
247
|
+
Area:
|
248
|
+
#{ self.areas.map{ |area| " #{ area }" }.join( "\n" )}
|
249
|
+
EOF
|
250
|
+
RCAP.format_lines_for_inspect( 'INFO', info_inspect )
|
251
|
+
end
|
252
|
+
|
253
|
+
# Returns a string representation of the event of the form
|
254
|
+
# event(urgency/severity/certainty)
|
255
|
+
def to_s
|
256
|
+
"#{ self.event }(#{ self.urgency }/#{ self.severity }/#{ self.certainty })"
|
257
|
+
end
|
258
|
+
|
214
259
|
def self.from_xml_element( info_xml_element ) # :nodoc:
|
215
260
|
self.new(
|
216
261
|
:language => RCAP.xpath_text( info_xml_element, LANGUAGE_XPATH ) || DEFAULT_LANGUAGE,
|
@@ -230,9 +275,92 @@ module RCAP
|
|
230
275
|
:instruction => RCAP.xpath_text( info_xml_element, INSTRUCTION_XPATH ),
|
231
276
|
:web => RCAP.xpath_text( info_xml_element, WEB_XPATH ),
|
232
277
|
:contact => RCAP.xpath_text( info_xml_element, CONTACT_XPATH ),
|
233
|
-
|
234
|
-
|
235
|
-
|
278
|
+
:event_codes => RCAP.xpath_match( info_xml_element, RCAP::EventCode::XPATH ).map{ |element| RCAP::EventCode.from_xml_element( element )},
|
279
|
+
:parameters => RCAP.xpath_match( info_xml_element, RCAP::Parameter::XPATH ).map{ |element| RCAP::Parameter.from_xml_element( element )},
|
280
|
+
:resources => RCAP.xpath_match( info_xml_element, RCAP::Resource::XPATH ).map{ |element| RCAP::Resource.from_xml_element( element )},
|
281
|
+
:areas => RCAP.xpath_match( info_xml_element, RCAP::Area::XPATH ).map{ |element| RCAP::Area.from_xml_element( element )}
|
282
|
+
)
|
283
|
+
end
|
284
|
+
|
285
|
+
LANGUAGE_YAML = 'Language'
|
286
|
+
CATEGORIES_YAML = 'Categories'
|
287
|
+
EVENT_YAML = 'Event'
|
288
|
+
RESPONSE_TYPES_YAML = 'Response Types'
|
289
|
+
URGENCY_YAML = 'Urgency'
|
290
|
+
SEVERITY_YAML = 'Severity'
|
291
|
+
CERTAINTY_YAML = 'Certainty'
|
292
|
+
AUDIENCE_YAML = 'Audience'
|
293
|
+
EFFECTIVE_YAML = 'Effective'
|
294
|
+
ONSET_YAML = 'Onset'
|
295
|
+
EXPIRES_YAML = 'Expires'
|
296
|
+
SENDER_NAME_YAML = 'Sender Name'
|
297
|
+
HEADLINE_YAML = 'Headline'
|
298
|
+
DESCRIPTION_YAML = 'Description'
|
299
|
+
INSTRUCTION_YAML = 'Instruction'
|
300
|
+
WEB_YAML = 'Web'
|
301
|
+
CONTACT_YAML = 'Contact'
|
302
|
+
EVENT_CODES_YAML = 'Event Codes'
|
303
|
+
PARAMETERS_YAML = 'Parameters'
|
304
|
+
RESOURCES_YAML = 'Resources'
|
305
|
+
AREAS_YAML = 'Areas'
|
306
|
+
|
307
|
+
def to_yaml( options = {} ) # :nodoc:
|
308
|
+
response_types_yaml = self.response_types
|
309
|
+
def response_types_yaml.to_yaml_style; :inline; end
|
310
|
+
|
311
|
+
categories_yaml = self.categories
|
312
|
+
def categories_yaml.to_yaml_style; :inline; end
|
313
|
+
|
314
|
+
parameter_to_hash = lambda{ |hash, parameter| hash.merge( parameter.name => parameter.value )}
|
315
|
+
|
316
|
+
RCAP.attribute_values_to_yaml_hash(
|
317
|
+
[ LANGUAGE_YAML, self.language ],
|
318
|
+
[ CATEGORIES_YAML, categories_yaml ],
|
319
|
+
[ EVENT_YAML, self.event ],
|
320
|
+
[ RESPONSE_TYPES_YAML, response_types_yaml ],
|
321
|
+
[ URGENCY_YAML, self.urgency ],
|
322
|
+
[ SEVERITY_YAML, self.severity ],
|
323
|
+
[ CERTAINTY_YAML, self.certainty ],
|
324
|
+
[ AUDIENCE_YAML, self.audience ],
|
325
|
+
[ EFFECTIVE_YAML, self.effective ],
|
326
|
+
[ ONSET_YAML, self.onset ],
|
327
|
+
[ EXPIRES_YAML, self.expires ],
|
328
|
+
[ SENDER_NAME_YAML, self.sender_name ],
|
329
|
+
[ HEADLINE_YAML, self.headline ],
|
330
|
+
[ DESCRIPTION_YAML, self.description ],
|
331
|
+
[ INSTRUCTION_YAML, self.instruction ],
|
332
|
+
[ WEB_YAML, self.web ],
|
333
|
+
[ CONTACT_YAML, self.contact ],
|
334
|
+
[ EVENT_CODES_YAML, self.event_codes.inject({}, ¶meter_to_hash )],
|
335
|
+
[ PARAMETERS_YAML, self.parameters.inject({}, ¶meter_to_hash )],
|
336
|
+
[ RESOURCES_YAML, self.resources ],
|
337
|
+
[ AREAS_YAML, self.areas ]
|
338
|
+
).to_yaml( options )
|
339
|
+
end
|
340
|
+
|
341
|
+
def self.from_yaml_data( info_yaml_data ) # :nodoc:
|
342
|
+
self.new(
|
343
|
+
:language => info_yaml_data [ LANGUAGE_YAML ],
|
344
|
+
:categories => info_yaml_data [ CATEGORIES_YAML ],
|
345
|
+
:event => info_yaml_data [ EVENT_YAML ],
|
346
|
+
:response_types => info_yaml_data [ RESPONSE_TYPES_YAML ],
|
347
|
+
:urgency => info_yaml_data [ URGENCY_YAML ],
|
348
|
+
:severity => info_yaml_data [ SEVERITY_YAML ],
|
349
|
+
:certainty => info_yaml_data [ CERTAINTY_YAML ],
|
350
|
+
:audience => info_yaml_data [ AUDIENCE_YAML ],
|
351
|
+
:effective => ( effective = info_yaml_data[ EFFECTIVE_YAML ]).blank? ? nil : DateTime.parse( effective.to_s ),
|
352
|
+
:onset => ( onset = info_yaml_data[ ONSET_YAML ]).blank? ? nil : DateTime.parse( onset.to_s ),
|
353
|
+
:expires => ( expires = info_yaml_data[ EXPIRES_YAML ]).blank? ? nil : DateTime.parse( expires.to_s ),
|
354
|
+
:sender_name => info_yaml_data [ SENDER_NAME_YAML ],
|
355
|
+
:headline => info_yaml_data [ HEADLINE_YAML ],
|
356
|
+
:description => info_yaml_data [ DESCRIPTION_YAML ],
|
357
|
+
:instruction => info_yaml_data [ INSTRUCTION_YAML ],
|
358
|
+
:web => info_yaml_data [ WEB_YAML ],
|
359
|
+
:contact => info_yaml_data [ CONTACT_YAML ],
|
360
|
+
:event_codes => Array( info_yaml_data [ EVENT_CODES_YAML ]).map{ |name,value| RCAP::EventCode.new( :name => name, :value => value )},
|
361
|
+
:parameters => Array( info_yaml_data [ PARAMETERS_YAML ]).map{ |parameter_yaml_data| RCAP::Parameter.new( :name => name, :value => value )},
|
362
|
+
:resources => Array( info_yaml_data [ RESOURCES_YAML ]).map{ |resource_yaml_data| RCAP::Resource.from_yaml_data( resource_yaml_data )},
|
363
|
+
:areas => Array( info_yaml_data [ AREAS_YAML ]).map{ |area_yaml_data| RCAP::Area.from_yaml_data( area_yaml_data )}
|
236
364
|
)
|
237
365
|
end
|
238
366
|
end
|
data/lib/rcap/parameter.rb
CHANGED
@@ -34,10 +34,12 @@ module RCAP
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def inspect # :nodoc:
|
37
|
-
|
37
|
+
"#{ self.name }: #{ self.value }"
|
38
38
|
end
|
39
39
|
|
40
|
-
|
40
|
+
# Returns a string representation of the parameter of the form
|
41
|
+
# name: value
|
42
|
+
def to_s
|
41
43
|
self.inspect
|
42
44
|
end
|
43
45
|
|
data/lib/rcap/point.rb
CHANGED
@@ -22,7 +22,9 @@ module RCAP
|
|
22
22
|
@longitude = attributes[ :longitude ]
|
23
23
|
end
|
24
24
|
|
25
|
-
|
25
|
+
# Returns a string representation of the point of the form
|
26
|
+
# lattitude,longitude
|
27
|
+
def to_s
|
26
28
|
"#{ self.lattitude },#{ self.longitude }"
|
27
29
|
end
|
28
30
|
|
@@ -32,8 +34,7 @@ module RCAP
|
|
32
34
|
|
33
35
|
# Two points are equivalent if they have the same lattitude and longitude
|
34
36
|
def ==( other )
|
35
|
-
self.lattitude == other.lattitude
|
36
|
-
self.longitude == other.longitude
|
37
|
+
[ self.lattitude, self.longitude ] == [ other.lattitude, other.longitude ]
|
37
38
|
end
|
38
39
|
end
|
39
40
|
end
|
data/lib/rcap/polygon.rb
CHANGED
@@ -18,7 +18,10 @@ module RCAP
|
|
18
18
|
@points = Array( attributes[ :points ])
|
19
19
|
end
|
20
20
|
|
21
|
-
|
21
|
+
# Returns a string representation of the polygon of the form
|
22
|
+
# points[0] points[1] points[2] ... points[n-1] points[0]
|
23
|
+
# where each point is formatted with RCAP::Point#to_s
|
24
|
+
def to_s
|
22
25
|
(@points.map{ |point| point.to_s } + [ @points.first ]).join( ' ' )
|
23
26
|
end
|
24
27
|
|
@@ -46,5 +49,19 @@ module RCAP
|
|
46
49
|
points = coordinates.map{ |lattitude, longitude| RCAP::Point.new( :lattitude => lattitude, :longitude => longitude )}[0..-2]
|
47
50
|
polygon = self.new( :points => points )
|
48
51
|
end
|
52
|
+
|
53
|
+
|
54
|
+
def to_yaml( options = {} ) # :nodoc:
|
55
|
+
yaml_points = self.points.map{ |point| [ point.lattitude, point.longitude ]}
|
56
|
+
def yaml_points.to_yaml_style; :inline; end
|
57
|
+
|
58
|
+
yaml_points.to_yaml( options )
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.from_yaml_data( polyon_yaml_data ) # :nodoc:
|
62
|
+
self.new(
|
63
|
+
:points => Array( polygon_yaml_data ).map{ |lattitude, longitude| Point.new( :lattitude => lattitude, :longitude => longitude )}
|
64
|
+
)
|
65
|
+
end
|
49
66
|
end
|
50
67
|
end
|
data/lib/rcap/resource.rb
CHANGED
@@ -69,13 +69,48 @@ module RCAP
|
|
69
69
|
[ self.resource_desc, self.uri, self.mime_type, self.size ? format( "%.1fKB", self.size_in_kb ) : nil ].compact.join(' - ')
|
70
70
|
end
|
71
71
|
|
72
|
+
# Returns a string representation of the resource of the form
|
73
|
+
# resource_desc
|
74
|
+
def to_s
|
75
|
+
self.resource_desc
|
76
|
+
end
|
77
|
+
|
72
78
|
def self.from_xml_element( resource_xml_element ) # :nodoc:
|
73
79
|
resource = self.new( :resource_desc => RCAP.xpath_text( resource_xml_element, RESOURCE_DESC_XPATH ),
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
80
|
+
:uri => RCAP.xpath_text( resource_xml_element, URI_XPATH ),
|
81
|
+
:mime_type => RCAP.xpath_text( resource_xml_element, MIME_TYPE_XPATH ),
|
82
|
+
:deref_uri => RCAP.xpath_text( resource_xml_element, DEREF_URI_XPATH ),
|
83
|
+
:size => RCAP.xpath_text( resource_xml_element, SIZE_XPATH ),
|
84
|
+
:digest => RCAP.xpath_text( resource_xml_element, DIGEST_XPATH ))
|
85
|
+
end
|
86
|
+
|
87
|
+
RESOURCE_DESC_YAML = "Resource Description" # :nodoc:
|
88
|
+
URI_YAML = "URI" # :nodoc:
|
89
|
+
MIME_TYPE_YAML = "Mime Type" # :nodoc:
|
90
|
+
DEREF_URI_YAML = "Derefrenced URI Data" # :nodoc:
|
91
|
+
SIZE_YAML = "Size" # :nodoc:
|
92
|
+
DIGEST_YAML = "Digest" # :nodoc:
|
93
|
+
|
94
|
+
def to_yaml( options ) # :nodoc:
|
95
|
+
RCAP.attribute_values_to_yaml_hash(
|
96
|
+
[ RESOURCE_DESC_YAML, self.resource_desc ],
|
97
|
+
[ URI_YAML, self.uri ],
|
98
|
+
[ MIME_TYPE_YAML, self.mime_type ],
|
99
|
+
[ DEREF_URI_YAML, self.deref_uri ],
|
100
|
+
[ SIZE_YAML, self.size ],
|
101
|
+
[ DIGEST_YAML, self.digest ]
|
102
|
+
).to_yaml( options )
|
103
|
+
end
|
104
|
+
|
105
|
+
def self.from_yaml_data( resource_yaml_data ) # :nodoc:
|
106
|
+
self.new(
|
107
|
+
:resource_desc => reource_yaml_data[ RESOURCE_DESC_YAML ],
|
108
|
+
:uri => reource_yaml_data[ URI_YAML ],
|
109
|
+
:mime_type => reource_yaml_data[ MIME_TYPE_YAML ],
|
110
|
+
:deref_uri => reource_yaml_data[ DEREF_URI_YAML ],
|
111
|
+
:size => reource_yaml_data[ SIZE_YAML ],
|
112
|
+
:digest => reource_yaml_data[ DIGEST_YAML ]
|
113
|
+
)
|
79
114
|
end
|
80
115
|
end
|
81
116
|
end
|
data/lib/rcap/utilities.rb
CHANGED
@@ -29,8 +29,11 @@ class DateTime # :nodoc:
|
|
29
29
|
end
|
30
30
|
|
31
31
|
class Time # :nodoc:
|
32
|
-
|
32
|
+
RCAP_TIME_FORMAT = "%Y-%m-%dT%H:%M:%S"
|
33
|
+
RCAP_ZONE_FORMAT = "%+02i:00"
|
33
34
|
|
35
|
+
def to_s_for_cap
|
36
|
+
self.strftime( RCAP_TIME_FORMAT ) + format( RCAP_ZONE_FORMAT , self.utc_hours_offset )
|
34
37
|
end
|
35
38
|
end
|
36
39
|
|
@@ -47,4 +50,17 @@ module RCAP # :nodoc:
|
|
47
50
|
def self.xpath_match( xml_element, xpath )
|
48
51
|
REXML::XPath.match( xml_element, xpath, { 'cap' => RCAP::XMLNS })
|
49
52
|
end
|
53
|
+
|
54
|
+
def self.format_lines_for_inspect( header, inspect_string )
|
55
|
+
max_line_length = inspect_string.lines.inject( 0 ){ |max_length, line| line.chomp.length > max_length ? line.chomp.length : max_length }
|
56
|
+
"\n." + '-' * max_line_length + ".\n"+
|
57
|
+
'|' + header.ljust( max_line_length ) + "|\n"+
|
58
|
+
'|' + '-' * max_line_length + "|\n"+
|
59
|
+
inspect_string.lines.map{ |line| '|' + line.chomp.ljust( max_line_length ) +'|'}.join( "\n" ) + "\n" +
|
60
|
+
"'" + '-' * max_line_length + "'\n"
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.attribute_values_to_yaml_hash( *attribute_values )
|
64
|
+
Hash[ *attribute_values.reject{ |key, value| value.blank? }.flatten( 1 )]
|
65
|
+
end
|
50
66
|
end
|
data/lib/rcap/validations.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
module Validation # :nodoc:
|
2
2
|
module ClassMethods # :nodoc:
|
3
3
|
|
4
|
-
|
5
|
-
|
4
|
+
CAP_NUMBER_RE = /^-{0,1}\d*\.{0,1}\d+$/
|
5
|
+
CAP_INTEGER_RE = /\-{0,1}A[+-]?\d+\Z/
|
6
6
|
|
7
7
|
def validates_inclusion_of( *attributes )
|
8
8
|
options = {
|
@@ -88,7 +88,7 @@ module Validation # :nodoc:
|
|
88
88
|
:message => 'is not a number',
|
89
89
|
}.merge!(attributes.extract_options!)
|
90
90
|
|
91
|
-
re = options[:only_integer] ?
|
91
|
+
re = options[:only_integer] ? CAP_INTEGER_RE : CAP_NUMBER_RE
|
92
92
|
|
93
93
|
validates_each( *attributes ) do |object, attribute, value|
|
94
94
|
next if (value.nil? && options[ :allow_nil ]) || (value.blank? && options[ :allow_blank ])
|
data/lib/rcap.rb
CHANGED
data/spec/alert_spec.rb
CHANGED
@@ -20,6 +20,26 @@ describe( RCAP::Alert ) do
|
|
20
20
|
it( 'should not have any incidents' ){ @alert.incidents.should( be_empty )}
|
21
21
|
it( 'should not have any infos' ){ @alert.infos.should( be_empty )}
|
22
22
|
|
23
|
+
shared_examples_for( "a successfully parsed alert" ) do
|
24
|
+
it( 'should parse identifier correctly' ){ @alert.identifier.should == @original_alert.identifier }
|
25
|
+
it( 'should parse sender correctly' ){ @alert.sender.should == @original_alert.sender }
|
26
|
+
it( 'should parse sent correctly' ){ @alert.sent.should( be_close( @original_alert.sent, Rational( 1, 86400 )))}
|
27
|
+
it( 'should parse status correctly' ){ @alert.status.should == @original_alert.status }
|
28
|
+
it( 'should parse msg_type correctly' ){ @alert.msg_type.should == @original_alert.msg_type }
|
29
|
+
it( 'should parse source correctly' ){ @alert.source.should == @original_alert.source }
|
30
|
+
it( 'should parse scope correctly' ){ @alert.scope.should == @original_alert.scope }
|
31
|
+
it( 'should parse restriction correctly' ){ @alert.restriction.should == @original_alert.restriction }
|
32
|
+
it( 'should parse addresses correctly' ){ @alert.addresses.should == @original_alert.addresses }
|
33
|
+
it( 'should parse code correctly' ){ @alert.code.should == @original_alert.code }
|
34
|
+
it( 'should parse note correctly' ){ @alert.note.should == @original_alert.note }
|
35
|
+
it( 'should parse references correctly' ){ @alert.references.should == @original_alert.references }
|
36
|
+
it( 'should parse incidents correctly' ){ @alert.incidents.should == @original_alert.incidents }
|
37
|
+
it( 'should parse infos correctly' ) do
|
38
|
+
@alert.infos.size.should == @original_alert.infos.size
|
39
|
+
@alert.infos.each{ |info| info.class.should == RCAP::Info }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
23
43
|
context( 'from XML' ) do
|
24
44
|
before( :each ) do
|
25
45
|
@original_alert = RCAP::Alert.new( :sender => 'Sender',
|
@@ -39,24 +59,28 @@ describe( RCAP::Alert ) do
|
|
39
59
|
@alert = RCAP::Alert.from_xml_element( @alert_element )
|
40
60
|
end
|
41
61
|
|
42
|
-
|
43
|
-
|
44
|
-
it( 'should parse sent correctly' ){ @alert.sent.should( be_close( @original_alert.sent, Rational( 1, 86400 )))}
|
45
|
-
it( 'should parse status correctly' ){ @alert.status.should == @original_alert.status }
|
46
|
-
it( 'should parse msg_type correctly' ){ @alert.msg_type.should == @original_alert.msg_type }
|
47
|
-
it( 'should parse source correctly' ){ @alert.source.should == @original_alert.source }
|
48
|
-
it( 'should parse scope correctly' ){ @alert.scope.should == @original_alert.scope }
|
49
|
-
it( 'should parse restriction correctly' ){ @alert.restriction.should == @original_alert.restriction }
|
50
|
-
it( 'should parse addresses correctly' ){ @alert.addresses.should == @original_alert.addresses }
|
51
|
-
it( 'should parse code correctly' ){ @alert.code.should == @original_alert.code }
|
52
|
-
it( 'should parse note correctly' ){ @alert.note.should == @original_alert.note }
|
53
|
-
it( 'should parse references correctly' ){ @alert.references.should == @original_alert.references }
|
54
|
-
it( 'should parse incidents correctly' ){ @alert.incidents.should == @original_alert.incidents }
|
55
|
-
it( 'should parse infos correctly' ) do
|
56
|
-
@alert.infos.size.should == @original_alert.infos.size
|
57
|
-
@alert.infos.each{ |info| info.class.should == RCAP::Info }
|
58
|
-
end
|
62
|
+
it_should_behave_like( "a successfully parsed alert" )
|
63
|
+
|
59
64
|
end
|
65
|
+
|
66
|
+
context( 'from YAML' ) do
|
67
|
+
before( :each ) do
|
68
|
+
@original_alert = RCAP::Alert.new( :sender => 'Sender',
|
69
|
+
:status => RCAP::Alert::STATUS_TEST,
|
70
|
+
:scope => RCAP::Alert::SCOPE_PUBLIC,
|
71
|
+
:source => 'Source',
|
72
|
+
:restriction => 'No Restriction',
|
73
|
+
:addresses => [ 'Address 1', 'Address 2'],
|
74
|
+
:code => 'Code',
|
75
|
+
:note => 'Note',
|
76
|
+
:references => [ RCAP::Alert.new( :sender => 'Sender1' ).to_reference, RCAP::Alert.new( :sender => 'Sender2' ).to_reference ],
|
77
|
+
:incidents => [ 'Incident1', 'Incident2' ],
|
78
|
+
:infos => [ RCAP::Info.new, RCAP::Info.new ])
|
79
|
+
@yaml_string = @original_alert.to_yaml
|
80
|
+
@alert = RCAP::Alert.from_yaml( @yaml_string )
|
81
|
+
end
|
82
|
+
it_should_behave_like( "a successfully parsed alert" )
|
83
|
+
end
|
60
84
|
end
|
61
85
|
|
62
86
|
describe( 'is not valid if it' ) do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rcap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: "0.
|
4
|
+
version: "0.2"
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Farrel Lifson
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-11-
|
12
|
+
date: 2009-11-20 00:00:00 +02:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -39,8 +39,8 @@ executables: []
|
|
39
39
|
extensions: []
|
40
40
|
|
41
41
|
extra_rdoc_files:
|
42
|
-
- README
|
43
|
-
- CHANGELOG
|
42
|
+
- README.rdoc
|
43
|
+
- CHANGELOG.rdoc
|
44
44
|
files:
|
45
45
|
- lib/rcap.rb
|
46
46
|
- lib/rcap/area.rb
|
@@ -55,8 +55,8 @@ files:
|
|
55
55
|
- lib/rcap/parameter.rb
|
56
56
|
- lib/rcap/event_code.rb
|
57
57
|
- lib/rcap/info.rb
|
58
|
-
- README
|
59
|
-
- CHANGELOG
|
58
|
+
- README.rdoc
|
59
|
+
- CHANGELOG.rdoc
|
60
60
|
has_rdoc: true
|
61
61
|
homepage: http://www.aimred.com/projects/rcap
|
62
62
|
licenses: []
|
data/CHANGELOG
DELETED