podio 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Florian Munz
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,13 @@
1
+ Podio
2
+ =====
3
+
4
+ Ruby wrapper for the Podio API.
5
+
6
+ Meta
7
+ ----
8
+
9
+ * Code: `git clone git://github.com/podio/podio-rb.git`
10
+ * Home: <https://github.com/podio/podio-rb>
11
+ * Bugs: <https://github.com/podio/podio-rb/issues>
12
+
13
+ This project uses [Semantic Versioning](http://semver.org/).
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ require 'rake/testtask'
2
+ require 'bundler'
3
+
4
+ Bundler::GemHelper.install_tasks
5
+
6
+ desc 'Run tests'
7
+ Rake::TestTask.new(:test) do |t|
8
+ t.libs << 'lib'
9
+ t.pattern = 'test/**/*_test.rb'
10
+ t.verbose = true
11
+ end
data/lib/podio.rb ADDED
@@ -0,0 +1,34 @@
1
+ require 'faraday'
2
+
3
+ module Podio
4
+ class << self
5
+ attr_accessor :api_key
6
+ attr_accessor :api_secret
7
+ attr_accessor :api_url
8
+ attr_accessor :debug
9
+
10
+ def configure
11
+ yield self
12
+ true
13
+ end
14
+ end
15
+
16
+ class OAuthToken < Struct.new(:access_token, :refresh_token, :expires_at)
17
+ def initialize(params = {})
18
+ self.access_token = params['access_token']
19
+ self.refresh_token = params['refresh_token']
20
+ self.expires_at = Time.now + params['expires_in'] if params['expires_in']
21
+ end
22
+ end
23
+
24
+ module Error
25
+ class TokenExpired < StandardError; end
26
+ class AuthorizationError < StandardError; end
27
+ class BadRequestError < StandardError; end
28
+ class ServerError < StandardError; end
29
+ class NotFoundError < StandardError; end
30
+ class GoneError < StandardError; end
31
+ end
32
+ end
33
+
34
+ require 'podio/client'
@@ -0,0 +1,75 @@
1
+ require 'podio/middleware/logger'
2
+ require 'podio/middleware/oauth2'
3
+ require 'podio/middleware/mashify'
4
+ require 'podio/middleware/podio_api'
5
+ require 'podio/middleware/yajl_response'
6
+ require 'podio/middleware/error_response'
7
+
8
+ module Podio
9
+ class Client
10
+ attr_reader :api_url, :api_key, :api_secret, :debug, :oauth_token, :connection
11
+
12
+ def initialize(options = {})
13
+ @api_url = options[:api_url] || Podio.api_url || 'https://api.podio.com'
14
+ @api_key = options[:api_key] || Podio.api_key
15
+ @api_secret = options[:api_secret] || Podio.api_secret
16
+ @debug = options[:debug] || Podio.debug
17
+ @oauth_token = options[:oauth_token]
18
+
19
+ @connection = configure_connection
20
+ @oauth_connection = configure_oauth_connection
21
+ end
22
+
23
+ def get_access_token(username, password)
24
+ response = connection.post do |req|
25
+ req.url '/oauth/token', :grant_type => 'password', :client_id => api_key, :client_secret => api_secret, :username => username, :password => password
26
+ end
27
+
28
+ @oauth_token = OAuthToken.new(response.body)
29
+ configure_oauth
30
+ @oauth_token
31
+ end
32
+
33
+ def refresh_access_token
34
+ response = @oauth_connection.post do |req|
35
+ req.url '/oauth/token', :grant_type => 'refresh_token', :refresh_token => oauth_token.refresh_token, :client_id => api_key, :client_secret => api_secret
36
+ end
37
+
38
+ @oauth_token.access_token = response.body['access_token']
39
+ configure_oauth
40
+ end
41
+
42
+ private
43
+
44
+ def configure_connection
45
+ params = {}
46
+ params[:oauth_token] = oauth_token.access_token if oauth_token
47
+
48
+ Faraday::Connection.new(:url => api_url, :params => params, :headers => default_headers, :request => {:client => self}) do |builder|
49
+ builder.use Faraday::Request::Yajl
50
+ builder.use Middleware::PodioApi
51
+ builder.use Middleware::OAuth2
52
+ builder.use Middleware::Logger
53
+ builder.adapter Faraday.default_adapter
54
+ builder.use Middleware::YajlResponse
55
+ builder.use Middleware::Mashify
56
+ builder.use Middleware::ErrorResponse
57
+ end
58
+ end
59
+
60
+ def configure_oauth_connection
61
+ conn = @connection.dup
62
+ conn.options[:client] = self
63
+ conn.params = {}
64
+ conn
65
+ end
66
+
67
+ def configure_oauth
68
+ @connection = configure_connection
69
+ end
70
+
71
+ def default_headers
72
+ { :user_agent => 'Podio Ruby Library' }
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,34 @@
1
+ # Handle HTTP response status codes
2
+ #
3
+ module Podio
4
+ module Middleware
5
+ class ErrorResponse < Faraday::Response::Middleware
6
+ def self.register_on_complete(env)
7
+ env[:response].on_complete do |finished_env|
8
+ case finished_env[:status]
9
+ when 400
10
+ raise Error::BadRequestError, finished_env[:body].error
11
+ when 401
12
+ if finished_env[:body]['error'] == 'Unauthorized: expired_token'
13
+ raise Error::TokenExpired, finished_env[:body].error
14
+ else
15
+ raise Error::AuthorizationError, finished_env[:body].error
16
+ end
17
+ when 403
18
+ raise Error::AuthorizationError, finished_env[:body].error
19
+ when 404
20
+ raise Error::NotFoundError, "#{finished_env[:method].to_s.upcase} #{finished_env[:url]}"
21
+ when 410
22
+ raise Error::GoneError, "#{finished_env[:method].to_s.upcase} #{finished_env[:url]}"
23
+ when 500
24
+ raise Error::ServerError, finished_env[:body].error
25
+ end
26
+ end
27
+ end
28
+
29
+ def initialize(app)
30
+ super
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,19 @@
1
+ # Do some debug logging
2
+ #
3
+ module Podio
4
+ module Middleware
5
+ class Logger < Faraday::Middleware
6
+ def call(env)
7
+ if env[:request][:client].debug
8
+ puts "\n==> #{env[:method].to_s.upcase} #{env[:url]} \n\n"
9
+ end
10
+
11
+ @app.call(env)
12
+ end
13
+
14
+ def initialize(app)
15
+ super
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,27 @@
1
+ module Podio
2
+ module Middleware
3
+ class Mashify < Faraday::Response::Middleware
4
+ begin
5
+ require 'hashie'
6
+ rescue LoadError, NameError => error
7
+ self.load_error = error
8
+ end
9
+
10
+ def self.register_on_complete(env)
11
+ env[:response].on_complete do |response|
12
+ response_body = response[:body]
13
+ if response_body.is_a?(Hash)
14
+ response[:body] = ::Hashie::Mash.new(response_body)
15
+ elsif response_body.is_a?(Array) and response_body.first.is_a?(Hash)
16
+ response[:body] = response_body.map{|item| ::Hashie::Mash.new(item)}
17
+ end
18
+ end
19
+ end
20
+
21
+ def initialize(app)
22
+ super
23
+ @parser = nil
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,28 @@
1
+ # Retry requests with expired access tokens
2
+ #
3
+ module Podio
4
+ module Middleware
5
+ class OAuth2 < Faraday::Middleware
6
+ def call(env)
7
+ podio_client = env[:request][:client]
8
+ orig_env = env.dup
9
+
10
+ begin
11
+ @app.call(env)
12
+ rescue Error::TokenExpired
13
+ podio_client.refresh_access_token
14
+
15
+ params = orig_env[:url].query_values || {}
16
+ orig_env[:url].query_values = params.merge('oauth_token' => podio_client.oauth_token.access_token)
17
+
18
+ # redo the request with the new access token
19
+ @app.call(orig_env)
20
+ end
21
+ end
22
+
23
+ def initialize(app)
24
+ super
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,19 @@
1
+ # Fix for Podio API specific weirdnesses
2
+ #
3
+ module Podio
4
+ module Middleware
5
+ class PodioApi < Faraday::Middleware
6
+ def call(env)
7
+ if env[:method] == :post && env[:body].nil?
8
+ env[:body] = ''
9
+ end
10
+
11
+ @app.call(env)
12
+ end
13
+
14
+ def initialize(app)
15
+ super
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,31 @@
1
+ # Custom Yajl until I find a way to only parse the body once in
2
+ # the OAuth2 retry case
3
+ #
4
+ module Podio
5
+ module Middleware
6
+ class YajlResponse < Faraday::Response::Middleware
7
+ begin
8
+ require 'yajl'
9
+
10
+ def self.register_on_complete(env)
11
+ env[:response].on_complete do |finished_env|
12
+ finished_env[:body] = parse(finished_env[:body]) if finished_env[:body].is_a?(String)
13
+ end
14
+ end
15
+ rescue LoadError, NameError => e
16
+ self.load_error = e
17
+ end
18
+
19
+ def initialize(app)
20
+ super
21
+ @parser = nil
22
+ end
23
+
24
+ def self.parse(body)
25
+ Yajl::Parser.parse(body)
26
+ rescue Object => err
27
+ raise Faraday::Error::ParsingError.new(err)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,3 @@
1
+ module Podio
2
+ VERSION = '0.1.0'
3
+ end
data/podio.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ $:.push File.expand_path('../lib', __FILE__)
2
+ require 'podio/version'
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = 'podio'
6
+ s.version = Podio::VERSION
7
+ s.platform = Gem::Platform::RUBY
8
+ s.summary = 'Ruby wrapper for the Podio API'
9
+ s.homepage = 'https://github.com/podio/podio-rb'
10
+ s.email = 'florian@podio.com'
11
+ s.authors = ['Florian Munz']
12
+ s.has_rdoc = false
13
+
14
+ s.files = `git ls-files`.split("\n")
15
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ s.require_paths = ['lib']
17
+
18
+ s.add_runtime_dependency 'faraday', '~> 0.5.1'
19
+ s.add_runtime_dependency 'hashie', '~> 0.4.0'
20
+ s.add_runtime_dependency 'yajl-ruby', '~> 0.7.0'
21
+
22
+ s.add_development_dependency 'fakeweb', '~> 1.3'
23
+
24
+ s.description = <<desc
25
+ The humble beginnings of the Ruby wrapper for the Podio API.
26
+ desc
27
+ end
@@ -0,0 +1,81 @@
1
+ require_relative 'test_helper'
2
+
3
+ context 'ClientTest' do
4
+ context 'client configuration' do
5
+ setup do
6
+ Podio.configure do |config|
7
+ config.api_url = 'https://api.podio.com'
8
+ config.api_key = 'client_id'
9
+ config.api_secret = 'client_secret'
10
+ end
11
+ end
12
+
13
+ test 'should configure defaults' do
14
+ podio = Podio::Client.new
15
+
16
+ assert_equal 'https://api.podio.com', podio.api_url
17
+ assert_equal 'client_id', podio.api_key
18
+ assert_equal 'client_secret', podio.api_secret
19
+ end
20
+
21
+ test 'should overwrite defaults' do
22
+ podio = Podio::Client.new(:api_url => 'https://new.podio.com', :api_key => 'new_client_id', :api_secret => 'new_client_secret')
23
+
24
+ assert_equal 'https://new.podio.com', podio.api_url
25
+ assert_equal 'new_client_id', podio.api_key
26
+ assert_equal 'new_client_secret', podio.api_secret
27
+ end
28
+
29
+ test 'should setup connection' do
30
+ token = Podio::OAuthToken.new('access_token' => 'access', 'refresh_token' => 'refresh')
31
+ podio = Podio::Client.new(:oauth_token => token)
32
+
33
+ assert_equal token, podio.oauth_token
34
+
35
+ assert_equal({'oauth_token' => 'access'}, podio.connection.params)
36
+ end
37
+ end
38
+
39
+ context 'oauth2 authorization' do
40
+ test 'should get an access token' do
41
+ client = Podio::Client.new(:api_url => 'https://api.podio.com', :api_key => 'client_id', :api_secret => 'client_secret')
42
+ assert_nil client.oauth_token
43
+
44
+ stub_post(
45
+ '/oauth/token?grant_type=password&client_id=client_id&client_secret=client_secret&username=username&password=password',
46
+ {'access_token' => 'access', 'refresh_token' => 'refresh', 'expires_in' => 3600}
47
+ )
48
+
49
+ client.get_access_token('username', 'password')
50
+
51
+ assert_equal 'access', client.oauth_token.access_token
52
+ assert_equal 'refresh', client.oauth_token.refresh_token
53
+ end
54
+
55
+ test 'should be able to refresh access token' do
56
+ client = podio_test_client
57
+
58
+ stub_post(
59
+ '/oauth/token?grant_type=refresh_token&refresh_token=refresh&client_id=client_id&client_secret=client_secret',
60
+ {'access_token' => 'new_access', 'refresh_token' => 'refresh', 'expires_in' => 3600}
61
+ )
62
+
63
+ client.refresh_access_token
64
+
65
+ assert_equal 'new_access', client.oauth_token.access_token
66
+ assert_equal 'refresh', client.oauth_token.refresh_token
67
+ end
68
+ end
69
+
70
+ context 'raw connection' do
71
+ test 'should be able to make arbitrary requests' do
72
+ client = podio_test_client
73
+
74
+ stub_get('/?oauth_token=access', { 'messaging' => true, 'version' => '1.0.1' })
75
+
76
+ response = client.connection.get('/')
77
+ assert_equal 200, response.status
78
+ assert_equal({ 'messaging' => true, 'version' => '1.0.1' }, response.body)
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,61 @@
1
+ require 'test/unit'
2
+ require 'yajl'
3
+ require 'fakeweb'
4
+
5
+ require 'podio'
6
+
7
+
8
+ FakeWeb.allow_net_connect = false
9
+
10
+ def podio_test_client
11
+ token = Podio::OAuthToken.new('access_token' => 'access', 'refresh_token' => 'refresh', 'expires_in' => 3600)
12
+ Podio::Client.new(:api_url => 'https://api.podio.com', :api_key => 'client_id', :api_secret => 'client_secret', :oauth_token => token)
13
+ end
14
+
15
+ def fixture_file(filename)
16
+ return '' if filename == ''
17
+ file_path = File.expand_path(File.dirname(__FILE__) + '/fixtures/' + filename)
18
+ File.read(file_path)
19
+ end
20
+
21
+ def stub_get(url, body, options = {})
22
+ opts = {
23
+ :body => body.is_a?(String) ? body : Yajl::Encoder.encode(body),
24
+ :content_type => 'application/json; charset=utf-8'
25
+ }.merge(options)
26
+ FakeWeb.register_uri(:get, "https://api.podio.com#{url}", opts)
27
+ end
28
+
29
+ def stub_post(url, body, options = {})
30
+ opts = {
31
+ :body => body.is_a?(String) ? body : Yajl::Encoder.encode(body),
32
+ :content_type => 'application/json; charset=utf-8'
33
+ }.merge(options)
34
+ FakeWeb.register_uri(:post, "https://api.podio.com#{url}", opts)
35
+ end
36
+
37
+
38
+ ##
39
+ # test/spec/mini 5
40
+ # http://gist.github.com/307649
41
+ # chris@ozmm.org
42
+ #
43
+ def context(*args, &block)
44
+ return super unless (name = args.first) && block
45
+ require 'test/unit'
46
+ klass = Class.new(defined?(ActiveSupport::TestCase) ? ActiveSupport::TestCase : Test::Unit::TestCase) do
47
+ def self.test(name, &block)
48
+ define_method("test_#{name.to_s.gsub(/\W/,'_')}", &block) if block
49
+ end
50
+ def self.xtest(*args) end
51
+ def self.context(*args, &block) instance_eval(&block) end
52
+ def self.setup(&block)
53
+ define_method(:setup) { self.class.setups.each { |s| instance_eval(&s) } }
54
+ setups << block
55
+ end
56
+ def self.setups; @setups ||= [] end
57
+ def self.teardown(&block) define_method(:teardown, &block) end
58
+ end
59
+ (class << klass; self end).send(:define_method, :name) { name.gsub(/\W/,'_') }
60
+ klass.class_eval(&block)
61
+ end
metadata ADDED
@@ -0,0 +1,148 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: podio
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Florian Munz
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-12-07 00:00:00 +01:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: faraday
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ hash: 9
30
+ segments:
31
+ - 0
32
+ - 5
33
+ - 1
34
+ version: 0.5.1
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: hashie
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ hash: 15
46
+ segments:
47
+ - 0
48
+ - 4
49
+ - 0
50
+ version: 0.4.0
51
+ type: :runtime
52
+ version_requirements: *id002
53
+ - !ruby/object:Gem::Dependency
54
+ name: yajl-ruby
55
+ prerelease: false
56
+ requirement: &id003 !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ hash: 3
62
+ segments:
63
+ - 0
64
+ - 7
65
+ - 0
66
+ version: 0.7.0
67
+ type: :runtime
68
+ version_requirements: *id003
69
+ - !ruby/object:Gem::Dependency
70
+ name: fakeweb
71
+ prerelease: false
72
+ requirement: &id004 !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ hash: 9
78
+ segments:
79
+ - 1
80
+ - 3
81
+ version: "1.3"
82
+ type: :development
83
+ version_requirements: *id004
84
+ description: |
85
+ The humble beginnings of the Ruby wrapper for the Podio API.
86
+
87
+ email: florian@podio.com
88
+ executables: []
89
+
90
+ extensions: []
91
+
92
+ extra_rdoc_files: []
93
+
94
+ files:
95
+ - .gitignore
96
+ - Gemfile
97
+ - LICENSE
98
+ - README.md
99
+ - Rakefile
100
+ - lib/podio.rb
101
+ - lib/podio/client.rb
102
+ - lib/podio/middleware/error_response.rb
103
+ - lib/podio/middleware/logger.rb
104
+ - lib/podio/middleware/mashify.rb
105
+ - lib/podio/middleware/oauth2.rb
106
+ - lib/podio/middleware/podio_api.rb
107
+ - lib/podio/middleware/yajl_response.rb
108
+ - lib/podio/version.rb
109
+ - podio.gemspec
110
+ - test/client_test.rb
111
+ - test/test_helper.rb
112
+ has_rdoc: true
113
+ homepage: https://github.com/podio/podio-rb
114
+ licenses: []
115
+
116
+ post_install_message:
117
+ rdoc_options: []
118
+
119
+ require_paths:
120
+ - lib
121
+ required_ruby_version: !ruby/object:Gem::Requirement
122
+ none: false
123
+ requirements:
124
+ - - ">="
125
+ - !ruby/object:Gem::Version
126
+ hash: 3
127
+ segments:
128
+ - 0
129
+ version: "0"
130
+ required_rubygems_version: !ruby/object:Gem::Requirement
131
+ none: false
132
+ requirements:
133
+ - - ">="
134
+ - !ruby/object:Gem::Version
135
+ hash: 3
136
+ segments:
137
+ - 0
138
+ version: "0"
139
+ requirements: []
140
+
141
+ rubyforge_project:
142
+ rubygems_version: 1.3.7
143
+ signing_key:
144
+ specification_version: 3
145
+ summary: Ruby wrapper for the Podio API
146
+ test_files:
147
+ - test/client_test.rb
148
+ - test/test_helper.rb