meal_ticket 0.0.0 → 0.1.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/Gemfile CHANGED
@@ -3,6 +3,13 @@ source "http://rubygems.org"
3
3
  # Example:
4
4
  # gem "activesupport", ">= 2.3.5"
5
5
 
6
+ gem 'crack'
7
+ gem 'json'
8
+
9
+ group :test do
10
+ gem 'webmock'
11
+ end
12
+
6
13
  # Add dependencies to develop your gem here.
7
14
  # Include everything needed to run rake, tests, features, etc.
8
15
  group :development do
data/Gemfile.lock CHANGED
@@ -1,20 +1,29 @@
1
1
  GEM
2
2
  remote: http://rubygems.org/
3
3
  specs:
4
+ addressable (2.2.4)
5
+ crack (0.1.8)
4
6
  git (1.2.5)
5
7
  jeweler (1.5.2)
6
8
  bundler (~> 1.0.0)
7
9
  git (>= 1.2.5)
8
10
  rake
11
+ json (1.5.1)
9
12
  rake (0.8.7)
10
13
  rcov (0.9.9)
11
14
  shoulda (2.11.3)
15
+ webmock (1.6.2)
16
+ addressable (>= 2.2.2)
17
+ crack (>= 0.1.7)
12
18
 
13
19
  PLATFORMS
14
20
  ruby
15
21
 
16
22
  DEPENDENCIES
17
23
  bundler (~> 1.0.0)
24
+ crack
18
25
  jeweler (~> 1.5.2)
26
+ json
19
27
  rcov
20
28
  shoulda
29
+ webmock
data/README.rdoc CHANGED
@@ -1,6 +1,123 @@
1
- = meal_ticket
1
+ MealTicket simplifies the process of authenticating with 3rd-party APIs by eliminating the stuff that's the same for everyone,
2
+ letting you focus solely on the parts of authentication that matter to you.
2
3
 
