rcap 0.4 → 1.0.0.rc.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. data/.gitignore +7 -0
  2. data/CHANGELOG.rdoc +6 -0
  3. data/Gemfile +4 -0
  4. data/README.rdoc +104 -85
  5. data/Rakefile +35 -0
  6. data/lib/rcap.rb +21 -14
  7. data/lib/rcap/alert.rb +26 -325
  8. data/lib/rcap/cap_1_1/alert.rb +363 -0
  9. data/lib/rcap/cap_1_1/area.rb +180 -0
  10. data/lib/rcap/cap_1_1/circle.rb +81 -0
  11. data/lib/rcap/cap_1_1/event_code.rb +22 -0
  12. data/lib/rcap/cap_1_1/geocode.rb +22 -0
  13. data/lib/rcap/cap_1_1/info.rb +470 -0
  14. data/lib/rcap/cap_1_1/parameter.rb +68 -0
  15. data/lib/rcap/cap_1_1/point.rb +55 -0
  16. data/lib/rcap/cap_1_1/polygon.rb +89 -0
  17. data/lib/rcap/cap_1_1/resource.rb +145 -0
  18. data/lib/rcap/cap_1_2/alert.rb +363 -0
  19. data/lib/rcap/cap_1_2/area.rb +180 -0
  20. data/lib/rcap/cap_1_2/circle.rb +81 -0
  21. data/lib/rcap/cap_1_2/event_code.rb +22 -0
  22. data/lib/rcap/cap_1_2/geocode.rb +22 -0
  23. data/lib/rcap/cap_1_2/info.rb +472 -0
  24. data/lib/rcap/cap_1_2/parameter.rb +68 -0
  25. data/lib/rcap/cap_1_2/point.rb +55 -0
  26. data/lib/rcap/cap_1_2/polygon.rb +90 -0
  27. data/lib/rcap/cap_1_2/resource.rb +147 -0
  28. data/lib/rcap/utilities.rb +14 -9
  29. data/lib/rcap/validations.rb +39 -7
  30. data/lib/rcap/version.rb +3 -0
  31. data/rcap.gemspec +30 -0
  32. data/spec/alert_spec.rb +109 -172
  33. data/spec/cap_1_1/alert_spec.rb +222 -0
  34. data/spec/cap_1_1/area_spec.rb +247 -0
  35. data/spec/cap_1_1/circle_spec.rb +88 -0
  36. data/spec/{geocode_spec.rb → cap_1_1/geocode_spec.rb} +8 -8
  37. data/spec/{info_spec.rb → cap_1_1/info_spec.rb} +143 -75
  38. data/spec/{point_spec.rb → cap_1_1/point_spec.rb} +8 -8
  39. data/spec/cap_1_1/polygon_spec.rb +97 -0
  40. data/spec/{resource_spec.rb → cap_1_1/resource_spec.rb} +24 -24
  41. data/spec/cap_1_2/alert_spec.rb +233 -0
  42. data/spec/cap_1_2/area_spec.rb +248 -0
  43. data/spec/cap_1_2/circle_spec.rb +95 -0
  44. data/spec/cap_1_2/geocode_spec.rb +38 -0
  45. data/spec/cap_1_2/info_spec.rb +338 -0
  46. data/spec/cap_1_2/point_spec.rb +46 -0
  47. data/spec/cap_1_2/polygon_spec.rb +102 -0
  48. data/spec/cap_1_2/resource_spec.rb +161 -0
  49. data/spec/spec.opts +2 -0
  50. data/spec/validations_spec.rb +80 -7
  51. metadata +122 -37
  52. data/lib/rcap/area.rb +0 -156
  53. data/lib/rcap/circle.rb +0 -78
  54. data/lib/rcap/event_code.rb +0 -20
  55. data/lib/rcap/geocode.rb +0 -20
  56. data/lib/rcap/info.rb +0 -437
  57. data/lib/rcap/parameter.rb +0 -66
  58. data/lib/rcap/point.rb +0 -53
  59. data/lib/rcap/polygon.rb +0 -77
  60. data/lib/rcap/resource.rb +0 -143
  61. data/spec/area_spec.rb +0 -179
  62. data/spec/circle_spec.rb +0 -88
  63. data/spec/polygon_spec.rb +0 -68
