rcap 0.4 → 1.0.0.rc.1

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.
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