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
@@ -0,0 +1,180 @@
1
+ module RCAP
2
+ module CAP_1_2
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 )
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 = <<EOF
99
+ Area Description: #{ self.area_desc }
100
+ Polygons:
101
+ #{ self.polygons.map{ |polygon| " " + polygon.inspect }.join("\n" )}
102
+ Circles: #{ self.circles.inspect }
103
+ Geocodes: #{ self.geocodes.inspect }
104
+ EOF
105
+ RCAP.format_lines_for_inspect( 'AREA', area_inspect )
106
+ end
107
+
108
+ # Returns a string representation of the area of the form
109
+ # area_desc
110
+ def to_s
111
+ self.area_desc
112
+ end
113
+
114
+ def self.from_xml_element( area_xml_element ) # :nodoc:
115
+ self.new( :area_desc => RCAP.xpath_text( area_xml_element, AREA_DESC_XPATH, Alert::XMLNS ),
116
+ :altitude => (( alt = RCAP.xpath_text( area_xml_element, ALTITUDE_XPATH, Alert::XMLNS )) ? alt.to_f : nil ),
117
+ :ceiling => (( ceil = RCAP.xpath_text( area_xml_element, CEILING_XPATH, Alert::XMLNS )) ? ceil.to_f : nil ),
118
+ :circles => RCAP.xpath_match( area_xml_element, Circle::XPATH, Alert::XMLNS ).map{ |circle_element| Circle.from_xml_element( circle_element )},
119
+ :geocodes => RCAP.xpath_match( area_xml_element, Geocode::XPATH, Alert::XMLNS ).map{ |geocode_element| Geocode.from_xml_element( geocode_element )},
120
+ :polygons => RCAP.xpath_match( area_xml_element, Polygon::XPATH, Alert::XMLNS ).map{ |polygon_element| Polygon.from_xml_element( polygon_element )})
121
+ end
122
+
123
+ AREA_DESC_YAML = 'Area Description' # :nodoc:
124
+ ALTITUDE_YAML = 'Altitude' # :nodoc:
125
+ CEILING_YAML = 'Ceiling' # :nodoc:
126
+ CIRCLES_YAML = 'Circles' # :nodoc:
127
+ GEOCODES_YAML = 'Geocodes' # :nodoc:
128
+ POLYGONS_YAML = 'Polygons' # :nodoc:
129
+
130
+ def to_yaml( options = {} ) # :nodoc:
131
+ circles_yaml = self.circles.map{ |circle| [ circle.lattitude, circle.longitude, circle.radius ]}
132
+ def circles_yaml.to_yaml_style; :inline; end
133
+
134
+ RCAP.attribute_values_to_hash(
135
+ [ AREA_DESC_YAML, self.area_desc ],
136
+ [ ALTITUDE_YAML, self.altitude ],
137
+ [ CEILING_YAML, self.ceiling ],
138
+ [ CIRCLES_YAML, circles_yaml ],
139
+ [ GEOCODES_YAML, self.geocodes.inject({}){|h,geocode| h.merge( geocode.name => geocode.value )}],
140
+ [ POLYGONS_YAML, self.polygons ]
141
+ ).to_yaml( options )
142
+ end
143
+
144
+ def self.from_yaml_data( area_yaml_data ) # :nodoc:
145
+ self.new( :area_desc => area_yaml_data[ AREA_DESC_YAML ],
146
+ :altitude => area_yaml_data[ ALTITUDE_YAML ],
147
+ :ceiling => area_yaml_data[ CEILING_YAML ],
148
+ :circles => Array( area_yaml_data[ CIRCLES_YAML ]).map{ |circle_yaml_data| Circle.from_yaml_data( circle_yaml_data )},
149
+ :geocodes => Array( area_yaml_data[ GEOCODES_YAML ]).map{ |name, value| Geocode.new( :name => name, :value => value )},
150
+ :polygons => Array( area_yaml_data[ POLYGONS_YAML ]).map{ |polyon_yaml_data| Polygon.from_yaml_data( polyon_yaml_data )})
151
+ end
152
+
153
+ AREA_DESC_KEY = 'area_desc' # :nodoc:
154
+ ALTITUDE_KEY = 'altitude' # :nodoc:
155
+ CEILING_KEY = 'ceiling' # :nodoc:
156
+ CIRCLES_KEY = 'circles' # :nodoc:
157
+ GEOCODES_KEY = 'geocodes' # :nodoc:
158
+ POLYGONS_KEY = 'polygons' # :nodoc:
159
+
160
+ def to_h # :nodoc:
161
+ { AREA_DESC_KEY => self.area_desc,
162
+ ALTITUDE_KEY => self.altitude,
163
+ CEILING_KEY => self.ceiling,
164
+ CIRCLES_KEY => self.circles.map{ |circle| circle.to_h },
165
+ GEOCODES_KEY => self.geocodes.map{ |geocode| geocode.to_h },
166
+ POLYGONS_KEY => self.polygons.map{ |polygon| polygon.to_h }}
167
+ end
168
+
169
+ def self.from_h( area_hash ) # :nodoc:
170
+ self.new(
171
+ :area_desc => area_hash[ AREA_DESC_KEY ],
172
+ :altitude => area_hash[ ALTITUDE_KEY ],
173
+ :ceiling => area_hash[ CEILING_KEY ],
174
+ :circles => area_hash[ CIRCLES_KEY ].map{ |circle_hash| Circle.from_h( circle_hash )},
175
+ :geocodes => area_hash[ GEOCODES_KEY ].map{ |geocode_hash| Geocode.from_h( geocode_hash )},
176
+ :polygons => area_hash[ POLYGONS_KEY ].map{ |polygon_hash| Polygon.from_h( polygon_hash )})
177
+ end
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,81 @@
1
+ module RCAP
2
+ module CAP_1_2
3
+ # A Circle object is valid if
4
+ # * it has a valid lattitude and longitude
5
+ # * it has a radius with a value greater than zero
6
+ class Circle < Point
7
+ include Validation
8
+
9
+ # Expresed in kilometers
10
+ attr_accessor( :radius )
11
+
12
+ validates_presence_of( :radius )
13
+ validates_numericality_of( :radius , :greater_than => 0 )
14
+
15
+ XML_ELEMENT_NAME = 'circle' # :nodoc:
16
+
17
+ XPATH = 'cap:circle' # :nodoc:
18
+
19
+ def initialize( attributes = {} )
20
+ super( attributes )
21
+ @radius = attributes[ :radius ]
22
+ end
23
+
24
+ # Returns a string representation of the circle of the form
25
+ # lattitude,longitude,radius
26
+ def to_s # :nodoc:
27
+ "#{ self.lattitude },#{ self.longitude } #{ self.radius }"
28
+ end
29
+
30
+ def inspect # :nodoc:
31
+ "(#{ self.to_s })"
32
+ end
33
+
34
+ def to_xml_element # :nodoc:
35
+ xml_element = REXML::Element.new( XML_ELEMENT_NAME )
36
+ xml_element.add_text( self.to_s )
37
+ xml_element
38
+ end
39
+
40
+ def to_xml # :nodoc:
41
+ self.to_xml_element.to_s
42
+ end
43
+
44
+ def self.parse_circle_string( circle_string ) # :nodoc:
45
+ coordinates, radius = circle_string.split( ' ' )
46
+ lattitude, longitude = coordinates.split( ',' )
47
+ [ lattitude, longitude, radius ].map{ |e| e.to_f }
48
+ end
49
+
50
+ def self.from_xml_element( circle_xml_element ) # :nodoc:
51
+ lattitude, longitude, radius = self.parse_circle_string( circle_xml_element.text )
52
+ circle = self.new( :lattitude => lattitude,
53
+ :longitude => longitude,
54
+ :radius => radius )
55
+ end
56
+
57
+ # Two circles are equivalent if their lattitude, longitude and radius are equal.
58
+ def ==( other )
59
+ [ self.lattitude, self.longitude, self.radius ] == [ other.lattitude, other.longitude, other.radius ]
60
+ end
61
+
62
+ def self.from_yaml_data( circle_yaml_data ) # :nodoc:
63
+ lattitude, longitude,radius = circle_yaml_data
64
+ self.new( :lattitude => lattitude, :longitude => longitude, :radius => radius )
65
+ end
66
+
67
+ RADIUS_KEY = 'radius' # :nodoc:
68
+ LATTITUDE_KEY = 'lattitude' # :nodoc:
69
+ LONGITUDE_KEY = 'longitude' # :nodoc:
70
+ def to_h # :nodoc:
71
+ RCAP.attribute_values_to_hash( [ RADIUS_KEY, self.radius ],
72
+ [ LATTITUDE_KEY, self.lattitude ],
73
+ [ LONGITUDE_KEY, self.longitude ])
74
+ end
75
+
76
+ def self.from_h( circle_hash ) # :nodoc:
77
+ self.new( :radius => circle_hash[ RADIUS_KEY ], :lattitude => circle_hash[ LATTITUDE_KEY ], :longitude => circle_hash[ LONGITUDE_KEY ])
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,22 @@
1
+ module RCAP
2
+ module CAP_1_2
3
+ class EventCode < Parameter
4
+
5
+ XML_ELEMENT_NAME = 'eventCode' # :nodoc:
6
+
7
+ XPATH = "cap:#{ XML_ELEMENT_NAME }" # :nodoc:
8
+
9
+ def to_xml_element # :nodoc:
10
+ xml_element = REXML::Element.new( XML_ELEMENT_NAME )
11
+ xml_element.add_element( NAME_ELEMENT_NAME ).add_text( self.name )
12
+ xml_element.add_element( VALUE_ELEMENT_NAME ).add_text( self.value )
13
+ xml_element
14
+ end
15
+
16
+ def self.from_xml_element( event_code_xml_element ) # :nodoc:
17
+ EventCode.new( :name => RCAP.xpath_text( event_code_xml_element, NAME_XPATH, Alert::XMLNS ),
18
+ :value => RCAP.xpath_text( event_code_xml_element, VALUE_XPATH, Alert::XMLNS ))
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ module RCAP
2
+ module CAP_1_2
3
+ class Geocode < Parameter
4
+
5
+ XML_ELEMENT_NAME = 'geocode' # :nodoc:
6
+
7
+ XPATH = "cap:#{ XML_ELEMENT_NAME }" # :nodoc:
8
+
9
+ def to_xml_element # :nodoc:
10
+ xml_element = REXML::Element.new( XML_ELEMENT_NAME )
11
+ xml_element.add_element( NAME_ELEMENT_NAME ).add_text( @name )
12
+ xml_element.add_element( VALUE_ELEMENT_NAME ).add_text( @value )
13
+ xml_element
14
+ end
15
+
16
+ def self.from_xml_element( geocode_xml_element ) # :nodoc:
17
+ self.new( :name => RCAP.xpath_text( geocode_xml_element, NAME_XPATH, Alert::XMLNS ),
18
+ :value => RCAP.xpath_text( geocode_xml_element, VALUE_XPATH, Alert::XMLNS ))
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,472 @@
1
+ module RCAP
2
+ module CAP_1_2
3
+ # In Info object is valid if
4
+ # * it has an event
5
+ # * it has an urgency with a valid value
6
+ # * it has a severity with a valid value
7
+ # * it has a certainty with a valid value
8
+ # * all categories are valid and categories has at minimum 1 entry
9
+ # * all Resource objects in the resources collection are valid
10
+ # * all Area objects in the areas collection are valid
11
+ class Info
12
+ include Validation
13
+
14
+ CATEGORY_GEO = "Geo" # :nodoc:
15
+ CATEGORY_MET = "Met" # :nodoc:
16
+ CATEGORY_SAFETY = "Safety" # :nodoc:
17
+ CATEGORY_SECURITY = "Security" # :nodoc:
18
+ CATEGORY_RESCUE = "Rescue" # :nodoc:
19
+ CATEGORY_FIRE = "Fire" # :nodoc:
20
+ CATEGORY_HEALTH = "Health" # :nodoc:
21
+ CATEGORY_ENV = "Env" # :nodoc:
22
+ CATEGORY_TRANSPORT = "Transport" # :nodoc:
23
+ CATEGORY_INFRA = "Infra" # :nodoc:
24
+ CATEGORY_CBRNE = "CBRNE" # :nodoc:
25
+ CATEGORY_OTHER = "Other" # :nodoc:
26
+ # Valid values for categories
27
+ VALID_CATEGORIES = [ CATEGORY_GEO, CATEGORY_MET, CATEGORY_SAFETY,
28
+ CATEGORY_SECURITY, CATEGORY_RESCUE, CATEGORY_FIRE, CATEGORY_HEALTH,
29
+ CATEGORY_ENV, CATEGORY_TRANSPORT, CATEGORY_INFRA, CATEGORY_CBRNE,
30
+ CATEGORY_OTHER ]
31
+
32
+ RESPONSE_TYPE_SHELTER = "Shelter" # :nodoc:
33
+ RESPONSE_TYPE_EVACUATE = "Evacuate" # :nodoc:
34
+ RESPONSE_TYPE_PREPARE = "Prepare" # :nodoc:
35
+ RESPONSE_TYPE_EXECUTE = "Execute" # :nodoc:
36
+ RESPONSE_TYPE_MONITOR = "Monitor" # :nodoc:
37
+ RESPONSE_TYPE_ASSESS = "Assess" # :nodoc:
38
+ RESPONSE_TYPE_ALL_CLEAR = "All Clear" # :nodoc:
39
+ RESPONSE_TYPE_AVOID = "Avoid" # :nodoc:
40
+ RESPONSE_TYPE_NONE = "None" # :nodoc:
41
+ # Valid values for response_type
42
+ VALID_RESPONSE_TYPES = [ RESPONSE_TYPE_SHELTER, RESPONSE_TYPE_EVACUATE,
43
+ RESPONSE_TYPE_PREPARE, RESPONSE_TYPE_EXECUTE, RESPONSE_TYPE_MONITOR,
44
+ RESPONSE_TYPE_ASSESS, RESPONSE_TYPE_AVOID, RESPONSE_TYPE_ALL_CLEAR, RESPONSE_TYPE_NONE ]
45
+
46
+ URGENCY_IMMEDIATE = "Immediate" # :nodoc:
47
+ URGENCY_EXPECTED = "Expected" # :nodoc:
48
+ URGENCY_FUTURE = "Future" # :nodoc:
49
+ URGENCY_PAST = "Past" # :nodoc:
50
+ URGENCY_UNKNOWN = "Unknown" # :nodoc:
51
+ # Valid values for urgency
52
+ VALID_URGENCIES = [ URGENCY_IMMEDIATE, URGENCY_EXPECTED, URGENCY_FUTURE,
53
+ URGENCY_PAST, URGENCY_UNKNOWN ]
54
+
55
+ SEVERITY_EXTREME = "Extreme" # :nodoc:
56
+ SEVERITY_SEVERE = "Severe" # :nodoc:
57
+ SEVERITY_MODERATE = "Moderate" # :nodoc:
58
+ SEVERITY_MINOR = "Minor" # :nodoc:
59
+ SEVERITY_UNKNOWN = "Unknown" # :nodoc:
60
+ # Valid values for severity
61
+ VALID_SEVERITIES = [ SEVERITY_EXTREME, SEVERITY_SEVERE, SEVERITY_MODERATE,
62
+ SEVERITY_MINOR, SEVERITY_UNKNOWN ]
63
+
64
+ CERTAINTY_OBSERVED = "Observed" # :nodoc:
65
+ CERTAINTY_LIKELY = "Likely" # :nodoc:
66
+ CERTAINTY_POSSIBLE = "Possible" # :nodoc:
67
+ CERTAINTY_UNLIKELY = "Unlikely" # :nodoc:
68
+ CERTAINTY_UNKNOWN = "Unknown" # :nodoc:
69
+ # Valid valies for certainty
70
+ VALID_CERTAINTIES = [ CERTAINTY_OBSERVED, CERTAINTY_LIKELY,
71
+ CERTAINTY_POSSIBLE, CERTAINTY_UNLIKELY, CERTAINTY_UNKNOWN ]
72
+
73
+ XML_ELEMENT_NAME = 'info' # :nodoc:
74
+ LANGUAGE_ELEMENT_NAME = 'language' # :nodoc:
75
+ CATEGORY_ELEMENT_NAME = 'category' # :nodoc:
76
+ EVENT_ELEMENT_NAME = 'event' # :nodoc:
77
+ RESPONSE_TYPE_ELEMENT_NAME = 'responseType' # :nodoc:
78
+ URGENCY_ELEMENT_NAME = 'urgency' # :nodoc:
79
+ SEVERITY_ELEMENT_NAME = 'severity' # :nodoc:
80
+ CERTAINTY_ELEMENT_NAME = 'certainty' # :nodoc:
81
+ AUDIENCE_ELEMENT_NAME = 'audience' # :nodoc:
82
+ EVENT_CODE_ELEMENT_NAME = 'eventCode' # :nodoc:
83
+ EFFECTIVE_ELEMENT_NAME = 'effective' # :nodoc:
84
+ ONSET_ELEMENT_NAME = 'onset' # :nodoc:
85
+ EXPIRES_ELEMENT_NAME = 'expires' # :nodoc:
86
+ SENDER_NAME_ELEMENT_NAME = 'senderName' # :nodoc:
87
+ HEADLINE_ELEMENT_NAME = 'headline' # :nodoc:
88
+ DESCRIPTION_ELEMENT_NAME = 'description' # :nodoc:
89
+ INSTRUCTION_ELEMENT_NAME = 'instruction' # :nodoc:
90
+ WEB_ELEMENT_NAME = 'web' # :nodoc:
91
+ CONTACT_ELEMENT_NAME = 'contact' # :nodoc:
92
+
93
+ XPATH = "cap:#{ XML_ELEMENT_NAME }" # :nodoc:
94
+ LANGUAGE_XPATH = "cap:#{ LANGUAGE_ELEMENT_NAME }" # :nodoc:
95
+ EVENT_XPATH = "cap:#{ EVENT_ELEMENT_NAME }" # :nodoc:
96
+ URGENCY_XPATH = "cap:#{ URGENCY_ELEMENT_NAME }" # :nodoc:
97
+ RESPONSE_TYPE_XPATH = "cap:#{ RESPONSE_TYPE_ELEMENT_NAME }" # :nodoc:
98
+ CATEGORY_XPATH = "cap:#{ CATEGORY_ELEMENT_NAME }" # :nodoc:
99
+ SEVERITY_XPATH = "cap:#{ SEVERITY_ELEMENT_NAME }" # :nodoc:
100
+ CERTAINTY_XPATH = "cap:#{ CERTAINTY_ELEMENT_NAME }" # :nodoc:
101
+ AUDIENCE_XPATH = "cap:#{ AUDIENCE_ELEMENT_NAME }" # :nodoc:
102
+ EVENT_CODE_XPATH = "cap:#{ EVENT_CODE_ELEMENT_NAME }" # :nodoc:
103
+ EFFECTIVE_XPATH = "cap:#{ EFFECTIVE_ELEMENT_NAME }" # :nodoc:
104
+ ONSET_XPATH = "cap:#{ ONSET_ELEMENT_NAME }" # :nodoc:
105
+ EXPIRES_XPATH = "cap:#{ EXPIRES_ELEMENT_NAME }" # :nodoc:
106
+ SENDER_NAME_XPATH = "cap:#{ SENDER_NAME_ELEMENT_NAME }" # :nodoc:
107
+ HEADLINE_XPATH = "cap:#{ HEADLINE_ELEMENT_NAME }" # :nodoc:
108
+ DESCRIPTION_XPATH = "cap:#{ DESCRIPTION_ELEMENT_NAME }" # :nodoc:
109
+ INSTRUCTION_XPATH = "cap:#{ INSTRUCTION_ELEMENT_NAME }" # :nodoc:
110
+ WEB_XPATH = "cap:#{ WEB_ELEMENT_NAME }" # :nodoc:
111
+ CONTACT_XPATH = "cap:#{ CONTACT_ELEMENT_NAME }" # :nodoc:
112
+
113
+ DEFAULT_LANGUAGE = 'en-US'
114
+
115
+ validates_presence_of( :event, :urgency, :severity, :certainty )
116
+ validates_length_of( :categories, :minimum => 1 )
117
+ validates_inclusion_of( :certainty, :allow_nil => true, :in => VALID_CERTAINTIES, :message => "can only be assigned the following values: #{ VALID_CERTAINTIES.join(', ') }")
118
+ validates_inclusion_of( :severity, :allow_nil => true, :in => VALID_SEVERITIES, :message => "can only be assigned the following values: #{ VALID_SEVERITIES.join(', ') }" )
119
+ validates_inclusion_of( :urgency, :allow_nil => true, :in => VALID_URGENCIES, :message => "can only be assigned the following values: #{ VALID_URGENCIES.join(', ') }" )
120
+ validates_inclusion_of_members_of( :response_types, :in => VALID_RESPONSE_TYPES, :allow_blank => true )
121
+ validates_inclusion_of_members_of( :categories, :in => VALID_CATEGORIES, :allow_blank => true )
122
+ validates_collection_of( :resources, :areas, :event_codes, :parameters )
123
+
124
+ attr_accessor( :event )
125
+ # Value can only be one of VALID_URGENCIES
126
+ attr_accessor( :urgency )
127
+ # Value can only be one of VALID_SEVERITIES
128
+ attr_accessor( :severity )
129
+ # Value can only be one of VALID_CERTAINTIES
130
+ attr_accessor( :certainty )
131
+ attr_accessor( :language )
132
+ attr_accessor( :audience )
133
+ # Effective start time of information
134
+ attr_accessor( :effective )
135
+ # Expected start of event
136
+ attr_accessor( :onset )
137
+ # Effective expiry time of information
138
+ attr_accessor( :expires )
139
+ attr_accessor( :sender_name )
140
+ attr_accessor( :headline )
141
+ attr_accessor( :description )
142
+ attr_accessor( :instruction )
143
+ attr_accessor( :web )
144
+ attr_accessor( :contact )
145
+
146
+ # Collection of textual categories; elements can be one of VALID_CATEGORIES
147
+ attr_reader( :categories )
148
+ # Collection of textual response types
149
+ attr_reader( :response_types )
150
+ # Collectoin of EventCode objects
151
+ attr_reader( :event_codes )
152
+ # Collection of Parameter objects
153
+ attr_reader( :parameters )
154
+ # Collection of Resource objects
155
+ attr_reader( :resources )
156
+ # Collection of Area objects
157
+ attr_reader( :areas )
158
+
159
+ def initialize( attributes = {} )
160
+ @language = attributes[ :language ] || DEFAULT_LANGUAGE
161
+ @categories = Array( attributes[ :categories ])
162
+ @event = attributes [ :event ]
163
+ @response_types = Array( attributes[ :response_types ])
164
+ @urgency = attributes[ :urgency ]
165
+ @severity = attributes[ :severity ]
166
+ @certainty = attributes[ :certainty ]
167
+ @effective = attributes[ :effective ]
168
+ @onset = attributes[ :onset ]
169
+ @expires = attributes[ :expires ]
170
+ @event_codes = Array( attributes[ :event_codes ])
171
+ @sender_name = attributes[ :sender_name ]
172
+ @headline = attributes[ :headline ]
173
+ @description = attributes[ :description ]
174
+ @instruction = attributes[ :instruction ]
175
+ @web = attributes[ :web ]
176
+ @contact = attributes[ :contact ]
177
+ @parameters = Array( attributes[ :parameters ])
178
+ @resources = Array( attributes[ :resources ])
179
+ @areas = Array( attributes[ :areas ])
180
+ end
181
+
182
+ # Creates a new EventCode object and adds it to the event_codes array. The
183
+ # event_code_attributes are passed as a parameter to EventCode.new.
184
+ def add_event_code( event_code_attributes = {})
185
+ event_code = EventCode.new( event_code_attributes )
186
+ self.event_codes << event_code
187
+ event_code
188
+ end
189
+
190
+ # Creates a new Parameter object and adds it to the parameters array. The
191
+ # parameter_attributes are passed as a parameter to Parameter.new.
192
+ def add_parameter( parameter_attributes = {})
193
+ parameter = Parameter.new( parameter_attributes )
194
+ self.parameters << parameter
195
+ parameter
196
+ end
197
+
198
+ # Creates a new Resource object and adds it to the resources array. The
199
+ # resource_attributes are passed as a parameter to Resource.new.
200
+ def add_resource( resource_attributes = {})
201
+ resource = Resource.new( resource_attributes )
202
+ self.resources << resource
203
+ resource
204
+ end
205
+
206
+ # Creates a new Area object and adds it to the areas array. The
207
+ # area_attributes are passed as a parameter to Area.new.
208
+ def add_area( area_attributes = {})
209
+ area = Area.new( area_attributes )
210
+ self.areas << area
211
+ area
212
+ end
213
+
214
+ def to_xml_element # :nodoc:
215
+ xml_element = REXML::Element.new( XML_ELEMENT_NAME )
216
+ xml_element.add_element( LANGUAGE_ELEMENT_NAME ).add_text( self.language ) if self.language
217
+ @categories.each do |category|
218
+ xml_element.add_element( CATEGORY_ELEMENT_NAME ).add_text( category )
219
+ end
220
+ xml_element.add_element( EVENT_ELEMENT_NAME ).add_text( self.event )
221
+ @response_types.each do |response_type|
222
+ xml_element.add_element( RESPONSE_TYPE_ELEMENT_NAME ).add_text( response_type )
223
+ end
224
+ xml_element.add_element( URGENCY_ELEMENT_NAME ).add_text( self.urgency )
225
+ xml_element.add_element( SEVERITY_ELEMENT_NAME ).add_text( self.severity )
226
+ xml_element.add_element( CERTAINTY_ELEMENT_NAME ).add_text( self.certainty )
227
+ xml_element.add_element( AUDIENCE_ELEMENT_NAME ).add_text( self.audience ) if self.audience
228
+ @event_codes.each do |event_code|
229
+ xml_element.add_element( event_code.to_xml_element )
230
+ end
231
+ xml_element.add_element( EFFECTIVE_ELEMENT_NAME ).add_text( self.effective.to_s_for_cap ) if self.effective
232
+ xml_element.add_element( ONSET_ELEMENT_NAME ).add_text( self.onset.to_s_for_cap ) if self.onset
233
+ xml_element.add_element( EXPIRES_ELEMENT_NAME ).add_text( self.expires.to_s_for_cap ) if self.expires
234
+ xml_element.add_element( SENDER_NAME_ELEMENT_NAME ).add_text( self.sender_name ) if self.sender_name
235
+ xml_element.add_element( HEADLINE_ELEMENT_NAME ).add_text( self.headline ) if self.headline
236
+ xml_element.add_element( DESCRIPTION_ELEMENT_NAME ).add_text( self.description ) if self.description
237
+ xml_element.add_element( INSTRUCTION_ELEMENT_NAME ).add_text( self.instruction ) if self.instruction
238
+ xml_element.add_element( WEB_ELEMENT_NAME ).add_text( self.web ) if self.web
239
+ xml_element.add_element( CONTACT_ELEMENT_NAME ).add_text( self.contact ) if self.contact
240
+ @parameters.each do |parameter|
241
+ xml_element.add_element( parameter.to_xml_element )
242
+ end
243
+ @resources.each do |resource|
244
+ xml_element.add_element( resource.to_xml_element )
245
+ end
246
+ @areas.each do |area|
247
+ xml_element.add_element( area.to_xml_element )
248
+ end
249
+ xml_element
250
+ end
251
+
252
+ def to_xml # :nodoc:
253
+ self.to_xml_element.to_s
254
+ end
255
+
256
+ def inspect # :nodoc:
257
+ info_inspect = <<EOF
258
+ Language: #{ self.language }
259
+ Categories: #{ self.categories.to_s_for_cap }
260
+ Event: #{ self.event }
261
+ Response Types: #{ self.response_types.to_s_for_cap }
262
+ Urgency: #{ self.urgency }
263
+ Severity: #{ self.severity }
264
+ Certainty: #{ self.certainty }
265
+ Audience: #{ self.audience }
266
+ Event Codes: #{ self.event_codes.inspect }
267
+ Effective: #{ self.effective }
268
+ Onset: #{ self.onset }
269
+ Expires: #{ self.expires }
270
+ Sender Name: #{ self.sender_name }
271
+ Headline: #{ self.headline }
272
+ Description:
273
+ #{ self.description.to_s.lines.map{ |line| " " + line }.join }
274
+ Instruction: #{ self.instruction }
275
+ Web: #{ self.web }
276
+ Contact: #{ self.contact }
277
+ Parameters:
278
+ #{ self.parameters.map{ |parameter| parameter.inspect }.join( "\n" )}
279
+ Resources:
280
+ #{ self.resources.map{ |resource| " " + resource.inspect }.join( "\n" )}
281
+ Area:
282
+ #{ self.areas.map{ |area| " #{ area }" }.join( "\n" )}
283
+ EOF
284
+ RCAP.format_lines_for_inspect( 'INFO', info_inspect )
285
+ end
286
+
287
+ # Returns a string representation of the event of the form
288
+ # event(urgency/severity/certainty)
289
+ def to_s
290
+ "#{ self.event }(#{ self.urgency }/#{ self.severity }/#{ self.certainty })"
291
+ end
292
+
293
+ def self.from_xml_element( info_xml_element ) # :nodoc:
294
+ self.new(
295
+ :language => RCAP.xpath_text( info_xml_element, LANGUAGE_XPATH, Alert::XMLNS ) || DEFAULT_LANGUAGE,
296
+ :categories => RCAP.xpath_match( info_xml_element, CATEGORY_XPATH, Alert::XMLNS ).map{ |element| element.text },
297
+ :event => RCAP.xpath_text( info_xml_element, EVENT_XPATH, Alert::XMLNS ),
298
+ :response_types => RCAP.xpath_match( info_xml_element, RESPONSE_TYPE_XPATH, Alert::XMLNS ).map{ |element| element.text },
299
+ :urgency => RCAP.xpath_text( info_xml_element, URGENCY_XPATH, Alert::XMLNS ),
300
+ :severity => RCAP.xpath_text( info_xml_element, SEVERITY_XPATH, Alert::XMLNS ),
301
+ :certainty => RCAP.xpath_text( info_xml_element, CERTAINTY_XPATH, Alert::XMLNS ),
302
+ :audience => RCAP.xpath_text( info_xml_element, AUDIENCE_XPATH, Alert::XMLNS ),
303
+ :effective => (( effective = RCAP.xpath_first( info_xml_element, EFFECTIVE_XPATH, Alert::XMLNS )) ? DateTime.parse( effective.text ) : nil ),
304
+ :onset => (( onset = RCAP.xpath_first( info_xml_element, ONSET_XPATH, Alert::XMLNS )) ? DateTime.parse( onset.text ) : nil ),
305
+ :expires => (( expires = RCAP.xpath_first( info_xml_element, EXPIRES_XPATH, Alert::XMLNS )) ? DateTime.parse( expires.text ) : nil ),
306
+ :sender_name => RCAP.xpath_text( info_xml_element, SENDER_NAME_XPATH, Alert::XMLNS ),
307
+ :headline => RCAP.xpath_text( info_xml_element, HEADLINE_XPATH, Alert::XMLNS ),
308
+ :description => RCAP.xpath_text( info_xml_element, DESCRIPTION_XPATH, Alert::XMLNS ),
309
+ :instruction => RCAP.xpath_text( info_xml_element, INSTRUCTION_XPATH, Alert::XMLNS ),
310
+ :web => RCAP.xpath_text( info_xml_element, WEB_XPATH, Alert::XMLNS ),
311
+ :contact => RCAP.xpath_text( info_xml_element, CONTACT_XPATH, Alert::XMLNS ),
312
+ :event_codes => RCAP.xpath_match( info_xml_element, EventCode::XPATH, Alert::XMLNS ).map{ |element| EventCode.from_xml_element( element )},
313
+ :parameters => RCAP.xpath_match( info_xml_element, Parameter::XPATH, Alert::XMLNS ).map{ |element| Parameter.from_xml_element( element )},
314
+ :resources => RCAP.xpath_match( info_xml_element, Resource::XPATH, Alert::XMLNS ).map{ |element| Resource.from_xml_element( element )},
315
+ :areas => RCAP.xpath_match( info_xml_element, Area::XPATH, Alert::XMLNS ).map{ |element| Area.from_xml_element( element )}
316
+ )
317
+ end
318
+
319
+ LANGUAGE_YAML = 'Language' # :nodoc:
320
+ CATEGORIES_YAML = 'Categories' # :nodoc:
321
+ EVENT_YAML = 'Event' # :nodoc:
322
+ RESPONSE_TYPES_YAML = 'Response Types' # :nodoc:
323
+ URGENCY_YAML = 'Urgency' # :nodoc:
324
+ SEVERITY_YAML = 'Severity' # :nodoc:
325
+ CERTAINTY_YAML = 'Certainty' # :nodoc:
326
+ AUDIENCE_YAML = 'Audience' # :nodoc:
327
+ EFFECTIVE_YAML = 'Effective' # :nodoc:
328
+ ONSET_YAML = 'Onset' # :nodoc:
329
+ EXPIRES_YAML = 'Expires' # :nodoc:
330
+ SENDER_NAME_YAML = 'Sender Name' # :nodoc:
331
+ HEADLINE_YAML = 'Headline' # :nodoc:
332
+ DESCRIPTION_YAML = 'Description' # :nodoc:
333
+ INSTRUCTION_YAML = 'Instruction' # :nodoc:
334
+ WEB_YAML = 'Web' # :nodoc:
335
+ CONTACT_YAML = 'Contact' # :nodoc:
336
+ EVENT_CODES_YAML = 'Event Codes' # :nodoc:
337
+ PARAMETERS_YAML = 'Parameters' # :nodoc:
338
+ RESOURCES_YAML = 'Resources' # :nodoc:
339
+ AREAS_YAML = 'Areas' # :nodoc:
340
+
341
+ def to_yaml( options = {} ) # :nodoc:
342
+ response_types_yaml = self.response_types
343
+ def response_types_yaml.to_yaml_style; :inline; end
344
+
345
+ categories_yaml = self.categories
346
+ def categories_yaml.to_yaml_style; :inline; end
347
+
348
+ parameter_to_hash = lambda{ |hash, parameter| hash.merge( parameter.name => parameter.value )}
349
+
350
+ RCAP.attribute_values_to_hash(
351
+ [ LANGUAGE_YAML, self.language ],
352
+ [ CATEGORIES_YAML, categories_yaml ],
353
+ [ EVENT_YAML, self.event ],
354
+ [ RESPONSE_TYPES_YAML, response_types_yaml ],
355
+ [ URGENCY_YAML, self.urgency ],
356
+ [ SEVERITY_YAML, self.severity ],
357
+ [ CERTAINTY_YAML, self.certainty ],
358
+ [ AUDIENCE_YAML, self.audience ],
359
+ [ EFFECTIVE_YAML, self.effective ],
360
+ [ ONSET_YAML, self.onset ],
361
+ [ EXPIRES_YAML, self.expires ],
362
+ [ SENDER_NAME_YAML, self.sender_name ],
363
+ [ HEADLINE_YAML, self.headline ],
364
+ [ DESCRIPTION_YAML, self.description ],
365
+ [ INSTRUCTION_YAML, self.instruction ],
366
+ [ WEB_YAML, self.web ],
367
+ [ CONTACT_YAML, self.contact ],
368
+ [ EVENT_CODES_YAML, self.event_codes.inject({}, &parameter_to_hash )],
369
+ [ PARAMETERS_YAML, self.parameters.inject({}, &parameter_to_hash )],
370
+ [ RESOURCES_YAML, self.resources ],
371
+ [ AREAS_YAML, self.areas ]
372
+ ).to_yaml( options )
373
+ end
374
+
375
+ def self.from_yaml_data( info_yaml_data ) # :nodoc:
376
+ self.new(
377
+ :language => info_yaml_data [ LANGUAGE_YAML ],
378
+ :categories => info_yaml_data [ CATEGORIES_YAML ],
379
+ :event => info_yaml_data [ EVENT_YAML ],
380
+ :response_types => info_yaml_data [ RESPONSE_TYPES_YAML ],
381
+ :urgency => info_yaml_data [ URGENCY_YAML ],
382
+ :severity => info_yaml_data [ SEVERITY_YAML ],
383
+ :certainty => info_yaml_data [ CERTAINTY_YAML ],
384
+ :audience => info_yaml_data [ AUDIENCE_YAML ],
385
+ :effective => ( effective = info_yaml_data[ EFFECTIVE_YAML ]).blank? ? nil : DateTime.parse( effective.to_s ),
386
+ :onset => ( onset = info_yaml_data[ ONSET_YAML ]).blank? ? nil : DateTime.parse( onset.to_s ),
387
+ :expires => ( expires = info_yaml_data[ EXPIRES_YAML ]).blank? ? nil : DateTime.parse( expires.to_s ),
388
+ :sender_name => info_yaml_data [ SENDER_NAME_YAML ],
389
+ :headline => info_yaml_data [ HEADLINE_YAML ],
390
+ :description => info_yaml_data [ DESCRIPTION_YAML ],
391
+ :instruction => info_yaml_data [ INSTRUCTION_YAML ],
392
+ :web => info_yaml_data [ WEB_YAML ],
393
+ :contact => info_yaml_data [ CONTACT_YAML ],
394
+ :event_codes => Array( info_yaml_data [ EVENT_CODES_YAML ]).map{ |name,value| EventCode.new( :name => name, :value => value )},
395
+ :parameters => Array( info_yaml_data [ PARAMETERS_YAML ]).map{ |parameter_yaml_data| Parameter.new( :name => name, :value => value )},
396
+ :resources => Array( info_yaml_data [ RESOURCES_YAML ]).map{ |resource_yaml_data| Resource.from_yaml_data( resource_yaml_data )},
397
+ :areas => Array( info_yaml_data [ AREAS_YAML ]).map{ |area_yaml_data| Area.from_yaml_data( area_yaml_data )}
398
+ )
399
+ end
400
+
401
+ LANGUAGE_KEY = 'language' # :nodoc:
402
+ CATEGORIES_KEY = 'categories' # :nodoc:
403
+ EVENT_KEY = 'event' # :nodoc:
404
+ RESPONSE_TYPES_KEY = 'response_types' # :nodoc:
405
+ URGENCY_KEY = 'urgency' # :nodoc:
406
+ SEVERITY_KEY = 'severity' # :nodoc:
407
+ CERTAINTY_KEY = 'certainty' # :nodoc:
408
+ AUDIENCE_KEY = 'audience' # :nodoc:
409
+ EFFECTIVE_KEY = 'effective' # :nodoc:
410
+ ONSET_KEY = 'onset' # :nodoc:
411
+ EXPIRES_KEY = 'expires' # :nodoc:
412
+ SENDER_NAME_KEY = 'sender_name' # :nodoc:
413
+ HEADLINE_KEY = 'headline' # :nodoc:
414
+ DESCRIPTION_KEY = 'description' # :nodoc:
415
+ INSTRUCTION_KEY = 'instruction' # :nodoc:
416
+ WEB_KEY = 'web' # :nodoc:
417
+ CONTACT_KEY = 'contact' # :nodoc:
418
+ RESOURCES_KEY = 'resources' # :nodoc:
419
+ EVENT_CODES_KEY = 'event_codes' # :nodoc:
420
+ PARAMETERS_KEY = 'parameters' # :nodoc:
421
+ AREAS_KEY = 'areas' # :nodoc:
422
+
423
+ def to_h # :nodoc:
424
+ RCAP.attribute_values_to_hash( [ LANGUAGE_KEY, self.language ],
425
+ [ CATEGORIES_KEY, self.categories ],
426
+ [ EVENT_KEY, self.event ],
427
+ [ RESPONSE_TYPES_KEY, self.response_types ],
428
+ [ URGENCY_KEY, self.urgency ],
429
+ [ SEVERITY_KEY, self.severity ],
430
+ [ CERTAINTY_KEY, self.certainty ],
431
+ [ AUDIENCE_KEY, self.audience ],
432
+ [ EFFECTIVE_KEY, RCAP.to_s_for_cap( self.effective )],
433
+ [ ONSET_KEY, RCAP.to_s_for_cap( self.onset )],
434
+ [ EXPIRES_KEY, RCAP.to_s_for_cap( self.expires )],
435
+ [ SENDER_NAME_KEY, self.sender_name ],
436
+ [ HEADLINE_KEY, self.headline ],
437
+ [ DESCRIPTION_KEY, self.description ],
438
+ [ INSTRUCTION_KEY, self.instruction ],
439
+ [ WEB_KEY, self.web ],
440
+ [ CONTACT_KEY, self.contact ],
441
+ [ RESOURCES_KEY, self.resources.map{ |resource| resource.to_h } ],
442
+ [ EVENT_CODES_KEY, self.event_codes.map{ |event_code| event_code.to_h } ],
443
+ [ PARAMETERS_KEY, self.parameters.map{ |parameter| parameter.to_h } ],
444
+ [ AREAS_KEY, self.areas.map{ |area| area.to_h }])
445
+ end
446
+
447
+ def self.from_h( info_hash ) # :nodoc:
448
+ self.new( :language => info_hash[ LANGUAGE_KEY ],
449
+ :categories => info_hash[ CATEGORIES_KEY ],
450
+ :event => info_hash[ EVENT_KEY ],
451
+ :response_types => info_hash[ RESPONSE_TYPES_KEY ],
452
+ :urgency => info_hash[ URGENCY_KEY ],
453
+ :severity => info_hash[ SEVERITY_KEY ],
454
+ :certainty => info_hash[ CERTAINTY_KEY ],
455
+ :audience => info_hash[ AUDIENCE_KEY ],
456
+ :effective => RCAP.parse_datetime( info_hash[ EFFECTIVE_KEY ]),
457
+ :onset => RCAP.parse_datetime( info_hash[ ONSET_KEY ]),
458
+ :expires => RCAP.parse_datetime( info_hash[ EXPIRES_KEY ]),
459
+ :sender_name => info_hash[ SENDER_NAME_KEY ],
460
+ :headline => info_hash[ HEADLINE_KEY ],
461
+ :description => info_hash[ DESCRIPTION_KEY ],
462
+ :instruction => info_hash[ INSTRUCTION_KEY ],
463
+ :web => info_hash[ WEB_KEY ],
464
+ :contact => info_hash[ CONTACT_KEY ],
465
+ :resources => Array( info_hash[ RESOURCES_KEY ]).map{ |resource_hash| Resource.from_h( resource_hash ) },
466
+ :event_codes => Array( info_hash[ EVENT_CODES_KEY ]).map{ |event_code_hash| EventCode.from_h( event_code_hash )},
467
+ :parameters => Array( info_hash[ PARAMETERS_KEY ]).map{ |parameter_hash| Parameter.from_h( parameter_hash )},
468
+ :areas => Array( info_hash[ AREAS_KEY ]).map{ |area_hash| Area.from_h( area_hash )})
469
+ end
470
+ end
471
+ end
472
+ end