data/.gitignore ADDED
@@ -0,0 +1,7 @@
1
+ pkg/*
2
+ doc/*
3
+ tmp/*
4
+ tags
5
+ *.gem
6
+ .bundle
7
+ Gemfile.lock
data/CHANGELOG.rdoc CHANGED
@@ -1,5 +1,11 @@
1
1
  = Change Log
2
2
 
3
+ == 1.0 - 27th March 2011
4
+
5
+ * Added CAP 1.2 Support
6
+ * Added namespaces (RCAP::CAP_1_1 and RCAP::CAP_1_2) to seperate CAP 1.1 and CAP 1.2 classes
7
+ * Added factory methods to RCAP::Alert module to parse in files and return the correct RCAP objects
8
+
3
9
  == 0.4 - 6th March 2011
4
10
 
5
11
  * Implemented Hash generation and parsing
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'http://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rcap.gemspec
4
+ gemspec
data/README.rdoc CHANGED
@@ -2,37 +2,37 @@
2
2
 
3
3
  == Overview
4
4
 
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.
5
+ The Common Alerting Protocol is a lightweight standard to facilitate the distribution of alerting data. RCAP is an implementation of the CAP document protocol in Ruby. It allows for the creation of RCAP messages from Ruby applications and the parsing of external messages.
6
6
 
7
- RCAP currently supports only CAP Version 1.1.
7
+ RCAP currently supports CAP 1.1 and 1.2.
8
8
 
9
9
  == Version
10
10
 
11
- 0.4
11
+ 1.0
12
12
 
13
13
  == Dependencies
14
14
 
15
15
  RCAP depends on the following gems
16
16
 
17
- * {Assistance}[http://assistance.rubyforge.org]
18
- * {UUIDTools}[http://uuidtools.rubyforge.org]
19
- * {JSON}[http://json.rubyforge.org]
17
+ * Assistance[http://assistance.rubyforge.org]
18
+ * UUIDTools[http://uuidtools.rubyforge.org]
19
+ * JSON[http://json.rubyforge.org]
20
20
 
21
21
  RCAP uses the REXML API, included in Ruby, to parse and generate XML.
22
22
 
23
23
  == Installation
24
24
 
25
- 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
+ RCAP is distributed as a Ruby gem and is available from Rubygems.org[http://rubygems.org].
26
26
 
27
27
  gem install rcap
28
28
 
29
- The gem is also available for download and manual installtion at http://www.aimred.com/gems .
29
+ The gem is also available for download and manual installtion at {www.aimred.com/gems}[http://www.aimred.com/gems].
30
30
 
31
31
  == Web resources
32
32
 
33
- * The RCAP project page can be found at http://www.aimred.com/projects/rcap
34
- * The RCAP API docs can be fount at http://www.aimred.com/projects/rcap/api
35
- * A public git repository can be found at git://github.com/farrel/RCAP.git
33
+ * The RCAP project page can be found at http://www.aimred.com/projects/rcap
34
+ * The RCAP API docs can be found at http://www.aimred.com/projects/rcap/api
35
+ * A public git repository can be found at git://github.com/farrel/RCAP.git
36
36
 
37
37
  == Usage
38
38
 
@@ -42,33 +42,83 @@ To include RCAP into your application add the following require
42
42
 
43
43
  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.
44
44
 
45
- alert = RCAP::Alert.new(...
46
-
47
- include RCAP # Include RCAP module into namespace
45
+ include RCAP:CAP_1_2 # Include RCAP:CAP_1_2 module into namespace
48
46
  alert = Alert.new(...
49
47
 
50
48
  === Creating an Alert
51
49
 
52
- alert = Alert.new( :sender => 'cape_town_disaster_relief@capetown.municipal.za',
53
- :status => Alert::STATUS_ACTUAL,
54
- :msg_type => Alert::MSG_TYPE_ALERT,
55
- :scope => Alert::SCOPE_PUBLIC,
56
- :infos => Info.new( :event => 'Liquid Petroleoum Tanker Fire',
57
- :language => 'en-ZA',
58
- :categories => [ Info::CATEGORY_TRANSPORT, Info::CATEGORY_FIRE ],
59
- :urgency => Info::URGENCY_IMMEDIATE,
60
- :severity => Info::SEVERITY_SEVERE,
61
- :certainty => Info::CERTAINTY_OBSERVED,
62
- :headline => 'LIQUID PETROLEOUM TANKER FIRE ON N2 INCOMING FREEWAY',
63
- :description => 'A liquid petroleoum tanker has caught fire on the N2 incoming freeway 1km
64
- after the R300 interchange. Municipal fire fighting crews have been dispatched.
65
- Traffic control officers are on the scene and have diverted traffic onto
66
- alternate routes.' ))
50
+ alert = Alert.new( sender: 'cape_town_disaster_relief@capetown.municipal.za',
51
+ status: Alert::STATUS_ACTUAL,
52
+ msg_type: Alert::MSG_TYPE_ALERT,
53
+ scope: Alert::SCOPE_PUBLIC )
54
+
55
+ alert.add_info( event: 'Liquid Petroleoum Tanker Fire',
56
+ language: 'en-ZA',
57
+ categories: [ Info::CATEGORY_TRANSPORT, Info::CATEGORY_FIRE ],
58
+ urgency: Info::URGENCY_IMMEDIATE,
59
+ severity: Info::SEVERITY_SEVERE,
60
+ certainty: Info::CERTAINTY_OBSERVED,
61
+ headline: 'LIQUID PETROLEOUM TANKER FIRE ON N2 INCOMING FREEWAY',
62
+ description: 'A liquid petroleoum tanker has caught fire on the N2 incoming freeway 1km
63
+ after the R300 interchange. Municipal fire fighting crews have been dispatched.
64
+ Traffic control officers are on the scene and have diverted traffic onto
65
+ alternate routes.' )
66
+
67
+ alert.infos.first.add_area( area_desc: 'N2 Highway/R300 Interchange' ).add_geocode( name: 'Intersection', value: 'N2-15' )
67
68
 
68
69
  # Accessing attributes
69
- puts alert.status # Print out "Actual"
70
- puts alert.infos[0].language # Print out "en-ZA"
71
- puts alert.infos[0].categories.join( ' ' ) # Print out "Transport Fire"
70
+ alert.status # "Actual"
71
+ alert.infos[0].language # "en-ZA"
72
+ alert.infos[0].categories.join( ', ' ) # "Transport, Fire"
73
+ alert.infos[0].areas[0].first # "N2 Highway/R300 Interchange"
74
+
75
+ === Parsing an Alert From An External Source
76
+
77
+ RCAP can parse a CAP alert from a varierty of file formats. Besides the {standard XML}[http://www.oasis-emergency.org/cap] representation, YAML[http://yaml.org] and JSON[http://json.org] support is also included.
78
+
79
+ To ensure the correct RCAP Alert object (RCAP::CAP_1_1::Alert or RCAP::CAP_1_2::Alert) is returned from an external source, a number of factories are defined in the RCAP::Alert module. If the version of the document to be parsed can not be ascertained a CAP 1.2 document will be assumed.
80
+
81
+ ==== From XML
82
+
83
+ RCAP allows for the parsing of a CAP XML string
84
+
85
+ alert = RCAP::Alert.from_xml( xml_string )
86
+
87
+ ==== From YAML
88
+
89
+ Alert messgaes can be read in from text files containing data formatted in YAML[http://yaml.org] as generated by Alert#to_yaml.
90
+
91
+ alert = RCAP::Alert.from_yaml( yaml_string )
92
+
93
+ ==== From JSON
94
+
95
+ An Alert can also be initialised from a JSON[http://json.org] string produced by Alert#to_json
96
+
97
+ alert = RCAP::Alert.from_json( json_string )
98
+
99
+ === Validating an alert
100
+
101
+ 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.
102
+
103
+ info = Info.new( event: 'Liquid Petroleoum Tanker Fire',
104
+ language: 'en-ZA',
105
+ categories: [ Info::CATEGORY_TRANSPORT, Info::CATEGORY_FIRE ],
106
+ urgency: Info::URGENCY_IMMEDIATE,
107
+ severity: nil, # Severity is not assigned
108
+ certainty: 'Unknown Certainty' ) # Certainty is assigned in incorrect value
109
+
110
+ puts "Is info valid: #{ info.valid? }"
111
+ info.errors.full_messages.each{ |message| puts "Error: #{ message }" }
112
+
113
+ Will produce the folling output:
114
+
115
+ Is info valid: false
116
+ Error: severity is not present
117
+ Error: certainty can only be assigned the following values: Observed, Likely, Possible, Unlikely, Unknown
118
+
119
+ All RCAP classes include the Validation module.
120
+
121
+ A full spec suite using {RSpec}[http://www.rspec.info] was used to test the validations and currently numbers over 600 tests.
72
122
 
73
123
  === Exporting an Alert
74
124
 
@@ -81,7 +131,7 @@ Using the alert message created above
81
131
  Will print the following CAP XML
82
132
 
83
133
  <?xml version='1.0'?>
84
- <alert xmlns='urn:oasis:names:tc:emergency:cap:1.1'>
134
+ <alert xmlns='urn:oasis:names:tc:emergency:cap:1.2'>
85
135
  <identifier>494207a7-f86b-4060-8318-a4b2a3ce565e</identifier>
86
136
  <sender>cape_town_disaster_relief@capetown.municipal.za</sender>
87
137
  <sent>2009-10-26T21:04:51+02:00</sent>
@@ -108,20 +158,21 @@ Will print the following CAP XML
108
158
 
109
159
  ==== To YAML
110
160
 
111
- 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
161
+ 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 friendly as possible. The following code
112
162
 
113
163
  alert.to_yaml
114
164
 
115
165
  will produce the following YAML document
116
166
 
117
167
  ---
168
+ CAP Version: 1.2
118
169
  Identifier: 2a1ba96d-16e4-4f52-85ea-0258c1440bd5
119
170
  Sender: cape_town_disaster_relief@capetown.municipal.za
120
171
  Sent: 2009-11-19T02:41:29+02:00
121
172
  Status: Actual
122
173
  Message Type: Alert
123
174
  Scope: Public
124
- Information:
175
+ Information:
125
176
  - Language: en-ZA
126
177
  Categories: [Transport, Fire]
127
178
  Event: Liquid Petroleoum Tanker Fire
@@ -135,7 +186,7 @@ will produce the following YAML document
135
186
  Traffic control officers are on the scene and have diverted traffic onto
136
187
  alternate routes.
137
188
 
138
- 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.
189
+ 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.
139
190
 
140
191
  === To JSON
141
192
 
@@ -145,13 +196,14 @@ JSON(JavaScript Object Notation) is a text serialization format that can be easi
145
196
 
146
197
  will produce the following JSON string
147
198
 
148
- {"identifier":"0eb97e40-195b-437b-9a01-55fe89691def",
199
+ {"cap_version":"1.2",
200
+ "identifier":"0eb97e40-195b-437b-9a01-55fe89691def",
149
201
  "sender":"cape_town_disaster_relief@capetown.municipal.za",
150
202
  "sent":"2011-03-04T15:58:01+02:00",
151
203
  "status":"Actual",
152
204
  "msg_type":"Alert",
153
205
  "scope":"Public",
154
- "infos":[
206
+ "infos":[
155
207
  {"language":"en-ZA",
156
208
  "categories":["Transport","Fire"],
157
209
  "event":"Liquid Petroleoum Tanker Fire",
@@ -163,64 +215,31 @@ will produce the following JSON string
163
215
  after the R300 interchange. Municipal fire fighting crews have been dispatched.
164
216
  Traffic control officers are on the scene and have diverted traffic onto \nalternate routes."}]}
165
217
 
166
- === Parsing an Alert From An External Source
167
-
168
- ==== From XML
169
-
170
- RCAP allows for the parsing of a CAP XML string
171
-
172
- alert = RCAP::Alert.from_xml( xml_string )
173
-
174
- Currently RCAP only supports version 1.1 of the CAP standard and the parser is as strict as possible when parsing data.
175
-
176
- ==== From YAML
177
-
178
- Alert messgaes can be read in from text files containing data formatted in YAML as generated by Alert#to_yaml.
179
-
180
- alert = RCAP::Alert.from_yaml( yaml_string )
181
-
182
- ==== From JSON
183
-
184
- An Alert can also be initialised from a JSON string produced by Alert#to_json
185
-
186
- alert = RCAP::Alert.from_json( json_string )
187
-
188
- === Validating an alert
189
-
190
- 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.
191
218
 
192
- info = Info.new( :event => 'Liquid Petroleoum Tanker Fire',
193
- :language => 'en-ZA',
194
- :categories => [ Info::CATEGORY_TRANSPORT, Info::CATEGORY_FIRE ],
195
- :urgency => Info::URGENCY_IMMEDIATE,
196
- :severity => nil, # Severity is not assigned
197
- :certainty => 'Unknown Certainty' ) # Certainty is assigned in incorrect value
219
+ === DateTime and Time
198
220
 
199
- puts "Is info valid: #{ info.valid? }"
200
- info.errors.full_messages.each{ |message| puts "Error: #{ message }" }
221
+ 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.
201
222
 
202
- Will produce the folling output:
223
+ == Authors
203
224
 
204
- Is info valid: false
205
- Error: severity is not present
206
- Error: certainty can only be assigned the following values: Observed, Likely, Possible, Unlikely, Unknown
225
+ * Farrel Lifson - farrel.lifson@aimred.com - http://www.aimred.com
207
226
 
208
- All RCAP classes include the Validation module.
227
+ === Contributors
209
228
 
210
- A full spec suite using {RSpec}[http://www.rspec.info] was used to test the validations and currently numbers over 250 tests.
229
+ * Earle Clubb - http://github.com/eclubb
211
230
 
212
- === DateTime and Time
231
+ == License
213
232
 
214
- 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.
233
+ RCAP is released under the BSD License.
215
234
 
216
- == Authors
235
+ Copyright 2010 - 2011 AIMRED CC. All rights reserved.
217
236
 
218
- Farrel Lifson - farrel.lifson@aimred.com
237
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
219
238
 
220
- == License
239
+ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
221
240
 
222
- RCAP is released under the BSD License.
241
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
223
242
 
224
- == Copyright
243
+ THIS SOFTWARE IS PROVIDED BY AIMRED CC ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL AIMRED CC OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
225
244
 
226
- 2009-2010 Aimred CC
245
+ The views and conclusions contained in the software and documentation are those of the authors and should not be interpreted as representing official policies, either expressed or implied, of AIMRED CC.
data/Rakefile ADDED
@@ -0,0 +1,35 @@
1
+ require 'bundler'
2
+ require 'hanna/rdoctask'
3
+ require 'rspec/core/rake_task'
4
+
5
+ Bundler::GemHelper.install_tasks
6
+
7
+ Rake::RDocTask.new do |rdoc|
8
+ rdoc.main = 'README.rdoc'
9
+ rdoc.rdoc_files.include('README.rdoc', 'CHANGELOG.rdoc', 'lib/**/*.rb')
10
+ rdoc.rdoc_dir = 'doc'
11
+ rdoc.title = 'RCAP Ruby API'
12
+ end
13
+
14
+ RSpec::Core::RakeTask.new do |spec|
15
+ spec.rspec_opts = ['--options spec/spec.opts']
16
+ end
17
+
18
+ desc 'Generate a new tag file'
19
+ task :tags do |t|
20
+ Kernel.system('ctags --recurse lib/*')
21
+ end
22
+
23
+ desc 'Clean up whitespace across the entire application (strip trailing whitespace and convert tab => 2 spaces).'
24
+ task :whitespace do
25
+ require 'rbconfig'
26
+ if Config::CONFIG['host_os'] =~ /linux/
27
+ sh %{find . -name '*.*rb' -exec sed -i 's/\t/ /g' {} \\; -exec sed -i 's/ *$//g' {} \\; }
28
+ elsif Config::CONFIG['host_os'] =~ /darwin/
29
+ sh %{find . -name '*.*rb' -exec sed -i '' 's/\t/ /g' {} \\; -exec sed -i '' 's/ *$//g' {} \\; }
30
+ else
31
+ puts "This doesn't work on systems other than OSX or Linux. Please use a custom whitespace tool for your platform '#{Config::CONFIG["host_os"]}'."
32
+ end
33
+ end
34
+
35
+ task :default => :spec
data/lib/rcap.rb CHANGED
@@ -4,20 +4,27 @@ require 'uuidtools'
4
4
  require 'yaml'