3
- Description goes here.
4
+ == Overview
5
+
6
+ 1. You pick a service you want to authenticate against
7
+ 1. You decide what permissions you need
8
+ 1. You redirect your user to something like facebook_auth_url(root_url, "user_photos,publish_stream")
9
+ 1. MealTicket handles the gruesome details of the various psuedo-OAuth schemes
10
+ 1. MealTicket redirects the user back to a url of your choice along with their access token
11
+
12
+ == Currently Supported Services
13
+
14
+ * facebook
15
+ * flickr
16
+
17
+ == Getting Started
18
+
19
+ 1. Require the gem. In your gemfile:
20
+
21
+ gem 'meal_ticket'
22
+
23
+ 1. Install the gem. In your console:
24
+
25
+ bundle install (or maybe 'sudo bundle install')
26
+
27
+ 1. Install MealTicket as middleware to handle cross-domain communication. In Rails, you'd add something like this to your application.rb:
28
+
29
+ module YourAppName
30
+ class Application < Rails::Application
31
+ config.middleware.use "MealTicket"
32
+
33
+ 1. Make meal_ticket URLs available to your views. In Rails, you'd add something like this to your application_helper.rb:
34
+
35
+ require 'meal_ticket'
36
+
37
+ module ApplicationHelper
38
+ include MealTicketRoutes
39
+ end
40
+
41
+ 1. Optionally, make meal_ticket URLs available to your controllers. In Rails, you'd add something like this to your application_controller.rb:
42
+
43
+ require 'meal_ticket'
44
+
45
+ class ApplicationController < ActionController::Base
46
+ include MealTicketRoutes
47
+ end
48
+
49
+ Now that you've finished installing MealTicket, look below for further instructions on how to connect with individual services.
50
+
51
+ = Service-Specific Instructions
52
+
53
+ For each service you want to integrate with, find it here and follow the steps to get your API keys.
54
+
55
+ In general, you'll need to do a couple things for each service:
56
+
57
+ 1. Go to their site, get your API keys, and make global constants for them.
58
+ 1. Create a callback method to receive the user's access token. Make sure you also map a route for this method.
59
+
60
+ == Facebook
61
+
62
+ 1. Log in to Facebook.
63
+ 1. Go to https://www.facebook.com/developers/apps.php and click the "Set Up New App" button.
64
+ 1. Fill out the forms to create a new app.
65
+ 1. Once you land on the "Edit" page, click the "Web Site" tab on the left.
66
+ 1. In the "Site Url" field, type the address of your site. For development, use something like +localhost:3000+. You may want
67
+ to set up a separate app for production.
68
+
69
+ Create global constants that look something like this:
70
+
71
+ FACEBOOK_APP_ID = "158079864105359" # facebook calls this "App ID"
72
+ FACEBOOK_SECRET = "98882d6d6cf0d7b69a5de5cc43abc10" # facebook calls this "App Secret"
73
+ FACEBOOK_CALLBACK = "path/to/my/facebook/callback" # whatever URL you've created to grab the user data and do something useful
74
+
75
+ Now, redirect users to facebook_auth_url, passing the permissions you want to ask for. Like so:
76
+
77
+ # For a full list of permissions, see https://developers.facebook.com/docs/authentication/permissions/
78
+ redirect_to facebook_auth_url("user_photos,publish_stream")
79
+
80
+
81
+ After they authenticate, they'll be redirected to your +FACEBOOK_CALLBACK+ URL with query string params like:
82
+
83
+ ?facebook[token]=q2jf89ojq.j32f|FQf9j23la&facebook[expires]=4829
84
+ - or, more legibly: -
85
+ {:facebook => {:token => "q2jf89ojq.j32f|FQf9j23la", :expires => "4829"}}
86
+
87
+
88
+ Notes:
89
+
90
+ * Your facebook "API key" is never actually used.
91
+ * The expires value is the number of seconds for while this token is valid. If you request the offline_access permission, expires will be blank and the token is valid forever.
92
+
93
+ == Flickr
94
+
95
+ 1. Log in to Flickr.
96
+ 1. Go to http://www.flickr.com/services/apps/create/apply/ to register for API keys.
97
+ 1. Fill out the forms to create a new app.
98
+ 1. Once you're done, find the "edit authentication flow" page ({www.flickr.com/services/apps/YOUR_FLICKR_APP_ID/auth/}[www.flickr.com/services/apps]) and
99
+ set the Callback URL to <your root url>/meal_ticket/flickr_callback
100
+
101
+ Create global constants that look something like this:
102
+
103
+ FLICKR_TOKEN = "3637b1e30ae90503fedf9aaca8a4c370"
104
+ FLICKR_SECRET = "3570d29a7a3c086b"
105
+ FLICKR_CALLBACK = "path/to/my/flickr/callback" # whatever URL you've created to grab the user data and do something useful
106
+
107
+ Now, redirect users to flickr_auth_url, passing the permission level you want to ask for. Like so:
108
+
109
+ # For a full list of permissions, see https://developers.facebook.com/docs/authentication/permissions/
110
+ redirect_to flickr_auth_url("write")
111
+
112
+ After they authenticate, they'll be redirected to your +FACEBOOK_CALLBACK+ method with params like:
113
+
114
+ ?flickr[token]=q2jf89ojq.j32f|FQf9j23la&facebook[user_id]=
115
+ - or, more legibly: -
116
+ {:flickr => {:token => "3215562516a046266-919fd54999d6e104", :user_id => "27934656@N00"}}
117
+
118
+ Notes:
119
+
120
+ * _nothing_ _to_ _note_
4
121
 
5
122
  == Contributing to meal_ticket
6
123
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.0
1
+ 0.1.0
data/lib/meal_ticket.rb CHANGED
@@ -1,14 +1,176 @@
1
- #
2
- # == Steps
3
- #
4
- # 1. Developer signs up for 3rd-part API (let's say Facebook)
5
- # 1. Developer copies & pastes access info into some config location
6
- # 1. Developer creates a callback action that will receive the user's token
7
- # !. Developer redirects the user to something like facebook_auth_url() passing a parameter indicating what permissions he needs
8
- #
9
- #
10
- module MealTicket
11
-
12
-
13
1
 
