douban_api 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.
@@ -0,0 +1,92 @@
1
+ require 'faraday'
2
+ require File.expand_path('../../faraday/parts/filepart', __FILE__)
3
+ require File.expand_path('../version', __FILE__)
4
+
5
+ module Douban
6
+ # Defines constants and methods related to configuration
7
+ module Configuration
8
+ # An array of valid keys in the options hash when configuring a {Douban::API}
9
+ VALID_OPTIONS_KEYS = [
10
+ :adapter,
11
+ :client_id,
12
+ :client_secret,
13
+ :access_token,
14
+ :endpoint,
15
+ :format,
16
+ :user_agent,
17
+ :proxy,
18
+ :user_id
19
+ ].freeze
20
+
21
+ # An array of valid request/response formats
22
+ #
23
+ # @note Not all methods support the XML format.
24
+ VALID_FORMATS = [
25
+ :json].freeze
26
+
27
+ # The adapter that will be used to connect if none is set
28
+ #
29
+ # @note The default faraday adapter is Net::HTTP.
30
+ DEFAULT_ADAPTER = Faraday.default_adapter
31
+
32
+ # By default, don't set an application ID
33
+ DEFAULT_CLIENT_ID = nil
34
+
35
+ # By default, don't set an application secret
36
+ DEFAULT_CLIENT_SECRET = nil
37
+
38
+ # By default, don't set an application redirect uri
39
+ DEFAULT_REDIRECT_URI = nil
40
+
41
+ # By default, don't set a user access token
42
+ DEFAULT_ACCESS_TOKEN = nil
43
+
44
+ # The endpoint that will be used to connect if none is set
45
+ #
46
+ # @note There is no reason to use any other endpoint at this time
47
+ DEFAULT_ENDPOINT = 'https://api.douban.com/'.freeze
48
+
49
+ # The response format appended to the path and sent in the 'Accept' header if none is set
50
+ #
51
+ # @note JSON is the only available format at this time
52
+ DEFAULT_FORMAT = :json
53
+
54
+ # By default, don't use a proxy server
55
+ DEFAULT_PROXY = nil
56
+
57
+ # The user agent that will be sent to the API endpoint if none is set
58
+ DEFAULT_USER_AGENT = "Douban Ruby Gem #{Douban::VERSION}".freeze
59
+
60
+ # @private
61
+ attr_accessor *VALID_OPTIONS_KEYS
62
+
63
+ # When this module is extended, set all configuration options to their default values
64
+ def self.extended(base)
65
+ base.reset
66
+ end
67
+
68
+ # Convenience method to allow configuration options to be set in a block
69
+ def configure
70
+ yield self
71
+ end
72
+
73
+ # Create a hash of options and their values
74
+ def options
75
+ VALID_OPTIONS_KEYS.inject({}) do |option, key|
76
+ option.merge!(key => send(key))
77
+ end
78
+ end
79
+
80
+ # Reset all configuration options to defaults
81
+ def reset
82
+ self.adapter = DEFAULT_ADAPTER
83
+ self.client_id = DEFAULT_CLIENT_ID
84
+ self.client_secret = DEFAULT_CLIENT_SECRET
85
+ self.access_token = DEFAULT_ACCESS_TOKEN
86
+ self.endpoint = DEFAULT_ENDPOINT
87
+ self.format = DEFAULT_FORMAT
88
+ self.user_agent = DEFAULT_USER_AGENT
89
+ self.proxy = DEFAULT_PROXY
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,33 @@
1
+ require 'faraday_middleware'
2
+ Dir[File.expand_path('../../faraday/*.rb', __FILE__)].each{|f| require f}
3
+
4
+ module Douban
5
+ # @private
6
+ module Connection
7
+ private
8
+
9
+ def connection(raw=false)
10
+ options = {
11
+ :headers => {'Accept' => "application/#{format}; charset=utf-8", 'User-Agent' => user_agent},
12
+ :proxy => proxy,
13
+ :ssl => {:verify => false},
14
+ :url => endpoint,
15
+ }
16
+
17
+ Faraday::Connection.new(options) do |connection|
18
+ connection.use FaradayMiddleware::OAuth2, client_id, access_token
19
+ connection.use FaradayMiddleware::Mashify unless raw
20
+ connection.use Faraday::Request::Multipart
21
+ connection.use Faraday::Request::UrlEncoded
22
+ connection.use FaradayMiddleware::Mashify unless raw
23
+ unless raw
24
+ case format.to_s.downcase
25
+ when 'json' then connection.use Faraday::Response::ParseJson
26
+ end
27
+ end
28
+ connection.use FaradayMiddleware::RaiseHttpException
29
+ connection.adapter(adapter)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,19 @@
1
+ module Douban
2
+ # Custom error class for rescuing from all Douban errors
3
+ class Error < StandardError; end
4
+
5
+ # Raised when Douban returns the HTTP status code 400
6
+ class BadRequest < Error; end
7
+
8
+ # Raised when Douban returns the HTTP status code 404
9
+ class NotFound < Error; end
10
+
11
+ # Raised when Douban returns the HTTP status code 500
12
+ class InternalServerError < Error; end
13
+
14
+ # Raised when Douban returns the HTTP status code 503
15
+ class ServiceUnavailable < Error; end
16
+
17
+ # Raised when a subscription payload hash is invalid
18
+ class InvalidSignature < Error; end
19
+ end
@@ -0,0 +1,27 @@
1
+ module Douban
2
+ # Defines HTTP request methods
3
+ module OAuth
4
+ # Return URL for OAuth authorization
5
+ def authorize_url(options={})
6
+ options[:response_type] ||= "code"
7
+ params = access_token_params.merge(options)
8
+ connection.build_url("https://www.douban.com/service/auth2/auth", params).to_s
9
+ end
10
+
11
+ # Return an access token from authorization
12
+ def get_access_token(code, options={})
13
+ options[:grant_type] ||= "authorization_code"
14
+ params = access_token_params.merge(options)
15
+ post("https://www.douban.com/service/auth2/token", params.merge(:code => code), raw=false)
16
+ end
17
+
18
+ private
19
+
20
+ def access_token_params
21
+ {
22
+ :client_id => client_id,
23
+ :client_secret => client_secret
24
+ }
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,40 @@
1
+ module Douban
2
+ # Defines HTTP request methods
3
+ module Request
4
+ # Perform an HTTP GET request
5
+ def get(path, options={}, raw=false)
6
+ request(:get, path, options, raw)
7
+ end
8
+
9
+ # Perform an HTTP POST request
10
+ def post(path, options={}, raw=false)
11
+ request(:post, path, options, raw)
12
+ end
13
+
14
+ # Perform an HTTP PUT request
15
+ def put(path, options={}, raw=false)
16
+ request(:put, path, options, raw)
17
+ end
18
+
19
+ # Perform an HTTP DELETE request
20
+ def delete(path, options={}, raw=false)
21
+ request(:delete, path, options, raw)
22
+ end
23
+
24
+ private
25
+
26
+ # Perform an HTTP request
27
+ def request(method, path, options={}, raw=false)
28
+ response = connection(raw).send(method) do |request|
29
+ case method
30
+ when :get, :delete
31
+ request.url(path, options)
32
+ when :post, :put
33
+ request.path = path
34
+ request.body = options unless options.empty?
35
+ end
36
+ end
37
+ raw ? response : response.body
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,3 @@
1
+ module Douban
2
+ VERSION = '0.1.0'.freeze unless defined?(::Douban::VERSION)
3
+ end
data/lib/douban_api.rb ADDED
@@ -0,0 +1,27 @@
1
+ require File.expand_path('../douban_api/error', __FILE__)
2
+ require File.expand_path('../douban_api/configuration', __FILE__)
3
+ require File.expand_path('../douban_api/api', __FILE__)
4
+ require File.expand_path('../douban_api/client', __FILE__)
5
+
6
+ module Douban
7
+ extend Configuration
8
+
9
+ # Alias for Douban::Client.new
10
+ #
11
+ # @return [Douban::Client]
12
+ def self.client(options={})
13
+ Douban::Client.new(options)
14
+ end
15
+
16
+ # Delegate to Douban::Client
17
+ def self.method_missing(method, *args, &block)
18
+ return super unless client.respond_to?(method)
19
+ client.send(method, *args, &block)
20
+ end
21
+
22
+ # Delegate to Douban::Client
23
+ def self.respond_to?(method)
24
+ return client.respond_to?(method) || super
25
+ end
26
+
27
+ end
@@ -0,0 +1,39 @@
1
+ require 'faraday'
2
+
3
+ # @private
4
+ module FaradayMiddleware
5
+ # @private
6
+ class OAuth2 < Faraday::Middleware
7
+ def call(env)
8
+ if env[:method] == :get or env[:method] == :delete
9
+ if env[:url].query.nil?
10
+ query = {}
11
+ else
12
+ query = Faraday::Utils.parse_query(env[:url].query)
13
+ end
14
+ if @access_token and not query["client_secret"]
15
+ # env[:url].query = Faraday::Utils.build_query(query.merge(:access_token => @access_token))
16
+ env[:request_headers] = env[:request_headers].merge('Authorization' => "Bearer #{@access_token}")
17
+ elsif @client_id
18
+ env[:url].query = Faraday::Utils.build_query(query.merge(:appkey => @client_id))
19
+ end
20
+ else
21
+ if @access_token and not env[:body] && env[:body][:client_secret]
22
+ env[:body] = {} if env[:body].nil?
23
+ # env[:body] = env[:body].merge(:access_token => @access_token)
24
+ env[:request_headers] = env[:request_headers].merge('Authorization' => "Bearer #{@access_token}")
25
+ elsif @client_id
26
+ env[:body] = env[:body].merge(:client_id => @client_id)
27
+ end
28
+ end
29
+
30
+ @app.call env
31
+ end
32
+
33
+ def initialize(app, client_id, access_token=nil)
34
+ @app = app
35
+ @client_id = client_id
36
+ @access_token = access_token
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,10 @@
1
+ # monkey patch from @liluo https://gist.github.com/4176272
2
+ class Faraday::Parts::FilePart
3
+ def initialize(boundary, name, io)
4
+ file_length = io.respond_to?(:length) ? io.length : File.size(io.local_path)
5
+ @head = build_head(boundary, name, io.original_filename, io.content_type, file_length,
6
+ io.respond_to?(:opts) ? io.opts : {})
7
+ @length = @head.length + file_length
8
+ @io = CompositeReadIO.new(StringIO.new(@head), io, StringIO.new("\r\n"))
9
+ end
10
+ end
@@ -0,0 +1,52 @@
1
+ require 'faraday'
2
+
3
+ # @private
4
+ module FaradayMiddleware
5
+ # @private
6
+ class RaiseHttpException < Faraday::Middleware
7
+ def call(env)
8
+ @app.call(env).on_complete do |response|
9
+ p response
10
+ case response[:status].to_i
11
+ when 400
12
+ raise Douban::BadRequest, error_message_400(response)
13
+ when 404
14
+ raise Douban::NotFound, error_message_400(response)
15
+ when 500
16
+ raise Douban::InternalServerError, error_message_500(response, "Something is technically wrong.")
17
+ when 503
18
+ raise Douban::ServiceUnavailable, error_message_500(response, "Douban is rate limiting your requests.")
19
+ end
20
+ end
21
+ end
22
+
23
+ def initialize(app)
24
+ super app
25
+ @parser = nil
26
+ end
27
+
28
+ private
29
+
30
+ def error_message_400(response)
31
+ "#{response[:method].to_s.upcase} #{response[:url].to_s}: #{response[:status]}#{error_body(response[:body])}"
32
+ end
33
+
34
+ def error_body(body)
35
+ # body gets passed as a string, not sure if it is passed as something else from other spots?
36
+ if not body.nil? and not body.empty? and body.kind_of?(String)
37
+ # removed multi_json thanks to wesnolte's commit
38
+ body = ::JSON.parse(body)
39
+ end
40
+
41
+ if body.nil?
42
+ nil
43
+ elsif body['meta'] and body['meta']['error_message'] and not body['meta']['error_message'].empty?
44
+ ": #{body['meta']['error_message']}"
45
+ end
46
+ end
47
+
48
+ def error_message_500(response, body=nil)
49
+ "#{response[:method].to_s.upcase} #{response[:url].to_s}: #{[response[:status].to_s + ':', body].compact.join(' ')}"
50
+ end
51
+ end
52
+ end
metadata ADDED
@@ -0,0 +1,268 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: douban_api
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Sean Lee
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-12-05 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: faraday
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0.7'
22
+ - - <
23
+ - !ruby/object:Gem::Version
24
+ version: '0.9'
25
+ type: :runtime
26
+ prerelease: false
27
+ version_requirements: !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0.7'
33
+ - - <
34
+ - !ruby/object:Gem::Version
35
+ version: '0.9'
36
+ - !ruby/object:Gem::Dependency
37
+ name: faraday_middleware
38
+ requirement: !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: '0.8'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: !ruby/object:Gem::Requirement
47
+ none: false
48
+ requirements:
49
+ - - ~>
50
+ - !ruby/object:Gem::Version
51
+ version: '0.8'
52
+ - !ruby/object:Gem::Dependency
53
+ name: multi_json
54
+ requirement: !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ! '>='
58
+ - !ruby/object:Gem::Version
59
+ version: 1.0.3
60
+ - - ~>
61
+ - !ruby/object:Gem::Version
62
+ version: '1.0'
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ! '>='
69
+ - !ruby/object:Gem::Version
70
+ version: 1.0.3
71
+ - - ~>
72
+ - !ruby/object:Gem::Version
73
+ version: '1.0'
74
+ - !ruby/object:Gem::Dependency
75
+ name: hashie
76
+ requirement: !ruby/object:Gem::Requirement
77
+ none: false
78
+ requirements:
79
+ - - ! '>='
80
+ - !ruby/object:Gem::Version
81
+ version: 0.4.0
82
+ type: :runtime
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ none: false
86
+ requirements:
87
+ - - ! '>='
88
+ - !ruby/object:Gem::Version
89
+ version: 0.4.0
90
+ - !ruby/object:Gem::Dependency
91
+ name: json
92
+ requirement: !ruby/object:Gem::Requirement
93
+ none: false
94
+ requirements:
95
+ - - ~>
96
+ - !ruby/object:Gem::Version
97
+ version: '1.7'
98
+ type: :development
99
+ prerelease: false
100
+ version_requirements: !ruby/object:Gem::Requirement
101
+ none: false
102
+ requirements:
103
+ - - ~>
104
+ - !ruby/object:Gem::Version
105
+ version: '1.7'
106
+ - !ruby/object:Gem::Dependency
107
+ name: rspec
108
+ requirement: !ruby/object:Gem::Requirement
109
+ none: false
110
+ requirements:
111
+ - - ~>
112
+ - !ruby/object:Gem::Version
113
+ version: '2.4'
114
+ type: :development
115
+ prerelease: false
116
+ version_requirements: !ruby/object:Gem::Requirement
117
+ none: false
118
+ requirements:
119
+ - - ~>
120
+ - !ruby/object:Gem::Version
121
+ version: '2.4'
122
+ - !ruby/object:Gem::Dependency
123
+ name: webmock
124
+ requirement: !ruby/object:Gem::Requirement
125
+ none: false
126
+ requirements:
127
+ - - ~>
128
+ - !ruby/object:Gem::Version
129
+ version: '1.6'
130
+ type: :development
131
+ prerelease: false
132
+ version_requirements: !ruby/object:Gem::Requirement
133
+ none: false
134
+ requirements:
135
+ - - ~>
136
+ - !ruby/object:Gem::Version
137
+ version: '1.6'
138
+ - !ruby/object:Gem::Dependency
139
+ name: bluecloth
140
+ requirement: !ruby/object:Gem::Requirement
141
+ none: false
142
+ requirements:
143
+ - - ~>
144
+ - !ruby/object:Gem::Version
145
+ version: 2.0.11
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ none: false
150
+ requirements:
151
+ - - ~>
152
+ - !ruby/object:Gem::Version
153
+ version: 2.0.11
154
+ - !ruby/object:Gem::Dependency
155
+ name: rake
156
+ requirement: !ruby/object:Gem::Requirement
157
+ none: false
158
+ requirements:
159
+ - - ! '>='
160
+ - !ruby/object:Gem::Version
161
+ version: '0'
162
+ type: :development
163
+ prerelease: false
164
+ version_requirements: !ruby/object:Gem::Requirement
165
+ none: false
166
+ requirements:
167
+ - - ! '>='
168
+ - !ruby/object:Gem::Version
169
+ version: '0'
170
+ - !ruby/object:Gem::Dependency
171
+ name: yard
172
+ requirement: !ruby/object:Gem::Requirement
173
+ none: false
174
+ requirements:
175
+ - - ! '>='
176
+ - !ruby/object:Gem::Version
177
+ version: '0'
178
+ type: :development
179
+ prerelease: false
180
+ version_requirements: !ruby/object:Gem::Requirement
181
+ none: false
182
+ requirements:
183
+ - - ! '>='
184
+ - !ruby/object:Gem::Version
185
+ version: '0'
186
+ - !ruby/object:Gem::Dependency
187
+ name: pry
188
+ requirement: !ruby/object:Gem::Requirement
189
+ none: false
190
+ requirements:
191
+ - - ! '>='
192
+ - !ruby/object:Gem::Version
193
+ version: '0'
194
+ type: :development
195
+ prerelease: false
196
+ version_requirements: !ruby/object:Gem::Requirement
197
+ none: false
198
+ requirements:
199
+ - - ! '>='
200
+ - !ruby/object:Gem::Version
201
+ version: '0'
202
+ description: A Ruby wrapper for the Douban API v2 based on instagram's official ruby
203
+ gem
204
+ email:
205
+ - iseansay@gmail.com
206
+ executables: []
207
+ extensions: []
208
+ extra_rdoc_files: []
209
+ files:
210
+ - .gitignore
211
+ - Gemfile
212
+ - LICENSE.md
213
+ - Rakefile
214
+ - douban_api.gemspec
215
+ - lib/douban_api.rb
216
+ - lib/douban_api/api.rb
217
+ - lib/douban_api/client.rb
218
+ - lib/douban_api/client/album.rb
219
+ - lib/douban_api/client/book.rb
220
+ - lib/douban_api/client/comment.rb
221
+ - lib/douban_api/client/discussion.rb
222
+ - lib/douban_api/client/doumail.rb
223
+ - lib/douban_api/client/event.rb
224
+ - lib/douban_api/client/movie.rb
225
+ - lib/douban_api/client/music.rb
226
+ - lib/douban_api/client/note.rb
227
+ - lib/douban_api/client/online.rb
228
+ - lib/douban_api/client/shuo.rb
229
+ - lib/douban_api/client/user.rb
230
+ - lib/douban_api/client/utils.rb
231
+ - lib/douban_api/configuration.rb
232
+ - lib/douban_api/connection.rb
233
+ - lib/douban_api/error.rb
234
+ - lib/douban_api/oauth.rb
235
+ - lib/douban_api/request.rb
236
+ - lib/douban_api/version.rb
237
+ - lib/faraday/oauth2.rb
238
+ - lib/faraday/parts/filepart.rb
239
+ - lib/faraday/raise_http_exception.rb
240
+ homepage:
241
+ licenses: []
242
+ post_install_message:
243
+ rdoc_options: []
244
+ require_paths:
245
+ - lib
246
+ required_ruby_version: !ruby/object:Gem::Requirement
247
+ none: false
248
+ requirements:
249
+ - - ! '>='
250
+ - !ruby/object:Gem::Version
251
+ version: '0'
252
+ segments:
253
+ - 0
254
+ hash: 4030718453987531716
255
+ required_rubygems_version: !ruby/object:Gem::Requirement
256
+ none: false
257
+ requirements:
258
+ - - ! '>='
259
+ - !ruby/object:Gem::Version
260
+ version: 1.3.6
261
+ requirements: []
262
+ rubyforge_project:
263
+ rubygems_version: 1.8.24
264
+ signing_key:
265
+ specification_version: 3
266
+ summary: Ruby wrapper for the Douban API v2
267
+ test_files: []
268
+ has_rdoc: