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