2
+ # :include:README.rdoc
3
+ require 'cgi'
4
+ module MealTicketRoutes
5
+
6
+ FLICKR_API_BASE_URL = "http://api.flickr.com/services/" # :nodoc:
7
+
8
+ # [root_url] The base url of your app, eg "http://www.google.com/". If you're running a rails app, you can literally type root_url
9
+ # [scope] A comma-separated list of permissions. For a full list of permissions, see https://developers.facebook.com/docs/authentication/permissions/
10
+ def facebook_auth_url(root_url, scope)
11
+ "https://graph.facebook.com/oauth/authorize?client_id=#{FACEBOOK_APP_ID}&redirect_uri=#{root_url}meal_ticket/facebook_callback&scope=#{scope}"
12
+ end
13
+
14
+ # Generates the URL for the 2nd step of Facebook auth - exchanging the code for user data
15
+ def facebook_exchange_url(root_url, code) # :nodoc:
16
+ "https://graph.facebook.com/oauth/access_token?client_id=#{FACEBOOK_APP_ID}&redirect_uri=#{root_url}meal_ticket/facebook_callback&client_secret=#{FACEBOOK_SECRET}&code=#{CGI::escape code}"
17
+ end
18
+
19
+ # [perm] A single permission level. Permissions can be read, write, or delete. Each successive permission implies the ones before it, eg "write" implies "read". For more information, see http://www.flickr.com/services/api/auth.spec.html
20
+ def flickr_auth_url(perm)
21
+ flickr_url({"perms" => perm}, "auth")
22
+ end
23
+
24
+ # Generates the URL for the 2nd step of Facebook auth - exchanging the frob for user data
25
+ def flickr_frob_url(frob) # :nodoc:
26
+ flickr_url({"method" => "flickr.auth.getToken", "frob" => frob})
27
+ end
28
+
29
+ private
30
+
31
+ def flickr_url(arg_hash, endpoint = "rest")
32
+ arg_hash.merge!({"api_key" => FLICKR_TOKEN})
33
+ arg_list = []
34
+ arg_hash.each do |key, value|
35
+ arg_list << "#{key}=#{value}"
36
+ end
37
+ "#{FLICKR_API_BASE_URL}#{endpoint}/?&api_sig=#{flickr_sign(arg_hash)}&#{arg_list.join('&')}"
38
+ end
39
+
40
+ def flickr_sign(arg_hash)
41
+ arg_list = []
42
+ arg_hash.keys.sort.each do |key|
43
+ arg_list << key
44
+ arg_list << arg_hash[key]
45
+ end
46
+ Digest::MD5.hexdigest("#{FLICKR_SECRET}#{arg_list.join()}")
47
+ end
14
48
  end
