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.
Files changed (49) hide show
  1. data/README.markdown +47 -21
  2. data/geocaching.gemspec +3 -0
  3. data/lib/geocaching.rb +20 -6
  4. data/lib/geocaching/cache.rb +237 -94
  5. data/lib/geocaching/cache_type.rb +17 -13
  6. data/lib/geocaching/http.rb +12 -16
  7. data/lib/geocaching/log.rb +67 -46
  8. data/lib/geocaching/log_type.rb +17 -13
  9. data/lib/geocaching/my_logs.rb +22 -16
  10. data/lib/geocaching/pocket_query.rb +213 -0
  11. data/lib/geocaching/user.rb +46 -69
  12. data/lib/geocaching/version.rb +3 -1
  13. data/lib/geocaching/watchlist.rb +106 -0
  14. data/spec/cache/ape.rb +4 -0
  15. data/spec/cache/cito.rb +4 -0
  16. data/spec/cache/earthcache.rb +4 -0
  17. data/spec/cache/event.rb +4 -0
  18. data/spec/cache/letterbox.rb +4 -0
  19. data/spec/cache/lfevent.rb +4 -0
  20. data/spec/cache/locationless.rb +4 -0
  21. data/spec/cache/megaevent.rb +4 -0
  22. data/spec/cache/multi.rb +4 -0
  23. data/spec/cache/mystery.rb +4 -0
  24. data/spec/cache/traditional.rb +4 -0
  25. data/spec/cache/virtual.rb +4 -0
  26. data/spec/cache/webcam.rb +4 -0
  27. data/spec/cache/wherigo.rb +4 -0
  28. data/spec/cache_spec.rb +7 -1
  29. data/spec/log/announcement.rb +1 -1
  30. data/spec/log/archive.rb +1 -1
  31. data/spec/log/attended.rb +1 -1
  32. data/spec/log/coords_update.rb +1 -1
  33. data/spec/log/disable.rb +1 -1
  34. data/spec/log/dnf.rb +1 -1
  35. data/spec/log/enable.rb +1 -1
  36. data/spec/log/found.rb +1 -1
  37. data/spec/log/needs_archived.rb +1 -1
  38. data/spec/log/needs_maintenance.rb +1 -1
  39. data/spec/log/note.rb +1 -1
  40. data/spec/log/owner_maintenance.rb +1 -1
  41. data/spec/log/publish.rb +1 -1
  42. data/spec/log/retract.rb +1 -1
  43. data/spec/log/reviewer_note.rb +1 -1
  44. data/spec/log/unarchive.rb +1 -1
  45. data/spec/log/webcam_photo_taken.rb +1 -1
  46. data/spec/log/will_attend.rb +1 -1
  47. data/spec/log_spec.rb +7 -1
  48. metadata +22 -6
  49. data/lib/geocaching/pq.rb +0 -72
@@ -1,5 +1,9 @@
1
+ # encoding: utf-8
2
+
1
3
  module Geocaching
2
- # This class represents a cache type.
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
- # Return a {CacheType} object for the given cache type id, or
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
- # Return a {CacheType} object for the given cache type title, or
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
- # Create a new instance. You should not need to create an instance
50
- # of this class on your own. Use {for_id} and {for_title}.
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
- # Return the cache type’s ID.
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
- # Return the cache type’s name.
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
- # Return the symbol that describes this cache type. See the {TYPES}
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
- # Overload the == operator.
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
@@ -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 this lib is logged in as a user.
122
+ # Returns whether you‘ve already logged in.
128
123
  #
129
- # @return [Boolean] Logged in?
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
- params = params.merge(metadata(path)).map { |k,v| "#{k}=#{v}" }.join("&")
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
@@ -1,7 +1,11 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module Geocaching
4
- # This class represents a log on geocaching.com.
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, "Need to be logged in to fetch log information" unless HTTP.loggedin?
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
- # Whether information have successfully been fetched
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
- # The cache that belongs to this log.
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
- def title
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 [String] Username
104
- # @raise [Geocaching::NotFetchedError] Need to call {#fetch} before
105
- # @raise [Geocaching::ExtractError] Could not extract username from website
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 ExtractError, "Could not extract username from website"
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 ExtractError, "Could not extract date from website"
127
+ raise ParseError, "Could not extract date from website"
130
128
  end
131
129
  end
132
130
  end
133
131
 
134
- # The log’s raw message with all format codes.
132
+ # Returns the log’s raw message with all format codes.
135
133
  #
136
- # @return [String] Log message
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 ExtractError, "Could not extract message from website"
142
+ raise ParseError, "Could not extract message from website"
147
143
  end
148
144
  end
149
145
  end
150
146
 
151
- # The short coord.info URL for this log.
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 ExtractError, "Could not extract short URL from website"
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 ExtractError, "Could not extract cache GUID from website"
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
@@ -1,5 +1,9 @@
1
+ # encoding: utf-8
2
+
1
3
  module Geocaching
2
- # This class represents a log type.
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
- # Return a {LogType} object for the given log type icon, or nil if
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
- # Return a {LogType} object for the given log type title, or nil if
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
- # Create a new instance. You should not need to create an instance
54
- # of this class on your own. Use {for_icon} and {for_title}.
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
- # Return the log type’s icon.
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
- # Return the log type’s title.
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
- # Return the symbol representatin of the log type. See the {TYPES}
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
- # Overload the == operator.
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