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.
@@ -1,94 +1,55 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module Geocaching
4
- # The {CacheType} class represents a cache type.
5
- #
6
- # == Usage
7
- #
8
- # if @cache.type == :traditional
9
- # puts "Cache is a Traditional Cache"
10
- # end
11
4
  class CacheType
12
- # A mapping of cache types to their corresponding ID and name
13
- # on geocaching.com.
14
- TYPES = {
15
- :traditional => [2, "Traditional Cache"],
16
- :multi => [3, "Multi-cache"],
17
- :mystery => [8, "Unknown Cache"],
18
- :letterbox => [5, "Letterbox Hybrid"],
19
- :wherigo => [1858, "Wherigo Cache"],
20
- :event => [6, "Event Cache"],
21
- :megaevent => [453, "Mega-Event Cache"],
22
- :cito => [13, "Cache In Trash Out Event"],
23
- :earthcache => [137, "Earthcache"],
24
- :lfevent => [3653, "Lost and Found Event Cache"],
25
- :locationless => [12, "Locationless (Reverse) Cache"],
26
- :webcam => [11, "Webcam Cache"],
27
- :virtual => [4, "Virtual Cache"],
28
- :ape => [9, "Project APE Cache"]
29
- }
30
-
31
- # Returns a {CacheType} object for the given cache type id, or
32
- # nil if no appropriate cache id is found.
33
- #
34
- # @return [Geocaching::CacheType]
35
- # @return [nil] If no appropriate cache type is found
36
- def self.for_id(id)
37
- if info = TYPES.to_a.select { |(k,v)| v[0] == id } and info.size == 1
38
- new(info.first)
39
- end
40
- end
41
-
42
- # Returns a {CacheType} object for the given cache type title, or
43
- # nil if no appropriate cache type is found.
44
- #
45
- # @return [Geocaching::CacheType]
46
- # @return [nil] If no appropriate cache type is found
47
- def self.for_title(title)
48
- if info = TYPES.to_a.select { |(k,v)| v[1] == title } and info.size == 1
49
- new(info.first)
5
+ attr_reader :id
6
+
7
+ EARTHCACHE = 1
8
+ EVENT = 2
9
+ GPS_ADVENTURES_EXHIBIT = 3
10
+ LETTERBOX = 4
11
+ LOCATIONLESS = 5
12
+ MULTI = 6
13
+ PROJECT_APE = 7
14
+ TRADITIONAL = 8
15
+ UNKNOWN = 9
16
+ VIRTUAL = 10
17
+ WEBCAM = 11
18
+ WHERIGO = 12
19
+
20
+ MAPPING = [
21
+ [EARTHCACHE, "Earthcache" ],
22
+ [EVENT, "Event Cache" ],
23
+ [GPS_ADVENTURES_EXHIBIT, "GPS Adventures Exhibit" ],
24
+ [LETTERBOX, "Letterbox Hybrid" ],
25
+ [LOCATIONLESS, "Locationless (Reverse) Cache"],
26
+ [MULTI, "Multi-cache" ],
27
+ [PROJECT_APE, "Project APE Cache" ],
28
+ [TRADITIONAL, "Traditional Cache" ],
29
+ [UNKNOWN, "Unknown Cache" ],
30
+ [VIRTUAL, "Virtual Cache" ],
31
+ [WEBCAM, "Webcam Cache" ],
32
+ [WHERIGO, "Wherigo Cache" ]
33
+ ]
34
+
35
+ def self.from_name(name)
36
+ if type = MAPPING.find { |_, n| n == name }
37
+ new(type[0])
50
38
  end
51
39
  end
52
40
 
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}.
55
- def initialize(info)
56
- @info = info
41
+ def initialize(id)
42
+ @id = id
57
43
  end
58
44
 
59
- # Returns the cache type’s ID.
60
- #
61
- # @return [Fixnum] Cache type ID
62
- def id
63
- @info[1][0]
64
- end
65
-
66
- # Returns the cache type’s name.
67
- #
68
- # @return [String] Cache type name
69
45
  def name
70
- @info[1][1]
46
+ @name ||= MAPPING.find { |id, _| id == @id }[1]
71
47
  end
72
48
 