49
+
50
+
51
+ require 'net/http'
52
+ require 'net/https'
53
+ require 'crack'
54
+ require 'json'
55
+ class MealTicket
56
+ include MealTicketRoutes
57
+
58
+ def initialize(app)
59
+ @app = app
60
+ end
61
+
62
+ def call(env)
63
+ @env = env
64
+ if env["PATH_INFO"] =~ /^\/meal_ticket\/facebook_callback/
65
+ facebook_callback
66
+ elsif env["PATH_INFO"] =~ /^\/meal_ticket\/flickr_callback/
67
+ flickr_callback
68
+ else
69
+ @app.call(env)
70
+ end
71
+ end
72
+
73
+ def facebook_callback
74
+ response = get facebook_exchange_url(get_root_url, get_query_string_parameter("code"))
75
+
76
+ if response.body.include? "access_token"
77
+ response.body =~ /access_token=([^&]+)(?:&expires=(.*))?/ # TODO: genericize get_query_string_parameter to handle this?
78
+ token = $1 || ""
79
+ expires = $2 || ""
80
+ query_string = "facebook[token]=#{CGI.escape(token)}&facebook[expires]=#{CGI.escape(expires)}"
81
+ else
82
+ parsed = JSON.parse(response.body)
83
+ query_string = to_params({:facebook => parsed})
84
+ end
85
+
86
+ body = %(<html><body>You are being redirected.</body></html>)
87
+ headers = {
88
+ 'Location' => "#{get_root_url}#{FACEBOOK_CALLBACK}?#{query_string}",
89
+ 'Content-Type' => 'text/html',
90
+ 'Content-Length' => body.length.to_s
91
+ }
92
+ [302, headers, [body]]
93
+ end
94
+
95
+ def flickr_callback
96
+
97
+ xml = get flickr_frob_url(get_query_string_parameter("frob"))
98
+
99
+ parsed = Crack::XML.parse(xml.body)
100
+ if parsed["rsp"]["stat"] == "ok"
101
+ remote_id = parsed["rsp"]["auth"]["user"]["nsid"] || ""
102
+ token = parsed["rsp"]["auth"]["token"] || ""
103
+ query_string = "flickr[token]=#{CGI.escape(token)}&flickr[user_id]=#{CGI.escape(remote_id)}"
104
+ else
105
+ code = parsed["rsp"]["err"]["code"]
106
+ msg = parsed["rsp"]["err"]["msg"]
107
+ query_string = "flickr[error][code]=#{CGI.escape code}&flickr[error][msg]=#{CGI.escape msg}"
108
+ end
109
+
110
+ body = %(<html><body>You are being redirected.</body></html>)
111
+ headers = {
112
+ 'Location' => "#{get_root_url}#{FLICKR_CALLBACK}?#{query_string}",
113
+ 'Content-Type' => 'text/html',
114
+ 'Content-Length' => body.length.to_s
115
+ }
116
+ [302, headers, [body]]
117
+ end
118
+
119
+ private
120
+ def get(url)
121
+ use_ssl = url.include? 'https'
122
+ url = URI.parse(url)
123
+
124
+ path = url.query.blank? ? url.path : "#{url.path}?#{url.query}"
125
+
126
+ http = Net::HTTP.new(url.host, use_ssl ? 443 : nil)
127
+ http.use_ssl = use_ssl
128
+ res = http.get(path)
129
+
130
+ if res.code == '302'
131
+ return get(res['location']) # follow redirects
132
+ end
133
+ res
134
+ end
135
+
136
+ private
137
+ def get_query_string_parameter(param)
138
+ @env["QUERY_STRING"] =~ Regexp.new("#{param}=(.*)&|#{param}=(.*)$")
139
+ return $1 || $2 # whichever one worked
140
+ end
141
+
142
+ def get_root_url
143
+ @env["REQUEST_URI"] =~ /(^.*?:\/\/.*?\/)/ # looking for the form .....://.........../
144
+ return $1
145
+ end
146
+
147
+
148
+ # stolen from merb, according to StackOverflow
149
+ def to_params(obj)
150
+ params = ''
151
+ stack = []
152
+
153
+ obj.each do |k, v|
154
+ if v.is_a?(Hash)
155
+ stack << [k, v]
156
+ else
157
+ params << "#{CGI.escape k}=#{CGI.escape v}&"
158
+ end
159
+ end
160
+
161
+ stack.each do |parent, hash|
162
+ hash.each do |k, v|
163
+ if v.is_a?(Hash)
164
+ stack << ["#{parent}[#{k}]", v]
165
+ else
166
+ params << "#{parent}[#{CGI.escape k}]=#{CGI.escape v}&"
167
+ end
168
+ end
169
+ end
170
+
171
+ params.chop! # trailing &
172
+ params
173
+ end
174
+ end
175
+
176
+
data/meal_ticket.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{meal_ticket}
8
- s.version = "0.0.0"
8
+ s.version = "0.1.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Chris Doyle", "Kerri Miller", "John Postlethwait"]
12
- s.date = %q{2011-03-02}
12
+ s.date = %q{2011-03-08}
13
13
  s.description = %q{Meal Ticket is the easiest way to authenticate users for 3rd-party social APIs. Built to be used in conjunction with Buffet.}
14
14
  s.email = %q{archslide@gmail.com}
