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.
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +63 -0
- data/Rakefile +8 -0
- data/lib/singly/account.rb +63 -0
- data/lib/singly/api/auth/merge.rb +8 -0
- data/lib/singly/api/auth/service/oauth1_apply.rb +13 -0
- data/lib/singly/api/auth/service/oauth2_apply.rb +13 -0
- data/lib/singly/api/auth/service.rb +23 -0
- data/lib/singly/api/auth.rb +163 -0
- data/lib/singly/api/friends/group.rb +12 -0
- data/lib/singly/api/friends.rb +76 -0
- data/lib/singly/api/id.rb +6 -0
- data/lib/singly/api/multi.rb +6 -0
- data/lib/singly/api/profile.rb +6 -0
- data/lib/singly/api/profiles/delete.rb +8 -0
- data/lib/singly/api/profiles/delete_profile.rb +8 -0
- data/lib/singly/api/profiles/delete_service.rb +8 -0
- data/lib/singly/api/profiles/self.rb +8 -0
- data/lib/singly/api/profiles/self_update.rb +8 -0
- data/lib/singly/api/profiles/service.rb +18 -0
- data/lib/singly/api/profiles.rb +178 -0
- data/lib/singly/api/proxy/delete.rb +8 -0
- data/lib/singly/api/proxy/get.rb +8 -0
- data/lib/singly/api/proxy/post.rb +8 -0
- data/lib/singly/api/proxy/put.rb +8 -0
- data/lib/singly/api/proxy.rb +169 -0
- data/lib/singly/api/services/37signals.rb +14 -0
- data/lib/singly/api/services/bodymedia.rb +30 -0
- data/lib/singly/api/services/dropbox.rb +22 -0
- data/lib/singly/api/services/dwolla.rb +14 -0
- data/lib/singly/api/services/endpoint.rb +12 -0
- data/lib/singly/api/services/facebook.rb +62 -0
- data/lib/singly/api/services/fitbit.rb +34 -0
- data/lib/singly/api/services/flickr.rb +26 -0
- data/lib/singly/api/services/foursquare.rb +34 -0
- data/lib/singly/api/services/gcal.rb +18 -0
- data/lib/singly/api/services/gcontacts.rb +18 -0
- data/lib/singly/api/services/gdocs.rb +18 -0
- data/lib/singly/api/services/github.rb +34 -0
- data/lib/singly/api/services/gmail.rb +22 -0
- data/lib/singly/api/services/google.rb +14 -0
- data/lib/singly/api/services/gplus.rb +18 -0
- data/lib/singly/api/services/id_endpoint.rb +12 -0
- data/lib/singly/api/services/imgur.rb +14 -0
- data/lib/singly/api/services/instagram.rb +26 -0
- data/lib/singly/api/services/klout.rb +26 -0
- data/lib/singly/api/services/linkedin.rb +26 -0
- data/lib/singly/api/services/meetup.rb +30 -0
- data/lib/singly/api/services/paypal.rb +14 -0
- data/lib/singly/api/services/picasa.rb +22 -0
- data/lib/singly/api/services/rdio.rb +26 -0
- data/lib/singly/api/services/reddit.rb +14 -0
- data/lib/singly/api/services/runkeeper.rb +38 -0
- data/lib/singly/api/services/service.rb +48 -0
- data/lib/singly/api/services/shutterfly.rb +22 -0
- data/lib/singly/api/services/soundcloud.rb +14 -0
- data/lib/singly/api/services/stocktwits.rb +22 -0
- data/lib/singly/api/services/tout.rb +14 -0
- data/lib/singly/api/services/tumblr.rb +26 -0
- data/lib/singly/api/services/twitter.rb +42 -0
- data/lib/singly/api/services/withings.rb +18 -0
- data/lib/singly/api/services/wordpress.rb +22 -0
- data/lib/singly/api/services/yammer.rb +26 -0
- data/lib/singly/api/services/youtube.rb +18 -0
- data/lib/singly/api/services/zeo.rb +26 -0
- data/lib/singly/api/services.rb +158 -0
- data/lib/singly/api/types/type.rb +12 -0
- data/lib/singly/api/types.rb +68 -0
- data/lib/singly/endpoint.rb +112 -0
- data/lib/singly/error.rb +24 -0
- data/lib/singly/http.rb +38 -0
- data/lib/singly/logger.rb +14 -0
- data/lib/singly.rb +37 -0
- data/lib/version.rb +3 -0
- data/singly.gemspec +25 -0
- data/spec/integration/auth_spec.rb +14 -0
- data/spec/singly/account_spec.rb +108 -0
- data/spec/singly/api/auth_spec.rb +8 -0
- data/spec/singly/endpoint_spec.rb +100 -0
- data/spec/singly/error_spec.rb +40 -0
- data/spec/singly/http_spec.rb +52 -0
- data/spec/singly/logger_spec.rb +32 -0
- data/spec/singly_spec.rb +53 -0
- data/spec/spec_helper.rb +27 -0
- data/spec/vcr_cassettes/http_fetch_with_api_error.yml +39 -0
- data/spec/vcr_cassettes/http_fetch_with_json_response.yml +102 -0
- data/spec/vcr_cassettes/http_fetch_with_non_json_response.yml +39 -0
- 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
|
data/lib/singly/error.rb
ADDED
@@ -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
|
data/lib/singly/http.rb
ADDED
@@ -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
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
|