73
- alias to_s name
74
-
75
- # Returns the symbol that describes this cache type. See the {TYPES}
76
- # hash for a list of cache type symbols.
77
- #
78
- # @return [Symbol] Cache type symbol
79
- def to_sym
80
- @info[0]
81
- end
49
+ alias :to_s :name
82
50
 
83
- # Overloads the +==+ operator to compare by symbol.
84
- #
85
- # if @cache.type == :multi
86
- # puts "It's a multi cache."
87
- # end
88
- #
89
- # @return [Boolean] Does object match with argument?
90
- def ==(s)
91
- to_sym == s
51
+ def ==(id)
52
+ @id == id
92
53
  end
93
54
  end
94
55
  end
@@ -0,0 +1,45 @@
1
+ # encoding: utf-8
2
+
3
+ module Geocaching
4
+ class ContainerType
5
+ attr_reader :id
6
+
7
+ NOT_CHOSEN = 0
8
+ MICRO = 1
9
+ SMALL = 2
10
+ REGULAR = 3
11
+ LARGE = 4
12
+ VIRTUAL = 5
13
+ OTHER = 6
14
+
15
+ MAPPING = [
16
+ [NOT_CHOSEN, "Not chosen"],
17
+ [MICRO, "Micro" ],
18
+ [SMALL, "Small" ],
19
+ [REGULAR, "Regular" ],
20
+ [LARGE, "Large" ],
21
+ [VIRTUAL, "Virtual" ],
22
+ [OTHER, "Other" ]
23
+ ]
24
+
25
+ def self.from_name(name)
26
+ if type = MAPPING.find { |_, n| n == name }
27
+ new(type[0])
28
+ end
29
+ end
30
+
31
+ def initialize(id)
32
+ @id = id
33
+ end
34
+
35
+ def name
36
+ @name ||= MAPPING.find { |id, _| id == @id }[1]
37
+ end
38
+
39
+ alias :to_s :name
40
+
41
+ def ==(id)
42
+ @id = id
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,7 @@
1
+ # encoding: utf-8
2
+
3
+ module Geocaching
4
+ class Error < Exception; end
5
+ class HTTPError < Error; end
6
+ class ParseError < Error; end
7
+ end
@@ -1,212 +1,81 @@
1
1
  # encoding: utf-8
2
2
 
3
+ require "geocaching/parsers/log"
4
+
3
5
  module Geocaching
4
- # The {Log} class represents a log on geocaching.com.
5
6
  class Log
6
- # Creates a new instance and calls the {#fetch} method afterwards.
7
- # +:guid+ must be specified as an attribute.
8
- #
9
- # @param [Hash] attributes Hash of attributes
10
- # @return [Geocaching::Log]
11
- # @raise [ArgumentError] Unknown attribute provided
12
- # @raise [TypeError] Invalid attribute provided
13
- def self.fetch(attributes)
14
- log = new(attributes)
15
- log.fetch
16
- log
7
+ attr_accessor :guid
8
+ attr_accessor :message
9
+ attr_reader :user
10
+ attr_reader :logged_at
11
+ attr_reader :created_at
12
+ attr_reader :cache
13
+ attr_reader :type
14
+
15
+ def self.from_xml(doc)
16
+ Parsers::Log.new(doc).parse
17
17
  end
18
18
 
19
- # Creates a new instance. The following attributes may be specified
20
- # as parameters:
21
- #
22
- # * +:guid+ — The log’s Globally Unique Identifier
23
- #
24
- # @raise [ArgumentError] Trying to set an unknown attribute
25
19
  def initialize(attributes = {})
26
- @data, @doc, @guid, @cache = nil, nil, nil, nil
27
-
28
20
  attributes.each do |key, value|