15
15
  s.extra_rdoc_files = [
@@ -25,7 +25,6 @@ Gem::Specification.new do |s|
25
25
  "Rakefile",
26
26
  "VERSION",
27
27
  "lib/meal_ticket.rb",
28
- "meal_ticket-0.0.0.gem",
29
28
  "meal_ticket.gemspec",
30
29
  "test/helper.rb",
31
30
  "test/test_meal_ticket.rb"
@@ -44,17 +43,23 @@ Gem::Specification.new do |s|
44
43
  s.specification_version = 3
45
44
 
46
45
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
46
+ s.add_runtime_dependency(%q<crack>, [">= 0"])
47
+ s.add_runtime_dependency(%q<json>, [">= 0"])
47
48
  s.add_development_dependency(%q<shoulda>, [">= 0"])
48
49
  s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
49
50
  s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
50
51
  s.add_development_dependency(%q<rcov>, [">= 0"])
51
52
  else
53
+ s.add_dependency(%q<crack>, [">= 0"])
54
+ s.add_dependency(%q<json>, [">= 0"])
52
55
  s.add_dependency(%q<shoulda>, [">= 0"])
53
56
  s.add_dependency(%q<bundler>, ["~> 1.0.0"])
54
57
  s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
55
58
  s.add_dependency(%q<rcov>, [">= 0"])
56
59
  end
57
60
  else
61
+ s.add_dependency(%q<crack>, [">= 0"])
62
+ s.add_dependency(%q<json>, [">= 0"])
58
63
  s.add_dependency(%q<shoulda>, [">= 0"])
59
64
  s.add_dependency(%q<bundler>, ["~> 1.0.0"])
60
65
  s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
@@ -1,7 +1,184 @@
1
1
  require 'helper'
2
+ require 'webmock'
3
+
4
+ FACEBOOK_APP_ID = "123456789"
5
+ FACEBOOK_SECRET = "abcdef"
6
+ FACEBOOK_CALLBACK = "callbacks"
7
+
8
+ FLICKR_TOKEN = "abcde"
9
+ FLICKR_SECRET = "lmnop"
10
+ FLICKR_CALLBACK = "callbacks"
11
+
12
+ # reopen MealTicket so we can set the @env instance variable and call private methods
13
+ class MealTicket
14
+ attr_accessor :env
15
+
16
+ def _get_query_string_parameter(param)
17
+ get_query_string_parameter(param)
18
+ end
19
+
20
+ def _get_root_url
21
+ get_root_url
22
+ end
23
+ end
2
24
 
3
25
  class TestMealTicket < Test::Unit::TestCase
4
- should "probably rename this file and start testing for real" do
5
- flunk "hey buddy, you should probably rename this file and start testing for real"
26
+ include MealTicketRoutes
27
+ include WebMock::API
28
+
29
+ # utility methods
30
+ context 'Calling the get_query_string_parameter method' do
31
+ setup do
32
+ @rack_app = MealTicket.new({})
33
+ end
34
+
35
+ tests = ["", "one=1", "code=calliope", "one=1@code=calliope", "code=calliope&two=1", "one=1&code=calliope&two=2"]
36
+ expectations = [nil, nil, "calliope", "calliope", "calliope", "calliope"]
37
+
38
+ tests.each_with_index do |query_string, i|
39
+ context "on #{query_string}" do
40
+ setup do
41
+ @rack_app.env = {"QUERY_STRING" => query_string}
42
+ end
43
+
44
+ should "return #{expectations[i]}" do
45
+ assert_equal expectations[i], @rack_app._get_query_string_parameter("code")
46
+ end
47
+ end
48
+ end
6
49
  end
50
+
51
+ context 'Calling the get_root_url method' do
52
+ setup do
53
+ @rack_app = MealTicket.new({})
54
+ end
55
+
56
+ tests = ["http://localhost:3000/", "http://localhost:3000/u/r/i", "https://localhost:3000/", "http://a.b.c.d.e.f.g.com/", "http://a.b.c/d/e/f"]
57
+ expectations = ["http://localhost:3000/", "http://localhost:3000/", "https://localhost:3000/", "http://a.b.c.d.e.f.g.com/", "http://a.b.c/"]
58
+
59
+ tests.each_with_index do |uri, i|
60
+ context "on #{uri}" do
61
+ setup do
62
+ @rack_app.env = {"REQUEST_URI" => uri}
63
+ end
64
+
65
+ should "return #{expectations[i]}" do
66
+ assert_equal expectations[i], @rack_app._get_root_url
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ # facebook
73
+ context 'Requesting the facebook auth url' do
74
+ context 'with valid parameters' do
75
+ setup do
76
+ @url = facebook_auth_url("http://localhost:3000/", "user_photos")
77
+ end
78
+
79
+ should 'return a valid url' do
80
+ assert_equal "https://graph.facebook.com/oauth/authorize?client_id=123456789&redirect_uri=http://localhost:3000/meal_ticket/facebook_callback&scope=user_photos", @url
81
+ end
82
+ end
83
+ end
84
+
85
+ context "When a facebook authentication is successful, the facebook_callback method" do
86
+ setup do
87
+ stub_request(:get, "https://graph.facebook.com/oauth/access_token?client_id=123456789&client_secret=abcdef&code=12345&redirect_uri=http://localhost:3000/meal_ticket/facebook_callback").
88
+ with(:headers => {'Accept'=>'*/*'}).
89
+ to_return(:status => 200, :body => "access_token=12345&expires=1000", :headers => {})
90
+
91
+ # create a new instance
92
+ rack_app = MealTicket.new({})
93
+
94
+ # set up the environment - the request where facebook is redirecting the user to our callback
95
+ rack_app.env = {"REQUEST_URI" => "http://localhost:3000/meal_ticket/facebook_callback?code=12345",
96
+ "QUERY_STRING" => "code=12345",
97
+ "PATH_INFO" => "/meal_ticket/facebook_callback"}
98
+
99
+ # process the request
100
+ @response = rack_app.facebook_callback
101
+ end
102
+
103
+ should "redirect to a URL that includes the facebook token and expiration" do
104
+ assert_equal 302, @response[0]
105
+ assert_equal "http://localhost:3000/callbacks?facebook[token]=12345&facebook[expires]=1000", @response[1]['Location']
106
+ end
107
+ end
108
+
109
+ context "When a facebook authentication is unsuccessful, the facebook_callback method" do
110
+ setup do
111
+ stub_request(:get, "https://graph.facebook.com/oauth/access_token?client_id=123456789&client_secret=abcdef&code=12345&redirect_uri=http://localhost:3000/meal_ticket/facebook_callback").
112
+ with(:headers => {'Accept'=>'*/*'}).
113
+ to_return(:status => 400, :body => '{"error": {"type": "OAuthException","message": "Error validating verification code."}}', :headers => {})
114
+
115
+ # create a new instance
116
+ rack_app = MealTicket.new({})
117
+
118
+ # set up the environment - the request where facebook is redirecting the user to our callback
119
+ rack_app.env = {"REQUEST_URI" => "http://localhost:3000/meal_ticket/facebook_callback?code=12345",
120
+ "QUERY_STRING" => "code=12345",
121
+ "PATH_INFO" => "/meal_ticket/facebook_callback"}
122
+
123
+ # process the request
124
+ @response = rack_app.facebook_callback
125
+ end
126
+
127
+ should "redirect to a URL that includes the facebook token and expiration" do
128
+ assert_equal 302, @response[0]
129
+ assert_equal "http://localhost:3000/callbacks?facebook[error][type]=OAuthException&facebook[error][message]=Error+validating+verification+code.", @response[1]['Location']
130
+ end
131
+ end
132
+
133
+
134
+ #flickr
135
+ context "When a flickr authentication is successful, the flickr_callback method" do
136
+ setup do
137
+ stub_request(:get, "http://api.flickr.com/services/rest/?api_key=abcde&api_sig=5a97a411c4281b61eda6d3d746d7afc6&frob=12345&method=flickr.auth.getToken").
138
+ with(:headers => {'Accept'=>'*/*'}).
139
+ to_return(:status => 200, :body => '<?xml version="1.0" encoding="utf-8" ?> <rsp stat="ok"> <auth> <token>12345</token> <perms>delete</perms> <user nsid="12345@N00"/> </auth> </rsp> ', :headers => {})
140
+
141
+ # create a new instance
142
+ rack_app = MealTicket.new({})
143
+
144
+ # set up the environment - the request where facebook is redirecting the user to our callback
145
+ rack_app.env = {"REQUEST_URI" => "http://localhost:3000/meal_ticket/flickr_callback?frob=12345",
146
+ "QUERY_STRING" => "frob=12345",
147
+ "PATH_INFO" => "/meal_ticket/flickr_callback"}
148
+
149
+ # process the request
150
+ @response = rack_app.flickr_callback
151
+ end
152
+
153
+ should "redirect to a URL that includes the facebook token and expiration" do
154
+ assert_equal 302, @response[0]
155
+ assert_equal "http://localhost:3000/callbacks?flickr[token]=12345&flickr[user_id]=12345%40N00", @response[1]['Location']
156
+ end
157
+ end
158
+
159
+ context "When a flickr authentication is successful, the flickr_callback method" do
160
+ setup do
161
+ stub_request(:get, "http://api.flickr.com/services/rest/?api_key=abcde&api_sig=5a97a411c4281b61eda6d3d746d7afc6&frob=12345&method=flickr.auth.getToken").
162
+ with(:headers => {'Accept'=>'*/*'}).
163
+ to_return(:status => 200, :body => '<?xml version="1.0" encoding="utf-8" ?> <rsp stat="fail"> <err code="108" msg="Invalid frob" /> </rsp>', :headers => {})
164
+
165
+ # create a new instance
166
+ rack_app = MealTicket.new({})
167
+
168
+ # set up the environment - the request where facebook is redirecting the user to our callback
169
+ rack_app.env = {"REQUEST_URI" => "http://localhost:3000/meal_ticket/flickr_callback?frob=12345",
170
+ "QUERY_STRING" => "frob=12345",
171
+ "PATH_INFO" => "/meal_ticket/flickr_callback"}
172
+
173
+ # process the request
174
+ @response = rack_app.flickr_callback
175
+ end
176
+
177
+ should "redirect to a URL that includes the facebook token and expiration" do
178
+ assert_equal 302, @response[0]
179
+ assert_equal "http://localhost:3000/callbacks?flickr[error][code]=108&flickr[error][msg]=Invalid+frob", @response[1]['Location']
180
+ end
181
+ end
182
+
183
+
7
184
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: meal_ticket
3
3
  version: !ruby/object:Gem::Version
4
- hash: 31
4
+ hash: 27
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
+ - 1
8
9
  - 0
9
- - 0
10
- version: 0.0.0
10
+ version: 0.1.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Chris Doyle
@@ -17,12 +17,12 @@ autorequire:
17
17
  bindir: bin
18
18
  cert_chain: []
19
19
 
20
- date: 2011-03-02 00:00:00 -08:00
20
+ date: 2011-03-08 00:00:00 -08:00
21
21
  default_executable:
22
22
  dependencies:
23
23
  - !ruby/object:Gem::Dependency
24
24
  prerelease: false
25
- type: :development
25
+ type: :runtime
26
26
  requirement: &id001 !ruby/object:Gem::Requirement
27
27
  none: false
28
28
  requirements:
@@ -32,12 +32,40 @@ dependencies:
32
32
  segments:
33
33
  - 0
34
34
  version: "0"
35
- name: shoulda
35
+ name: crack
36
36
  version_requirements: *id001
37
37
  - !ruby/object:Gem::Dependency
38
38
  prerelease: false
39
- type: :development
39
+ type: :runtime
40
40
  requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ hash: 3
46
+ segments:
47
+ - 0
48
+ version: "0"
49
+ name: json
50
+ version_requirements: *id002
51
+ - !ruby/object:Gem::Dependency
52
+ prerelease: false
53
+ type: :development
54
+ requirement: &id003 !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 3
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ name: shoulda
64
+ version_requirements: *id003
65
+ - !ruby/object:Gem::Dependency
66
+ prerelease: false
67
+ type: :development
68
+ requirement: &id004 !ruby/object:Gem::Requirement
41
69
  none: false
42
70
  requirements:
43
71
  - - ~>
@@ -49,11 +77,11 @@ dependencies:
49
77
  - 0
50
78
  version: 1.0.0
51
79
  name: bundler
52
- version_requirements: *id002
80
+ version_requirements: *id004
53
81
  - !ruby/object:Gem::Dependency
54
82
  prerelease: false
55
83
  type: :development
56
- requirement: &id003 !ruby/object:Gem::Requirement
84
+ requirement: &id005 !ruby/object:Gem::Requirement
57
85
  none: false
58
86
  requirements:
59
87
  - - ~>
@@ -65,11 +93,11 @@ dependencies:
65
93
  - 2
66
94
  version: 1.5.2
67
95
  name: jeweler
68
- version_requirements: *id003
96
+ version_requirements: *id005
69
97
  - !ruby/object:Gem::Dependency
70
98
  prerelease: false
71
99
  type: :development
72
- requirement: &id004 !ruby/object:Gem::Requirement
100
+ requirement: &id006 !ruby/object:Gem::Requirement
73
101
  none: false
74
102
  requirements:
75
103
  - - ">="
@@ -79,7 +107,7 @@ dependencies:
79
107
  - 0
80
108
  version: "0"
81
109
  name: rcov
82
- version_requirements: *id004
110
+ version_requirements: *id006
83
111
  description: Meal Ticket is the easiest way to authenticate users for 3rd-party social APIs. Built to be used in conjunction with Buffet.
84
112
  email: archslide@gmail.com
85
113
  executables: []
@@ -98,7 +126,6 @@ files:
98
126
  - Rakefile
99
127
  - VERSION
100
128
  - lib/meal_ticket.rb
101
- - meal_ticket-0.0.0.gem
102
129
  - meal_ticket.gemspec
103
130
  - test/helper.rb
104
131
  - test/test_meal_ticket.rb