picturelife 0.0.1
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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/Guardfile +11 -0
- data/LICENSE.txt +22 -0
- data/README.md +58 -0
- data/Rakefile +8 -0
- data/lib/picturelife.rb +22 -0
- data/lib/picturelife/api.rb +37 -0
- data/lib/picturelife/base.rb +35 -0
- data/lib/picturelife/error.rb +51 -0
- data/lib/picturelife/oauth.rb +105 -0
- data/lib/picturelife/ruler.rb +103 -0
- data/lib/picturelife/stub.rb +22 -0
- data/lib/picturelife/util.rb +39 -0
- data/picturelife.gemspec +34 -0
- data/spec/picturelife_spec.rb +137 -0
- data/spec/spec_helper.rb +2 -0
- metadata +231 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 70cb3e82a2e7a9da6aef56b46beedb101c8dd3a3
|
4
|
+
data.tar.gz: 814c20f34212c76c3ef2ab463482c045eebc4fb4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7a293f8d2025a200ce2c9eddb7b81b4a8a273771817f9bed148f6a072b12552bec02b883bb562b130b9b84f2f1e3153efcbad3fa779b56418b904fce2198c15c
|
7
|
+
data.tar.gz: 4f630b5d9a3bf5ff323b32d1a604153c8dab6090be1dea547d6e41ddd9b927104c519c86d9765836ed1c779647ad2ac236c6108fb84853a390a3a3e9dbe50eba
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Albert Zak
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
# Picturelife
|
2
|
+
|
3
|
+
## Installation
|
4
|
+
|
5
|
+
Add this line to your application's Gemfile:
|
6
|
+
|
7
|
+
gem 'picturelife'
|
8
|
+
|
9
|
+
And then execute:
|
10
|
+
|
11
|
+
$ bundle
|
12
|
+
|
13
|
+
## Features
|
14
|
+
|
15
|
+
- Resumable photo upload
|
16
|
+
- Authentication via OAuth
|
17
|
+
- Minimal dependencies
|
18
|
+
- Ruby syntax
|
19
|
+
|
20
|
+
## Usage
|
21
|
+
|
22
|
+
### Setup
|
23
|
+
```ruby
|
24
|
+
|
25
|
+
require 'picturelife'
|
26
|
+
|
27
|
+
Picturelife.client_id = ENV['CLIENT_ID']
|
28
|
+
Picturelife.client_secret = ENV['CLIENT_SECRET']
|
29
|
+
|
30
|
+
Picturelife.redirect_uri = 'http://localhost:3000/oauth'
|
31
|
+
|
32
|
+
```
|
33
|
+
|
34
|
+
### Authorization
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
|
38
|
+
<a href="<%= Picturelife.authorization_uri %>">Click here</a> to connect to Picturelife!</a>
|
39
|
+
|
40
|
+
```
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
|
44
|
+
get '/oauth' do
|
45
|
+
code = params['code']
|
46
|
+
Picturelife.access_token_from_code = code
|
47
|
+
end
|
48
|
+
|
49
|
+
```
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
|
53
|
+
Picturelife::Medias.index(limit: 10)
|
54
|
+
|
55
|
+
|
56
|
+
```
|
57
|
+
|
58
|
+
|
data/Rakefile
ADDED
data/lib/picturelife.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
require 'json'
|
3
|
+
require 'oauth'
|
4
|
+
require 'excon'
|
5
|
+
require 'net/http'
|
6
|
+
require 'digest/sha2'
|
7
|
+
require 'picturelife/error'
|
8
|
+
require 'picturelife/util'
|
9
|
+
require 'picturelife/base'
|
10
|
+
require 'picturelife/stub'
|
11
|
+
require 'picturelife/oauth'
|
12
|
+
require 'picturelife/api'
|
13
|
+
require 'picturelife/ruler'
|
14
|
+
|
15
|
+
Excon.defaults[:ssl_verify_peer] = false
|
16
|
+
|
17
|
+
include Picturelife::Util
|
18
|
+
include Picturelife::Ruler
|
19
|
+
|
20
|
+
module Picturelife
|
21
|
+
VERSION = "0.0.1"
|
22
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Picturelife
|
2
|
+
|
3
|
+
API_ENDPOINT = "https://api.picturelife.com/"
|
4
|
+
|
5
|
+
class Api
|
6
|
+
class << self
|
7
|
+
|
8
|
+
def call(uri, args = nil)
|
9
|
+
needs_token!
|
10
|
+
|
11
|
+
api_get(build_uri(uri, args))
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def build_uri(uri, args = nil)
|
17
|
+
[API_ENDPOINT, uri, build_parameters(args)].join
|
18
|
+
end
|
19
|
+
|
20
|
+
def build_parameters(args = nil)
|
21
|
+
args = {} if args.nil?
|
22
|
+
|
23
|
+
args.merge!(access_token: Picturelife.access_token)
|
24
|
+
|
25
|
+
return ['?', URI.encode_www_form(args)].join
|
26
|
+
end
|
27
|
+
|
28
|
+
def api_get(uri)
|
29
|
+
uri = URI(URI.encode(uri))
|
30
|
+
response = Net::HTTP.get(uri)
|
31
|
+
response = JSON.parse(response)
|
32
|
+
raise ApiError.new(response["status"], response["error"], response) if (response["status"] != 20000 && response["status"] != 200)
|
33
|
+
response
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Picturelife
|
2
|
+
class << self
|
3
|
+
attr_accessor :client_id
|
4
|
+
attr_accessor :client_secret
|
5
|
+
attr_accessor :access_token
|
6
|
+
attr_accessor :refresh_token
|
7
|
+
attr_reader :redirect_uri
|
8
|
+
|
9
|
+
def redirect_uri=(uri)
|
10
|
+
@redirect_uri = escape_uri(uri)
|
11
|
+
end
|
12
|
+
|
13
|
+
def configured?
|
14
|
+
@client_id &&
|
15
|
+
@client_secret &&
|
16
|
+
@redirect_uri
|
17
|
+
end
|
18
|
+
|
19
|
+
def authenticated?
|
20
|
+
!! @access_token
|
21
|
+
end
|
22
|
+
|
23
|
+
def reset_configuration!
|
24
|
+
@client_id = nil
|
25
|
+
@client_secret = nil
|
26
|
+
@redirect_uri = nil
|
27
|
+
reset_authentication!
|
28
|
+
end
|
29
|
+
|
30
|
+
def reset_authentication!
|
31
|
+
@access_token = nil
|
32
|
+
@refresh_token = nil
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Picturelife
|
2
|
+
|
3
|
+
class NotConfiguredError < StandardError
|
4
|
+
def initialize
|
5
|
+
super <<-MSG.gsub(/^ {6}/, '')
|
6
|
+
|
7
|
+
You need to set up your API Credentials like this:
|
8
|
+
Picturelife.client_id = ENV['CLIENT_ID']
|
9
|
+
Picturelife.client_secret = ENV['CLIENT_SECRET']
|
10
|
+
Picturelife.redirect_uri = 'http://localhost:3000/oauth'
|
11
|
+
MSG
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class NotAuthenticatedError < StandardError
|
16
|
+
def initialize
|
17
|
+
super <<-MSG.gsub(/^ {6}/, '')
|
18
|
+
|
19
|
+
Send your User to `Picturelife.authorization_uri` and catch
|
20
|
+
the access token from your `Picturelife.redirect_uri` like so:
|
21
|
+
Picturelife.access_token(params[:code])
|
22
|
+
MSG
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class ApiError < StandardError
|
27
|
+
attr_reader :code
|
28
|
+
def initialize(code = nil, msg = nil, response = nil)
|
29
|
+
puts @code = code
|
30
|
+
puts response.to_s
|
31
|
+
super msg
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class RulerError < ApiError; end
|
36
|
+
class OAuthError < ApiError; end
|
37
|
+
|
38
|
+
module Util
|
39
|
+
|
40
|
+
def needs_configuration!
|
41
|
+
raise NotConfiguredError unless Picturelife.configured?
|
42
|
+
end
|
43
|
+
|
44
|
+
def needs_token!
|
45
|
+
needs_configuration!
|
46
|
+
raise NotAuthenticatedError unless Picturelife.authenticated?
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module Picturelife
|
2
|
+
|
3
|
+
OAUTH_ENDPOINT = "https://api.picturelife.com/oauth/"
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def authorization_uri
|
7
|
+
needs_configuration!
|
8
|
+
|
9
|
+
[
|
10
|
+
OAUTH_ENDPOINT,
|
11
|
+
"authorize?",
|
12
|
+
"client_id=#{@client_id}",
|
13
|
+
"&client_secret=#{@client_secret}",
|
14
|
+
"&redirect_uri=#{@redirect_uri}",
|
15
|
+
"&response_type=code"
|
16
|
+
].join
|
17
|
+
end
|
18
|
+
|
19
|
+
def access_token_from_code=(code)
|
20
|
+
needs_configuration!
|
21
|
+
|
22
|
+
uri = [
|
23
|
+
OAUTH_ENDPOINT,
|
24
|
+
"access_token?",
|
25
|
+
"client_id=#{@client_id}",
|
26
|
+
"&client_secret=#{@client_secret}",
|
27
|
+
"&code=#{code}",
|
28
|
+
"&client_uuid=#{client_uuid}"
|
29
|
+
].join
|
30
|
+
|
31
|
+
res = api_oauth_get(uri)
|
32
|
+
|
33
|
+
if res['status'] != 200
|
34
|
+
raise OAuthError.new(res['status'], res['error'], res)
|
35
|
+
else
|
36
|
+
@refresh_token = res['refresh_token']
|
37
|
+
@user_id = res['user_id']
|
38
|
+
@token_expires = Time.at(res['expires'])
|
39
|
+
@access_token = res['access_token']
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def api_oauth_get(uri)
|
46
|
+
uri = URI(URI.encode(uri))
|
47
|
+
response = Net::HTTP.get(uri)
|
48
|
+
response = JSON.parse(hashrocket_to_json(response))
|
49
|
+
raise OAuthError.new(response["error"]) if response["status"] != 200
|
50
|
+
response
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
class OAuth
|
56
|
+
|
57
|
+
attr_accessor :verb, :uri, :body
|
58
|
+
|
59
|
+
def initialize(verb, uri, body)
|
60
|
+
@verb = verb
|
61
|
+
@uri = uri
|
62
|
+
@body = body
|
63
|
+
end
|
64
|
+
|
65
|
+
def authorization_header
|
66
|
+
header = "OAuth "
|
67
|
+
oauth_params.each do |k, v|
|
68
|
+
header += "#{k}=\"#{v}\", "
|
69
|
+
end
|
70
|
+
header.slice(0..-3) # chop off last ", "
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def oauth_params
|
76
|
+
{
|
77
|
+
'oauth_timestamp' => Time.now.getutc.to_i.to_s,
|
78
|
+
'oauth_token' => escape_uri(Picturelife.access_token),
|
79
|
+
'oauth_consumer_key' => Picturelife.client_id,
|
80
|
+
'oauth_signature_method' => 'HMAC-SHA1',
|
81
|
+
'oauth_signature' => signature,
|
82
|
+
'oauth_nonce' => nonce,
|
83
|
+
'oauth_version' => '1.0'
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
def signature
|
88
|
+
body = @body.dup
|
89
|
+
body['access_token'] = Picturelife.access_token
|
90
|
+
body = body.map { |k, v| [k.to_s, v.to_s] }
|
91
|
+
body = body.sort.map { |k, v| escape_uri("#{k}=#{v}") }.join('%26')
|
92
|
+
|
93
|
+
base = [verb, escape_uri(uri), body].join('&')
|
94
|
+
key = [Picturelife.client_secret, Picturelife.access_token].join('&')
|
95
|
+
escape_uri(sign(key, base))
|
96
|
+
end
|
97
|
+
|
98
|
+
def sign(key, base)
|
99
|
+
digest = OpenSSL::Digest::Digest.new('sha1')
|
100
|
+
hmac = OpenSSL::HMAC.digest(digest, key, base)
|
101
|
+
Base64.encode64(hmac).chomp.gsub(/\n/, '')
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module Picturelife
|
2
|
+
|
3
|
+
RULER_ENDPOINT = "https://services.picturelife.com/ruler/"
|
4
|
+
|
5
|
+
module Ruler
|
6
|
+
|
7
|
+
def upload(file_path, options = {}, processing_needs = [])
|
8
|
+
needs_token!
|
9
|
+
|
10
|
+
@file_path = file_path
|
11
|
+
@signature = get_signature(file_path)
|
12
|
+
@filename = get_filename(file_path)
|
13
|
+
@force = !! options[:force]
|
14
|
+
@options = options
|
15
|
+
@processing_needs = processing_needs
|
16
|
+
|
17
|
+
media_exists || perform_upload
|
18
|
+
|
19
|
+
rescue RulerError => e
|
20
|
+
(e.code == 519256) ? retry : raise(e)
|
21
|
+
rescue Timeout::Error, Errno::ECONNRESET
|
22
|
+
retry
|
23
|
+
end
|
24
|
+
|
25
|
+
def head(file_path)
|
26
|
+
@file_path = file_path
|
27
|
+
@signature = get_signature(file_path)
|
28
|
+
@filename = get_filename(file_path)
|
29
|
+
|
30
|
+
ruler('HEAD')
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def media_exists
|
36
|
+
medias = Picturelife::Medias.check_signatures(signatures: @signature)['signatures'] || {}
|
37
|
+
return false if medias.empty?
|
38
|
+
return Picturelife::Medias.show(media_id: medias[@signature]['media_id']) unless @force
|
39
|
+
return false
|
40
|
+
end
|
41
|
+
|
42
|
+
def perform_upload
|
43
|
+
header = { 'Content-Range' => "bytes #{filesize_uploaded}-#{filesize_total}/#{filesize_total}" }
|
44
|
+
location = ruler(:put, file_partial, header)['location']
|
45
|
+
create_media_from_ruler(location)
|
46
|
+
end
|
47
|
+
|
48
|
+
def file_partial
|
49
|
+
IO.binread(@file_path, nil, filesize_uploaded)
|
50
|
+
end
|
51
|
+
|
52
|
+
def filesize_total
|
53
|
+
File.size(@file_path)
|
54
|
+
end
|
55
|
+
|
56
|
+
def filesize_uploaded
|
57
|
+
@filesize_uploaded ||= lambda do
|
58
|
+
uploaded = ruler(:head)['X-Ruler-Size']
|
59
|
+
uploaded = uploaded.first if uploaded
|
60
|
+
uploaded.to_i
|
61
|
+
end.call
|
62
|
+
end
|
63
|
+
|
64
|
+
def create_media_from_ruler(location)
|
65
|
+
Picturelife::Media.create({
|
66
|
+
url: location,
|
67
|
+
signature: @signature,
|
68
|
+
force: (!!@force).to_s,
|
69
|
+
local_path: @file_path,
|
70
|
+
processing_needs: @processing_needs.map(&:to_s).join(',')
|
71
|
+
}.merge(@options))
|
72
|
+
end
|
73
|
+
|
74
|
+
def auth
|
75
|
+
{
|
76
|
+
'Authorization' => "Bearer #{Picturelife.access_token}",
|
77
|
+
'User-Agent' => "rubygem/#{VERSION}"
|
78
|
+
}
|
79
|
+
end
|
80
|
+
|
81
|
+
def ruler(verb, data = nil, headers = {})
|
82
|
+
http = Excon.new(RULER_ENDPOINT + @filename)
|
83
|
+
res = http.request(
|
84
|
+
method: verb,
|
85
|
+
query: { signature: @signature },
|
86
|
+
headers: auth.merge(headers),
|
87
|
+
body: data,
|
88
|
+
idempotent: true
|
89
|
+
)
|
90
|
+
|
91
|
+
body = res.body
|
92
|
+
body = (body && body.length >= 2) ? JSON.parse(body) : {}
|
93
|
+
body.merge!(res.headers)
|
94
|
+
|
95
|
+
unless body['X-Ruler-Error-Code'].to_s.empty?
|
96
|
+
raise RulerError.new(body['X-Ruler-Error-Code'], body['X-Ruler-Error'], body)
|
97
|
+
else
|
98
|
+
body
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Picturelife
|
2
|
+
|
3
|
+
def self.const_missing(name)
|
4
|
+
Stub.new(underscore(name.to_s))
|
5
|
+
end
|
6
|
+
|
7
|
+
class Stub
|
8
|
+
|
9
|
+
attr_reader :module_name
|
10
|
+
|
11
|
+
def initialize(module_name)
|
12
|
+
@module_name = module_name
|
13
|
+
end
|
14
|
+
|
15
|
+
def method_missing(name, args = nil)
|
16
|
+
method = [module_name, name].join('/')
|
17
|
+
Api.call(method, args)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Picturelife
|
2
|
+
module Util
|
3
|
+
|
4
|
+
def escape_uri(text)
|
5
|
+
return '' if text.nil?
|
6
|
+
CGI::escape(text)
|
7
|
+
end
|
8
|
+
|
9
|
+
def underscore(camelcase)
|
10
|
+
camelcase.gsub(/(.)([A-Z])/,'\1_\2').downcase
|
11
|
+
end
|
12
|
+
|
13
|
+
def api_get(uri)
|
14
|
+
uri = URI(URI.encode(uri))
|
15
|
+
JSON.parse(Net::HTTP.get(uri))
|
16
|
+
end
|
17
|
+
|
18
|
+
def hashrocket_to_json(string)
|
19
|
+
string.gsub('=>', ':')
|
20
|
+
end
|
21
|
+
|
22
|
+
def client_uuid
|
23
|
+
@client_uuid ||= rand(10**10).to_s
|
24
|
+
end
|
25
|
+
|
26
|
+
def nonce
|
27
|
+
rand(10**10).to_s
|
28
|
+
end
|
29
|
+
|
30
|
+
def get_signature(file_path)
|
31
|
+
Digest::SHA256.hexdigest File.read(file_path)
|
32
|
+
end
|
33
|
+
|
34
|
+
def get_filename(file_path)
|
35
|
+
[get_signature(file_path), '_', client_uuid, File.extname(file_path)].join
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
data/picturelife.gemspec
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'picturelife'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'picturelife'
|
8
|
+
spec.version = Picturelife::VERSION
|
9
|
+
spec.authors = ['Albert Zak']
|
10
|
+
spec.email = ['me@albertzak.com']
|
11
|
+
spec.summary = %q{Picturelife API. OAuth. Resumable Uploads.}
|
12
|
+
spec.description = 'A gem to access the Picturelife API.'
|
13
|
+
spec.homepage = 'https://github.com/albertzak/picturelife'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.add_dependency 'json'
|
22
|
+
spec.add_dependency 'excon'
|
23
|
+
spec.add_dependency 'oauth'
|
24
|
+
|
25
|
+
spec.add_development_dependency 'bundler'
|
26
|
+
spec.add_development_dependency 'rake'
|
27
|
+
spec.add_development_dependency 'rspec'
|
28
|
+
spec.add_development_dependency 'rspec-nc'
|
29
|
+
spec.add_development_dependency 'guard'
|
30
|
+
spec.add_development_dependency 'guard-rspec'
|
31
|
+
spec.add_development_dependency 'pry'
|
32
|
+
spec.add_development_dependency 'pry-remote'
|
33
|
+
spec.add_development_dependency 'pry-nav'
|
34
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Picturelife do
|
4
|
+
before(:each) do
|
5
|
+
Picturelife.client_id = 'aaa'
|
6
|
+
Picturelife.client_secret = 'bbb'
|
7
|
+
Picturelife.redirect_uri = 'ccc'
|
8
|
+
|
9
|
+
Picturelife.stub(:authenticated?).and_return(true)
|
10
|
+
|
11
|
+
Net::HTTP.stub(:get).and_return('{"status":20000}')
|
12
|
+
|
13
|
+
File.stub(:read).and_return('image')
|
14
|
+
File.stub(:extname).and_return('jpg')
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'accepts api keys' do
|
18
|
+
expect { Picturelife.client_id = 'test' }.not_to raise_error
|
19
|
+
expect { Picturelife.client_secret = 'test' }.not_to raise_error
|
20
|
+
expect { Picturelife.redirect_uri = 'test' }.not_to raise_error
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'raises error if not configured' do
|
24
|
+
Picturelife.reset_configuration!
|
25
|
+
expect { Picturelife.authorization_uri }. to raise_error(Picturelife::NotConfiguredError)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'raises error if not authenticated' do
|
29
|
+
Picturelife.stub(:authenticated?).and_return(false)
|
30
|
+
expect { Picturelife::Api.call('/') }. to raise_error(Picturelife::NotAuthenticatedError)
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'escapes redirect uri' do
|
34
|
+
Picturelife.redirect_uri = 'http://localhost:4567/?code=a'
|
35
|
+
expect(Picturelife.redirect_uri).to eq 'http%3A%2F%2Flocalhost%3A4567%2F%3Fcode%3Da'
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'builds valid authorization url' do
|
39
|
+
expect(Picturelife.authorization_uri).to include 'aaa'
|
40
|
+
expect(Picturelife.authorization_uri).to include 'bbb'
|
41
|
+
expect(Picturelife.authorization_uri).to include 'ccc'
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'gets and sets access token' do
|
45
|
+
Net::HTTP.stub(:get).and_return('{"access_token":"token","status":200}')
|
46
|
+
|
47
|
+
Picturelife.access_token_from_code = 'code'
|
48
|
+
expect(Picturelife.access_token).to eq 'token'
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'generated client uuid' do
|
52
|
+
expect(Picturelife.send(:client_uuid)).to be_kind_of(String)
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'caches uuid' do
|
56
|
+
uuid = Proc.new { Picturelife.send(:client_uuid) }
|
57
|
+
expect(uuid).to eq uuid
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'queries API' do
|
61
|
+
expect(Net::HTTP).to receive(:get) do |uri|
|
62
|
+
expect(uri.scheme).to eq 'https'
|
63
|
+
expect(uri.query).to include 'client_id=aaa'
|
64
|
+
expect(uri.query).to include 'client_secret=bbb'
|
65
|
+
expect(uri.query).to include 'code=code'
|
66
|
+
expect(uri.to_s).to include Picturelife::OAUTH_ENDPOINT
|
67
|
+
end.and_return('{"access_token"=>"ddd","status"=>200}')
|
68
|
+
|
69
|
+
Picturelife.access_token_from_code = 'code'
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'API returns hashrocket that is converted to json' do
|
73
|
+
parsable = hashrocket_to_json('{"access_token"=>"ddd"}')
|
74
|
+
expect { JSON.parse(parsable) }.not_to raise_error
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'stubs missing constant names' do
|
78
|
+
expect { Picturelife::SmartFilters }.not_to raise_error
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'stubs missing methods on missing constants' do
|
82
|
+
expect { Picturelife::SmartFilters.index }.not_to raise_error
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'stubs call correct api method' do
|
86
|
+
expect(Picturelife::Api).to receive(:call).with('smart_filters/index', nil)
|
87
|
+
Picturelife::SmartFilters.index
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'stubs call correct api method with argument' do
|
91
|
+
expect(Picturelife::Api).to receive(:call).with('smart_filters/index', include_system: true)
|
92
|
+
Picturelife::SmartFilters.index(include_system: true)
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'stubs call correct api method with multiple args' do
|
96
|
+
expect(Picturelife::Api).to receive(:call).with('smart_filters/index', { include_system: true, limit: 10 })
|
97
|
+
Picturelife::SmartFilters.index({ include_system: true, limit: 10 })
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'builds url parameters without args' do
|
101
|
+
url = Picturelife::Api.send(:build_uri, 'smart_filters/index')
|
102
|
+
expect(url).to include('https://api.picturelife.com/smart_filters/index')
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'builds url parameters from args' do
|
106
|
+
url = Picturelife::Api.send(:build_uri, 'smart_filters/index', {limit: 10})
|
107
|
+
expect(url).to include('https://api.picturelife.com/smart_filters/index?limit=10')
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'builds url parameters from multiple args' do
|
111
|
+
url = Picturelife::Api.send(:build_uri, 'smart_filters/index', {limit: 10, include_system: true})
|
112
|
+
expect(url).to include('https://api.picturelife.com/smart_filters/index?limit=10&include_system=true')
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'adds access token to url parameters' do
|
116
|
+
Picturelife.stub(:access_token).and_return('123')
|
117
|
+
url = Picturelife::Api.send(:build_uri, 'smart_filters/index', {limit: 10, include_system: true})
|
118
|
+
expect(url).to eq('https://api.picturelife.com/smart_filters/index?limit=10&include_system=true&access_token=123')
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'needs authentication for calls to api' do
|
122
|
+
Picturelife.stub(:authenticated?).and_return(false)
|
123
|
+
expect { Picturelife::Api.call('index') }.to raise_error(Picturelife::NotAuthenticatedError)
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'needs authentication for calls to ruler' do
|
127
|
+
Picturelife.stub(:authenticated?).and_return(false)
|
128
|
+
expect { Picturelife::Ruler.upload(StringIO.new) }.to raise_error(Picturelife::NotAuthenticatedError)
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'needs authentication for calls to ruler' do
|
132
|
+
Picturelife::Ruler.should_receive(:media_exists).and_return('media_id')
|
133
|
+
expect(Picturelife::Ruler.upload(StringIO.new)).to eq('media_id')
|
134
|
+
end
|
135
|
+
|
136
|
+
|
137
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,231 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: picturelife
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Albert Zak
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-06-06 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: json
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: excon
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: oauth
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: bundler
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rspec-nc
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: guard
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - '>='
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: guard-rspec
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - '>='
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - '>='
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: pry
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - '>='
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - '>='
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: pry-remote
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - '>='
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - '>='
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: pry-nav
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - '>='
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - '>='
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
181
|
+
description: A gem to access the Picturelife API.
|
182
|
+
email:
|
183
|
+
- me@albertzak.com
|
184
|
+
executables: []
|
185
|
+
extensions: []
|
186
|
+
extra_rdoc_files: []
|
187
|
+
files:
|
188
|
+
- .gitignore
|
189
|
+
- Gemfile
|
190
|
+
- Guardfile
|
191
|
+
- LICENSE.txt
|
192
|
+
- README.md
|
193
|
+
- Rakefile
|
194
|
+
- lib/picturelife.rb
|
195
|
+
- lib/picturelife/api.rb
|
196
|
+
- lib/picturelife/base.rb
|
197
|
+
- lib/picturelife/error.rb
|
198
|
+
- lib/picturelife/oauth.rb
|
199
|
+
- lib/picturelife/ruler.rb
|
200
|
+
- lib/picturelife/stub.rb
|
201
|
+
- lib/picturelife/util.rb
|
202
|
+
- picturelife.gemspec
|
203
|
+
- spec/picturelife_spec.rb
|
204
|
+
- spec/spec_helper.rb
|
205
|
+
homepage: https://github.com/albertzak/picturelife
|
206
|
+
licenses:
|
207
|
+
- MIT
|
208
|
+
metadata: {}
|
209
|
+
post_install_message:
|
210
|
+
rdoc_options: []
|
211
|
+
require_paths:
|
212
|
+
- lib
|
213
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
214
|
+
requirements:
|
215
|
+
- - '>='
|
216
|
+
- !ruby/object:Gem::Version
|
217
|
+
version: '0'
|
218
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
219
|
+
requirements:
|
220
|
+
- - '>='
|
221
|
+
- !ruby/object:Gem::Version
|
222
|
+
version: '0'
|
223
|
+
requirements: []
|
224
|
+
rubyforge_project:
|
225
|
+
rubygems_version: 2.0.3
|
226
|
+
signing_key:
|
227
|
+
specification_version: 4
|
228
|
+
summary: Picturelife API. OAuth. Resumable Uploads.
|
229
|
+
test_files:
|
230
|
+
- spec/picturelife_spec.rb
|
231
|
+
- spec/spec_helper.rb
|