5
5
  require 'json'
6
6
  require 'rexml/document'
7
+ require 'rcap/version'
7
8
  require 'rcap/utilities'
8
9
  require 'rcap/validations'
9
10
  require 'rcap/alert'
10
- require 'rcap/parameter'
11
- require 'rcap/event_code'
12
- require 'rcap/info'
13
- require 'rcap/resource'
14
- require 'rcap/point'
15
- require 'rcap/circle'
16
- require 'rcap/polygon'
17
- require 'rcap/geocode'
18
- require 'rcap/area'
19
-
20
- module RCAP
21
- XMLNS = "urn:oasis:names:tc:emergency:cap:1.1"
22
- VERSION = "0.4"
23
- end
11
+ require 'rcap/cap_1_1/alert'
12
+ require 'rcap/cap_1_1/parameter'
13
+ require 'rcap/cap_1_1/event_code'
14
+ require 'rcap/cap_1_1/info'
15
+ require 'rcap/cap_1_1/resource'
16
+ require 'rcap/cap_1_1/point'
17
+ require 'rcap/cap_1_1/circle'
18
+ require 'rcap/cap_1_1/polygon'
19
+ require 'rcap/cap_1_1/geocode'
20
+ require 'rcap/cap_1_1/area'
21
+ require 'rcap/cap_1_2/alert'
22
+ require 'rcap/cap_1_2/parameter'
23
+ require 'rcap/cap_1_2/event_code'
24
+ require 'rcap/cap_1_2/info'
25
+ require 'rcap/cap_1_2/resource'
26
+ require 'rcap/cap_1_2/point'
27
+ require 'rcap/cap_1_2/circle'
28
+ require 'rcap/cap_1_2/polygon'
29
+ require 'rcap/cap_1_2/geocode'
30
+ require 'rcap/cap_1_2/area'
data/lib/rcap/alert.rb CHANGED
@@ -1,341 +1,42 @@
1
1
  module RCAP
