singly 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.
Files changed (90) hide show
  1. data/.gitignore +17 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE.txt +22 -0
  4. data/README.md +63 -0
  5. data/Rakefile +8 -0
  6. data/lib/singly/account.rb +63 -0
  7. data/lib/singly/api/auth/merge.rb +8 -0
  8. data/lib/singly/api/auth/service/oauth1_apply.rb +13 -0
  9. data/lib/singly/api/auth/service/oauth2_apply.rb +13 -0
  10. data/lib/singly/api/auth/service.rb +23 -0
  11. data/lib/singly/api/auth.rb +163 -0
  12. data/lib/singly/api/friends/group.rb +12 -0
  13. data/lib/singly/api/friends.rb +76 -0
  14. data/lib/singly/api/id.rb +6 -0
  15. data/lib/singly/api/multi.rb +6 -0
  16. data/lib/singly/api/profile.rb +6 -0
  17. data/lib/singly/api/profiles/delete.rb +8 -0
  18. data/lib/singly/api/profiles/delete_profile.rb +8 -0
  19. data/lib/singly/api/profiles/delete_service.rb +8 -0
  20. data/lib/singly/api/profiles/self.rb +8 -0
  21. data/lib/singly/api/profiles/self_update.rb +8 -0
  22. data/lib/singly/api/profiles/service.rb +18 -0
  23. data/lib/singly/api/profiles.rb +178 -0
  24. data/lib/singly/api/proxy/delete.rb +8 -0
  25. data/lib/singly/api/proxy/get.rb +8 -0
  26. data/lib/singly/api/proxy/post.rb +8 -0
  27. data/lib/singly/api/proxy/put.rb +8 -0
  28. data/lib/singly/api/proxy.rb +169 -0
  29. data/lib/singly/api/services/37signals.rb +14 -0
  30. data/lib/singly/api/services/bodymedia.rb +30 -0
  31. data/lib/singly/api/services/dropbox.rb +22 -0
  32. data/lib/singly/api/services/dwolla.rb +14 -0
  33. data/lib/singly/api/services/endpoint.rb +12 -0
  34. data/lib/singly/api/services/facebook.rb +62 -0
  35. data/lib/singly/api/services/fitbit.rb +34 -0
  36. data/lib/singly/api/services/flickr.rb +26 -0
  37. data/lib/singly/api/services/foursquare.rb +34 -0
  38. data/lib/singly/api/services/gcal.rb +18 -0
  39. data/lib/singly/api/services/gcontacts.rb +18 -0
  40. data/lib/singly/api/services/gdocs.rb +18 -0
  41. data/lib/singly/api/services/github.rb +34 -0
  42. data/lib/singly/api/services/gmail.rb +22 -0
  43. data/lib/singly/api/services/google.rb +14 -0
  44. data/lib/singly/api/services/gplus.rb +18 -0
  45. data/lib/singly/api/services/id_endpoint.rb +12 -0
  46. data/lib/singly/api/services/imgur.rb +14 -0
  47. data/lib/singly/api/services/instagram.rb +26 -0
  48. data/lib/singly/api/services/klout.rb +26 -0
  49. data/lib/singly/api/services/linkedin.rb +26 -0
  50. data/lib/singly/api/services/meetup.rb +30 -0
  51. data/lib/singly/api/services/paypal.rb +14 -0
  52. data/lib/singly/api/services/picasa.rb +22 -0
  53. data/lib/singly/api/services/rdio.rb +26 -0
  54. data/lib/singly/api/services/reddit.rb +14 -0
  55. data/lib/singly/api/services/runkeeper.rb +38 -0
  56. data/lib/singly/api/services/service.rb +48 -0
  57. data/lib/singly/api/services/shutterfly.rb +22 -0
  58. data/lib/singly/api/services/soundcloud.rb +14 -0
  59. data/lib/singly/api/services/stocktwits.rb +22 -0
  60. data/lib/singly/api/services/tout.rb +14 -0
  61. data/lib/singly/api/services/tumblr.rb +26 -0
  62. data/lib/singly/api/services/twitter.rb +42 -0
  63. data/lib/singly/api/services/withings.rb +18 -0
  64. data/lib/singly/api/services/wordpress.rb +22 -0
  65. data/lib/singly/api/services/yammer.rb +26 -0
  66. data/lib/singly/api/services/youtube.rb +18 -0
  67. data/lib/singly/api/services/zeo.rb +26 -0
  68. data/lib/singly/api/services.rb +158 -0
  69. data/lib/singly/api/types/type.rb +12 -0
  70. data/lib/singly/api/types.rb +68 -0
  71. data/lib/singly/endpoint.rb +112 -0
  72. data/lib/singly/error.rb +24 -0
  73. data/lib/singly/http.rb +38 -0
  74. data/lib/singly/logger.rb +14 -0
  75. data/lib/singly.rb +37 -0
  76. data/lib/version.rb +3 -0
  77. data/singly.gemspec +25 -0
  78. data/spec/integration/auth_spec.rb +14 -0
  79. data/spec/singly/account_spec.rb +108 -0
  80. data/spec/singly/api/auth_spec.rb +8 -0
  81. data/spec/singly/endpoint_spec.rb +100 -0
  82. data/spec/singly/error_spec.rb +40 -0
  83. data/spec/singly/http_spec.rb +52 -0
  84. data/spec/singly/logger_spec.rb +32 -0
  85. data/spec/singly_spec.rb +53 -0
  86. data/spec/spec_helper.rb +27 -0
  87. data/spec/vcr_cassettes/http_fetch_with_api_error.yml +39 -0
  88. data/spec/vcr_cassettes/http_fetch_with_json_response.yml +102 -0
  89. data/spec/vcr_cassettes/http_fetch_with_non_json_response.yml +39 -0
  90. metadata +216 -0
