geocaching 0.6.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,42 @@
1
+ # encoding: utf-8
2
+
3
+ require "nokogiri"
4
+ require "time"
5
+
6
+ require "geocaching/parsers/cache_gpx_cache_waypoint"
7
+ require "geocaching/parsers/cache_gpx_additional_waypoint"
8
+
9
+ module Geocaching
10
+ module Parsers
11
+ class CacheGPX
12
+ def initialize(doc)
13
+ @doc = doc
14
+ end
15
+
16
+ def parse
17
+ cache = nil
18
+ is_first_wpt = true
19
+
20
+ @doc.xpath("//gpx:gpx/gpx:wpt", ns).each do |wpt_node|
21
+ if is_first_wpt
22
+ cache = CacheGPXCacheWaypoint.new(wpt_node).parse
23
+ is_first_wpt = false
24
+ else
25
+ cache.waypoints << CacheGPXAdditionalWaypoint.new(wpt_node).parse
26
+ end
27
+ end
28
+
29
+ cache
30
+ end
31
+
32
+ private
33
+
34
+ def ns
35
+ {
36
+ "gpx" => "http://www.topografix.com/GPX/1/1",
37
+ "cache" => "http://www.groundspeak.com/cache/1/0"
38
+ }
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,100 @@
1
+ # encoding: utf-8
2
+
3
+ module Geocaching
4
+ module Parsers
5
+ class CacheGPXAdditionalWaypoint
6
+ COORDINATE_REGEXP = /^-?[0-9]{1,3}\.[0-9]{1,}$/
7
+
8
+ def initialize(wpt_node)
9
+ @wpt_node = wpt_node
10
+ end
11
+
12
+ def parse
13
+ waypoint = Waypoint.new
14
+
15
+ private_methods.each do |method|
16
+ if method.to_s =~ /^parse_([a-z_]+)$/
17
+ value = send(method, @wpt_node)
18
+ waypoint.send("#{$1}=", value) unless value.nil?
19
+ end
20
+ end
21
+
22
+ waypoint
23
+ end
24
+
25
+ private
26
+
27
+ def parse_code(wpt_node)
28
+ if node = wpt_node.xpath("./gpx:name", ns).first
29
+ node.text
30
+ else
31
+ raise ParseError, "Could not parse waypoint code from GPX"
32
+ end
33
+ end
34
+
35
+ def parse_latitude(wpt_node)
36
+ if wpt_node["lat"] =~ COORDINATE_REGEXP
37
+ wpt_node["lat"].to_f
38
+ else
39
+ raise ParseError, "Invalid waypoint latitude in GPX"
40
+ end
41
+ end
42
+
43
+ def parse_longitude(wpt_node)
44
+ if wpt_node["lon"] =~ COORDINATE_REGEXP
45
+ wpt_node["lon"].to_f
46
+ else
47
+ raise ParseError, "Invalid waypoint longitude in GPX"
48
+ end
49
+ end
50
+
51
+ def parse_created_at(wpt_node)
52
+ if node = wpt_node.xpath("./gpx:time", ns).first
53
+ begin
54
+ time = Time.parse(node.text)
55
+ Time.mktime(time.year, time.month, time.day)
56
+ rescue ArgumentError
57
+ raise ParseError, "Invalid waypoint creation date in GPX"
58
+ end
59
+ else
60
+ raise ParseError, "Could not parse waypoint creation date from GPX"
61
+ end
62
+ end
63
+
64
+ def parse_name(wpt_node)
65
+ if node = wpt_node.xpath("./gpx:desc", ns).first
66
+ node.text
67
+ else
68
+ raise ParseError, "Could not parse waypoint name from GPX"
69
+ end
70
+ end
71
+
72
+ def parse_note(wpt_node)
73
+ if node = wpt_node.xpath("./gpx:cmt", ns).first
74
+ node.text
75
+ else
76
+ raise ParseError, "Could not parse waypoint note from GPX"
77
+ end
78
+ end
79
+
80
+ def parse_type(wpt_node)
81
+ if node = wpt_node.xpath("./gpx:sym", ns).first
82
+ if waypoint_type = WaypointType.from_name(node.text)
83
+ waypoint_type
84
+ else
85
+ raise ParseError, "Unknown waypoint type in GPX"
86
+ end
87
+ else
88
+ raise ParseError, "Could not parse waypoint type from GPX"
89
+ end
90
+ end
91
+
92
+ def ns
93
+ {
94
+ "gpx" => "http://www.topografix.com/GPX/1/1",
95
+ "cache" => "http://www.groundspeak.com/cache/1/0"
96
+ }
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,234 @@
1
+ # encoding: utf-8
2
+
3
+ module Geocaching
4
+ module Parsers
5
+ class CacheGPXCacheWaypoint
6
+ CACHE_CODE_REGEXP = /^GC[A-Z0-9]+$/
7
+ COORDINATE_REGEXP = /^-?[0-9]{1,3}\.[0-9]{1,}$/
8
+
9
+ WPT_NODE_ATTRIBUTES = %w(guid code latitude longitude hidden_at)
10
+
11
+ def initialize(wpt_node)
12
+ @wpt_node = wpt_node
13
+ end
14
+
15
+ def parse
16
+ cache = Cache.new
17
+
18
+ WPT_NODE_ATTRIBUTES.each do |attribute|
19
+ value = send("parse_#{attribute}", @wpt_node)
20
+ cache.send("#{attribute}=", value) unless value.nil?
21
+ end
22
+
23
+ if cache_node = @wpt_node.xpath("./gpx:extensions/cache:cache", ns).first
24
+ private_methods.each do |method|
25
+ if method.to_s =~ /^parse_([a-z_]+)$/
26
+ unless WPT_NODE_ATTRIBUTES.include?($1)
27
+ value = send(method, cache_node)
28
+ cache.send("#{$1}=", value) unless value.nil?
29
+ end
30
+ end
31
+ end
32
+ else
33
+ raise ParseError, "Could not parse GPX"
34
+ end
35
+
36
+ cache
37
+ end
38
+
39
+ private
40
+
41
+ def parse_code(wpt_node)
42
+ if node = wpt_node.xpath("./gpx:name", ns).first
43
+ node.text
44
+ else
45
+ raise ParseError, "Could not parse cache code from GPX"
46
+ end
47
+ end
48
+
49
+ def parse_guid(wpt_node)
50
+ if node = wpt_node.xpath("./gpx:link", ns).first and node["href"] =~ /guid=([a-f0-9-]{36})/
51
+ $1
52
+ else
53
+ raise ParseError, "Could not parse cache GUID from GPX"
54
+ end
55
+ end
56
+
57
+ def parse_latitude(wpt_node)
58
+ if wpt_node["lat"] =~ COORDINATE_REGEXP
59
+ wpt_node["lat"].to_f
60
+ else
61
+ raise ParseError, "Invalid latitude in GPX"
62
+ end
63
+ end
64
+
65
+ def parse_longitude(wpt_node)
66
+ if wpt_node["lon"] =~ COORDINATE_REGEXP
67
+ wpt_node["lon"].to_f
68
+ else
69
+ raise ParseError, "Invalid longitude in GPX"
70
+ end
71
+ end
72
+
73
+ def parse_hidden_at(wpt_node)
74
+ if node = wpt_node.xpath("./gpx:time", ns).first
75
+ begin
76
+ time = Time.parse(node.text)
77
+ Time.mktime(time.year, time.month, time.day)
78
+ rescue ArgumentError
79
+ raise ParseError, "Invalid hidden date in GPX"
80
+ end
81
+ else
82
+ raise ParseError, "Could not parse hidden date from GPX"
83
+ end
84
+ end
85
+
86
+ def parse_name(cache_node)
87
+ if node = cache_node.xpath("./cache:name", ns).first
88
+ node.text
89
+ else
90
+ raise ParseError, "Could not parse cache name from GPX"
91
+ end
92
+ end
93
+
94
+ def parse_type(cache_node)
95
+ if node = cache_node.xpath("./cache:type", ns).first
96
+ if cache_type = CacheType.from_name(node.text)
97
+ cache_type
98
+ else
99
+ raise ParseError, "Unknown cache type in GPX"
100
+ end
101
+ else
102
+ raise ParseError, "Could not parse cache type from GPX"
103
+ end
104
+ end
105
+
106
+ def parse_container_type(cache_node)
107
+ if node = cache_node.xpath("./cache:container", ns).first
108
+ if container_type = ContainerType.from_name(node.text)
109
+ container_type
110
+ else
111
+ raise ParseError, "Unknown container type in GPX"
112
+ end
113
+ else
114
+ raise ParseError, "Could not parse cache container type from GPX"
115
+ end
116
+ end
117
+
118
+ def parse_owner(cache_node)
119
+ if node = cache_node.xpath("./cache:owner", ns).first and node.text
120
+ User.new(:name => node.text)
121
+ else
122
+ raise ParseError, "Could not parse cache owner from GPX"
123
+ end
124
+ end
125
+
126
+ def parse_is_available(cache_node)
127
+ if cache_node["available"]
128
+ if %w(True False).include?(cache_node["available"])
129
+ cache_node["available"] == "True"
130
+ else
131
+ raise ParseError, "Invalid cache availability in GPX"
132
+ end
133
+ else
134
+ raise ParseError, "Could not parse cache availability from GPX"
135
+ end
136
+ end
137
+
138
+ def parse_is_archived(cache_node)
139
+ if cache_node["archived"]
140
+ if %w(True False).include?(cache_node["archived"])
141
+ cache_node["archived"] == "True"
142
+ else
143
+ raise ParseError, "Invalid cache archive status in GPX"
144
+ end
145
+ else
146
+ raise ParseError, "Could not parse cache archive status from GPX"
147
+ end
148
+ end
149
+
150
+ def parse_difficulty(cache_node)
151
+ if node = cache_node.xpath("./cache:difficulty", ns).first
152
+ difficulty = node.text.to_f
153
+
154
+ if difficulty > 0
155
+ difficulty
156
+ else
157
+ raise ParseError, "Invalid difficulty rating in GPX"
158
+ end
159
+ else
160
+ raise ParseError, "Could not parse difficulty rating from GPX"
161
+ end
162
+ end
163
+
164
+ def parse_terrain(cache_node)
165
+ if node = cache_node.xpath("./cache:terrain", ns).first
166
+ terrain = node.text.to_f
167
+
168
+ if terrain > 0
169
+ terrain
170
+ else
171
+ raise ParseError, "Invalid terrain terrain rating in GPX"
172
+ end
173
+ else
174
+ raise ParseError, "Could not parse terrain rating from GPX"
175
+ end
176
+ end
177
+
178
+ def parse_country(cache_node)
179
+ if node = cache_node.xpath("./cache:country", ns).first
180
+ node.text
181
+ else
182
+ raise ParseError, "Could not parse country from GPX"
183
+ end
184
+ end
185
+
186
+ def parse_state(cache_node)
187
+ if node = cache_node.xpath("./cache:state", ns).first
188
+ node.text unless node.text == "None"
189
+ else
190
+ raise ParseError, "Could not parse state from GPX"
191
+ end
192
+ end
193
+
194
+ def parse_placed_by(cache_node)
195
+ if node = cache_node.xpath("./cache:placed_by", ns).first
196
+ node.text
197
+ else
198
+ raise ParseError, "Could not parse placed by name from GPX"
199
+ end
200
+ end
201
+
202
+ def parse_short_description(cache_node)
203
+ if node = cache_node.xpath("./cache:short_description", ns).first
204
+ node.text
205
+ else
206
+ raise ParseError, "Could not parse short description from GPX"
207
+ end
208
+ end
209
+
210
+ def parse_long_description(cache_node)
211
+ if node = cache_node.xpath("./cache:long_description", ns).first
212
+ node.text
213
+ else
214
+ raise ParseError, "Could not parse long description from GPX"
215
+ end
216
+ end
217
+
218
+ def parse_hint(cache_node)
219
+ if node = cache_node.xpath("./cache:encoded_hints", ns).first
220
+ node.text
221
+ else
222
+ raise ParseError, "Could not parse hint from GPX"
223
+ end
224
+ end
225
+
226
+ def ns
227
+ {
228
+ "gpx" => "http://www.topografix.com/GPX/1/1",
229
+ "cache" => "http://www.groundspeak.com/cache/1/0"
230
+ }
231
+ end
232
+ end
233
+ end
234
+ end
@@ -0,0 +1,219 @@
1
+ # encoding: utf-8
2
+
3
+ require "nokogiri"
4
+ require "time"
5
+
6
+ module Geocaching
7
+ module Parsers
8
+ class CacheSimple
9
+ CACHE_CODE_REGEXP = /^GC[A-Z0-9]+$/
10
+ COORDINATE_REGEXP = /^-?[0-9]{1,3}\.[0-9]{1,}$/
11
+
12
+ def initialize(doc)
13
+ @doc = doc
14
+ end
15
+
16
+ def parse
17
+ caches = []
18
+
19
+ @doc.xpath("//xmlns:CacheSimpleDataSet/xmlns:Cache").each do |cache_node|
20
+ cache = Cache.new
21
+
22
+ private_methods.each do |method|
23
+ if method.to_s =~ /^parse_([a-z_]+)$/
24
+ value = send(method, cache_node)
25
+ cache.send("#{$1}=", value) unless value.nil?
26
+ end
27
+ end
28
+
29
+ caches << cache
30
+ end
31
+
32
+ caches
33
+ end
34
+
35
+ private
36
+
37
+ def parse_code(cache_node)
38
+ if node = cache_node.xpath("./xmlns:CacheCode").first
39
+ if node.text =~ CACHE_CODE_REGEXP
40
+ node.text
41
+ else
42
+ raise ParseError, "Got an invalid value for cache code from API"
43
+ end
44
+ else
45
+ raise ParseError, "Could not parse cache code from XML"
46
+ end
47
+ end
48
+
49
+ def parse_name(cache_node)
50
+ if node = cache_node.xpath("./xmlns:Name").first
51
+ node.text
52
+ else
53
+ raise ParseError, "Could not parse cache name from XML"
54
+ end
55
+ end
56
+
57
+ def parse_latitude(cache_node)
58
+ if node = cache_node.xpath("./xmlns:Latitude").first
59
+ if node.text =~ COORDINATE_REGEXP
60
+ node.text.to_f
61
+ else
62
+ raise ParseError, "Got a non-numeric value for latitude from API"
63
+ end
64
+ else
65
+ raise ParseError, "Could not parse latitude from XML"
66
+ end
67
+ end
68
+
69
+ def parse_longitude(cache_node)
70
+ if node = cache_node.xpath("./xmlns:Longitude").first
71
+ if node.text =~ COORDINATE_REGEXP
72
+ node.text.to_f
73
+ else
74
+ raise ParseError, "Got a non-numeric value for longitude from API"
75
+ end
76
+ else
77
+ raise ParseError, "Could not parse longitude from XML"
78
+ end
79
+ end
80
+
81
+ def parse_difficulty(cache_node)
82
+ if node = cache_node.xpath("./xmlns:DifficultyRating").first
83
+ difficulty = node.text.to_f
84
+
85
+ if difficulty > 0
86
+ difficulty
87
+ else
88
+ raise ParseError, "Got a non-numeric value for cache difficulty rating from API"
89
+ end
90
+ else
91
+ raise ParseError, "Could not parse difficulty rating from XML"
92
+ end
93
+ end
94
+
95
+ def parse_terrain(cache_node)
96
+ if node = cache_node.xpath("./xmlns:TerrainRating").first
97
+ terrain = node.text.to_f
98
+
99
+ if terrain > 0
100
+ terrain
101
+ else
102
+ raise ParseError, "Got a non-numeric value for cache terrain rating from API"
103
+ end
104
+ else
105
+ raise ParseError, "Could not parse terrain rating from XML"
106
+ end
107
+ end
108
+
109
+ def parse_country(cache_node)
110
+ if node = cache_node.xpath("./xmlns:CountryName").first
111
+ node.text
112
+ else
113
+ raise ParseError, "Could not parse country from XML"
114
+ end
115
+ end
116
+
117
+ def parse_state(cache_node)
118
+ if node = cache_node.xpath("./xmlns:StateName").first
119
+ node.text unless node.text == "None"
120
+ else
121
+ raise ParseError, "Could not parse state from XML"
122
+ end
123
+ end
124
+
125
+ def parse_placed_by(cache_node)
126
+ if node = cache_node.xpath("./xmlns:ContactName").first
127
+ node.text
128
+ else
129
+ raise ParseError, "Could not parse placed by name from XML"
130
+ end
131
+ end
132
+
133
+ def parse_is_available(cache_node)
134
+ if node = cache_node.xpath("./xmlns:IsAvailable").first
135
+ if %w(true false).include?(node.text)
136
+ node.text == "true"
137
+ else
138
+ raise ParseError, "Got a non-bool value for cache availability from API"
139
+ end
140
+ else
141
+ raise ParseError, "Could not parse cache availability from XML"
142
+ end
143
+ end
144
+
145
+ def parse_is_archived(cache_node)
146
+ if node = cache_node.xpath("./xmlns:IsArchived").first
147
+ if %w(true false).include?(node.text)
148
+ node.text == "true"
149
+ else
150
+ raise ParseError, "Got a non-bool value for cache archived status from API"
151
+ end
152
+ else
153
+ raise ParseError, "Could not parse cache archived status from XML"
154
+ end
155
+ end
156
+
157
+ def parse_is_premium(cache_node)
158
+ if node = cache_node.xpath("./xmlns:IsPremiumListing").first
159
+ if %w(true false).include?(node.text)
160
+ node.text == "true"
161
+ else
162
+ raise ParseError, "Got a non-bool value for cache premium status from API"
163
+ end
164
+ else
165
+ raise ParseError, "Could not parse cache premium status from XML"
166
+ end
167
+ end
168
+
169
+ def parse_hidden_at(cache_node)
170
+ if node = cache_node.xpath("./xmlns:Created").first
171
+ begin
172
+ time = Time.parse(node.text)
173
+ Time.mktime(time.year, time.month, time.day)
174
+ rescue ArgumentError
175
+ raise ParseError, "Got an invalid hidden date from API"
176
+ end
177
+ else
178
+ raise ParseError, "Could not parse hidden date from XML"
179
+ end
180
+ end
181
+
182
+ def parse_type(cache_node)
183
+ if node = cache_node.xpath("./xmlns:CacheTypeName").first
184
+ if cache_type = CacheType.from_name(node.text)
185
+ cache_type
186
+ else
187
+ raise ParseError, "Got an unknown cache type from API"
188
+ end
189
+ else
190
+ raise ParseError, "Could not parse cache type from XML"
191
+ end
192
+ end
193
+
194
+ def parse_container_type(cache_node)
195
+ if node = cache_node.xpath("./xmlns:ContainerName").first
196
+ if container_type = ContainerType.from_name(node.text)
197
+ container_type
198
+ else
199
+ raise ParseError, "Got an unknown container type from API"
200
+ end
201
+ else
202
+ raise ParseError, "Could not parse container type from XML"
203
+ end
204
+ end
205
+
206
+ def parse_owner(cache_node)
207
+ if user_guid_node = cache_node.xpath("./xmlns:UserGUID").first
208
+ if user_name_node = cache_node.xpath("./xmlns:UserName").first
209
+ User.new(:guid => user_guid_node.text, :name => user_name_node.text)
210
+ else
211
+ raise ParseError, "Could not parse owner name from XML"
212
+ end
213
+ else
214
+ raise ParseError, "Could not parse owner GUID from XML"
215
+ end
216
+ end
217
+ end
218
+ end
219
+ end