picturelife 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in picturelife.gemspec
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,11 @@
1
+ guard 'rspec' do
2
+
3
+ watch(%r{^lib/(.+).rb$}) do |m|
4
+ "spec/#{m[1]}_spec.rb"
5
+ end
6
+
7
+ watch(%r{^spec/(.+).rb$}) do |m|
8
+ "spec/#{m[1]}.rb"
9
+ end
10
+
11
+ end
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
@@ -0,0 +1,8 @@
1
+ require 'rspec/core/rake_task'
2
+ require 'bundler/gem_tasks'
3
+
4
+ RSpec::Core::RakeTask.new(:spec) do |task|
5
+ task.rspec_opts = ['--color', '--format', 'nested']
6
+ end
7
+
8
+ task :default => :spec
@@ -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
@@ -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
@@ -0,0 +1,2 @@
1
+ require 'pry'
2
+ require 'picturelife'
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