2
- # An Alert object is valid if
3
- # * it has an identifier
4
- # * it has a sender
5
- # * it has a sent time
6
- # * it has a valid status value
7
- # * it has a valid messge type value
8
- # * it has a valid scope value
9
- # * all Info objects contained in infos are valid
10
- class Alert
11
- include Validation
2
+ module Alert
12
3
 
13
- STATUS_ACTUAL = "Actual"
14
- STATUS_EXERCISE = "Exercise"
15
- STATUS_SYSTEM = "System"
16
- STATUS_TEST = "Test"
17
- STATUS_DRAFT = "Draft"
18
- # Valid values for status
19
- VALID_STATUSES = [ STATUS_ACTUAL, STATUS_EXERCISE, STATUS_SYSTEM, STATUS_TEST, STATUS_DRAFT ]
4
+ XMLNS_KEY = "xmlns"
5
+ YAML_CAP_VERSION_KEY = "CAP Version"
6
+ JSON_CAP_VERSION_KEY = "cap_version"
20
7
 
21
- MSG_TYPE_ALERT = "Alert"
22
- MSG_TYPE_UPDATE = "Update"
23
- MSG_TYPE_CANCEL = "Cancel"
24
- MSG_TYPE_ACK = "Ack"
25
- MSG_TYPE_ERROR = "Error"
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 ]
8
+ def self.from_xml( xml, namespace_key = XMLNS_KEY )
9
+ xml_document = REXML::Document.new( xml )
28
10
 