@@ -0,0 +1,158 @@
1
+ module Singly
2
+ class Services
3
+ include Singly::Endpoint
4
+
5
+ endpoint :get, "/services", required: [:access_token]
6
+
7
+ def thirtysevensignals(params={})
8
+ service(Singly::Services::ThirtySevenSignals, "37signals", params)
9
+ end
10
+
11
+ def bodymedia(params={})
12
+ service(Singly::Services::Bodymedia, __method__, params)
13
+ end
14
+
15
+ def dropbox(params={})
16
+ service(Singly::Services::Dropbox, __method__, params)
17
+ end
18
+
19
+ def dwolla(params={})
20
+ service(Singly::Services::Dwolla, __method__, params)
21
+ end
22
+
23
+ def facebook(params={})
24
+ service(Singly::Services::Facebook, __method__, params)
25
+ end
26
+
27
+ def fitbit(params={})
28
+ service(Singly::Services::Fitbit, __method__, params)
29
+ end
30
+
31
+ def flickr(params={})
32
+ service(Singly::Services::Flickr, __method__, params)
33
+ end
34
+
35
+ def foursquare(params={})
36
+ service(Singly::Services::Foursquare, __method__, params)
37
+ end
38
+
39
+ def gcal(params={})
40
+ service(Singly::Services::Gcal, __method__, params)
41
+ end
42
+
43
+ def gcontacts(params={})
44
+ service(Singly::Services::Gcontacts, __method__, params)
45
+ end
46
+
47
+ def gdocs(params={})
48
+ service(Singly::Services::Gdocs, __method__, params)
49
+ end
50
+
51
+ def github(params={})
52
+ service(Singly::Services::Github, __method__, params)
53
+ end
54
+
55
+ def gmail(params={})
56
+ service(Singly::Services::Gmail, __method__, params)
57
+ end
58
+
59
+ def google(params={})
60
+ service(Singly::Services::Google, __method__, params)
61
+ end
62
+
63
+ def gplus(params={})
64
+ service(Singly::Services::Gplus, __method__, params)
65
+ end
66
+
67
+ def imgur(params={})
68
+ service(Singly::Services::Imgur, __method__, params)
69
+ end
70
+
71
+ def instagram(params={})
72
+ service(Singly::Services::Instagram, __method__, params)
73
+ end
74
+
75
+ def klout(params={})
76
+ service(Singly::Services::Klout, __method__, params)
77
+ end
78
+
79
+ def linkedin(params={})
80
+ service(Singly::Services::Linkedin, __method__, params)
81
+ end
82
+
83
+ def meetup(params={})
84
+ service(Singly::Services::Meetup, __method__, params)
85
+ end
86
+
87
+ def paypal(params={})
88
+ service(Singly::Services::Paypal, __method__, params)
89
+ end
90
+
91
+ def picasa(params={})
92
+ service(Singly::Services::Picasa, __method__, params)
93
+ end
94
+
95
+ def rdio(params={})
96
+ service(Singly::Services::Rdio, __method__, params)
97
+ end
98
+
99
+ def reddit(params={})
100
+ service(Singly::Services::Reddit, __method__, params)
101
+ end
102
+
103
+ def runkeeper(params={})
104
+ service(Singly::Services::Runkeeper, __method__, params)
105
+ end
106
+
107
+ def shutterfly(params={})
108
+ service(Singly::Services::Shutterfly, __method__, params)
109
+ end
110
+
111
+ def soundcloud(params={})
112
+ service(Singly::Services::Soundcloud, __method__, params)
113
+ end
114
+
115
+ def stocktwits(params={})
116
+ service(Singly::Services::Stocktwits, __method__, params)
117
+ end
118
+
119
+ def tout(params={})
120
+ service(Singly::Services::Tout, __method__, params)
121
+ end
122
+
123
+ def tumblr(params={})
124
+ service(Singly::Services::Tumblr, __method__, params)
125
+ end
126
+
127
+ def twitter(params={})
128
+ service(Singly::Services::Twitter, __method__, params)
129
+ end
130
+
131
+ def withings(params={})
132
+ service(Singly::Services::Withings, __method__, params)
133
+ end
134
+
135
+ def wordpress(params={})
136
+ service(Singly::Services::Wordpress, __method__, params)
137
+ end
138
+
139
+ def yammer(params={})
140
+ service(Singly::Services::Yammer, __method__, params)
141
+ end
142
+
143
+ def youtube(params={})
144
+ service(Singly::Services::Youtube, __method__, params)
145
+ end
146
+
147
+ def zeo(params={})
148
+ service(Singly::Services::Zeo, __method__, params)
149
+ end
150
+
151
+ private
152
+
153
+ def service(endpoint_type, service, params)
154
+ params ||= {}
155
+ endpoint_type.new(creds.merge(params.merge(service: service.to_s)))
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,12 @@
1
+ module Singly
2
+ class Types
3
+ class Type
4
+ include Singly::Endpoint
5
+
6
+ endpoint :get, "/types/:type", {
7
+ required: [:access_token],
8
+ optional: [:fields, :limit, :since, :until, :near, :within, :q, :participants, :map, :dedup, :services]
9
+ }
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,68 @@
1
+ module Singly
2
+ class Types
3
+ include Singly::Endpoint
4
+
5
+ endpoint :get, "/types", required: [:access_token]
6
+
7
+ def photos(params={})
8
+ types_endpoint(__method__, params)
9
+ end
10
+
11
+ def photos_feed(params={})
12
+ types_endpoint(__method__, params)
13
+ end
14
+
15
+ def news(params={})
16
+ types_endpoint(__method__, params)
17
+ end
18
+
19
+ def news_feed(params={})
20
+ types_endpoint(__method__, params)
21
+ end
22
+
23
+ def videos(params={})
24
+ types_endpoint(__method__, params)
25
+ end
26
+
27
+ def videos_feed(params={})
28
+ types_endpoint(__method__, params)
29
+ end
30
+
31
+ def checkins(params={})
32
+ types_endpoint(__method__, params)
33
+ end
34
+
35
+ def checkins_feed(params={})
36
+ types_endpoint(__method__, params)
37
+ end
38
+
39
+ def statuses(params={})
40
+ types_endpoint(__method__, params)
41
+ end
42
+
43
+ def statuses_feed(params={})
44
+ types_endpoint(__method__, params)
45
+ end
46
+
47
+ def contacts(params={})
48
+ types_endpoint(__method__, params)
49
+ end
50
+
51
+ def all(params={})
52
+ types_endpoint(__method__, params)
53
+ end
54
+
55
+ def all_feed(params={})
56
+ types_endpoint(__method__, params)
57
+ end
58
+
59
+ private
60
+
61
+ def types_endpoint(type_name, params)
62
+ params ||= {}
63
+ Singly::Types::Type.new(creds.merge(params.merge({
64
+ type: type_name.to_s
65
+ })))
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,112 @@
1
+ # This module makes one significant assumption about the
2
+ # structure of a singly endpoint: That no full path will
3
+ # contain the ':' semicolon character. Semicolons here are
4
+ # used to describe the structure of an endpoint. For example,
5
+ # when invoking the #endpoint class method:
6
+ #
7
+ # endpoint :get, "/services/:service/:endpoint", required: [:access_token]
8
+ #
9
+ module Singly
10
+ module Endpoint
11
+ attr_reader :type
12
+ attr_reader :route
13
+ attr_reader :route_params
14
+ attr_reader :query_params
15
+
16
+ def initialize(params={})
17
+ params = params || {}
18
+ @route = self.class.route
19
+ @type = self.class.type
20
+ route_param_keys = self.class.required_route_params
21
+ @query_params = params.reject{|key| route_param_keys.include? key }
22
+ @route_params = params.select{|key| route_param_keys.include? key }
23
+ end
24
+
25
+ def fetch
26
+ validate
27
+ Singly::Http.fetch(type, path, query_params)
28
+ end
29
+
30
+ # Raises an error if any required parameters have not been
31
+ # supplied or if any path components are missing.
32
+ def validate
33
+ missing_route_params = self.class.required_route_params - route_params.keys
34
+ Singly::Error.y_u_no?("has route params :#{missing_route_params.join(', :')}") { missing_route_params.any? }
35
+ missing_query_params = self.class.required_params - query_params.keys
36
+ Singly::Error.y_u_no?("has query params :#{missing_query_params.join(', :')}") { missing_query_params.any? }
37
+ true
38
+ end
39
+
40
+ # Constructs the path by replacing path components
41
+ # defined in the route with their corresponding
42
+ # values in the @parameters attribute.
43
+ # "/profiles/:id@:service" -> "/profiles/12345@twitter"
44
+ def path
45
+ route_params.inject(route) do |route, param|
46
+ route.sub(":#{param[0]}", CGI.escape("#{param[1]}"))
47
+ end
48
+ end
49
+
50
+ # String representation of the composed endpoint.
51
+ # Parameter order is deterministic so it can be
52
+ # used as a key in the /multi endpoint.
53
+ def url
54
+ validate
55
+ query_string = query_params.sort.inject([]) do |queries, param|
56
+ queries << ("#{CGI.escape(param[0].to_s)}=#{CGI.escape(param[1].to_s)}")
57
+ end.join("&")
58
+ url = "#{Singly::Http.base_url}#{path}"
59
+ url += "?#{query_string}" unless query_string.empty?
60
+ return url
61
+ end
62
+
63
+ private
64
+
65
+ # Use this to pass credentials down the pipe
66
+ # eg:
67
+ # def photos(params={})
68
+ # Singly::Services::Facebook.new(creds.merge(params))
69
+ # end
70
+ def creds
71
+ creds = {}
72
+ creds[:access_token] = query_params[:access_token] if query_params[:access_token]
73
+ creds[:client_id] = query_params[:client_id] if query_params[:client_id]
74
+ creds[:client_secret] = query_params[:client_secret] if query_params[:client_secret]
75
+ creds
76
+ end
77
+
78
+ def self.included(base)
79
+ base.extend ClassMethods
80
+ end
81
+
82
+ module ClassMethods
83
+ def type
84
+ @type || :get
85
+ end
86
+
87
+ def route
88
+ @route || ""
89
+ end
90
+
91
+ def required_route_params
92
+ route.scan(/:(\w+)/).flatten.map(&:to_sym)
93
+ end
94
+
95
+ def required_params
96
+ (@params && @params[:required]) || []
97
+ end
98
+
99
+ def optional_params
100
+ (@params && @params[:optional]) || []
101
+ end
102
+
103
+ private
104
+
105
+ def endpoint(type, route, params={})
106
+ @type = type
107
+ @route = route
108
+ @params = params
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,24 @@
1
+ module Singly
2
+ class Error < StandardError
3
+ class << self
4
+ # Racist closure for raising errors
5
+ # Usage:
6
+ # unhappy = true
7
+ # Singly.y_u_no?("happy") { unhappy }
8
+ # => Singly::Error: y u no happy???
9
+ def y_u_no?(msg)
10
+ raise Error.new("y u no #{msg}???") if yield
11
+ end
12
+ end
13
+ end
14
+ class ApiError < Error
15
+ def initialize(response)
16
+ super("#{response.code} #{response.body} #{response.effective_url}")
17
+ end
18
+ end
19
+ class TimeoutError < Error
20
+ def initialize(response)
21
+ super("Response timed out after #{response.time} seconds. #{response.effective_url}")
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,38 @@
1
+ module Singly
2
+ class Http
3
+ class << self
4
+ include Singly::Logger
5
+
6
+ def base_url
7
+ "https://api.singly.com/#{Singly.version}"
8
+ end
9
+
10
+ def fetch(type, path, params={})
11
+ path = "#{base_url}#{path}"
12
+ log("#{type.upcase} #{path}")
13
+ log("PARAMS #{params}")
14
+ response = Typhoeus::Request.send(type, path, params: params, timeout: Singly.timeout)
15
+ validate_response(response)
16
+ parse_response(response)
17
+ end
18
+
19
+ private
20
+
21
+ # Singly does not yet guarantee that all responses conform to
22
+ # valid json. Sometimes we can get a TypeError
23
+ def parse_response(response)
24
+ body = response.body
25
+ log(body)
26
+ JSON.parse(body) rescue body
27
+ end
28
+
29
+ # Does two things:
30
+ # 1) Handles timeout errors
31
+ # 2) Handles generic non-200 code errors
32
+ def validate_response(response)
33
+ raise Singly::TimeoutError.new(response) if response.timed_out?
34
+ raise Singly::ApiError.new(response) if response.code != 200
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,14 @@
1
+ module Singly
2
+ module Logger
3
+ def log(msg)
4
+ return unless Singly.verbose
5
+ if defined?(Rails)
6
+ Rails.logger.info("[singly] #{msg}") # Naive logging.
7
+ # This lumberjack will log in Rails if defined.
8
+ # Otherwise it just puts all logs to stdout
9
+ else
10
+ puts "[singly] #{msg}"
11
+ end
12
+ end
13
+ end
14
+ end
data/lib/singly.rb ADDED
@@ -0,0 +1,37 @@
1
+ ($LOAD_PATH << File.dirname(__FILE__)) unless $LOAD_PATH.include? File.dirname(__FILE__)
2
+
3
+ require "cgi"
4
+ require "rubygems"
5
+ require "typhoeus"
6
+ require "json"
7
+ require "singly/logger"
8
+ require "singly/endpoint"
9
+ require "singly/api/services/service"
10
+
11
+ # Descend into and require the entire project
12
+ Dir["#{File.dirname(__FILE__)}/**/*.rb"].each {|f| require(f)}
13
+
14
+ module Singly
15
+ @version = "v0"
16
+ @timeout = 2000
17
+
18
+ class << self
19
+ attr_accessor :client_id
20
+ attr_accessor :client_secret
21
+ attr_accessor :verbose
22
+ attr_accessor :version
23
+ attr_accessor :timeout
24
+
25
+ def account(access_token, account=nil)
26
+ Singly::Account.new(access_token, account)
27
+ end
28
+
29
+ def auth
30
+ Singly::Auth.new
31
+ end
32
+
33
+ def multi(urls)
34
+ Singly::Multi.new(urls: urls.join(","))
35
+ end
36
+ end
37
+ end
data/lib/version.rb ADDED
@@ -0,0 +1,3 @@
1
+ module Singly
2
+ VERSION = "0.1.0"
3
+ end
data/singly.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "singly"
8
+ gem.version = Singly::VERSION
9
+ gem.authors = ["Kevin Cantwell"]
10
+ gem.email = ["kevin@timehop.com"]
11
+ gem.description = %q{Object-oriented interaction with the Singly API}
12
+ gem.summary = %q{See documentation at TBD}
13
+ gem.homepage = ""
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ # Gemfile woulda-beens
21
+ gem.add_dependency("json", ["~> 1.7.6"])
22
+ gem.add_dependency("typhoeus", [">= 0.5.4", "< 0.7"])
23
+ gem.add_development_dependency("rspec", ["~> 2.8"])
24
+ gem.add_development_dependency("vcr", ["~> 2.3"])
25
+ end
@@ -0,0 +1,14 @@
1
+ require 'singly'
2
+ require 'yaml'
3
+
4
+ describe "Auth Integration" do
5
+ if ENV["CONFIG"]
6
+ config = YAML::load_file( ENV["CONFIG"] )
7
+ Singly.client_id = config[:singly][:client_id]
8
+ Singly.client_secret = config[:singly][:client_secret]
9
+
10
+ params = {token: config[:facebook][:token]}
11
+ r = Singly.auth.facebook.apply(params).fetch
12
+ puts r
13
+ end
14
+ end
@@ -0,0 +1,108 @@
1
+ require 'spec_helper'
2
+
3
+ describe Singly::Account do
4
+ describe "#initialize" do
5
+ context "with access_token" do
6
+ subject { Singly::Account.new("access_tolkien").access_token }
7
+ it { should == "access_tolkien" }
8
+ end
9
+ context "with account_id" do
10
+ subject { Singly::Account.new("access_tolkien", "a_count_eye_dee").account_id }
11
+ it { should == "a_count_eye_dee" }
12
+ end
13
+ end
14
+ let(:account) { Singly::Account.new(access_token, account_id) }
15
+ let(:access_token) { "fake_access_token" }
16
+ let(:account_id) { nil }
17
+ describe "#account_id" do
18
+ context "when an account_id is specified" do
19
+ let(:account_id) { "fake_account_id" }
20
+ subject { account.account_id }
21
+ it { should == "fake_account_id" }
22
+ end
23
+ context "when no account_id is specified" do
24
+ subject { account.account_id }
25
+ before { account.stub_chain(:profiles, :fetch).and_return("id" => "this_tests_account_id") }
26
+ it { should == "this_tests_account_id" }
27
+ end
28
+ end
29
+ describe "#profile" do
30
+ subject { account.profile }
31
+ it { subject.class.should == Singly::Profile }
32
+ it { subject.validate.should be_true }
33
+ end
34
+ describe "#profiles" do
35
+ context "without params" do
36
+ subject { account.profiles }
37
+ it { subject.class.should == Singly::Profiles }
38
+ it { subject.validate.should be_true }
39
+ end
40
+ context "with optional params" do
41
+ subject { account.profiles(data: true, verify: true) }
42
+ it { subject.class.should == Singly::Profiles }
43
+ it { subject.validate.should be_true }
44
+ it { subject.query_params.should include(data: true, verify: true) }
45
+ end
46
+ end
47
+ describe "#types" do
48
+ subject { account.types }
49
+ it { subject.class.should == Singly::Types }
50
+ it { subject.validate.should be_true }
51
+ end
52
+ describe "#services" do
53
+ subject { account.services }
54
+ it { subject.class.should == Singly::Services }
55
+ it { subject.validate.should be_true }
56
+ end
57
+ describe "#id" do
58
+ context "without params" do
59
+ subject { account.id("abc123") }
60
+ it { subject.class.should == Singly::Id }
61
+ it { subject.validate.should be_true }
62
+ end
63
+ context "with optional params" do
64
+ subject { account.id("abc123", map: true) }
65
+ it { subject.class.should == Singly::Id }
66
+ it { subject.validate.should be_true }
67
+ it { subject.query_params.should include(map: true) }
68
+ end
69
+ end
70
+ describe "#delete" do
71
+ context "where we delete all profiles by service" do
72
+ subject { account.delete(:facebook) }
73
+ it { subject.class.should == Singly::Profiles::DeleteService }
74
+ it { subject.validate.should be_true }
75
+ it { subject.route_params.should include(service: "facebook") }
76
+ end
77
+ context "where we delete a specific profile by id" do
78
+ subject { account.delete(:facebook, 987654321) }
79
+ it { subject.class.should == Singly::Profiles::DeleteProfile }
80
+ it { subject.validate.should be_true }
81
+ it { subject.route_params.should include(service: "facebook", id: "987654321") }
82
+ end
83
+ end
84
+ describe "#merge" do
85
+ subject { account.merge("fake_source_access_token") }
86
+ it { subject.class.should == Singly::Auth::Merge }
87
+ it { subject.validate.should be_true }
88
+ it { subject.query_params.should include(
89
+ source: "fake_source_access_token",
90
+ dest: "fake_access_token"
91
+ ) }
92
+ end
93
+ describe "#apply" do
94
+ let(:account_id) { "fake_account_id" }
95
+ context "with an oauth2 service" do
96
+ subject { account.apply(:instagram, token: "tokin") }
97
+ it { subject.class.should == Singly::Auth::Service::Oauth2Apply }
98
+ it { subject.validate.should be_true }
99
+ it { subject.query_params.should include(account: "fake_account_id") }
100
+ end
101
+ context "with an oauth1 service" do
102
+ subject { account.apply(:flickr, token: "tokin", token_secret: "shhh_tokin") }
103
+ it { subject.class.should == Singly::Auth::Service::Oauth1Apply }
104
+ it { subject.validate.should be_true }
105
+ it { subject.query_params.should include(account: "fake_account_id") }
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+
3
+ describe Singly::Auth do
4
+ describe "#merge" do
5
+ subject { Singly::Auth.new.merge(source: "abc", dest: "123").url }
6
+ it { should == "https://api.singly.com/v0/auth/merge?dest=123&source=abc" }
7
+ end
8
+ end