rcap 1.3.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.md +5 -0
- data/README.md +78 -151
- data/Rakefile +1 -1
- data/lib/rcap/alert.rb +2 -2
- data/lib/rcap/base/alert.rb +446 -0
- data/lib/rcap/base/area.rb +228 -0
- data/lib/rcap/base/circle.rb +121 -0
- data/lib/rcap/base/event_code.rb +6 -0
- data/lib/rcap/base/geocode.rb +6 -0
- data/lib/rcap/base/info.rb +498 -0
- data/lib/rcap/base/parameter.rb +88 -0
- data/lib/rcap/base/point.rb +87 -0
- data/lib/rcap/base/polygon.rb +120 -0
- data/lib/rcap/base/resource.rb +168 -0
- data/lib/rcap/cap_1_0/alert.rb +59 -342
- data/lib/rcap/cap_1_0/area.rb +13 -188
- data/lib/rcap/cap_1_0/circle.rb +2 -100
- data/lib/rcap/cap_1_0/event_code.rb +8 -3
- data/lib/rcap/cap_1_0/geocode.rb +8 -3
- data/lib/rcap/cap_1_0/info.rb +16 -468
- data/lib/rcap/cap_1_0/parameter.rb +9 -67
- data/lib/rcap/cap_1_0/point.rb +2 -61
- data/lib/rcap/cap_1_0/polygon.rb +5 -95
- data/lib/rcap/cap_1_0/resource.rb +4 -144
- data/lib/rcap/cap_1_1/alert.rb +7 -412
- data/lib/rcap/cap_1_1/area.rb +13 -188
- data/lib/rcap/cap_1_1/circle.rb +2 -100
- data/lib/rcap/cap_1_1/event_code.rb +8 -3
- data/lib/rcap/cap_1_1/geocode.rb +7 -3
- data/lib/rcap/cap_1_1/info.rb +127 -386
- data/lib/rcap/cap_1_1/parameter.rb +4 -76
- data/lib/rcap/cap_1_1/point.rb +2 -61
- data/lib/rcap/cap_1_1/polygon.rb +5 -95
- data/lib/rcap/cap_1_1/resource.rb +37 -143
- data/lib/rcap/cap_1_2/alert.rb +8 -413
- data/lib/rcap/cap_1_2/area.rb +13 -188
- data/lib/rcap/cap_1_2/circle.rb +2 -100
- data/lib/rcap/cap_1_2/event_code.rb +8 -3
- data/lib/rcap/cap_1_2/geocode.rb +8 -3
- data/lib/rcap/cap_1_2/info.rb +132 -419
- data/lib/rcap/cap_1_2/parameter.rb +4 -76
- data/lib/rcap/cap_1_2/point.rb +2 -61
- data/lib/rcap/cap_1_2/polygon.rb +5 -92
- data/lib/rcap/cap_1_2/resource.rb +31 -134
- data/lib/rcap/config.rb +3 -0
- data/lib/{extensions → rcap/extensions}/array.rb +1 -1
- data/lib/rcap/extensions/date.rb +11 -0
- data/lib/{extensions → rcap/extensions}/date_time.rb +2 -5
- data/lib/{extensions → rcap/extensions}/string.rb +1 -1
- data/lib/{extensions → rcap/extensions}/time.rb +2 -4
- data/lib/rcap/utilities.rb +11 -11
- data/lib/rcap/validations.rb +7 -2
- data/lib/rcap/version.rb +1 -1
- data/lib/rcap.rb +21 -4
- data/spec/alert_spec.rb +69 -37
- data/spec/cap_1_0/alert_spec.rb +46 -61
- data/spec/cap_1_0/area_spec.rb +77 -37
- data/spec/cap_1_0/circle_spec.rb +26 -6
- data/spec/cap_1_0/event_code_spec.rb +10 -3
- data/spec/cap_1_0/geocode_spec.rb +11 -4
- data/spec/cap_1_0/info_spec.rb +74 -77
- data/spec/cap_1_0/parameter_spec.rb +18 -5
- data/spec/cap_1_0/point_spec.rb +9 -2
- data/spec/cap_1_0/polygon_spec.rb +52 -9
- data/spec/cap_1_0/resource_spec.rb +28 -21
- data/spec/cap_1_1/alert_spec.rb +47 -60
- data/spec/cap_1_1/area_spec.rb +66 -43
- data/spec/cap_1_1/circle_spec.rb +29 -6
- data/spec/cap_1_1/event_code_spec.rb +11 -3
- data/spec/cap_1_1/geocode_spec.rb +11 -3
- data/spec/cap_1_1/info_spec.rb +262 -118
- data/spec/cap_1_1/parameter_spec.rb +12 -3
- data/spec/cap_1_1/point_spec.rb +8 -2
- data/spec/cap_1_1/polygon_spec.rb +57 -18
- data/spec/cap_1_1/resource_spec.rb +70 -20
- data/spec/cap_1_2/alert_spec.rb +97 -110
- data/spec/cap_1_2/area_spec.rb +59 -41
- data/spec/cap_1_2/circle_spec.rb +15 -8
- data/spec/cap_1_2/event_code_spec.rb +11 -3
- data/spec/cap_1_2/geocode_spec.rb +11 -3
- data/spec/cap_1_2/info_spec.rb +266 -119
- data/spec/cap_1_2/parameter_spec.rb +11 -3
- data/spec/cap_1_2/point_spec.rb +10 -3
- data/spec/cap_1_2/polygon_spec.rb +25 -10
- data/spec/cap_1_2/resource_spec.rb +33 -28
- data/spec/{utilities_spec.rb → extensions_spec.rb} +0 -0
- data/spec/validations_spec.rb +18 -2
- metadata +20 -46
data/lib/rcap/cap_1_2/alert.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
module RCAP
|
2
2
|
module CAP_1_2
|
3
|
+
|
3
4
|
# An Alert object is valid if
|
4
5
|
# * it has an identifier
|
5
6
|
# * it has a sender
|
@@ -8,429 +9,23 @@ module RCAP
|
|
8
9
|
# * it has a valid messge type value
|
9
10
|
# * it has a valid scope value
|
10
11
|
# * all Info objects contained in infos are valid
|
11
|
-
class Alert
|
12
|
-
include Validation
|
12
|
+
class Alert < RCAP::Base::Alert
|
13
13
|
|
14
14
|
XMLNS = "urn:oasis:names:tc:emergency:cap:1.2"
|
15
15
|
CAP_VERSION = "1.2"
|
16
16
|
|
17
|
-
|
18
|
-
STATUS_EXERCISE = "Exercise"
|
19
|
-
STATUS_SYSTEM = "System"
|
20
|
-
STATUS_TEST = "Test"
|
21
|
-
STATUS_DRAFT = "Draft"
|
17
|
+
STATUS_DRAFT = "Draft"
|
22
18
|
# Valid values for status
|
23
19
|
VALID_STATUSES = [ STATUS_ACTUAL, STATUS_EXERCISE, STATUS_SYSTEM, STATUS_TEST, STATUS_DRAFT ]
|
24
20
|
|
25
|
-
MSG_TYPE_ALERT = "Alert"
|
26
|
-
MSG_TYPE_UPDATE = "Update"
|
27
|
-
MSG_TYPE_CANCEL = "Cancel"
|
28
|
-
MSG_TYPE_ACK = "Ack"
|
29
|
-
MSG_TYPE_ERROR = "Error"
|
30
|
-
# Valid values for msg_type
|
31
|
-
VALID_MSG_TYPES = [ MSG_TYPE_ALERT, MSG_TYPE_UPDATE, MSG_TYPE_CANCEL, MSG_TYPE_ACK, MSG_TYPE_ERROR ]
|
32
|
-
|
33
|
-
SCOPE_PUBLIC = "Public"
|
34
|
-
SCOPE_RESTRICTED = "Restricted"
|
35
|
-
SCOPE_PRIVATE = "Private"
|
36
|
-
# Valid values for scope
|
37
|
-
VALID_SCOPES = [ SCOPE_PUBLIC, SCOPE_PRIVATE, SCOPE_RESTRICTED ]
|
38
|
-
|
39
|
-
# @return [String] If not set a UUID will be set by default
|
40
|
-
attr_accessor( :identifier)
|
41
|
-
# @return [String]
|
42
|
-
attr_accessor( :sender )
|
43
|
-
# @return [DateTime] If not set will value will be time of creation.
|
44
|
-
attr_accessor( :sent )
|
45
|
-
# @return [String] Can only be one of {VALID_STATUSES}
|
46
|
-
attr_accessor( :status )
|
47
|
-
# @return [String] Can only be one of {VALID_MSG_TYPES}
|
48
|
-
attr_accessor( :msg_type )
|
49
|
-
# @return [String]
|
50
|
-
attr_accessor( :password )
|
51
|
-
# @return [String] Can only be one of {VALID_SCOPES}
|
52
|
-
attr_accessor( :scope )
|
53
|
-
# @return [String]
|
54
|
-
attr_accessor( :source )
|
55
|
-
# @return [String ] Depends on scope being {SCOPE_RESTRICTED}
|
56
|
-
attr_accessor( :restriction )
|
57
|
-
# @return [String]
|
58
|
-
attr_accessor( :note )
|
59
|
-
|
60
|
-
# @return [Array<String>] Collection of address strings. Depends on scope being {SCOPE_PRIVATE}
|
61
|
-
attr_reader( :addresses )
|
62
|
-
# @return [Array<String>]
|
63
|
-
attr_reader( :codes )
|
64
|
-
# @return [Array<String>] See {#to_reference}
|
65
|
-
attr_reader( :references)
|
66
|
-
# @return [Array<String>] Collection of incident strings
|
67
|
-
attr_reader( :incidents )
|
68
|
-
# @return [Array<Info>] Collection of {Info} objects
|
69
|
-
attr_reader( :infos )
|
70
|
-
|
71
|
-
validates_presence_of( :identifier, :sender, :sent, :status, :msg_type, :scope )
|
72
|
-
|
73
|
-
validates_inclusion_of( :status, in: VALID_STATUSES )
|
74
|
-
validates_inclusion_of( :msg_type, in: VALID_MSG_TYPES )
|
75
|
-
validates_inclusion_of( :scope, in: VALID_SCOPES )
|
76
|
-
|
77
|
-
validates_format_of( :identifier, with: ALLOWED_CHARACTERS )
|
78
|
-
validates_format_of( :sender , with: ALLOWED_CHARACTERS )
|
79
|
-
|
80
|
-
validates_conditional_presence_of( :addresses, when: :scope, is: SCOPE_PRIVATE )
|
81
|
-
validates_conditional_presence_of( :restriction, when: :scope, is: SCOPE_RESTRICTED )
|
82
|
-
|
83
|
-
validates_collection_of( :infos )
|
84
|
-
|
85
|
-
# @example
|
86
|
-
# Alert.new( sender: 'disaster_management@cape_town.municipal.za',
|
87
|
-
# sent: Date.today,
|
88
|
-
# status: Alert::STATUS_ACTUAL,
|
89
|
-
# msg_type: Alert::MSG_TYPE_ALERT,
|
90
|
-
# scope: Alert::SCOPE_PUBLIC )
|
91
|
-
#
|
92
|
-
# @param [Hash] attributes
|
93
|
-
# @option attributes [String] :identifier Unique identifier - autogenerated if not given
|
94
|
-
# @option attributes [String] :sender
|
95
|
-
# @option attributes [DateTime] :sent
|
96
|
-
# @option attributes [String] :status A member of {VALID_STATUSES}
|
97
|
-
# @option attributes [String] :msg_type A member of {VALID_MSG_TYPES}
|
98
|
-
# @option attributes [String] :password
|
99
|
-
# @option attributes [String] :scope A member of {VALID_SCOPES}
|
100
|
-
# @option attributes [String] :source
|
101
|
-
# @option attributes [String] :restriction
|
102
|
-
# @option attributes [Array<String>] :addresses
|
103
|
-
# @option attributes [Array<String>] :codes
|
104
|
-
# @option attributes [Array<String>] :references see {#to_reference}
|
105
|
-
# @option attributes [Array<String>] :incidents
|
106
|
-
# @option attributes [Array<Info>] :infos
|
107
|
-
def initialize( attributes = {})
|
108
|
-
@identifier = attributes[ :identifier ] || RCAP.generate_identifier
|
109
|
-
@sender = attributes[ :sender ]
|
110
|
-
@sent = attributes[ :sent ]
|
111
|
-
@status = attributes[ :status ]
|
112
|
-
@msg_type = attributes[ :msg_type ]
|
113
|
-
@scope = attributes[ :scope ]
|
114
|
-
@source = attributes[ :source ]
|
115
|
-
@restriction = attributes[ :restriction ]
|
116
|
-
@addresses = Array( attributes[ :addresses ])
|
117
|
-
@codes = Array( attributes[ :codes ])
|
118
|
-
@references = Array( attributes[ :references ])
|
119
|
-
@incidents = Array( attributes[ :incidents ])
|
120
|
-
@infos = Array( attributes[ :infos ])
|
121
|
-
end
|
122
|
-
|
123
|
-
# Creates a new {Info} object and adds it to the {#infos array}.
|
124
|
-
#
|
125
|
-
# @see Info#initialize
|
126
|
-
# @param [Hash] info_attributes Info attributes - see {Info#initialize}
|
127
|
-
# @return [Info]
|
128
|
-
def add_info( info_attributes = {})
|
129
|
-
info = Info.new( info_attributes )
|
130
|
-
@infos << info
|
131
|
-
info
|
132
|
-
end
|
133
|
-
|
134
|
-
XML_ELEMENT_NAME = 'alert'
|
135
|
-
IDENTIFIER_ELEMENT_NAME = 'identifier'
|
136
|
-
SENDER_ELEMENT_NAME = 'sender'
|
137
|
-
SENT_ELEMENT_NAME = 'sent'
|
138
|
-
STATUS_ELEMENT_NAME = 'status'
|
139
|
-
MSG_TYPE_ELEMENT_NAME = 'msgType'
|
140
|
-
SOURCE_ELEMENT_NAME = 'source'
|
141
|
-
SCOPE_ELEMENT_NAME = 'scope'
|
142
|
-
RESTRICTION_ELEMENT_NAME = 'restriction'
|
143
|
-
ADDRESSES_ELEMENT_NAME = 'addresses'
|
144
|
-
CODE_ELEMENT_NAME = 'code'
|
145
|
-
NOTE_ELEMENT_NAME = 'note'
|
146
|
-
REFERENCES_ELEMENT_NAME = 'references'
|
147
|
-
INCIDENTS_ELEMENT_NAME = 'incidents'
|
148
|
-
|
149
|
-
# @return [REXML::Element]
|
150
|
-
def to_xml_element
|
151
|
-
xml_element = REXML::Element.new( XML_ELEMENT_NAME )
|
152
|
-
xml_element.add_namespace( XMLNS )
|
153
|
-
xml_element.add_element( IDENTIFIER_ELEMENT_NAME ).add_text( @identifier ) if @identifier
|
154
|
-
xml_element.add_element( SENDER_ELEMENT_NAME ).add_text( @sender ) if @sender
|
155
|
-
xml_element.add_element( SENT_ELEMENT_NAME ).add_text( @sent.to_s_for_cap ) if @sent
|
156
|
-
xml_element.add_element( STATUS_ELEMENT_NAME ).add_text( @status ) if @status
|
157
|
-
xml_element.add_element( MSG_TYPE_ELEMENT_NAME ).add_text( @msg_type ) if @msg_type
|
158
|
-
xml_element.add_element( SOURCE_ELEMENT_NAME ).add_text( @source ) if @source
|
159
|
-
xml_element.add_element( SCOPE_ELEMENT_NAME ).add_text( @scope ) if @scope
|
160
|
-
xml_element.add_element( RESTRICTION_ELEMENT_NAME ).add_text( @restriction ) if @restriction
|
161
|
-
if @addresses.any?
|
162
|
-
xml_element.add_element( ADDRESSES_ELEMENT_NAME ).add_text( @addresses.to_s_for_cap )
|
163
|
-
end
|
164
|
-
@codes.each do |code|
|
165
|
-
xml_element.add_element( CODE_ELEMENT_NAME ).add_text( code )
|
166
|
-
end
|
167
|
-
xml_element.add_element( NOTE_ELEMENT_NAME ).add_text( @note ) if @note
|
168
|
-
if @references.any?
|
169
|
-
xml_element.add_element( REFERENCES_ELEMENT_NAME ).add_text( @references.join( ' ' ))
|
170
|
-
end
|
171
|
-
if @incidents.any?
|
172
|
-
xml_element.add_element( INCIDENTS_ELEMENT_NAME ).add_text( @incidents.join( ' ' ))
|
173
|
-
end
|
174
|
-
@infos.each do |info|
|
175
|
-
xml_element.add_element( info.to_xml_element )
|
176
|
-
end
|
177
|
-
xml_element
|
178
|
-
end
|
179
|
-
|
180
|
-
# @return [REXML::Document]
|
181
|
-
def to_xml_document
|
182
|
-
xml_document = REXML::Document.new
|
183
|
-
xml_document.add( REXML::XMLDecl.new )
|
184
|
-
xml_document.add( self.to_xml_element )
|
185
|
-
xml_document
|
186
|
-
end
|
187
|
-
|
188
|
-
# Returns a string containing the XML representation of the alert.
|
189
|
-
#
|
190
|
-
# @param [true,false] pretty_print Pretty print output
|
191
|
-
# @return [String]
|
192
|
-
def to_xml( pretty_print = false )
|
193
|
-
if pretty_print
|
194
|
-
xml_document = ""
|
195
|
-
RCAP::XML_PRETTY_PRINTER.write( self.to_xml_document, xml_document )
|
196
|
-
xml_document
|
197
|
-
else
|
198
|
-
self.to_xml_document.to_s
|
199
|
-
end
|
200
|
-
end
|
201
|
-
|
202
|
-
# Returns a string representation of the alert suitable for usage as a reference in a CAP message of the form
|
203
|
-
# sender,identifier,sent
|
204
|
-
#
|
205
|
-
# @return [String]
|
206
|
-
def to_reference
|
207
|
-
"#{ @sender },#{ @identifier },#{ @sent }"
|
208
|
-
end
|
209
|
-
|
210
|
-
# @return [String]
|
211
|
-
def inspect
|
212
|
-
alert_inspect = [ "CAP Version: #{ CAP_VERSION }",
|
213
|
-
"Identifier: #{ @identifier }",
|
214
|
-
"Sender: #{ @sender }",
|
215
|
-
"Sent: #{ @sent }",
|
216
|
-
"Status: #{ @status }",
|
217
|
-
"Message Type: #{ @msg_type }",
|
218
|
-
"Source: #{ @source }",
|
219
|
-
"Scope: #{ @scope }",
|
220
|
-
"Restriction: #{ @restriction }",
|
221
|
-
"Addresses: #{ @addresses.to_s_for_cap }",
|
222
|
-
"Codes:",
|
223
|
-
@codes.map{ |code| " " + code }.join("\n")+"",
|
224
|
-
"Note: #{ @note }",
|
225
|
-
"References: #{ @references.join( ' ' )}",
|
226
|
-
"Incidents: #{ @incidents.join( ' ')}",
|
227
|
-
"Information:",
|
228
|
-
@infos.map{ |info| " " + info.to_s }.join( "\n" )].join( "\n" )
|
229
|
-
RCAP.format_lines_for_inspect( 'ALERT', alert_inspect )
|
230
|
-
end
|
231
|
-
|
232
|
-
# Returns a string representation of the alert of the form
|
233
|
-
# sender/identifier/sent
|
234
|
-
# See {#to_reference} for another string representation suitable as a CAP reference.
|
235
|
-
#
|
236
|
-
# @return [String]
|
237
|
-
def to_s
|
238
|
-
"#{ @sender }/#{ @identifier }/#{ @sent }"
|
239
|
-
end
|
240
|
-
|
241
|
-
XPATH = 'cap:alert'
|
242
|
-
IDENTIFIER_XPATH = "cap:#{ IDENTIFIER_ELEMENT_NAME }"
|
243
|
-
SENDER_XPATH = "cap:#{ SENDER_ELEMENT_NAME }"
|
244
|
-
SENT_XPATH = "cap:#{ SENT_ELEMENT_NAME }"
|
245
|
-
STATUS_XPATH = "cap:#{ STATUS_ELEMENT_NAME }"
|
246
|
-
MSG_TYPE_XPATH = "cap:#{ MSG_TYPE_ELEMENT_NAME }"
|
247
|
-
SOURCE_XPATH = "cap:#{ SOURCE_ELEMENT_NAME }"
|
248
|
-
SCOPE_XPATH = "cap:#{ SCOPE_ELEMENT_NAME }"
|
249
|
-
RESTRICTION_XPATH = "cap:#{ RESTRICTION_ELEMENT_NAME }"
|
250
|
-
ADDRESSES_XPATH = "cap:#{ ADDRESSES_ELEMENT_NAME }"
|
251
|
-
CODE_XPATH = "cap:#{ CODE_ELEMENT_NAME }"
|
252
|
-
NOTE_XPATH = "cap:#{ NOTE_ELEMENT_NAME }"
|
253
|
-
REFERENCES_XPATH = "cap:#{ REFERENCES_ELEMENT_NAME }"
|
254
|
-
INCIDENTS_XPATH = "cap:#{ INCIDENTS_ELEMENT_NAME }"
|
255
|
-
|
256
|
-
# @param [REXML::Element] alert_xml_element
|
257
|
-
# @return [Alert]
|
258
|
-
def self.from_xml_element( alert_xml_element )
|
259
|
-
self.new( :identifier => RCAP.xpath_text( alert_xml_element, IDENTIFIER_XPATH, Alert::XMLNS ),
|
260
|
-
:sender => RCAP.xpath_text( alert_xml_element, SENDER_XPATH, Alert::XMLNS ),
|
261
|
-
:sent => (( sent = RCAP.xpath_first( alert_xml_element, SENT_XPATH, Alert::XMLNS )) ? DateTime.parse( sent.text ) : nil ),
|
262
|
-
:status => RCAP.xpath_text( alert_xml_element, STATUS_XPATH, Alert::XMLNS ),
|
263
|
-
:msg_type => RCAP.xpath_text( alert_xml_element, MSG_TYPE_XPATH, Alert::XMLNS ),
|
264
|
-
:source => RCAP.xpath_text( alert_xml_element, SOURCE_XPATH, Alert::XMLNS ),
|
265
|
-
:scope => RCAP.xpath_text( alert_xml_element, SCOPE_XPATH, Alert::XMLNS ),
|
266
|
-
:restriction => RCAP.xpath_text( alert_xml_element, RESTRICTION_XPATH, Alert::XMLNS ),
|
267
|
-
:addresses => (( address = RCAP.xpath_text( alert_xml_element, ADDRESSES_XPATH, Alert::XMLNS )) ? address.unpack_cap_list : nil ),
|
268
|
-
:codes => RCAP.xpath_match( alert_xml_element, CODE_XPATH, Alert::XMLNS ).map{ |element| element.text },
|
269
|
-
:note => RCAP.xpath_text( alert_xml_element, NOTE_XPATH, Alert::XMLNS ),
|
270
|
-
:references => (( references = RCAP.xpath_text( alert_xml_element, REFERENCES_XPATH, Alert::XMLNS )) ? references.split( ' ' ) : nil ),
|
271
|
-
:incidents => (( incidents = RCAP.xpath_text( alert_xml_element, INCIDENTS_XPATH, Alert::XMLNS )) ? incidents.split( ' ' ) : nil ),
|
272
|
-
:infos => RCAP.xpath_match( alert_xml_element, Info::XPATH, Alert::XMLNS ).map{ |element| Info.from_xml_element( element )})
|
273
|
-
end
|
274
|
-
|
275
|
-
# @param [REXML::Document] xml_document
|
276
|
-
# @return [Alert]
|
277
|
-
def self.from_xml_document( xml_document )
|
278
|
-
self.from_xml_element( xml_document.root )
|
279
|
-
end
|
280
|
-
|
281
|
-
# Initialise an Alert object from an XML string. Any object that is a subclass of IO (e.g. File) can be passed in.
|
282
|
-
#
|
283
|
-
# @param [String] xml
|
284
|
-
# @return [Alert]
|
285
|
-
def self.from_xml( xml )
|
286
|
-
self.from_xml_document( REXML::Document.new( xml ))
|
287
|
-
end
|
288
|
-
|
289
|
-
CAP_VERSION_YAML = "CAP Version"
|
290
|
-
IDENTIFIER_YAML = "Identifier"
|
291
|
-
SENDER_YAML = "Sender"
|
292
|
-
SENT_YAML = "Sent"
|
293
|
-
STATUS_YAML = "Status"
|
294
|
-
MSG_TYPE_YAML = "Message Type"
|
295
|
-
SOURCE_YAML = "Source"
|
296
|
-
SCOPE_YAML = "Scope"
|
297
|
-
RESTRICTION_YAML = "Restriction"
|
298
|
-
ADDRESSES_YAML = "Addresses"
|
299
|
-
CODES_YAML = "Codes"
|
300
|
-
NOTE_YAML = "Note"
|
301
|
-
REFERENCES_YAML = "References"
|
302
|
-
INCIDENTS_YAML = "Incidents"
|
303
|
-
INFOS_YAML = "Information"
|
304
|
-
|
305
|
-
# Returns a string containing the YAML representation of the alert.
|
306
|
-
#
|
307
|
-
# @return [String]
|
308
|
-
def to_yaml( options = {} )
|
309
|
-
RCAP.attribute_values_to_hash(
|
310
|
-
[ CAP_VERSION_YAML, CAP_VERSION ],
|
311
|
-
[ IDENTIFIER_YAML, @identifier ],
|
312
|
-
[ SENDER_YAML, @sender ],
|
313
|
-
[ SENT_YAML, @sent ],
|
314
|
-
[ STATUS_YAML, @status ],
|
315
|
-
[ MSG_TYPE_YAML, @msg_type ],
|
316
|
-
[ SOURCE_YAML, @source ],
|
317
|
-
[ SCOPE_YAML, @scope ],
|
318
|
-
[ RESTRICTION_YAML, @restriction ],
|
319
|
-
[ ADDRESSES_YAML, @addresses ],
|
320
|
-
[ CODES_YAML, @codes ],
|
321
|
-
[ NOTE_YAML, @note ],
|
322
|
-
[ REFERENCES_YAML, @references ],
|
323
|
-
[ INCIDENTS_YAML, @incidents ],
|
324
|
-
[ INFOS_YAML, @infos ]
|
325
|
-
).to_yaml( options )
|
326
|
-
end
|
327
|
-
|
328
|
-
# Initialise an Alert object from a YAML string. Any object that is a subclass of IO (e.g. File) can be passed in.
|
329
|
-
#
|
330
|
-
# @param [String] yaml
|
331
|
-
# @return [Alert]
|
332
|
-
def self.from_yaml( yaml )
|
333
|
-
self.from_yaml_data( YAML.load( yaml ))
|
334
|
-
end
|
335
|
-
|
336
|
-
# @param [Hash] yaml_data
|
337
|
-
# @return [Alert]
|
338
|
-
def self.from_yaml_data( alert_yaml_data )
|
339
|
-
Alert.new(
|
340
|
-
:identifier => alert_yaml_data[ IDENTIFIER_YAML ],
|
341
|
-
:sender => alert_yaml_data[ SENDER_YAML ],
|
342
|
-
:sent => ( sent = alert_yaml_data[ SENT_YAML ]).blank? ? nil : DateTime.parse( sent.to_s ),
|
343
|
-
:status => alert_yaml_data[ STATUS_YAML ],
|
344
|
-
:msg_type => alert_yaml_data[ MSG_TYPE_YAML ],
|
345
|
-
:source => alert_yaml_data[ SOURCE_YAML ],
|
346
|
-
:scope => alert_yaml_data[ SCOPE_YAML ],
|
347
|
-
:restriction => alert_yaml_data[ RESTRICTION_YAML ],
|
348
|
-
:addresses => alert_yaml_data[ ADDRESSES_YAML ],
|
349
|
-
:codes => alert_yaml_data[ CODES_YAML ],
|
350
|
-
:note => alert_yaml_data[ NOTE_YAML ],
|
351
|
-
:references => alert_yaml_data[ REFERENCES_YAML ],
|
352
|
-
:incidents => alert_yaml_data[ INCIDENTS_YAML ],
|
353
|
-
:infos => Array( alert_yaml_data[ INFOS_YAML ]).map{ |info_yaml_data| Info.from_yaml_data( info_yaml_data )}
|
354
|
-
)
|
355
|
-
end
|
356
|
-
|
357
|
-
CAP_VERSION_KEY = 'cap_version'
|
358
|
-
IDENTIFIER_KEY = 'identifier'
|
359
|
-
SENDER_KEY = 'sender'
|
360
|
-
SENT_KEY = 'sent'
|
361
|
-
STATUS_KEY = 'status'
|
362
|
-
MSG_TYPE_KEY = 'msg_type'
|
363
|
-
SOURCE_KEY = 'source'
|
364
|
-
SCOPE_KEY = 'scope'
|
365
|
-
RESTRICTION_KEY = 'restriction'
|
366
|
-
ADDRESSES_KEY = 'addresses'
|
367
|
-
CODES_KEY = 'codes'
|
368
|
-
NOTE_KEY = 'note'
|
369
|
-
REFERENCES_KEY = 'references'
|
370
|
-
INCIDENTS_KEY = 'incidents'
|
371
|
-
INFOS_KEY = 'infos'
|
372
|
-
|
373
|
-
# Returns a Hash representation of an Alert object
|
374
|
-
#
|
375
|
-
# @return [Hash]
|
376
|
-
def to_h
|
377
|
-
RCAP.attribute_values_to_hash( [ CAP_VERSION_KEY, CAP_VERSION ],
|
378
|
-
[ IDENTIFIER_KEY, @identifier ],
|
379
|
-
[ SENDER_KEY, @sender ],
|
380
|
-
[ SENT_KEY, RCAP.to_s_for_cap( @sent )],
|
381
|
-
[ STATUS_KEY, @status ],
|
382
|
-
[ MSG_TYPE_KEY, @msg_type ],
|
383
|
-
[ SOURCE_KEY, @source ],
|
384
|
-
[ SCOPE_KEY, @scope ],
|
385
|
-
[ RESTRICTION_KEY, @restriction ],
|
386
|
-
[ ADDRESSES_KEY, @addresses ],
|
387
|
-
[ CODES_KEY, @codes ],
|
388
|
-
[ NOTE_KEY, @note ],
|
389
|
-
[ REFERENCES_KEY, @references ],
|
390
|
-
[ INCIDENTS_KEY, @incidents ],
|
391
|
-
[ INFOS_KEY, @infos.map{ |info| info.to_h }])
|
392
|
-
end
|
393
|
-
|
394
|
-
# Initialises an Alert object from a Hash produced by Alert#to_h
|
395
|
-
#
|
396
|
-
# @param [Hash] alert_hash
|
397
|
-
# @return [Alert]
|
398
|
-
def self.from_h( alert_hash )
|
399
|
-
self.new(
|
400
|
-
:identifier => alert_hash[ IDENTIFIER_KEY ],
|
401
|
-
:sender => alert_hash[ SENDER_KEY ],
|
402
|
-
:sent => RCAP.parse_datetime( alert_hash[ SENT_KEY ]),
|
403
|
-
:status => alert_hash[ STATUS_KEY ],
|
404
|
-
:msg_type => alert_hash[ MSG_TYPE_KEY ],
|
405
|
-
:source => alert_hash[ SOURCE_KEY ],
|
406
|
-
:scope => alert_hash[ SCOPE_KEY ],
|
407
|
-
:restriction => alert_hash[ RESTRICTION_KEY ],
|
408
|
-
:addresses => alert_hash[ ADDRESSES_KEY ],
|
409
|
-
:codes => alert_hash[ CODES_KEY ],
|
410
|
-
:note => alert_hash[ NOTE_KEY ],
|
411
|
-
:references => alert_hash[ REFERENCES_KEY ],
|
412
|
-
:incidents => alert_hash[ INCIDENTS_KEY ],
|
413
|
-
:infos => Array( alert_hash[ INFOS_KEY ]).map{ |info_hash| Info.from_h( info_hash )})
|
414
|
-
end
|
415
|
-
|
416
|
-
# Returns a JSON string representation of an Alert object
|
417
|
-
#
|
418
|
-
# @param [true,false] pretty_print
|
419
21
|
# @return [String]
|
420
|
-
def
|
421
|
-
|
422
|
-
JSON.pretty_generate( self.to_h )
|
423
|
-
else
|
424
|
-
self.to_h.to_json
|
425
|
-
end
|
22
|
+
def xmlns
|
23
|
+
XMLNS
|
426
24
|
end
|
427
25
|
|
428
|
-
#
|
429
|
-
|
430
|
-
|
431
|
-
# @return [Alert]
|
432
|
-
def self.from_json( json_string )
|
433
|
-
self.from_h( JSON.parse( json_string ))
|
26
|
+
# @return [Class]
|
27
|
+
def info_class
|
28
|
+
Info
|
434
29
|
end
|
435
30
|
end
|
436
31
|
end
|
data/lib/rcap/cap_1_2/area.rb
CHANGED
@@ -1,207 +1,32 @@
|
|
1
1
|
module RCAP
|
2
2
|
module CAP_1_2
|
3
|
+
|
3
4
|
# An Area object is valid if
|
4
5
|
# * it has an area description
|
5
6
|
# * all Circle objects contained in circles are valid
|
6
7
|
# * all Geocode objects contained in geocodes are valid
|
7
8
|
# * all Polygon objects contained in polygons are valid
|
8
9
|
# * altitude has a value if ceiling is set
|
9
|
-
class Area
|
10
|
-
include Validation
|
11
|
-
|
12
|
-
# @return [String] Textual description of area.
|
13
|
-
attr_accessor( :area_desc )
|
14
|
-
# @return [Numeric] Expressed in feet above sea level
|
15
|
-
attr_accessor( :altitude )
|
16
|
-
# @return [Numeric] Expressed in feet above sea level.
|
17
|
-
attr_accessor( :ceiling )
|
18
|
-
# @return [Array<Circle>]
|
19
|
-
attr_reader( :circles )
|
20
|
-
# @return [Array<Geocode>]
|
21
|
-
attr_reader( :geocodes )
|
22
|
-
# @return [Array<Polygon>]
|
23
|
-
attr_reader( :polygons )
|
24
|
-
|
25
|
-
validates_presence_of( :area_desc )
|
26
|
-
validates_collection_of( :circles, :geocodes, :polygons, allow_empty: true )
|
27
|
-
validates_dependency_of( :ceiling, on: :altitude )
|
28
|
-
|
29
|
-
XML_ELEMENT_NAME = 'area'
|
30
|
-
AREA_DESC_ELEMENT_NAME = 'areaDesc'
|
31
|
-
ALTITUDE_ELEMENT_NAME = 'altitude'
|
32
|
-
CEILING_ELEMENT_NAME = 'ceiling'
|
33
|
-
|
34
|
-
XPATH = "cap:#{ XML_ELEMENT_NAME }"
|
35
|
-
AREA_DESC_XPATH = "cap:#{ AREA_DESC_ELEMENT_NAME }"
|
36
|
-
ALTITUDE_XPATH = "cap:#{ ALTITUDE_ELEMENT_NAME }"
|
37
|
-
CEILING_XPATH = "cap:#{ CEILING_ELEMENT_NAME }"
|
38
|
-
|
39
|
-
# @example
|
40
|
-
# Area.new( area_desc: 'Cape Town CBD' )
|
41
|
-
# @param [Hash] attributes Area attributes
|
42
|
-
# @option attributes [String] :area_desc Area description
|
43
|
-
# @option attributes [Numeric] :altitude Altitude above sea level (in feet)
|
44
|
-
# @option attributes [Numeric] :ceiling Ceiling above sea level (in feet)
|
45
|
-
# @option attributes [Array<Circle>] :circles Collection of {Circle}
|
46
|
-
# @option attributes [Array<Geocode>] :geocodes Collection of {Geocode}
|
47
|
-
# @option attributes [Array<Polygon>] :polygons Collection of {Polygon}
|
48
|
-
def initialize( attributes = {})
|
49
|
-
@area_desc = attributes[ :area_desc ]
|
50
|
-
@altitude = attributes[ :altitude ]
|
51
|
-
@ceiling = attributes[ :ceiling ]
|
52
|
-
@circles = Array( attributes[ :circles ])
|
53
|
-
@geocodes = Array( attributes[ :geocodes ])
|
54
|
-
@polygons = Array( attributes[ :polygons ])
|
55
|
-
end
|
56
|
-
|
57
|
-
# Creates a new {Polygon} object and adds it to the {#polygons} array.
|
58
|
-
#
|
59
|
-
# @see Polygon#initialize
|
60
|
-
# @param [Hash] polygon_attributes see {Polygon#initialize}
|
61
|
-
# @return [Polygon]
|
62
|
-
def add_polygon( polygon_attributes = {})
|
63
|
-
polygon = Polygon.new( polygon_attributes )
|
64
|
-
@polygons << polygon
|
65
|
-
polygon
|
66
|
-
end
|
67
|
-
|
68
|
-
# Creates a new {Circle} object and adds it to the {#circles} array.
|
69
|
-
#
|
70
|
-
# @see Circle#initialize
|
71
|
-
# @param [Hash] circle_attributes see {Circle#initialize}
|
72
|
-
# @return [Circle]
|
73
|
-
def add_circle( circle_attributes = {})
|
74
|
-
circle = Circle.new( circle_attributes )
|
75
|
-
@circles << circle
|
76
|
-
circle
|
77
|
-
end
|
78
|
-
|
79
|
-
# Creates a new {Geocode} object and adds it to the {#geocodes} array.
|
80
|
-
#
|
81
|
-
# @see Geocode#initialize
|
82
|
-
# @param [Hash] geocode_attributes see {Geocode#initialize}
|
83
|
-
# @return [Geocode]
|
84
|
-
def add_geocode( geocode_attributes = {})
|
85
|
-
geocode = Geocode.new( geocode_attributes )
|
86
|
-
@geocodes << geocode
|
87
|
-
geocode
|
88
|
-
end
|
89
|
-
|
90
|
-
# @return [REXML::Element]
|
91
|
-
def to_xml_element
|
92
|
-
xml_element = REXML::Element.new( XML_ELEMENT_NAME )
|
93
|
-
xml_element.add_element( AREA_DESC_ELEMENT_NAME ).add_text( @area_desc.to_s )
|
94
|
-
add_to_xml_element = lambda do |element, object|
|
95
|
-
element.add_element( object.to_xml_element )
|
96
|
-
element
|
97
|
-
end
|
98
|
-
@polygons.inject( xml_element, &add_to_xml_element )
|
99
|
-
@circles.inject( xml_element, &add_to_xml_element )
|
100
|
-
@geocodes.inject( xml_element, &add_to_xml_element )
|
101
|
-
xml_element.add_element( ALTITUDE_ELEMENT_NAME ).add_text( @altitude.to_s ) unless @altitude.blank?
|
102
|
-
xml_element.add_element( CEILING_ELEMENT_NAME ).add_text( @ceiling.to_s ) unless @altitude.blank?
|
103
|
-
xml_element
|
104
|
-
end
|
105
|
-
|
106
|
-
# @return [String] XML representation of the Area
|
107
|
-
def to_xml
|
108
|
-
self.to_xml_element.to_s
|
109
|
-
end
|
110
|
-
|
111
|
-
# Implements an equality operator for the Area object. Two Area objects are equal if all their attributes are equal.
|
112
|
-
#
|
113
|
-
# @param [Area] other Area object to compare
|
114
|
-
# @return [true,false]
|
115
|
-
def ==( other )
|
116
|
-
comparison_attributes = lambda{ |area| [ area.area_desc, area.altitude, area.ceiling, area.circles, area.geocodes, area.polygons ]}
|
117
|
-
comparison_attributes.call( self ) == comparison_attributes.call( other )
|
118
|
-
end
|
119
|
-
|
120
|
-
# @return [String]
|
121
|
-
def inspect
|
122
|
-
area_inspect = "Area Description: #{ @area_desc }\n"+
|
123
|
-
"Polygons:\n"+
|
124
|
-
@polygons.map{ |polygon| " " + polygon.inspect }.join("\n" )+"\n"+
|
125
|
-
"Circles: #{ @circles.inspect }\n"+
|
126
|
-
"Geocodes: #{ @geocodes.inspect }\n"
|
127
|
-
RCAP.format_lines_for_inspect( 'AREA', area_inspect )
|
128
|
-
end
|
10
|
+
class Area < RCAP::Base::Area
|
129
11
|
|
130
|
-
# Returns the area description
|
131
|
-
#
|
132
12
|
# @return [String]
|
133
|
-
def
|
134
|
-
|
135
|
-
end
|
136
|
-
|
137
|
-
# @param [REXML::Element] area_xml_element
|
138
|
-
# @return [Area]
|
139
|
-
def self.from_xml_element( area_xml_element )
|
140
|
-
self.new( :area_desc => RCAP.xpath_text( area_xml_element, AREA_DESC_XPATH, Alert::XMLNS ),
|
141
|
-
:altitude => (( alt = RCAP.xpath_text( area_xml_element, ALTITUDE_XPATH, Alert::XMLNS )) ? alt.to_f : nil ),
|
142
|
-
:ceiling => (( ceil = RCAP.xpath_text( area_xml_element, CEILING_XPATH, Alert::XMLNS )) ? ceil.to_f : nil ),
|
143
|
-
:circles => RCAP.xpath_match( area_xml_element, Circle::XPATH, Alert::XMLNS ).map{ |circle_element| Circle.from_xml_element( circle_element )},
|
144
|
-
:geocodes => RCAP.xpath_match( area_xml_element, Geocode::XPATH, Alert::XMLNS ).map{ |geocode_element| Geocode.from_xml_element( geocode_element )},
|
145
|
-
:polygons => RCAP.xpath_match( area_xml_element, Polygon::XPATH, Alert::XMLNS ).map{ |polygon_element| Polygon.from_xml_element( polygon_element )})
|
13
|
+
def xmlns
|
14
|
+
Alert::XMLNS
|
146
15
|
end
|
147
16
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
CIRCLES_YAML = 'Circles'
|
152
|
-
GEOCODES_YAML = 'Geocodes'
|
153
|
-
POLYGONS_YAML = 'Polygons'
|
154
|
-
|
155
|
-
# @return [String] YAML representation of object
|
156
|
-
def to_yaml( options = {} )
|
157
|
-
RCAP.attribute_values_to_hash(
|
158
|
-
[ AREA_DESC_YAML, @area_desc ],
|
159
|
-
[ ALTITUDE_YAML, @altitude ],
|
160
|
-
[ CEILING_YAML, @ceiling ],
|
161
|
-
[ CIRCLES_YAML, @circles.map{ |circle| [ circle.lattitude, circle.longitude, circle.radius ]} ],
|
162
|
-
[ GEOCODES_YAML, @geocodes.inject({}){|h,geocode| h.merge( geocode.name => geocode.value )}],
|
163
|
-
[ POLYGONS_YAML, @polygons ]
|
164
|
-
).to_yaml( options )
|
17
|
+
# @return [Class]
|
18
|
+
def polygon_class
|
19
|
+
Polygon
|
165
20
|
end
|
166
21
|
|
167
|
-
# @
|
168
|
-
|
169
|
-
|
170
|
-
self.new( :area_desc => area_yaml_data[ AREA_DESC_YAML ],
|
171
|
-
:altitude => area_yaml_data[ ALTITUDE_YAML ],
|
172
|
-
:ceiling => area_yaml_data[ CEILING_YAML ],
|
173
|
-
:circles => Array( area_yaml_data[ CIRCLES_YAML ]).map{ |circle_yaml_data| Circle.from_yaml_data( circle_yaml_data )},
|
174
|
-
:geocodes => Array( area_yaml_data[ GEOCODES_YAML ]).map{ |name, value| Geocode.new( :name => name, :value => value )},
|
175
|
-
:polygons => Array( area_yaml_data[ POLYGONS_YAML ]).map{ |polyon_yaml_data| Polygon.from_yaml_data( polyon_yaml_data )})
|
176
|
-
end
|
177
|
-
|
178
|
-
AREA_DESC_KEY = 'area_desc'
|
179
|
-
ALTITUDE_KEY = 'altitude'
|
180
|
-
CEILING_KEY = 'ceiling'
|
181
|
-
CIRCLES_KEY = 'circles'
|
182
|
-
GEOCODES_KEY = 'geocodes'
|
183
|
-
POLYGONS_KEY = 'polygons'
|
184
|
-
|
185
|
-
# @return [Hash]
|
186
|
-
def to_h
|
187
|
-
RCAP.attribute_values_to_hash( [ AREA_DESC_KEY, @area_desc ],
|
188
|
-
[ ALTITUDE_KEY, @altitude ],
|
189
|
-
[ CEILING_KEY, @ceiling ],
|
190
|
-
[ CIRCLES_KEY, @circles.map{ |circle| circle.to_h } ],
|
191
|
-
[ GEOCODES_KEY, @geocodes.map{ |geocode| geocode.to_h } ],
|
192
|
-
[ POLYGONS_KEY, @polygons.map{ |polygon| polygon.to_h } ])
|
22
|
+
# @return [Class]
|
23
|
+
def circle_class
|
24
|
+
Circle
|
193
25
|
end
|
194
26
|
|
195
|
-
# @
|
196
|
-
|
197
|
-
|
198
|
-
self.new(
|
199
|
-
:area_desc => area_hash[ AREA_DESC_KEY ],
|
200
|
-
:altitude => area_hash[ ALTITUDE_KEY ],
|
201
|
-
:ceiling => area_hash[ CEILING_KEY ],
|
202
|
-
:circles => Array( area_hash[ CIRCLES_KEY ]).map{ |circle_hash| Circle.from_h( circle_hash )},
|
203
|
-
:geocodes => Array( area_hash[ GEOCODES_KEY ]).map{ |geocode_hash| Geocode.from_h( geocode_hash )},
|
204
|
-
:polygons => Array( area_hash[ POLYGONS_KEY ]).map{ |polygon_hash| Polygon.from_h( polygon_hash )})
|
27
|
+
# @return [Class]
|
28
|
+
def geocode_class
|
29
|
+
Geocode
|
205
30
|
end
|
206
31
|
end
|
207
32
|
end
|