29
- SCOPE_PUBLIC = "Public"
30
- SCOPE_RESTRICTED = "Restricted"
31
- SCOPE_PRIVATE = "Private"
32
- # Valid values for scope
33
- VALID_SCOPES = [ SCOPE_PUBLIC, SCOPE_PRIVATE, SCOPE_RESTRICTED ]
34
-
35
- XML_ELEMENT_NAME = 'alert' # :nodoc:
36
- IDENTIFIER_ELEMENT_NAME = 'identifier' # :nodoc:
37
- SENDER_ELEMENT_NAME = 'sender' # :nodoc:
38
- SENT_ELEMENT_NAME = 'sent' # :nodoc:
39
- STATUS_ELEMENT_NAME = 'status' # :nodoc:
40
- MSG_TYPE_ELEMENT_NAME = 'msgType' # :nodoc:
41
- SOURCE_ELEMENT_NAME = 'source' # :nodoc:
42
- SCOPE_ELEMENT_NAME = 'scope' # :nodoc:
43
- RESTRICTION_ELEMENT_NAME = 'restriction' # :nodoc:
44
- ADDRESSES_ELEMENT_NAME = 'addresses' # :nodoc:
45
- CODE_ELEMENT_NAME = 'code' # :nodoc:
46
- NOTE_ELEMENT_NAME = 'note' # :nodoc:
47
- REFERENCES_ELEMENT_NAME = 'references' # :nodoc:
48
- INCIDENTS_ELEMENT_NAME = 'incidents' # :nodoc:
49
-
50
- XPATH = 'cap:alert' # :nodoc:
51
- IDENTIFIER_XPATH = "cap:#{ IDENTIFIER_ELEMENT_NAME }" # :nodoc:
52
- SENDER_XPATH = "cap:#{ SENDER_ELEMENT_NAME }" # :nodoc:
53
- SENT_XPATH = "cap:#{ SENT_ELEMENT_NAME }" # :nodoc:
54
- STATUS_XPATH = "cap:#{ STATUS_ELEMENT_NAME }" # :nodoc:
55
- MSG_TYPE_XPATH = "cap:#{ MSG_TYPE_ELEMENT_NAME }" # :nodoc:
56
- SOURCE_XPATH = "cap:#{ SOURCE_ELEMENT_NAME }" # :nodoc:
57
- SCOPE_XPATH = "cap:#{ SCOPE_ELEMENT_NAME }" # :nodoc:
58
- RESTRICTION_XPATH = "cap:#{ RESTRICTION_ELEMENT_NAME }" # :nodoc:
59
- ADDRESSES_XPATH = "cap:#{ ADDRESSES_ELEMENT_NAME }" # :nodoc:
60
- CODE_XPATH = "cap:#{ CODE_ELEMENT_NAME }" # :nodoc:
61
- NOTE_XPATH = "cap:#{ NOTE_ELEMENT_NAME }" # :nodoc:
62
- REFERENCES_XPATH = "cap:#{ REFERENCES_ELEMENT_NAME }" # :nodoc:
63
- INCIDENTS_XPATH = "cap:#{ INCIDENTS_ELEMENT_NAME }" # :nodoc:
64
-
65
- # If not set a UUID will be set by default
66
- attr_accessor( :identifier)
67
- attr_accessor( :sender )
68
- # Sent Time - If not set will value will be time of creation.
69
- attr_accessor( :sent )
70
- # Value can only be one of VALID_STATUSES
71
- attr_accessor( :status )
72
- # Value can only be one of VALID_MSG_TYPES
73
- attr_accessor( :msg_type )
74
- # Value can only be one of VALID_SCOPES
75
- attr_accessor( :scope )
76
- attr_accessor( :source )
77
- # Depends on scope being SCOPE_RESTRICTED.
78
- attr_accessor( :restriction )
79
- attr_accessor( :code )
80
- attr_accessor( :note )
81
-
82
- # Collection of address strings. Depends on scope being SCOPE_PRIVATE.
83
- attr_reader( :addresses )
84
- # Collection of reference strings - see Alert#to_reference
85
- attr_reader( :references)
86
- # Collection of incident strings
87
- attr_reader( :incidents )
88
- # Collection of Info objects
89
- attr_reader( :infos )
90
-
91
- validates_presence_of( :identifier, :sender, :sent, :status, :msg_type, :scope )
92
-
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 )
96
-
97
- validates_format_of( :identifier, :with => ALLOWED_CHARACTERS )
98
- validates_format_of( :sender , :with => ALLOWED_CHARACTERS )
99
-
100
- validates_dependency_of( :addresses, :on => :scope, :with_value => SCOPE_PRIVATE )
101
- validates_dependency_of( :restriction, :on => :scope, :with_value => SCOPE_RESTRICTED )
102
-
103
- validates_collection_of( :infos )
104
-
105
- def initialize( attributes = {})
106
- @identifier = attributes[ :identifier ] || UUIDTools::UUID.random_create.to_s
107
- @sender = attributes[ :sender ]
108
- @sent = attributes[ :sent ] || DateTime.now
109
- @status = attributes[ :status ]
110
- @msg_type = attributes[ :msg_type ]
111
- @scope = attributes[ :scope ]
112
- @source = attributes[ :source ]
113
- @restriction = attributes[ :restriction ]
114
- @addresses = Array( attributes[ :addresses ])
115
- @references = Array( attributes[ :references ])
116
- @incidents = Array( attributes[ :incidents ])
117
- @infos = Array( attributes[ :infos ])
118
- end
119
-
120
- def to_xml_element #:nodoc:
121
- xml_element = REXML::Element.new( XML_ELEMENT_NAME )
122
- xml_element.add_namespace( RCAP::XMLNS )
123
- xml_element.add_element( IDENTIFIER_ELEMENT_NAME ).add_text( self.identifier )
124
- xml_element.add_element( SENDER_ELEMENT_NAME ).add_text( self.sender )
125
- xml_element.add_element( SENT_ELEMENT_NAME ).add_text( self.sent.to_s )
126
- xml_element.add_element( STATUS_ELEMENT_NAME ).add_text( self.status )
127
- xml_element.add_element( MSG_TYPE_ELEMENT_NAME ).add_text( self.msg_type )
128
- xml_element.add_element( SOURCE_ELEMENT_NAME ).add_text( self.source ) if self.source
129
- xml_element.add_element( SCOPE_ELEMENT_NAME ).add_text( self.scope )
130
- xml_element.add_element( RESTRICTION_ELEMENT_NAME ).add_text( self.restriction ) if self.restriction
131
- unless self.addresses.empty?
132
- xml_element.add_element( ADDRESSES_ELEMENT_NAME ).add_text( self.addresses.to_s_for_cap )
133
- end
134
- xml_element.add_element( CODE_ELEMENT_NAME ).add_text( self.code ) if self.code
135
- xml_element.add_element( NOTE_ELEMENT_NAME ).add_text( self.note ) if self.note
136
- unless self.references.empty?
137
- xml_element.add_element( REFERENCES_ELEMENT_NAME ).add_text( self.references.join( ' ' ))
138
- end
139
- unless self.incidents.empty?
140
- xml_element.add_element( INCIDENTS_ELEMENT_NAME ).add_text( self.incidents.join( ' ' ))
11
+ case xml_document.root.namespaces[ namespace_key ]
12
+ when CAP_1_1::Alert::XMLNS
13
+ CAP_1_1::Alert.from_xml_document( xml_document )
14
+ else
15
+ CAP_1_2::Alert.from_xml_document( xml_document )
141
16
  end