29
- if [:guid, :title, :date, :cache, :user].include?(key)
30
- if key == :cache and not value.kind_of?(Geocaching::Cache)
31
- raise TypeError, "Attribute `cache' must be an instance of Geocaching::Cache"
32
- end
33
-
34
- if key == :date and not value.kind_of?(Time)
35
- raise TypeError, "Attribute `type' must be an instance of Time"
36
- end
37
-
38
- if key == :user and not value.kind_of?(User)
39
- raise TypeError, "Attribute `user' must be an instance of Geocaching::User"
40
- end
41
-
42
- instance_variable_set("@#{key}", value)
43
- else
44
- raise ArgumentError, "Trying to set unknown attribute `#{key}'"
45
- end
46
- end
47
- end
48
-
49
- # Fetches log information from geocaching.com.
50
- #
51
- # @return [void]
52
- # @raise [ArgumentError] GUID is not given
53
- def fetch
54
- raise ArgumentError, "No GUID given" unless @guid
55
- raise LoginError unless HTTP.loggedin?
56
-
57
- resp, @data = HTTP.get("/seek/log.aspx?LUID=#{@guid}")
58
- @doc = Nokogiri::HTML.parse(@data)
59
- end
60
-
61
- # Returns whether log information have successfully been fetched
62
- # from geocaching.com.
63
- #
64
- # @return [Boolean] Have log information beed fetched?
65
- def fetched?
66
- @data and @doc
67
- end
68
-
69
- # Returns the log’s GUID.
70
- #
71
- # @return [String] GUID
72
- def guid
73
- @guid
74
- end
75
-
76
- # Returns the cache that belongs to this log.
77
- #
78
- # @return [Geocaching::Cache] Cache
79
- def cache
80
- @cache ||= begin
81
- if guid = cache_guid
82
- Cache.new(:guid => guid)
83
- end
84
- end
85
- end
86
-
87
- # Returns the log’s type.
88
- #
89
- # @return [Geocaching::LogType] Log type
90
- def type
91
- @type ||= LogType.for_title(title)
92
- end
93
-
94
- # Returns the the user that posted the log.
95
- #
96
- # @return [Geocaching::User] User
97
- def user
98
- @user ||= begin
99
- raise NotFetchedError unless fetched?
100
-
101
- elements = @doc.search("#ctl00_ContentBody_LogBookPanel1_lbLogText > a")
102
-
103
- if elements.size > 0
104
- User.new(:name => HTTP.unescape(elements.first.inner_html))
21
+ if respond_to?("#{key}=")
22
+ send("#{key}=", value)
105
23
  else
106
- raise ParseError, "Could not extract username from website"
24
+ raise ArgumentError, "Unknown attribute `#{key}'"
107
25
  end
108
26
  end
109
27
  end
110
28
 
111
- # Returns the date the log was written at.
112
- #
113
- # @return [Time] Log date
114
- def date
115
- @date ||= begin
116
- raise NotFetchedError unless fetched?
117
-
118
- elements = @doc.search("#ctl00_ContentBody_LogBookPanel1_LogDate")
119
-
120
- if elements.size == 1
121
- Time.parse(elements.first.content)
122
- else
123
- raise ParseError, "Could not extract date from website"
124
- end
29
+ def logged_at=(time)
30
+ if time.kind_of?(Time)
31
+ @logged_at = time
32
+ else
33
+ raise TypeError, "`logged_at' must be an instance of Time"
125
34
  end
126
35
  end
127
36
 
128
- # Returns the log’s raw message with all format codes.
129
- #
130
- # @return [String] Raw log message
131
- def message
132
- @message ||= begin
133
- raise NotFetchedError unless fetched?
134
-
135
- if meta[:description]
136
- meta[:description].gsub(/\r\n/, "\n")
137
- else
138
- raise ParseError, "Could not extract message from website"
139
- end
37
+ def created_at=(time)
38
+ if time.kind_of?(Time)
39
+ @created_at = time
40
+ else
41
+ raise TypeError, "`created_at' must be an instance of Time"
140
42
  end
141
43
  end
142
44
 
143
- # Returns thehe short coord.info URL for the log.
144
- #
145
- # @return [String] coord.info URL
146
- def short_url
147
- @short_url ||= begin
148
- raise NotFetchedError unless fetched?
149
-
150
- meta[:url] || begin
151
- raise ParseError, "Could not extract short URL from website"
152
- end
45
+ def user=(user)
46
+ if user.kind_of?(Geocaching::User)
47
+ @user = user
48
+ else
49
+ raise TypeError, "`user' must be an instance of Geocaching::User"
153
50
  end
154
51
  end
155
52
 
