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.
- data/README.markdown +11 -57
- data/lib/geocaching.rb +13 -86
- data/lib/geocaching/cache.rb +79 -557
- data/lib/geocaching/cache_type.rb +39 -78
- data/lib/geocaching/container_type.rb +45 -0
- data/lib/geocaching/errors.rb +7 -0
- data/lib/geocaching/log.rb +49 -180
- data/lib/geocaching/log_type.rb +51 -81
- data/lib/geocaching/parsers/cache_gpx.rb +42 -0
- data/lib/geocaching/parsers/cache_gpx_additional_waypoint.rb +100 -0
- data/lib/geocaching/parsers/cache_gpx_cache_waypoint.rb +234 -0
- data/lib/geocaching/parsers/cache_simple.rb +219 -0
- data/lib/geocaching/parsers/log.rb +100 -0
- data/lib/geocaching/parsers/trackable.rb +107 -0
- data/lib/geocaching/session.rb +119 -0
- data/lib/geocaching/session/caches.rb +61 -0
- data/lib/geocaching/session/logs.rb +17 -0
- data/lib/geocaching/session/trackables.rb +26 -0
- data/lib/geocaching/trackable.rb +77 -0
- data/lib/geocaching/trackable_type.rb +17 -0
- data/lib/geocaching/user.rb +10 -202
- data/lib/geocaching/waypoint.rb +51 -0
- data/lib/geocaching/waypoint_type.rb +41 -0
- metadata +21 -25
- data/lib/geocaching/http.rb +0 -248
- data/lib/geocaching/my_logs.rb +0 -147
- data/lib/geocaching/version.rb +0 -5
- data/lib/geocaching/watchlist.rb +0 -93
data/lib/geocaching/user.rb
CHANGED
@@ -1,217 +1,25 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
require "time"
|
4
|
-
|
5
3
|
module Geocaching
|
6
|
-
# The {User} class represents a user on geocaching.com.
|
7
|
-
#
|
8
|
-
# == Usage
|
9
|
-
#
|
10
|
-
# user = Geocaching::User.fetch(:guid => "...")
|
11
|
-
# puts user.name #=> "Jack"
|
12
|
-
#
|
13
4
|
class User
|
14
|
-
|
15
|
-
|
16
|
-
#
|
17
|
-
# @param [Hash] attributes A hash of attributes
|
18
|
-
# @raise [ArgumentError] No GUID given
|
19
|
-
# @return [Geocaching::User]
|
20
|
-
def self.fetch(attributes)
|
21
|
-
user = new(attributes)
|
22
|
-
user.fetch
|
23
|
-
user
|
24
|
-
end
|
5
|
+
attr_accessor :guid
|
6
|
+
attr_accessor :name
|
25
7
|
|
26
|
-
# Creates a new instance. The following attributes may be specified
|
27
|
-
# as parameters:
|
28
|
-
#
|
29
|
-
# * +:guid+ — The user’s Globally Unique Identifier (GUID)
|
30
|
-
# * +:name+ — The user‘s name
|
31
|
-
#
|
32
|
-
# @param [Hash] attributes A hash of attributes
|
33
|
-
# @raise [ArgumentError] Trying to set an unknown attribute
|
34
8
|
def initialize(attributes = {})
|
35
|
-
@data, @doc, @guid = nil, nil, nil
|
36
|
-
|
37
9
|
attributes.each do |key, value|
|
38
|
-
if
|
39
|
-
|
40
|
-
else
|
41
|
-
raise ArgumentError, "Trying to set unknown attribute `#{key}'"
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
# Fetches user information from geocaching.com.
|
47
|
-
#
|
48
|
-
# @return [void]
|
49
|
-
# @raise [ArgumentError] No GUID given
|
50
|
-
def fetch
|
51
|
-
raise ArgumentError, "No GUID given" unless @guid
|
52
|
-
|
53
|
-
resp, @data = HTTP.get("/profile/?guid=#{guid}")
|
54
|
-
@doc = Nokogiri::HTML.parse(@data)
|
55
|
-
end
|
56
|
-
|
57
|
-
# Returns whether user information have successfully been fetched
|
58
|
-
# from geocaching.com.
|
59
|
-
#
|
60
|
-
# @return [Boolean] Have user information been fetched?
|
61
|
-
def fetched?
|
62
|
-
@data and @doc
|
63
|
-
end
|
64
|
-
|
65
|
-
# Returns the user’s Globally Unique Identifier (GUID).
|
66
|
-
#
|
67
|
-
# @return [String]
|
68
|
-
def guid
|
69
|
-
@guid
|
70
|
-
end
|
71
|
-
|
72
|
-
# Returns the user’s name.
|
73
|
-
#
|
74
|
-
# @return [String]
|
75
|
-
def name
|
76
|
-
@name ||= begin
|
77
|
-
raise NotFetchedError unless fetched?
|
78
|
-
|
79
|
-
elements = @doc.search("#ctl00_ContentBody_lblUserProfile")
|
80
|
-
|
81
|
-
if elements.size == 1 and elements.first.content =~ /Profile for User|Reviewer: (.+)/
|
82
|
-
HTTP.unescape($1)
|
10
|
+
if respond_to?("#{key}=")
|
11
|
+
send("#{key}=", value)
|
83
12
|
else
|
84
|
-
raise
|
13
|
+
raise ArgumentError, "Unknown attribute `#{key}'"
|
85
14
|
end
|
86
15
|
end
|
87
16
|
end
|
88
17
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
raise NotFetchedError unless fetched?
|
95
|
-
|
96
|
-
elements = @doc.search("#ctl00_ContentBody_ProfilePanel1_lblOccupationTxt")
|
97
|
-
|
98
|
-
if elements.size == 1
|
99
|
-
HTTP.unescape(elements.first.content)
|
100
|
-
else
|
101
|
-
raise ParseError, "Could not extract occupation from website"
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
# Returns the user’s location.
|
107
|
-
#
|
108
|
-
# @return [String] Location
|
109
|
-
def location
|
110
|
-
@location ||= begin
|
111
|
-
raise NotFetchedError unless fetched?
|
112
|
-
|
113
|
-
elements = @doc.search("#ctl00_ContentBody_ProfilePanel1_lblLocationTxt")
|
114
|
-
|
115
|
-
if elements.size == 1
|
116
|
-
HTTP.unescape(elements.first.content)
|
117
|
-
else
|
118
|
-
raise ParseError, "Could not extract location from website"
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
# Returns the user’s forum title.
|
124
|
-
#
|
125
|
-
# @return [String] Forum title
|
126
|
-
def forum_title
|
127
|
-
@forum_title ||= begin
|
128
|
-
raise NotFetchedError unless fetched?
|
129
|
-
|
130
|
-
elements = @doc.search("#ctl00_ContentBody_ProfilePanel1_lblForumTitleTxt")
|
131
|
-
|
132
|
-
if elements.size == 1
|
133
|
-
HTTP.unescape(elements.first.content)
|
134
|
-
else
|
135
|
-
raise ParseError, "Could not extract forum title from website"
|
136
|
-
end
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
|
-
# Returns the user’s homepage.
|
141
|
-
#
|
142
|
-
# @return [String] Homepage
|
143
|
-
def homepage
|
144
|
-
@homepage ||= begin
|
145
|
-
raise NotFetchedError unless fetched?
|
146
|
-
|
147
|
-
elements = @doc.search("#ctl00_ContentBody_ProfilePanel1_lnkHomePage")
|
148
|
-
elements.first["href"] if elements.size == 1
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
# Returns the user’s statuses.
|
153
|
-
#
|
154
|
-
# @return [Array<String>] Array of statuses
|
155
|
-
def status
|
156
|
-
@status ||= begin
|
157
|
-
raise NotFetchedError unless fetched?
|
158
|
-
|
159
|
-
elements = @doc.search("#ctl00_ContentBody_ProfilePanel1_lblStatusText")
|
160
|
-
|
161
|
-
if elements.size == 1
|
162
|
-
HTTP.unescape(elements.first.content).split(",").map(&:strip)
|
163
|
-
else
|
164
|
-
raise ParseError, "Could not extract status from website"
|
165
|
-
end
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
# Returns the user’s last visit date.
|
170
|
-
#
|
171
|
-
# @return [Time] Last visit date
|
172
|
-
def last_visit
|
173
|
-
@last_visit ||= begin
|
174
|
-
raise NotFetchedError unless fetched?
|
175
|
-
|
176
|
-
elements = @doc.search("#ctl00_ContentBody_ProfilePanel1_lblLastVisitDate")
|
177
|
-
|
178
|
-
if elements.size == 1
|
179
|
-
Time.parse(elements.first.content)
|
180
|
-
else
|
181
|
-
raise ParseError, "Could not extract last visit date from website"
|
182
|
-
end
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
# Returns the user’s member since date.
|
187
|
-
#
|
188
|
-
# @return [Time] Member since date
|
189
|
-
def member_since
|
190
|
-
@member_since ||= begin
|
191
|
-
raise NotFetchedError unless fetched?
|
192
|
-
|
193
|
-
elements = @doc.search("#ctl00_ContentBody_ProfilePanel1_lblMemberSinceDate")
|
194
|
-
|
195
|
-
if elements.size == 1
|
196
|
-
Time.parse(elements.first.content)
|
197
|
-
else
|
198
|
-
raise ParseError, "Could not extract member since date from website"
|
199
|
-
end
|
200
|
-
end
|
201
|
-
end
|
202
|
-
|
203
|
-
# Returns whether the user is a reviewer.
|
204
|
-
#
|
205
|
-
# @return [Boolean] Is user a reviewer?
|
206
|
-
def reviewer?
|
207
|
-
status.include?("Reviewer")
|
208
|
-
end
|
209
|
-
|
210
|
-
# Returns whether the user is a Premium Member.
|
211
|
-
#
|
212
|
-
# @return [Boolean] Is user a Premium Member?
|
213
|
-
def premium_member?
|
214
|
-
status.include?("Premium Member")
|
18
|
+
def to_hash
|
19
|
+
{
|
20
|
+
:guid => guid,
|
21
|
+
:name => name
|
22
|
+
}
|
215
23
|
end
|
216
24
|
end
|
217
25
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Geocaching
|
4
|
+
class Waypoint
|
5
|
+
attr_accessor :code
|
6
|
+
attr_accessor :name
|
7
|
+
attr_accessor :latitude
|
8
|
+
attr_accessor :longitude
|
9
|
+
attr_accessor :note
|
10
|
+
attr_reader :type
|
11
|
+
attr_reader :created_at
|
12
|
+
|
13
|
+
def initialize(attributes = {})
|
14
|
+
attributes.each do |key, value|
|
15
|
+
if respond_to?("#{key}=")
|
16
|
+
send("#{key}=", value)
|
17
|
+
else
|
18
|
+
raise ArgumentError, "Unknown attribute `#{key}'"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def type=(waypoint_type)
|
24
|
+
if waypoint_type.kind_of?(Geocaching::WaypointType)
|
25
|
+
@type = waypoint_type
|
26
|
+
else
|
27
|
+
raise TypeError, "`type' must be an instance of Geocaching::WaypointType"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def created_at=(time)
|
32
|
+
if time.kind_of?(Time)
|
33
|
+
@created_at = time
|
34
|
+
else
|
35
|
+
raise TypeError, "`created_at' must be an instance of Time"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_hash
|
40
|
+
{
|
41
|
+
:code => code,
|
42
|
+
:name => name,
|
43
|
+
:latitude => latitude,
|
44
|
+
:longitude => longitude,
|
45
|
+
:note => note,
|
46
|
+
:type => type,
|
47
|
+
:created_at => created_at
|
48
|
+
}
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Geocaching
|
4
|
+
class WaypointType
|
5
|
+
attr_reader :id
|
6
|
+
|
7
|
+
PARKING_AREA = 0
|
8
|
+
QUESTION_TO_ANSWER = 1
|
9
|
+
REFERENCE_POINT = 2
|
10
|
+
STAGE = 3
|
11
|
+
TRAILHEAD = 4
|
12
|
+
|
13
|
+
MAPPING = [
|
14
|
+
[PARKING_AREA, "Parking Area" ],
|
15
|
+
[QUESTION_TO_ANSWER, "Question to Answer" ],
|
16
|
+
[REFERENCE_POINT, "Reference Point" ],
|
17
|
+
[STAGE, "Stages of a Multicache"],
|
18
|
+
[TRAILHEAD, "Trailhead" ]
|
19
|
+
]
|
20
|
+
|
21
|
+
def self.from_name(name)
|
22
|
+
if type = MAPPING.find { |_, n| n == name }
|
23
|
+
new(type[0])
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize(id)
|
28
|
+
@id = id
|
29
|
+
end
|
30
|
+
|
31
|
+
def name
|
32
|
+
@name ||= MAPPING.find { |id, _| id == @id }[1]
|
33
|
+
end
|
34
|
+
|
35
|
+
alias :to_s :name
|
36
|
+
|
37
|
+
def ==(id)
|
38
|
+
@id = id
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
metadata
CHANGED
@@ -1,12 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: geocaching
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
prerelease:
|
5
|
-
|
6
|
-
- 0
|
7
|
-
- 6
|
8
|
-
- 1
|
9
|
-
version: 0.6.1
|
4
|
+
prerelease:
|
5
|
+
version: 0.7.0
|
10
6
|
platform: ruby
|
11
7
|
authors:
|
12
8
|
- Thomas Cyron
|
@@ -14,7 +10,7 @@ autorequire:
|
|
14
10
|
bindir: bin
|
15
11
|
cert_chain: []
|
16
12
|
|
17
|
-
date: 2011-
|
13
|
+
date: 2011-04-28 00:00:00 +02:00
|
18
14
|
default_executable:
|
19
15
|
dependencies:
|
20
16
|
- !ruby/object:Gem::Dependency
|
@@ -25,10 +21,6 @@ dependencies:
|
|
25
21
|
requirements:
|
26
22
|
- - ">="
|
27
23
|
- !ruby/object:Gem::Version
|
28
|
-
segments:
|
29
|
-
- 1
|
30
|
-
- 4
|
31
|
-
- 2
|
32
24
|
version: 1.4.2
|
33
25
|
type: :runtime
|
34
26
|
version_requirements: *id001
|
@@ -40,14 +32,10 @@ dependencies:
|
|
40
32
|
requirements:
|
41
33
|
- - ">="
|
42
34
|
- !ruby/object:Gem::Version
|
43
|
-
segments:
|
44
|
-
- 1
|
45
|
-
- 4
|
46
|
-
- 6
|
47
35
|
version: 1.4.6
|
48
36
|
type: :runtime
|
49
37
|
version_requirements: *id002
|
50
|
-
description: A Ruby library
|
38
|
+
description: A Ruby library providing an API for Geocaching.com
|
51
39
|
email: thomas@thcyron.de
|
52
40
|
executables: []
|
53
41
|
|
@@ -59,13 +47,25 @@ files:
|
|
59
47
|
- README.markdown
|
60
48
|
- lib/geocaching/cache.rb
|
61
49
|
- lib/geocaching/cache_type.rb
|
62
|
-
- lib/geocaching/
|
50
|
+
- lib/geocaching/container_type.rb
|
51
|
+
- lib/geocaching/errors.rb
|
63
52
|
- lib/geocaching/log.rb
|
64
53
|
- lib/geocaching/log_type.rb
|
65
|
-
- lib/geocaching/
|
54
|
+
- lib/geocaching/parsers/cache_gpx.rb
|
55
|
+
- lib/geocaching/parsers/cache_gpx_additional_waypoint.rb
|
56
|
+
- lib/geocaching/parsers/cache_gpx_cache_waypoint.rb
|
57
|
+
- lib/geocaching/parsers/cache_simple.rb
|
58
|
+
- lib/geocaching/parsers/log.rb
|
59
|
+
- lib/geocaching/parsers/trackable.rb
|
60
|
+
- lib/geocaching/session/caches.rb
|
61
|
+
- lib/geocaching/session/logs.rb
|
62
|
+
- lib/geocaching/session/trackables.rb
|
63
|
+
- lib/geocaching/session.rb
|
64
|
+
- lib/geocaching/trackable.rb
|
65
|
+
- lib/geocaching/trackable_type.rb
|
66
66
|
- lib/geocaching/user.rb
|
67
|
-
- lib/geocaching/
|
68
|
-
- lib/geocaching/
|
67
|
+
- lib/geocaching/waypoint.rb
|
68
|
+
- lib/geocaching/waypoint_type.rb
|
69
69
|
- lib/geocaching.rb
|
70
70
|
has_rdoc: false
|
71
71
|
homepage: http://nano.github.com/ruby-geocaching
|
@@ -81,21 +81,17 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
81
81
|
requirements:
|
82
82
|
- - ">="
|
83
83
|
- !ruby/object:Gem::Version
|
84
|
-
segments:
|
85
|
-
- 0
|
86
84
|
version: "0"
|
87
85
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
88
86
|
none: false
|
89
87
|
requirements:
|
90
88
|
- - ">="
|
91
89
|
- !ruby/object:Gem::Version
|
92
|
-
segments:
|
93
|
-
- 0
|
94
90
|
version: "0"
|
95
91
|
requirements: []
|
96
92
|
|
97
93
|
rubyforge_project:
|
98
|
-
rubygems_version: 1.
|
94
|
+
rubygems_version: 1.5.0
|
99
95
|
signing_key:
|
100
96
|
specification_version: 3
|
101
97
|
summary: Ruby API for geocaching.com
|
data/lib/geocaching/http.rb
DELETED
@@ -1,248 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
require "cgi"
|
4
|
-
require "net/http"
|
5
|
-
require "timeout"
|
6
|
-
|
7
|
-
module Geocaching
|
8
|
-
# The {HTTP} class handles the HTTP communication with geocaching.com.
|
9
|
-
class HTTP
|
10
|
-
# An array of user agent strings. A random one is chosen.
|
11
|
-
USER_AGENTS = [
|
12
|
-
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)",
|
13
|
-
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)",
|
14
|
-
"Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1)",
|
15
|
-
"Mozilla/5.0 (compatible; Konqueror/3.2; Linux 2.6.2) (KHTML, like Gecko)",
|
16
|
-
"Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/125.2 (KHTML, like Gecko) Safari/125.8",
|
17
|
-
"Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/0.A.B.C Safari/525.13",
|
18
|
-
"Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:1.9.0.10) Gecko/2009042316 Firefox/3.0.10",
|
19
|
-
"Mozilla/5.0 (X11; U; Linux i586; en-US; rv:1.7.3) Gecko/20040924 Epiphany/1.4.4 (Ubuntu)",
|
20
|
-
"Opera/9.80 (Macintosh; Intel Mac OS X; U; en) Presto/2.2.15 Version/10.00"
|
21
|
-
]
|
22
|
-
|
23
|
-
# The user agent sent with each request.
|
24
|
-
@user_agent = nil
|
25
|
-
|
26
|
-
# Timeout for sending and receiving HTTP data.
|
27
|
-
@timeout = 8
|
28
|
-
|
29
|
-
class << self
|
30
|
-
attr_accessor :user_agent, :timeout, :username, :password
|
31
|
-
|
32
|
-
# Returns the singleton instance of this class.
|
33
|
-
#
|
34
|
-
# @return [HTTP] Singleton instance of this class
|
35
|
-
def instance
|
36
|
-
@instance ||= new
|
37
|
-
end
|
38
|
-
|
39
|
-
# Alias for:
|
40
|
-
#
|
41
|
-
# Geocaching::HTTP.username = username
|
42
|
-
# Geocaching::HTTP.password = password
|
43
|
-
# Geocaching::HTTP.instance.login
|
44
|
-
def login(username = nil, password = nil)
|
45
|
-
self.username, self.password = username, password if username && password
|
46
|
-
self.instance.login
|
47
|
-
end
|
48
|
-
|
49
|
-
# Alias for +Geocaching::HTTP.instance.logout+.
|
50
|
-
def logout
|
51
|
-
self.instance.logout
|
52
|
-
end
|
53
|
-
|
54
|
-
# Alias for +Geocaching::HTTP.instance.loggedin?+.
|
55
|
-
def loggedin?
|
56
|
-
self.instance.loggedin?
|
57
|
-
end
|
58
|
-
|
59
|
-
# Alias for +Geocaching::HTTP.instance.get+.
|
60
|
-
def get(path)
|
61
|
-
self.instance.get(path)
|
62
|
-
end
|
63
|
-
|
64
|
-
# Alias for +Geocaching::HTTP.instance.post+.
|
65
|
-
def post(path, data = {}, extra_headers = {})
|
66
|
-
self.instance.post(path, data, extra_headers)
|
67
|
-
end
|
68
|
-
|
69
|
-
# Converts HTML entities to the corresponding UTF-8 symbols.
|
70
|
-
#
|
71
|
-
# @return [String] The converted string
|
72
|
-
def unescape(str)
|
73
|
-
str = str.force_encoding("UTF-8") if str.respond_to?(:force_encoding)
|
74
|
-
str = str.gsub(/&#(\d{3});/) { [$1.to_i].pack("U") }
|
75
|
-
CGI.unescapeHTML(str)
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
# Creates a new instance.
|
80
|
-
def initialize
|
81
|
-
@loggedin = false
|
82
|
-
@cookie = nil
|
83
|
-
end
|
84
|
-
|
85
|
-
# Logs in into geocaching.com. Username and password need to be set
|
86
|
-
# before calling this method.
|
87
|
-
#
|
88
|
-
# HTTP.username = "username"
|
89
|
-
# HTTP.password = "password"
|
90
|
-
#
|
91
|
-
# @return [void]
|
92
|
-
# @raise [ArgumentError] Username or password missing
|
93
|
-
def login
|
94
|
-
raise ArgumentError, "Missing username" unless self.class.username
|
95
|
-
raise ArgumentError, "Missing password" unless self.class.password
|
96
|
-
|
97
|
-
raise LoginError, "Already logged in" if @loggedin
|
98
|
-
|
99
|
-
resp, data = post("/login/default.aspx", {
|
100
|
-
"ctl00$ContentBody$myUsername" => self.class.username,
|
101
|
-
"ctl00$ContentBody$myPassword" => self.class.password,
|
102
|
-
"ctl00$ContentBody$Button1" => "Login",
|
103
|
-
"ctl00$ContentBody$cookie" => "on"
|
104
|
-
})
|
105
|
-
|
106
|
-
@cookie = resp.response["set-cookie"]
|
107
|
-
@loggedin = true
|
108
|
-
end
|
109
|
-
|
110
|
-
# Logs out from geocaching.com.
|
111
|
-
#
|
112
|
-
# @return [void]
|
113
|
-
def logout
|
114
|
-
raise LoginError, "Not logged in" unless @loggedin
|
115
|
-
|
116
|
-
@loggedin = false
|
117
|
-
@cookie = nil
|
118
|
-
|
119
|
-
get("/login/default.aspx?RESETCOMPLETE=Y")
|
120
|
-
end
|
121
|
-
|
122
|
-
# Returns whether you‘ve already logged in.
|
123
|
-
#
|
124
|
-
# @return [Boolean] Already logged in?
|
125
|
-
def loggedin?
|
126
|
-
@loggedin and @cookie
|
127
|
-
end
|
128
|
-
|
129
|
-
# Sends a GET request to +path+. The authentication cookie is sent
|
130
|
-
# with the request if available.
|
131
|
-
#
|
132
|
-
# @param [String] path Request path
|
133
|
-
# @return [Net::HTTP::Response] Reponse object from +Net::HTTP+
|
134
|
-
# @return [String] Actual content
|
135
|
-
def get(path)
|
136
|
-
resp = data = nil
|
137
|
-
header = default_header
|
138
|
-
header["Cookie"] = @cookie if @cookie
|
139
|
-
|
140
|
-
begin
|
141
|
-
Timeout::timeout(self.class.timeout) do
|
142
|
-
resp, data = http.get(path, header)
|
143
|
-
end
|
144
|
-
rescue Timeout::Error
|
145
|
-
raise TimeoutError, "Timeout hit for GET #{path}"
|
146
|
-
rescue
|
147
|
-
raise HTTPError
|
148
|
-
end
|
149
|
-
|
150
|
-
unless resp.kind_of?(Net::HTTPSuccess)
|
151
|
-
if resp.kind_of?(Net::HTTPRedirection) and resp.response["location"]
|
152
|
-
resp, data = get(resp.response["location"])
|
153
|
-
else
|
154
|
-
raise HTTPError
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
[resp, data]
|
159
|
-
end
|
160
|
-
|
161
|
-
# Sends a POST request to +path+ with the data given in the
|
162
|
-
# +params+ hash. The authentication cookie is sent with the
|
163
|
-
# request if available.
|
164
|
-
#
|
165
|
-
# Before sending the POST request, a GET request is sent to obtain the
|
166
|
-
# information like +__VIEWPORT+ that are used on geocaching.com to protect
|
167
|
-
# from Cross Site Request Forgery.
|
168
|
-
#
|
169
|
-
# @return [Net::HTTP::Response] Reponse object from +Net::HTTP+
|
170
|
-
# @return [String] Actual content
|
171
|
-
# @raise [Geocaching::TimeoutError] Timeout hit
|
172
|
-
# @raise [Geocaching::HTTPError] HTTP request failed
|
173
|
-
def post(path, params = {}, extra_headers = {})
|
174
|
-
if params.kind_of?(Hash)
|
175
|
-
params = params.merge(metadata(path)).map { |k,v| "#{k}=#{v}" }.join("&")
|
176
|
-
end
|
177
|
-
|
178
|
-
resp = data = nil
|
179
|
-
|
180
|
-
header = default_header.merge(extra_headers)
|
181
|
-
header["Cookie"] = @cookie if @cookie
|
182
|
-
|
183
|
-
begin
|
184
|
-
Timeout::timeout(self.class.timeout) do
|
185
|
-
resp, data = http.post(path, params, header)
|
186
|
-
end
|
187
|
-
rescue Timeout::Error
|
188
|
-
raise TimeoutError, "Timeout hit for POST #{path}"
|
189
|
-
rescue
|
190
|
-
raise HTTPError
|
191
|
-
end
|
192
|
-
|
193
|
-
unless resp.kind_of?(Net::HTTPSuccess)
|
194
|
-
if resp.kind_of?(Net::HTTPRedirection) and resp.response["location"]
|
195
|
-
resp, data = get(resp.response["location"])
|
196
|
-
else
|
197
|
-
raise HTTPError
|
198
|
-
end
|
199
|
-
end
|
200
|
-
|
201
|
-
[resp, data]
|
202
|
-
end
|
203
|
-
|
204
|
-
private
|
205
|
-
|
206
|
-
# Sends a GET request to +path+ to obtain form meta data used on
|
207
|
-
# geocaching.com to protect from CSRF.
|
208
|
-
#
|
209
|
-
# @return [Hash] Meta information
|
210
|
-
def metadata(path)
|
211
|
-
resp, data = get(path)
|
212
|
-
meta = {}
|
213
|
-
|
214
|
-
data.scan(/<input type="hidden" name="__([A-Z]+)" id="__[A-Z]+" value="(.*?)" \/>/).each do |match|
|
215
|
-
meta["__#{match[0]}"] = CGI.escape(match[1])
|
216
|
-
end
|
217
|
-
|
218
|
-
meta
|
219
|
-
end
|
220
|
-
|
221
|
-
# Returns the user agent string to use for the HTTP requests. If no
|
222
|
-
# user agent is set explicitly, a random one is chosen.
|
223
|
-
#
|
224
|
-
# @return [String] User agent
|
225
|
-
def user_agent
|
226
|
-
self.class.user_agent ||= USER_AGENTS.shuffle.first
|
227
|
-
end
|
228
|
-
|
229
|
-
# Returns an hash with the HTTP headers sent with each request.
|
230
|
-
#
|
231
|
-
# @return [Hash] Default HTTP headers
|
232
|
-
def default_header
|
233
|
-
{
|
234
|
-
"User-Agent" => user_agent
|
235
|
-
}
|
236
|
-
end
|
237
|
-
|
238
|
-
# Returns the instance of {Net::HTTP} or creates a new one for
|
239
|
-
# +www.geocaching.com+.
|
240
|
-
#
|
241
|
-
# @return [Net::HTTP]
|
242
|
-
def http
|
243
|
-
@http ||= begin
|
244
|
-
Net::HTTP.new("www.geocaching.com")
|
245
|
-
end
|
246
|
-
end
|
247
|
-
end
|
248
|
-
end
|