142
- self.infos.each do |info|
143
- xml_element.add_element( info.to_xml_element )
144
- end
145
- xml_element
146
- end
147
-
148
- def to_xml_document #:nodoc:
149
- xml_document = REXML::Document.new
150
- xml_document.add( REXML::XMLDecl.new )
151
- xml_document.add( self.to_xml_element )
152
- xml_document
153
- end
154
-
155
- # Returns a string containing the XML representation of the alert.
156
- def to_xml
157
- self.to_xml_document.to_s
158
- end
159
-
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
162
- def to_reference
163
- "#{ self.sender },#{ self.identifier },#{ self.sent }"
164
- end
165
17
 
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
18
  end
186
19
 
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
-
194
- def self.from_xml_element( alert_xml_element ) # :nodoc:
195
- self.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 )})
209
- end
210
-
211
- def self.from_xml_document( xml_document ) # :nodoc:
212
- self.from_xml_element( xml_document.root )
213
- end
214
-
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 ))
218
- end
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_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
20
  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
-
279
- IDENTIFIER_KEY = 'identifier' # :nodoc:
280
- SENDER_KEY = 'sender' # :nodoc:
281
- SENT_KEY = 'sent' # :nodoc:
282
- STATUS_KEY = 'status' # :nodoc:
283
- MSG_TYPE_KEY = 'msg_type' # :nodoc:
284
- SOURCE_KEY = 'source' # :nodoc:
285
- SCOPE_KEY = 'scope' # :nodoc:
286
- RESTRICTION_KEY = 'restriction' # :nodoc:
287
- ADDRESSES_KEY = 'addresses' # :nodoc:
288
- CODE_KEY = 'code' # :nodoc:
289
- NOTE_KEY = 'note' # :nodoc:
290
- REFERENCES_KEY = 'references' # :nodoc:
291
- INCIDENTS_KEY = 'incidents' # :nodoc:
292
- INFOS_KEY = 'infos' # :nodoc:
293
-
294
- # Returns a Hash representation of an Alert object
295
- def to_h
296
- RCAP.attribute_values_to_hash([ IDENTIFIER_KEY, self.identifier ],
297
- [ SENDER_KEY, self.sender ],
298
- [ SENT_KEY, RCAP.to_s_for_cap( self.sent )],
299
- [ STATUS_KEY, self.status ],
300
- [ MSG_TYPE_KEY, self.msg_type ],
301
- [ SOURCE_KEY, self.source ],
302
- [ SCOPE_KEY, self.scope ],
303
- [ RESTRICTION_KEY, self.restriction ],
304
- [ ADDRESSES_KEY, self.addresses ],
305
- [ CODE_KEY, self.code ],
306
- [ NOTE_KEY, self.note ],
307
- [ REFERENCES_KEY, self.references ],
308
- [ INCIDENTS_KEY, self.incidents ],
309
- [ INFOS_KEY, self.infos.map{ |info| info.to_h }])
310
- end
21
+ yaml_data = YAML.load( yaml )
311
22
 
