geocaching 0.5.0 → 0.6.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.
- data/README.markdown +47 -21
- data/geocaching.gemspec +3 -0
- data/lib/geocaching.rb +20 -6
- data/lib/geocaching/cache.rb +237 -94
- data/lib/geocaching/cache_type.rb +17 -13
- data/lib/geocaching/http.rb +12 -16
- data/lib/geocaching/log.rb +67 -46
- data/lib/geocaching/log_type.rb +17 -13
- data/lib/geocaching/my_logs.rb +22 -16
- data/lib/geocaching/pocket_query.rb +213 -0
- data/lib/geocaching/user.rb +46 -69
- data/lib/geocaching/version.rb +3 -1
- data/lib/geocaching/watchlist.rb +106 -0
- data/spec/cache/ape.rb +4 -0
- data/spec/cache/cito.rb +4 -0
- data/spec/cache/earthcache.rb +4 -0
- data/spec/cache/event.rb +4 -0
- data/spec/cache/letterbox.rb +4 -0
- data/spec/cache/lfevent.rb +4 -0
- data/spec/cache/locationless.rb +4 -0
- data/spec/cache/megaevent.rb +4 -0
- data/spec/cache/multi.rb +4 -0
- data/spec/cache/mystery.rb +4 -0
- data/spec/cache/traditional.rb +4 -0
- data/spec/cache/virtual.rb +4 -0
- data/spec/cache/webcam.rb +4 -0
- data/spec/cache/wherigo.rb +4 -0
- data/spec/cache_spec.rb +7 -1
- data/spec/log/announcement.rb +1 -1
- data/spec/log/archive.rb +1 -1
- data/spec/log/attended.rb +1 -1
- data/spec/log/coords_update.rb +1 -1
- data/spec/log/disable.rb +1 -1
- data/spec/log/dnf.rb +1 -1
- data/spec/log/enable.rb +1 -1
- data/spec/log/found.rb +1 -1
- data/spec/log/needs_archived.rb +1 -1
- data/spec/log/needs_maintenance.rb +1 -1
- data/spec/log/note.rb +1 -1
- data/spec/log/owner_maintenance.rb +1 -1
- data/spec/log/publish.rb +1 -1
- data/spec/log/retract.rb +1 -1
- data/spec/log/reviewer_note.rb +1 -1
- data/spec/log/unarchive.rb +1 -1
- data/spec/log/webcam_photo_taken.rb +1 -1
- data/spec/log/will_attend.rb +1 -1
- data/spec/log_spec.rb +7 -1
- metadata +22 -6
- data/lib/geocaching/pq.rb +0 -72
@@ -1,5 +1,9 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
module Geocaching
|
2
|
-
#
|
4
|
+
# The {CacheType} class represents a cache type.
|
5
|
+
#
|
6
|
+
# == Usage
|
3
7
|
#
|
4
8
|
# if @cache.type == :traditional
|
5
9
|
# puts "Cache is a Traditional Cache"
|
@@ -24,7 +28,7 @@ module Geocaching
|
|
24
28
|
:ape => [9, "Project APE Cache"]
|
25
29
|
}
|
26
30
|
|
27
|
-
#
|
31
|
+
# Returns a {CacheType} object for the given cache type id, or
|
28
32
|
# nil if no appropriate cache id is found.
|
29
33
|
#
|
30
34
|
# @return [Geocaching::CacheType]
|
@@ -35,7 +39,7 @@ module Geocaching
|
|
35
39
|
end
|
36
40
|
end
|
37
41
|
|
38
|
-
#
|
42
|
+
# Returns a {CacheType} object for the given cache type title, or
|
39
43
|
# nil if no appropriate cache type is found.
|
40
44
|
#
|
41
45
|
# @return [Geocaching::CacheType]
|
@@ -46,43 +50,43 @@ module Geocaching
|
|
46
50
|
end
|
47
51
|
end
|
48
52
|
|
49
|
-
#
|
50
|
-
# of this class on your own.
|
53
|
+
# Creates a new instance. You should not need to create an instance
|
54
|
+
# of this class on your own. Use {for_id} and {for_title}.
|
51
55
|
def initialize(info)
|
52
56
|
@info = info
|
53
57
|
end
|
54
58
|
|
55
|
-
#
|
59
|
+
# Returns the cache type’s ID.
|
56
60
|
#
|
57
|
-
# @return [Fixnum]
|
61
|
+
# @return [Fixnum] Cache type ID
|
58
62
|
def id
|
59
63
|
@info[1][0]
|
60
64
|
end
|
61
65
|
|
62
|
-
#
|
66
|
+
# Returns the cache type’s name.
|
63
67
|
#
|
64
|
-
# @return [String]
|
68
|
+
# @return [String] Cache type name
|
65
69
|
def name
|
66
70
|
@info[1][1]
|
67
71
|
end
|
68
72
|
|
69
73
|
alias to_s name
|
70
74
|
|
71
|
-
#
|
75
|
+
# Returns the symbol that describes this cache type. See the {TYPES}
|
72
76
|
# hash for a list of cache type symbols.
|
73
77
|
#
|
74
|
-
# @return [Symbol]
|
78
|
+
# @return [Symbol] Cache type symbol
|
75
79
|
def to_sym
|
76
80
|
@info[0]
|
77
81
|
end
|
78
82
|
|
79
|
-
#
|
83
|
+
# Overloads the +==+ operator to compare by symbol.
|
80
84
|
#
|
81
85
|
# if @cache.type == :multi
|
82
86
|
# puts "It's a multi cache."
|
83
87
|
# end
|
84
88
|
#
|
85
|
-
# @return [Boolean]
|
89
|
+
# @return [Boolean] Does object match with argument?
|
86
90
|
def ==(s)
|
87
91
|
to_sym == s
|
88
92
|
end
|
data/lib/geocaching/http.rb
CHANGED
@@ -5,6 +5,7 @@ require "net/http"
|
|
5
5
|
require "timeout"
|
6
6
|
|
7
7
|
module Geocaching
|
8
|
+
# The {HTTP} class handles the HTTP communication with geocaching.com.
|
8
9
|
class HTTP
|
9
10
|
# An array of user agent strings. A random one is chosen.
|
10
11
|
USER_AGENTS = [
|
@@ -61,8 +62,8 @@ module Geocaching
|
|
61
62
|
end
|
62
63
|
|
63
64
|
# Alias for +Geocaching::HTTP.instance.post+.
|
64
|
-
def post(path, data = {})
|
65
|
-
self.instance.post(path, data)
|
65
|
+
def post(path, data = {}, extra_headers = {})
|
66
|
+
self.instance.post(path, data, extra_headers)
|
66
67
|
end
|
67
68
|
|
68
69
|
# Converts HTML entities to the corresponding UTF-8 symbols.
|
@@ -89,9 +90,6 @@ module Geocaching
|
|
89
90
|
#
|
90
91
|
# @return [void]
|
91
92
|
# @raise [ArgumentError] Username or password missing
|
92
|
-
# @raise [Geocaching::LoginError] Already logged in
|
93
|
-
# @raise [Geocaching::TimeoutError] Timeout hit
|
94
|
-
# @raise [Geocaching::HTTPError] HTTP request failed
|
95
93
|
def login
|
96
94
|
raise ArgumentError, "Missing username" unless self.class.username
|
97
95
|
raise ArgumentError, "Missing password" unless self.class.password
|
@@ -112,9 +110,6 @@ module Geocaching
|
|
112
110
|
# Logs out from geocaching.com.
|
113
111
|
#
|
114
112
|
# @return [void]
|
115
|
-
# @raise [Geocaching::LoginError] Not logged in
|
116
|
-
# @raise [Geocaching::TimeoutError] Timeout hit
|
117
|
-
# @raise [Geocaching::HTTPError] HTTP request failed
|
118
113
|
def logout
|
119
114
|
raise LoginError, "Not logged in" unless @loggedin
|
120
115
|
|
@@ -124,9 +119,9 @@ module Geocaching
|
|
124
119
|
get("/login/default.aspx?RESET=Y")
|
125
120
|
end
|
126
121
|
|
127
|
-
# Returns whether
|
122
|
+
# Returns whether you‘ve already logged in.
|
128
123
|
#
|
129
|
-
# @return [Boolean]
|
124
|
+
# @return [Boolean] Already logged in?
|
130
125
|
def loggedin?
|
131
126
|
@loggedin and @cookie
|
132
127
|
end
|
@@ -134,11 +129,9 @@ module Geocaching
|
|
134
129
|
# Sends a GET request to +path+. The authentication cookie is sent
|
135
130
|
# with the request if available.
|
136
131
|
#
|
137
|
-
# @param [String] path
|
132
|
+
# @param [String] path Request path
|
138
133
|
# @return [Net::HTTP::Response] Reponse object from +Net::HTTP+
|
139
134
|
# @return [String] Actual content
|
140
|
-
# @raise [Geocaching::TimeoutError] Timeout hit
|
141
|
-
# @raise [Geocaching::HTTPError] HTTP request failed
|
142
135
|
def get(path)
|
143
136
|
resp = data = nil
|
144
137
|
header = default_header
|
@@ -173,11 +166,14 @@ module Geocaching
|
|
173
166
|
# @return [String] Actual content
|
174
167
|
# @raise [Geocaching::TimeoutError] Timeout hit
|
175
168
|
# @raise [Geocaching::HTTPError] HTTP request failed
|
176
|
-
def post(path, params = {})
|
177
|
-
|
169
|
+
def post(path, params = {}, extra_headers = {})
|
170
|
+
if params.kind_of?(Hash)
|
171
|
+
params = params.merge(metadata(path)).map { |k,v| "#{k}=#{v}" }.join("&")
|
172
|
+
end
|
173
|
+
|
178
174
|
resp = data = nil
|
179
175
|
|
180
|
-
header = default_header
|
176
|
+
header = default_header.merge(extra_headers)
|
181
177
|
header["Cookie"] = @cookie if @cookie
|
182
178
|
|
183
179
|
begin
|
data/lib/geocaching/log.rb
CHANGED
@@ -1,7 +1,11 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
module Geocaching
|
4
|
-
#
|
4
|
+
# The {Log} class represents a log on geocaching.com.
|
5
|
+
#
|
6
|
+
# This class does caching. That means that multiple calls of, for example,
|
7
|
+
# the {#date} method only do one HTTP request. If you want to override the
|
8
|
+
# cached information, call the {#fetch} method manually.
|
5
9
|
class Log
|
6
10
|
# Creates a new instance and calls the {#fetch} method afterwards.
|
7
11
|
# +:guid+ must be specified as an attribute.
|
@@ -10,8 +14,6 @@ module Geocaching
|
|
10
14
|
# @return [Geocaching::Log]
|
11
15
|
# @raise [ArgumentError] Unknown attribute provided
|
12
16
|
# @raise [TypeError] Invalid attribute provided
|
13
|
-
# @raise [Geocaching::TimeoutError] Timeout hit
|
14
|
-
# @raise [Geocaching::HTTPError] HTTP request failed
|
15
17
|
def self.fetch(attributes)
|
16
18
|
log = new(attributes)
|
17
19
|
log.fetch
|
@@ -22,15 +24,13 @@ module Geocaching
|
|
22
24
|
# as parameters:
|
23
25
|
#
|
24
26
|
# * +:guid+ — The log’s Globally Unique Identifier
|
25
|
-
# * +:cache+ — A {Geocaching::Cache} that belongs to this log
|
26
27
|
#
|
27
28
|
# @raise [ArgumentError] Trying to set an unknown attribute
|
28
|
-
# @raise [TypeError] +:code+ is not an instance of {Geocaching::Cache}
|
29
29
|
def initialize(attributes = {})
|
30
30
|
@data, @doc, @guid, @cache = nil, nil, nil, nil
|
31
31
|
|
32
32
|
attributes.each do |key, value|
|
33
|
-
if [:guid, :title, :date, :cache].include?(key)
|
33
|
+
if [:guid, :title, :date, :cache, :user].include?(key)
|
34
34
|
if key == :cache and not value.kind_of?(Geocaching::Cache)
|
35
35
|
raise TypeError, "Attribute `cache' must be an instance of Geocaching::Cache"
|
36
36
|
end
|
@@ -39,6 +39,10 @@ module Geocaching
|
|
39
39
|
raise TypeError, "Attribute `type' must be an instance of Time"
|
40
40
|
end
|
41
41
|
|
42
|
+
if key == :user and not value.kind_of?(User)
|
43
|
+
raise TypeError, "Attribute `user' must be an instance of Geocaching::User"
|
44
|
+
end
|
45
|
+
|
42
46
|
instance_variable_set("@#{key}", value)
|
43
47
|
else
|
44
48
|
raise ArgumentError, "Trying to set unknown attribute `#{key}'"
|
@@ -50,26 +54,30 @@ module Geocaching
|
|
50
54
|
#
|
51
55
|
# @return [void]
|
52
56
|
# @raise [ArgumentError] GUID is not given
|
53
|
-
# @raise [Geocaching::LoginError] Not logged in
|
54
|
-
# @raise [Geocaching::TimeoutError] Timeout hit
|
55
|
-
# @raise [Geocaching::HTTPError] HTTP request failed
|
56
57
|
def fetch
|
57
58
|
raise ArgumentError, "No GUID given" unless @guid
|
58
|
-
raise LoginError
|
59
|
+
raise LoginError unless HTTP.loggedin?
|
59
60
|
|
60
61
|
resp, @data = HTTP.get(path)
|
61
62
|
@doc = Nokogiri::HTML.parse(@data)
|
62
63
|
end
|
63
64
|
|
64
|
-
#
|
65
|
+
# Returns whether log information have successfully been fetched
|
65
66
|
# from geocaching.com.
|
66
67
|
#
|
67
|
-
# @return [Boolean] Have information beed fetched?
|
68
|
+
# @return [Boolean] Have log information beed fetched?
|
68
69
|
def fetched?
|
69
70
|
@data and @doc
|
70
71
|
end
|
71
72
|
|
72
|
-
#
|
73
|
+
# Returns the log’s GUID.
|
74
|
+
#
|
75
|
+
# @return [String] GUID
|
76
|
+
def guid
|
77
|
+
@guid
|
78
|
+
end
|
79
|
+
|
80
|
+
# Returns the cache that belongs to this log.
|
73
81
|
#
|
74
82
|
# @return [Geocaching::Cache] Cache
|
75
83
|
def cache
|
@@ -80,43 +88,33 @@ module Geocaching
|
|
80
88
|
end
|
81
89
|
end
|
82
90
|
|
91
|
+
# Returns the log’s type.
|
92
|
+
#
|
93
|
+
# @return [Geocaching::LogType] Log type
|
83
94
|
def type
|
84
95
|
@type ||= LogType.for_title(title)
|
85
96
|
end
|
86
97
|
|
87
|
-
|
88
|
-
@title ||= begin
|
89
|
-
raise NotFetchedError unless fetched?
|
90
|
-
|
91
|
-
imgs = @doc.search("#ctl00_ContentBody_LogBookPanel1_LogImage")
|
92
|
-
|
93
|
-
unless imgs.size == 1 and imgs.first["alt"]
|
94
|
-
raise ExtractError, "Could not extract title from website"
|
95
|
-
end
|
96
|
-
|
97
|
-
imgs.first["alt"]
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
# The name of the user that has posted this log.
|
98
|
+
# Returns the the user that posted the log.
|
102
99
|
#
|
103
|
-
# @return [
|
104
|
-
|
105
|
-
|
106
|
-
def username
|
107
|
-
@username ||= begin
|
100
|
+
# @return [Geocaching::User] User
|
101
|
+
def user
|
102
|
+
@user ||= begin
|
108
103
|
raise NotFetchedError unless fetched?
|
109
104
|
|
110
105
|
elements = @doc.search("#ctl00_ContentBody_LogBookPanel1_lbLogText > a")
|
111
106
|
|
112
107
|
if elements.size > 0
|
113
|
-
HTTP.unescape(elements.first.inner_html)
|
108
|
+
User.new(:name => HTTP.unescape(elements.first.inner_html))
|
114
109
|
else
|
115
|
-
raise
|
110
|
+
raise ParseError, "Could not extract username from website"
|
116
111
|
end
|
117
112
|
end
|
118
113
|
end
|
119
114
|
|
115
|
+
# Returns the date the log was written at.
|
116
|
+
#
|
117
|
+
# @return [Time] Log date
|
120
118
|
def date
|
121
119
|
@date ||= begin
|
122
120
|
raise NotFetchedError unless fetched?
|
@@ -126,16 +124,14 @@ module Geocaching
|
|
126
124
|
if elements.size == 1
|
127
125
|
Time.parse(elements.first.content)
|
128
126
|
else
|
129
|
-
raise
|
127
|
+
raise ParseError, "Could not extract date from website"
|
130
128
|
end
|
131
129
|
end
|
132
130
|
end
|
133
131
|
|
134
|
-
#
|
132
|
+
# Returns the log’s raw message with all format codes.
|
135
133
|
#
|
136
|
-
# @return [String]
|
137
|
-
# @raise [Geocaching::NotFetchedError] Need to call {#fetch} before
|
138
|
-
# @raise [Geocaching::ExtractError] Could not extract message from website
|
134
|
+
# @return [String] Raw log message
|
139
135
|
def message
|
140
136
|
@message ||= begin
|
141
137
|
raise NotFetchedError unless fetched?
|
@@ -143,28 +139,46 @@ module Geocaching
|
|
143
139
|
if meta[:description]
|
144
140
|
meta[:description].gsub(/\r\n/, "\n")
|
145
141
|
else
|
146
|
-
raise
|
142
|
+
raise ParseError, "Could not extract message from website"
|
147
143
|
end
|
148
144
|
end
|
149
145
|
end
|
150
146
|
|
151
|
-
#
|
147
|
+
# Returns thehe short coord.info URL for the log.
|
152
148
|
#
|
153
149
|
# @return [String] coord.info URL
|
154
|
-
# @raise [Geocaching::NotFetchedError] Need to call {#fetch} before
|
155
|
-
# @raise [Geocaching::ExtractError] Could not extract short URL from website
|
156
150
|
def short_url
|
157
151
|
@short_url ||= begin
|
158
152
|
raise NotFetchedError unless fetched?
|
159
153
|
|
160
154
|
meta[:url] || begin
|
161
|
-
raise
|
155
|
+
raise ParseError, "Could not extract short URL from website"
|
162
156
|
end
|
163
157
|
end
|
164
158
|
end
|
165
159
|
|
166
160
|
private
|
167
161
|
|
162
|
+
# Returns the log’s title which is used internally to get the log type.
|
163
|
+
#
|
164
|
+
# @return [String] Log title
|
165
|
+
def title
|
166
|
+
@title ||= begin
|
167
|
+
raise NotFetchedError unless fetched?
|
168
|
+
|
169
|
+
imgs = @doc.search("#ctl00_ContentBody_LogBookPanel1_LogImage")
|
170
|
+
|
171
|
+
unless imgs.size == 1 and imgs.first["alt"]
|
172
|
+
raise ParseError, "Could not extract title from website"
|
173
|
+
end
|
174
|
+
|
175
|
+
imgs.first["alt"]
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
# Returns the UUID of the cache that belongs to this log.
|
180
|
+
#
|
181
|
+
# @return [String] Cache UUID
|
168
182
|
def cache_guid
|
169
183
|
if @cache.kind_of?(Geocaching::Cache) and @cache.guid
|
170
184
|
@cache.guid
|
@@ -177,11 +191,15 @@ module Geocaching
|
|
177
191
|
elements[1]["href"] =~ /guid=([a-f0-9-]{36})/
|
178
192
|
$1
|
179
193
|
else
|
180
|
-
raise
|
194
|
+
raise ParseError, "Could not extract cache GUID from website"
|
181
195
|
end
|
182
196
|
end
|
183
197
|
end
|
184
198
|
|
199
|
+
# Returns an array of information that are provided on the website
|
200
|
+
# in <meta> tags.
|
201
|
+
#
|
202
|
+
# @return [Hash] Log information
|
185
203
|
def meta
|
186
204
|
@meta ||= begin
|
187
205
|
elements = @doc.search("meta").select { |e| e["name"] =~ /^og:/ }.flatten
|
@@ -195,6 +213,9 @@ module Geocaching
|
|
195
213
|
end
|
196
214
|
end
|
197
215
|
|
216
|
+
# Returns the HTTP request path.
|
217
|
+
#
|
218
|
+
# @return [String] HTTP request path
|
198
219
|
def path
|
199
220
|
"/seek/log.aspx?LUID=#{@guid}"
|
200
221
|
end
|
data/lib/geocaching/log_type.rb
CHANGED
@@ -1,5 +1,9 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
module Geocaching
|
2
|
-
#
|
4
|
+
# The {LogType} class represents a log type.
|
5
|
+
#
|
6
|
+
# == Usage
|
3
7
|
#
|
4
8
|
# if @log.type == :archive
|
5
9
|
# puts "#{@log.cache.code} has been archived"
|
@@ -28,7 +32,7 @@ module Geocaching
|
|
28
32
|
:reviewer_note => ["big_smile", "Post Reviewer Note"]
|
29
33
|
}
|
30
34
|
|
31
|
-
#
|
35
|
+
# Returns a {LogType} object for the given log type icon, or nil if
|
32
36
|
# no appropriate log type is found.
|
33
37
|
#
|
34
38
|
# @return [Geocaching::LogType]
|
@@ -39,7 +43,7 @@ module Geocaching
|
|
39
43
|
end
|
40
44
|
end
|
41
45
|
|
42
|
-
#
|
46
|
+
# Returns a {LogType} object for the given log type title, or nil if
|
43
47
|
# no appropriate log type is found.
|
44
48
|
#
|
45
49
|
# @return [Geocaching::LogType]
|
@@ -50,43 +54,43 @@ module Geocaching
|
|
50
54
|
end
|
51
55
|
end
|
52
56
|
|
53
|
-
#
|
54
|
-
# of this class on your own.
|
57
|
+
# Creates a new instance. You should not need to create an instance
|
58
|
+
# of this class on your own. Use {for_icon} and {for_title}.
|
55
59
|
def initialize(info)
|
56
60
|
@info = info
|
57
61
|
end
|
58
62
|
|
59
|
-
#
|
63
|
+
# Returns the log type’s icon.
|
60
64
|
#
|
61
|
-
# @return [String]
|
65
|
+
# @return [String] Icon
|
62
66
|
def icon
|
63
67
|
@info[1][0]
|
64
68
|
end
|
65
69
|
|
66
|
-
#
|
70
|
+
# Returns the log type’s description.
|
67
71
|
#
|
68
|
-
# @return [String]
|
72
|
+
# @return [String] Description
|
69
73
|
def description
|
70
74
|
@info[1][1]
|
71
75
|
end
|
72
76
|
|
73
77
|
alias to_s description
|
74
78
|
|
75
|
-
#
|
79
|
+
# Returns the symbol representaton of the log type. See the {TYPES}
|
76
80
|
# hash for a list of log type symbols.
|
77
81
|
#
|
78
|
-
# @return [Symbol]
|
82
|
+
# @return [Symbol] Log type symbol
|
79
83
|
def to_sym
|
80
84
|
@info[0]
|
81
85
|
end
|
82
86
|
|
83
|
-
#
|
87
|
+
# Overloads the +==+ operator to compare by symbol.
|
84
88
|
#
|
85
89
|
# if @log.type == :dnf
|
86
90
|
# puts "Someone could not find the cache"
|
87
91
|
# end
|
88
92
|
#
|
89
|
-
# @return [Boolean]
|
93
|
+
# @return [Boolean] Does object match with argument?
|
90
94
|
def ==(s)
|
91
95
|
to_sym == s
|
92
96
|
end
|