156
- private
157
-
158
- # Returns the log’s title which is used internally to get the log type.
159
- #
160
- # @return [String] Log title
161
- def title
162
- @title ||= begin
163
- raise NotFetchedError unless fetched?
164
-
165
- imgs = @doc.search("#ctl00_ContentBody_LogBookPanel1_LogImage")
166
-
167
- unless imgs.size == 1 and imgs.first["alt"]
168
- raise ParseError, "Could not extract title from website"
169
- end
170
-
171
- imgs.first["alt"]
53
+ def cache=(cache)
54
+ if cache.kind_of?(Geocaching::Cache)
55
+ @cache = cache
56
+ else
57
+ raise TypeError, "`cache' must be an instance of Geocaching::Cache"
172
58
  end
173
59
  end
174
60
 
175
- # Returns the UUID of the cache that belongs to this log.
176
- #
177
- # @return [String] Cache UUID
178
- def cache_guid
179
- if @cache.kind_of?(Geocaching::Cache) and @cache.guid
180
- @cache.guid
61
+ def type=(log_type)
62
+ if log_type.kind_of?(Geocaching::LogType)
63
+ @type = log_type
181
64
  else
182
- raise NotFetchedError unless fetched?
183
-
184
- elements = @doc.search("#ctl00_ContentBody_LogBookPanel1_lbLogText > a")
185
-
186
- if elements.size == 3 and elements[1]["href"]
187
- elements[1]["href"] =~ /guid=([a-f0-9-]{36})/
188
- $1
189
- else
190
- raise ParseError, "Could not extract cache GUID from website"
191
- end
65
+ raise TypeError, "`type' must be an instance of Geocaching::LogType"
192
66
  end
193
67
  end
194
68
 
195
- # Returns an array of information that are provided on the website
196
- # in <meta> tags.
197
- #
198
- # @return [Hash] Log information
199
- def meta
200
- @meta ||= begin
201
- elements = @doc.search("meta").select { |e| e["name"] =~ /^og:/ }.flatten
202
- info = {}
203
-
204
- elements.each do |element|
205
- info[element["name"].gsub(/^og:/, "").to_sym] = element["content"]
206
- end
207
-
208
- info
209
- end
69
+ def to_hash
70
+ {
71
+ :guid => guid,
72
+ :type => type,
73
+ :cache => cache,
74
+ :user => user,
75
+ :logged_at => logged_at,
76
+ :created_at => created_at,
77
+ :message => message
78
+ }
210
79
  end
211
80
  end
212
81
  end
@@ -1,98 +1,68 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  module Geocaching
4
- # The {LogType} class represents a log type.
5
- #
6
- # == Usage
7
- #
8
- # if @log.type == :archive
9
- # puts "#{@log.cache.code} has been archived"
10
- # end
11
4
  class LogType
12
- # Mapping of log types to their corresponding icon and title
13
- # on geocaching.com.
14
- TYPES = {
15
- :publish => ["icon_greenlight", "Publish Listing"],
16
- :retract => ["icon_redlight", "Retract Listing"],
17
- :dnf => ["icon_sad", "Didn't find it"],
18
- :found => ["icon_smile", "Found it"],
19
- :webcam_photo_taken => ["icon_camera", "Webcam Photo Taken"],
20
- :will_attend => ["icon_rsvp", "Will Attend"],
21
- :announcement => ["icon_announcement", "Announcement"],
22
- :attended => ["icon_attended", "Attended"],
23
- :needs_maintenance => ["icon_needsmaint", "Needs Maintenance"],
24
- :owner_maintenance => ["icon_maint", "Owner Maintenance"],
25
- :disable => ["icon_disabled", "Temporarily Disable Listing"],
26
- :enable => ["icon_enabled", "Enable Listing"],
27
- :note => ["icon_note", "Write note"],
28
- :needs_archived => ["icon_remove", "Needs Archived"],
29
- :archive => ["traffic_cone", "Archive"],
30
- :unarchive => ["traffic_cone", "Unarchive"],
31
- :coords_update => ["coord_update", "Update Coordinates"],
32
- :reviewer_note => ["big_smile", "Post Reviewer Note"]
33
- }
5
+ attr_reader :id
34
6
 