312
- # Initialises an Alert object from a Hash produced by Alert#to_h
313
- def self.from_h( alert_hash )
314
- self.new(
315
- :identifier => alert_hash[ IDENTIFIER_KEY ],
316
- :sender => alert_hash[ SENDER_KEY ],
317
- :sent => RCAP.parse_datetime( alert_hash[ SENT_KEY ]),
318
- :status => alert_hash[ STATUS_KEY ],
319
- :msg_type => alert_hash[ MSG_TYPE_KEY ],
320
- :source => alert_hash[ SOURCE_KEY ],
321
- :scope => alert_hash[ SCOPE_KEY ],
322
- :restriction => alert_hash[ RESTRICTION_KEY ],
323
- :addresses => alert_hash[ ADDRESSES_KEY ],
324
- :code => alert_hash[ CODE_KEY ],
325
- :note => alert_hash[ NOTE_KEY ],
326
- :references => alert_hash[ REFERENCES_KEY ],
327
- :incidents => alert_hash[ INCIDENTS_KEY ],
328
- :infos => Array( alert_hash[ INFOS_KEY ]).map{ |info_hash| RCAP::Info.from_h( info_hash )})
329
- end
330
-
331
- # Returns a JSON string representation of an Alert object
332
- def to_json
333
- self.to_h.to_json
23
+ case yaml_data[ YAML_CAP_VERSION_KEY ]
24
+ when CAP_1_1::Alert::CAP_VERSION
25
+ CAP_1_1::Alert.from_yaml_data( yaml_data )
26
+ else
27
+ CAP_1_2::Alert.from_yaml_data( yaml_data )
28
+ end
334
29
  end
335
30
 
336
- # Initiialises an Alert object from a JSON string produced by Alert#to_json
337
31
  def self.from_json( json_string )
338
- self.from_h( JSON.parse( json_string ))
32
+ json_hash = JSON.parse( json_string )
33
+
34
+ case json_hash[ JSON_CAP_VERSION_KEY ]
35
+ when CAP_1_1::Alert::CAP_VERSION
36
+ CAP_1_1::Alert.from_h( json_hash )
37
+ else
38
+ CAP_1_2::Alert.from_h( json_hash )
39
+ end
339
40
  end
340
41
  end
341
42
  end