quimby 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
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
+