35
- # Returns a {LogType} object for the given log type icon, or nil if
36
- # no appropriate log type is found.
37
- #
38
- # @return [Geocaching::LogType]
39
- # @return [nil] If no appropriate log type is found
40
- def self.for_icon(icon)
41
- if info = TYPES.to_a.select { |(k,v)| v[0] == icon } and info.size == 1
42
- new(info.first)
43
- end
44
- end
7
+ # Cache Log Types
8
+ FOUND = 0x001
9
+ DNF = 0x002
10
+ NOTE = 0x003
11
+ ARCHIVE = 0x004
12
+ UNARCHIVE = 0x005
13
+ NEEDS_ARCHIVED = 0x006
14
+ ANNOUNCEMENT = 0x007
15
+ WILL_ATTEND = 0x008
16
+ ATTENDED = 0x009
17
+ WEBCAM_PHOTO_TAKEN = 0x00A
18
+ PUBLISH_LISTING = 0x00B
19
+ RETRACT_LISTING = 0x00C
20
+ TEMPORARILY_DISABLE_LISTING = 0x00D
21
+ ENABLE_LISTING = 0x00E
22
+ UPDATE_COORDINATES = 0x00F
23
+ POST_REVIEWER_NOTE = 0x010
24
+ NEEDS_MAINTENANCE = 0x011
25
+ OWNER_MAINTENANCE = 0x012
45
26
 
46
- # Returns a {LogType} object for the given log type title, or nil if
47
- # no appropriate log type is found.
48
- #
49
- # @return [Geocaching::LogType]
50
- # @return [nil] If no appropriate log type is found
51
- def self.for_title(title)
52
- if info = TYPES.to_a.select { |(k,v)| v[1] == title } and info.size == 1
53
- new(info.first)
54
- end
55
- end
27
+ MAPPING = [
28
+ [FOUND, "Found it" ],
29
+ [DNF, "Didn't find it" ],
30
+ [NOTE, "Write note" ],
31
+ [ARCHIVE, "Archive" ],
32
+ [UNARCHIVE, "Unarchive" ],
33
+ [NEEDS_ARCHIVED, "Needs Archived" ],
34
+ [ANNOUNCEMENT, "Announcement" ],
35
+ [WILL_ATTEND, "Will Attend" ],
36
+ [ATTENDED, "Attended" ],
37
+ [WEBCAM_PHOTO_TAKEN, "Webcam Photo Taken" ],
38
+ [PUBLISH_LISTING, "Publish Listing" ],
39
+ [RETRACT_LISTING, "Retract Listing" ],
40
+ [TEMPORARILY_DISABLE_LISTING, "Temporarily Disable Listing"],
41
+ [ENABLE_LISTING, "Enable Listing" ],
42
+ [UPDATE_COORDINATES, "Update Coordinates" ],
43
+ [POST_REVIEWER_NOTE, "Post Reviewer Note" ],
44
+ [NEEDS_MAINTENANCE, "Needs Maintenance" ],
45
+ [OWNER_MAINTENANCE, "Owner Maintenance" ]
46
+ ]
56
47
 
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}.
59
- def initialize(info)
60
- @info = info
48
+ def self.from_name(name)
49
+ if type = MAPPING.find { |_, n| n == name }
50
+ new(type[0])
51
+ end
61
52
  end
62
53
 
63
- # Returns the log type’s icon.
64
- #
65
- # @return [String] Icon
66
- def icon
67
- @info[1][0]
54
+ def initialize(id)
55
+ @id = id
68
56
  end
69
57
 
70
- # Returns the log type’s description.
71
- #
72
- # @return [String] Description
73
- def description
74
- @info[1][1]
58
+ def name
59
+ @name ||= MAPPING.find { |id, _| id == @id }[1]
75
60
  end
76
61
 
77
- alias to_s description
78
-
79
- # Returns the symbol representaton of the log type. See the {TYPES}
80
- # hash for a list of log type symbols.
81
- #
82
- # @return [Symbol] Log type symbol
83
- def to_sym
84
- @info[0]
85
- end
62
+ alias :to_s :name
86
63
 
87
- # Overloads the +==+ operator to compare by symbol.
88
- #
89
- # if @log.type == :dnf
90
- # puts "Someone could not find the cache"
91
- # end
92
- #
93
- # @return [Boolean] Does object match with argument?
94
- def ==(s)
95
- to_sym == s
64
+ def ==(id)
65
+ @id == id
96
66
  end
97
67
  end
98
68
  end