quimby 0.2.2

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.md ADDED
@@ -0,0 +1,81 @@
1
+ # Quimby
2
+
3
+ ### Formerly named after a pop star whose lawyers decided they didn't like us using his name.
4
+
5
+ It's a Foursquare API wrapper. It uses objects instead of hashes, and tries to be smart about when to load things.
6
+
7
+ ## Usage
8
+
9
+ Get a foursquare:
10
+
11
+ foursquare = Foursquare::Base.new("ACCESS_TOKEN")
12
+
13
+ You can also user `client_id` and `client_secret`
14
+
15
+ foursquare = Foursquare::Base.new("CLIENT_ID", "CLIENT_SECRET")
16
+
17
+ ### Users
18
+
19
+ Find a user:
20
+
21
+ user = foursquare.users.find("USER_ID")
22
+
23
+ Now we've got a `Foursquare::User` object. You can call sweet methods like `user.name` and even
24
+ `user.last_checkin`. **In general, John Mayer's Foursquare object methods are just snake-cased
25
+ versions of the attributes returned in the JSON.** Now let's accidentally that user's friends:
26
+
27
+ user.friends
28
+
29
+ This will return an array of `Foursquare::User` objects. Don't worry about the fact that they're
30
+ populated by limited JSON. Quimby will fetch the extra JSON if you need it. For example:
31
+
32
+ friend = user.friends.first
33
+ friend.name # Will not trigger a network call, since we already have it
34
+ friend.twitter # Will trigger a network to load the user's contact information
35
+
36
+ ### Checkins
37
+
38
+ But wait, Foursquare isn't just users! It's checkins too! So let's find some checkins:
39
+
40
+ user.checkins
41
+
42
+ Now we have an array of `Foursquare::Checkin` objects. We can also grab a specific checkin:
43
+
44
+ checkin = foursquare.checkins.find("CHECKIN_ID")
45
+
46
+ ### Venues
47
+
48
+ We can get at a checkin's venue by calling `checkin.venue`. Pretty easy, RIGHT? Right. If you want to
49
+ find a venue directly, here ya go:
50
+
51
+ foursquare.venues.find("VENUE_ID")
52
+
53
+ You can also search venues:
54
+
55
+ foursquare.venues.search(:ll => "40.7236307,-73.9999479") # Returns all resulting groups
56
+ foursquare.venues.nearby(:ll => "40.7236307,-73.9999479") # Returns only nearby venues
57
+ foursquare.venues.trending(:ll => "40.7236307,-73.9999479") # Returns only trending venues
58
+ foursquare.venues.favorites(:ll => "40.7236307,-73.9999479") # Returns only favorite venues
59
+
60
+ The `:ll` option is required for venue searches. You can also feel free to pass any of the other
61
+ available Foursquare API options, as specified in the docs.
62
+
63
+ ### Logging
64
+
65
+ If you want to see what's going on up in there, you can set `Foursquare.verbose` to `true`
66
+
67
+ Foursquare.verbose = true
68
+
69
+ Right now it'll log to `STDOUT`. Maybe I'll add nicer logging later. If you're lucky. In the meantime,
70
+ if you want to use your own logger, and you're kind of a jerk like me, you can do something like this:
71
+
72
+ Foursquare.verbose = true
73
+ def Foursquare.log(message)
74
+ Rails.logger.info("[foursquare] #{message}") # HAX, SORRY BRANDON
75
+ end
76
+
77
+ ## TODO
78
+
79
+ * Creating checkins works, but it should really return notifications. Also, if the
80
+ checkin can't be created, it should return errors.
81
+ * I don't know, so much other stuff.
data/lib/foursquare.rb ADDED
@@ -0,0 +1,44 @@
1
+ $LOAD_PATH << File.dirname(__FILE__)
2
+
3
+ require "rubygems"
4
+ require "typhoeus"
5
+ require "json"
6
+ require "cgi"
7
+ require "foursquare/base"
8
+ require "foursquare/checkin_proxy"
9
+ require "foursquare/checkin"
10
+ require "foursquare/user_proxy"
11
+ require "foursquare/user"
12
+ require "foursquare/venue_proxy"
13
+ require "foursquare/venue"
14
+ require "foursquare/settings"
15
+ require "foursquare/tip"
16
+ require "foursquare/photo"
17
+
18
+ module Foursquare
19
+ class Error < StandardError ; end
20
+
21
+ def self.verbose=(setting)
22
+ @verbose = setting
23
+ end
24
+
25
+ def self.verbose?
26
+ @verbose
27
+ end
28
+
29
+ def self.log(msg)
30
+ return unless verbose?
31
+ puts "[foursquare] #{msg}"
32
+ end
33
+
34
+ ERRORS = {
35
+ "invalid_auth" => "OAuth token was not provided or was invalid.",
36
+ "param_error" => "A required parameter was missing or a parameter was malformed. This is also used if the resource ID in the path is incorrect.",
37
+ "endpoint_error" => "The requested path does not exist.",
38
+ "not_authorized" => "Although authentication succeeded, the acting user is not allowed to see this information due to privacy restrictions.",
39
+ "rate_limit_exceeded" => "Rate limit for this hour exceeded.",
40
+ "deprecated" => "Something about this request is using deprecated functionality, or the response format may be about to change.",
41
+ "server_error" => "Server is currently experiencing issues. Check status.foursquare.com for udpates.",
42
+ "other" => "Some other type of error occurred."
43
+ }
44
+ end
@@ -0,0 +1,81 @@
1
+ module Foursquare
2
+ class Base
3
+ API = "https://api.foursquare.com/v2/"
4
+
5
+ def initialize(*args)
6
+ case args.size
7
+ when 1
8
+ @access_token = args.first
9
+ when 2
10
+ @client_id, @client_secret = args
11
+ else
12
+ raise ArgumentError, "You need to pass either an access_token or client_id and client_secret"
13
+ end
14
+ end
15
+
16
+ def users
17
+ Foursquare::UserProxy.new(self)
18
+ end
19
+
20
+ def checkins
21
+ Foursquare::CheckinProxy.new(self)
22
+ end
23
+
24
+ def venues
25
+ Foursquare::VenueProxy.new(self)
26
+ end
27
+
28
+ def settings
29
+ @settings ||= Foursquare::Settings.new(self)
30
+ end
31
+
32
+ def get(path, params={})
33
+ params = camelize(params)
34
+ Foursquare.log("GET #{API + path}")
35
+ Foursquare.log("PARAMS: #{params.inspect}")
36
+ merge_auth_params(params)
37
+ response = JSON.parse(Typhoeus::Request.get(API + path, :params => params).body)
38
+ Foursquare.log(response.inspect)
39
+ error(response) || response["response"]
40
+ end
41
+
42
+ def post(path, params={})
43
+ params = camelize(params)
44
+ Foursquare.log("POST #{API + path}")
45
+ Foursquare.log("PARAMS: #{params.inspect}")
46
+ merge_auth_params(params)
47
+ response = JSON.parse(Typhoeus::Request.post(API + path, :params => params).body)
48
+ Foursquare.log(response.inspect)
49
+ error(response) || response["response"]
50
+ end
51
+
52
+ private
53
+
54
+ def camelize(params)
55
+ params.inject({}) { |o, (k, v)|
56
+ o[k.to_s.gsub(/(_[a-z])/) { |m| m[1..1].upcase }] = v
57
+ o
58
+ }
59
+ end
60
+
61
+ def error(response)
62
+ case response["meta"]["errorType"]
63
+ when nil
64
+ # It's all good.
65
+ when "deprecated"
66
+ Foursquare.log(Foursquare::ERRORS[response['meta']['errorType']])
67
+ nil
68
+ else
69
+ raise Foursquare::Error.new(Foursquare::ERRORS[response['meta']['errorType']])
70
+ end
71
+ end
72
+
73
+ def merge_auth_params(params)
74
+ if @access_token
75
+ params.merge!(:oauth_token => @access_token)
76
+ else
77
+ params.merge!(:client_id => @client_id, :client_secret => @client_secret)
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,56 @@
1
+ module Foursquare
2
+ class Checkin
3
+ attr_reader :json
4
+
5
+ def initialize(foursquare, json)
6
+ @foursquare, @json = foursquare, json
7
+ end
8
+
9
+ def id
10
+ @json["id"]
11
+ end
12
+
13
+ def fetch
14
+ @json = @foursquare.get("checkins/#{id}")["checkin"]
15
+ self
16
+ end
17
+
18
+ def type
19
+ @json["type"]
20
+ end
21
+
22
+ def shout?
23
+ type == "shout"
24
+ end
25
+
26
+ def created_at
27
+ Time.at(@json["createdAt"].to_i)
28
+ end
29
+
30
+ def shout
31
+ @json["shout"]
32
+ end
33
+
34
+ def mayor?
35
+ @json["isMayor"]
36
+ end
37
+
38
+ def timezone
39
+ @json["timeZone"]
40
+ end
41
+
42
+ def venue
43
+ @json["venue"] && Foursquare::Venue.new(@foursquare, @json["venue"])
44
+ end
45
+
46
+ def user(full=false)
47
+ fetch unless @json["user"]
48
+
49
+ if full
50
+ @foursquare.users.find(@json["user"]["id"])
51
+ else
52
+ Foursquare::User.new(@foursquare, @json["user"])
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,32 @@
1
+ module Foursquare
2
+ class CheckinProxy
3
+ def initialize(foursquare)
4
+ @foursquare = foursquare
5
+ end
6
+
7
+ def find(id)
8
+ Foursquare::Checkin.new(@foursquare, @foursquare.get("checkins/#{id}")["checkin"])
9
+ end
10
+
11
+ def recent(options={})
12
+ @foursquare.get("checkins/recent", options)["recent"].map do |json|
13
+ Foursquare::Checkin.new(@foursquare, json)
14
+ end
15
+ end
16
+
17
+ def all(options={})
18
+ @foursquare.get("users/self/checkins", options)["checkins"]["items"].map do |json|
19
+ Foursquare::Checkin.new(@foursquare, json)
20
+ end
21
+ end
22
+
23
+ def create(options={})
24
+ if json = @foursquare.post("checkins/add", options)
25
+ Foursquare::Checkin.new(@foursquare, json["checkin"])
26
+ else
27
+ nil
28
+ end
29
+ end
30
+ alias_method :add, :create
31
+ end
32
+ end
@@ -0,0 +1,38 @@
1
+ # Here's how I want this to work:
2
+ #
3
+ # users, venues = foursquare.multi do |request|
4
+ # request.users.search :twitter => "nakajima"
5
+ # request.venues.search :ll => "12,-71"
6
+ # end
7
+ #
8
+ # It's just difficult to implement. So it's not implemented yet.
9
+ module Foursquare
10
+ class Multi
11
+ def initialize(foursquare, block)
12
+ @foursquare = foursquare
13
+ @requests = []
14
+ @responses = []
15
+ end
16
+
17
+ def get(path, options={})
18
+ @requests << path + query(params)
19
+ end
20
+
21
+ def perform
22
+ responses = @foursquare.get("multi", :requests => @requests.join(','))
23
+ end
24
+
25
+ private
26
+
27
+ def query(params)
28
+ camelized = params.inject({}) { |o, (k, v)|
29
+ o[k.to_s.gsub(/(_[a-z])/) { |m| m[1..1].upcase }] = v
30
+ o
31
+ }
32
+ camelized.inject([]) { |o, (k, v)|
33
+ o << CGI.escape(k) + "=" CGI.escape(v)
34
+ o
35
+ }.join('&')
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,27 @@
1
+ module Foursquare
2
+ class Photo
3
+ def initialize(foursquare, json)
4
+ @foursquare, @json = foursquare, json
5
+ end
6
+
7
+ def id
8
+ @json["id"]
9
+ end
10
+
11
+ def name
12
+ @json["name"]
13
+ end
14
+
15
+ def created_at
16
+ @json["createdAt"]
17
+ end
18
+
19
+ def url
20
+ @json["url"]
21
+ end
22
+
23
+ def sizes
24
+ @json["sizes"]
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,31 @@
1
+ module Foursquare
2
+ class Settings
3
+ def initialize(foursquare, json={})
4
+ @foursquare, @json = foursquare, json
5
+ end
6
+
7
+ def fetch
8
+ @json = @foursquare.get('settings/all')["settings"]
9
+ end
10
+
11
+ def receive_pings?
12
+ fetch unless @json.has_key?('receivePings')
13
+ @json['receivePings']
14
+ end
15
+
16
+ def receive_comment_pings?
17
+ fetch unless @json.has_key?('receiveCommentPings')
18
+ @json['receiveCommentPings']
19
+ end
20
+
21
+ def send_to_twitter?
22
+ fetch unless @json.has_key?('sendToTwitter')
23
+ @json['sendToTwitter']
24
+ end
25
+
26
+ def send_to_facebook?
27
+ fetch unless @json.has_key?('sendToFacebook')
28
+ @json['sendToFacebook']
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,35 @@
1
+ module Foursquare
2
+ class Tip
3
+ def initialize(foursquare, json)
4
+ @foursquare, @json = foursquare, json
5
+ end
6
+
7
+ def id
8
+ @json["id"]
9
+ end
10
+
11
+ def text
12
+ @json["text"]
13
+ end
14
+
15
+ def created_at
16
+ @json["createdAt"]
17
+ end
18
+
19
+ def status
20
+ @json["status"]
21
+ end
22
+
23
+ def photo
24
+ @json["photo"]
25
+ end
26
+
27
+ def user
28
+ @json["user"] && Foursquare::User.new(@foursquare, @json["user"])
29
+ end
30
+
31
+ def venue
32
+ @json["venue"] && Foursquare::Venue.new(@foursquare, @json["venue"])
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,115 @@
1
+ module Foursquare
2
+ class User
3
+ attr_reader :json
4
+
5
+ def initialize(foursquare, json)
6
+ @foursquare, @json = foursquare, json
7
+ end
8
+
9
+ def fetch
10
+ @json = @foursquare.get("users/#{id}")["user"]
11
+ self
12
+ end
13
+
14
+ def id
15
+ @json["id"]
16
+ end
17
+
18
+ def name
19
+ [first_name, last_name].join(' ').strip
20
+ end
21
+
22
+ def first_name
23
+ @json["firstName"]
24
+ end
25
+
26
+ def last_name
27
+ @json["lastName"]
28
+ end
29
+
30
+ def photo
31
+ @json["photo"]
32
+ end
33
+
34
+ def gender
35
+ @json["gender"]
36
+ end
37
+
38
+ def home_city
39
+ @json["homeCity"]
40
+ end
41
+
42
+ def relationship
43
+ @json["relationship"]
44
+ end
45
+
46
+ def pings
47
+ fetch unless @json.has_key?("pings")
48
+ @json["pings"]
49
+ end
50
+
51
+ def contact
52
+ fetch unless @json.has_key?("contact")
53
+ @json["contact"]
54
+ end
55
+
56
+ def email
57
+ contact["email"]
58
+ end
59
+
60
+ def twitter
61
+ contact["twitter"]
62
+ end
63
+
64
+ def phone_number
65
+ contact["phone"]
66
+ end
67
+
68
+ def badge_count
69
+ fetch unless @json.has_key?("badges")
70
+ @json["badges"]["count"]
71
+ end
72
+
73
+ def mayorships
74
+ fetch unless @json.has_key?("mayorships")
75
+ @json["mayorships"]["items"]
76
+ end
77
+
78
+ def checkin_count
79
+ fetch unless @json.has_key?("checkins")
80
+ @json["checkins"]["count"]
81
+ end
82
+
83
+ def last_checkin
84
+ fetch unless @json.has_key?("checkins")
85
+ return unless @json["checkins"]["items"]
86
+ item = @json["checkins"]["items"].last
87
+ Foursquare::Checkin.new(@foursquare, item)
88
+ end
89
+
90
+ def checkins_here
91
+ checkin_json = @foursquare.get("venues/#{last_checkin.venue.id}/herenow")
92
+ checkin_json["hereNow"]["items"].map do |item|
93
+ begin
94
+ next unless json = @foursquare.get("checkins/#{item["id"]}")
95
+ checkin = json["checkin"]
96
+ Foursquare::Checkin.new(@foursquare, checkin)
97
+ rescue Foursquare::Error
98
+ # We can't get checkin information for people who aren't our friends.
99
+ end
100
+ end.compact
101
+ end
102
+
103
+ def friends(options={})
104
+ @foursquare.get("users/#{id}/friends", options)["friends"]["items"].map do |item|
105
+ Foursquare::User.new(@foursquare, item)
106
+ end
107
+ end
108
+
109
+ def tips(options={})
110
+ @foursquare.get("users/#{id}/tips", options)["tips"]["items"].map do |item|
111
+ Foursquare::Tip.new(@foursquare, item)
112
+ end
113
+ end
114
+ end
115
+ end
@@ -0,0 +1,21 @@
1
+ module Foursquare
2
+ class UserProxy
3
+ def initialize(foursquare)
4
+ @foursquare = foursquare
5
+ end
6
+
7
+ def self.search(foursquare, options={})
8
+
9
+ end
10
+
11
+ def find(id)
12
+ Foursquare::User.new(@foursquare, @foursquare.get("users/#{id}")["user"])
13
+ end
14
+
15
+ def search(options={})
16
+ @foursquare.get("users/search", options)["results"].map do |json|
17
+ Foursquare::User.new(@foursquare, json)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,51 @@
1
+ module Foursquare
2
+ class Venue
3
+ attr_reader :json
4
+
5
+ def initialize(foursquare, json)
6
+ @foursquare, @json = foursquare, json
7
+ end
8
+
9
+ def id
10
+ @json["id"]
11
+ end
12
+
13
+ def name
14
+ @json["name"]
15
+ end
16
+
17
+ def contact
18
+ @json["contact"]
19
+ end
20
+
21
+ def location
22
+ @json["location"]
23
+ end
24
+
25
+ def categories
26
+ @json["categories"]
27
+ end
28
+
29
+ def verified?
30
+ @json["verified"]
31
+ end
32
+
33
+ def checkins_count
34
+ @json["stats"]["checkinsCount"]
35
+ end
36
+
37
+ def users_count
38
+ @json["stats"]["usersCount"]
39
+ end
40
+
41
+ def todos_count
42
+ @json["todos"]["count"]
43
+ end
44
+
45
+ def photos(options={:group => "checkin"})
46
+ @foursquare.get("venues/#{id}/photos", options)["photos"]["items"].map do |item|
47
+ Foursquare::Photo.new(@foursquare, item)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,43 @@
1
+ module Foursquare
2
+ class VenueProxy
3
+ def initialize(foursquare)
4
+ @foursquare = foursquare
5
+ end
6
+
7
+ def find(id)
8
+ Foursquare::Venue.new(@foursquare, @foursquare.get("venues/#{id}")["venue"])
9
+ end
10
+
11
+ def search(options={})
12
+ raise ArgumentError, "You must include :ll" unless options[:ll]
13
+ response = @foursquare.get('venues/search', options)["groups"].inject({}) do |venues, group|
14
+ venues[group["type"]] ||= []
15
+ venues[group["type"]] << group["items"].map do |json|
16
+ Foursquare::Venue.new(@foursquare, json)
17
+ end
18
+ end
19
+ end
20
+
21
+ def trending(options={})
22
+ search_group("trending", options)
23
+ end
24
+
25
+ def favorites(options={})
26
+ search_group("favorites", options)
27
+ end
28
+
29
+ def nearby(options={})
30
+ search_group("nearby", options)
31
+ end
32
+
33
+ private
34
+
35
+ def search_group(name, options)
36
+ raise ArgumentError, "You must include :ll" unless options[:ll]
37
+ response = @foursquare.get('venues/search', options)["groups"].detect { |group| group["type"] == name }
38
+ response ? response["items"].map do |json|
39
+ Foursquare::Venue.new(@foursquare, json)
40
+ end : []
41
+ end
42
+ end
43
+ end
@@ -0,0 +1 @@
1
+ My bad. Whatevz.
metadata ADDED
@@ -0,0 +1,107 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: quimby
3
+ version: !ruby/object:Gem::Version
4
+ hash: 19
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 2
9
+ - 2
10
+ version: 0.2.2
11
+ platform: ruby
12
+ authors:
13
+ - Pat Nakajima
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-01-14 00:00:00 -05:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: typhoeus
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: json
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ hash: 3
44
+ segments:
45
+ - 0
46
+ version: "0"
47
+ type: :runtime
48
+ version_requirements: *id002
49
+ description:
50
+ email: pat@groupme.com
51
+ executables: []
52
+
53
+ extensions: []
54
+
55
+ extra_rdoc_files: []
56
+
57
+ files:
58
+ - README.md
59
+ - lib/foursquare.rb
60
+ - lib/foursquare/base.rb
61
+ - lib/foursquare/settings.rb
62
+ - lib/foursquare/tip.rb
63
+ - lib/foursquare/multi.rb
64
+ - lib/foursquare/checkin.rb
65
+ - lib/foursquare/photo.rb
66
+ - lib/foursquare/checkin_proxy.rb
67
+ - lib/foursquare/user.rb
68
+ - lib/foursquare/user_proxy.rb
69
+ - lib/foursquare/venue.rb
70
+ - lib/foursquare/venue_proxy.rb
71
+ - spec/THERE_ARENT_ANY
72
+ has_rdoc: true
73
+ homepage: https://github.com/groupme/quimby
74
+ licenses: []
75
+
76
+ post_install_message:
77
+ rdoc_options: []
78
+
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ hash: 3
87
+ segments:
88
+ - 0
89
+ version: "0"
90
+ required_rubygems_version: !ruby/object:Gem::Requirement
91
+ none: false
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ hash: 3
96
+ segments:
97
+ - 0
98
+ version: "0"
99
+ requirements: []
100
+
101
+ rubyforge_project:
102
+ rubygems_version: 1.4.1
103
+ signing_key:
104
+ specification_version: 3
105
+ summary: A Foursquare API wrapper
106
+ test_files: []
107
+