rcap-rails-generators 1.3

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