douban-ruby 0.0.8 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour -f nested
@@ -1,24 +1,25 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- douban-ruby (0.0.8)
4
+ douban-ruby (0.0.9)
5
5
  oauth
6
6
 
7
7
  GEM
8
8
  remote: http://rubygems.org/
9
9
  specs:
10
10
  diff-lcs (1.1.2)
11
- oauth (0.4.4)
12
- rake (0.8.7)
11
+ oauth (0.4.5)
12
+ rake (0.9.2)
13
13
  rcov (0.9.9)
14
- rspec (2.3.0)
15
- rspec-core (~> 2.3.0)
16
- rspec-expectations (~> 2.3.0)
17
- rspec-mocks (~> 2.3.0)
18
- rspec-core (2.3.1)
19
- rspec-expectations (2.3.0)
14
+ rdoc (3.8)
15
+ rspec (2.6.0)
16
+ rspec-core (~> 2.6.0)
17
+ rspec-expectations (~> 2.6.0)
18
+ rspec-mocks (~> 2.6.0)
19
+ rspec-core (2.6.4)
20
+ rspec-expectations (2.6.0)
20
21
  diff-lcs (~> 1.1.2)
21
- rspec-mocks (2.3.0)
22
+ rspec-mocks (2.6.0)
22
23
 
23
24
  PLATFORMS
24
25
  ruby
@@ -26,7 +27,7 @@ PLATFORMS
26
27
  DEPENDENCIES
27
28
  bundler (>= 1.0.0)
28
29
  douban-ruby!
29
- oauth
30
30
  rake
31
31
  rcov
32
+ rdoc
32
33
  rspec (~> 2.0)
@@ -1,3 +1,7 @@
1
+ === 0.0.9 / 2011-07-13
2
+ * Douban::Authorize
3
+ * access_token, request_token support :as_hash argument.
4
+
1
5
  === 0.0.8 / 2011-01-02
2
6
  * switch to rspec 2
3
7
  * Douban::Authorize
@@ -38,7 +38,7 @@ douban ruby client. including OAuth support.
38
38
  # step 1, initial state, store request_token to session
39
39
  callback_url = url_for :action => :index
40
40
  redirect_url = douban.get_authorize_url(callback_url)
41
- session[:request_token] = douban.request_token :as_token
41
+ session[:request_token] = douban.request_token :as_hash
42
42
  redirect_to redirect_url
43
43
  else
44
44
  # step 3, have access_token, now you can use douban API
@@ -51,7 +51,7 @@ douban ruby client. including OAuth support.
51
51
  douban.request_token = session[:request_token]
52
52
  douban.auth
53
53
  reset_session
54
- session[:access_token] = douban.access_token :as_token
54
+ session[:access_token] = douban.access_token :as_hash
55
55
  redirect_to :action => :index
56
56
  else
57
57
  # error branch, you return from douban, but no request_token in session
data/Rakefile CHANGED
@@ -2,7 +2,7 @@ require 'rubygems'
2
2
  require 'bundler'
3
3
  Bundler::GemHelper.install_tasks
4
4
  require 'rspec/core/rake_task'
5
- require 'rake/rdoctask'
5
+ require 'rdoc/task'
6
6
  require File.expand_path("../lib/douban/version", __FILE__)
7
7
 
8
8
  ENV["SPEC_OPTS"] ||= "-f nested --color -b"
@@ -22,6 +22,7 @@ Gem::Specification.new do |s|
22
22
  s.add_development_dependency 'rspec', '~> 2.0'
23
23
  s.add_development_dependency 'rake'
24
24
  s.add_development_dependency 'rcov'
25
+ s.add_development_dependency 'rdoc'
25
26
 
26
27
  s.files = `git ls-files`.split("\n")
27
28
  s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
@@ -1,58 +1,58 @@
1
- #=douban.rb
2
- #
3
- # Copyright (c) 2008 Hooopo
4
- # Written and maintained by Hooopo<hoooopo@gmail.com>
5
- #
6
- # Copyright (C) 2010-2011 LI Daobing <lidaobing@gmail.com>
7
- #
8
- #
9
- # This program is free software. You can re-distribute and/or
10
- # modify this program under the same terms of ruby itself ---
11
- # Ruby Distribution License or GNU General Public License.
12
- #
13
- # See Douban::Authorize for an overview and examples.
14
- #
15
- #== Douban API Client Based Ruby
16
- # This is the Douban Ruby library, which communicates to the Douban
17
- # API REST servers and converts Atom into proper Ruby objects.
18
- # In order to use the library, you need to sign up for a Douban account and
19
- # ensure you have the oauth and modules installed.
20
- # Douban account and ApiKey,SecrectKey
21
- # To get a Douban account,you need to sign up at:
22
- # http://www.douban.com/register
23
- # Once approved, you will need to create an application to get your api key and
24
- # secret key at :
25
- # http://www.douban.com/service/apikey/
26
- # You can get apikey and secrectkey ,if you create an application.
27
- # The keys can be placed into douban.yaml or specified at runtime
28
- # when you new a Douban::Authorize.
29
- # Depencencies
30
- # This library has some external dependencies:
31
- # oauth ( http://oauth.googlecode.com/svn/code/ruby )
32
- # You can install oauth via rubygem:
33
- # Gem install oauth -y
34
- #==Usage
35
- # require "douban"
36
- # client=Douban.authorize(apikey, secret)
37
- # authorize_url=client.get_authorize_url
38
- # NOTE:
39
- # Permission to access a user's data is restricted -- you can't just access
40
- # any user's data. In order to access a user's data, they need to have
41
- # visit the authorize_url and press the 'agree' button .
42
- # when user has press the 'agree' button ,we use client.auth to authorize.
43
- # client=client.auth
44
- # puts client.authoried? #return true or false
45
- # Get users' info via his uid
46
- # #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
47
- # people = client.get_people('hooopo')
48
- # people.class === People
49
- # people.title === hooopo
50
- # people.location === "长沙"
51
- #==Install and Wiki
52
- # http://code.google.com/p/doubanclient-ruby/
53
-
54
-
55
- module Douban
56
- autoload :Authorize, 'douban/authorize'
57
- end
58
- require 'douban/version'
1
+ #=douban.rb
2
+ #
3
+ # Copyright (c) 2008 Hooopo
4
+ # Written and maintained by Hooopo<hoooopo@gmail.com>
5
+ #
6
+ # Copyright (C) 2010-2011 LI Daobing <lidaobing@gmail.com>
7
+ #
8
+ #
9
+ # This program is free software. You can re-distribute and/or
10
+ # modify this program under the same terms of ruby itself ---
11
+ # Ruby Distribution License or GNU General Public License.
12
+ #
13
+ # See Douban::Authorize for an overview and examples.
14
+ #
15
+ #== Douban API Client Based Ruby
16
+ # This is the Douban Ruby library, which communicates to the Douban
17
+ # API REST servers and converts Atom into proper Ruby objects.
18
+ # In order to use the library, you need to sign up for a Douban account and
19
+ # ensure you have the oauth and modules installed.
20
+ # Douban account and ApiKey,SecrectKey
21
+ # To get a Douban account,you need to sign up at:
22
+ # http://www.douban.com/register
23
+ # Once approved, you will need to create an application to get your api key and
24
+ # secret key at :
25
+ # http://www.douban.com/service/apikey/
26
+ # You can get apikey and secrectkey ,if you create an application.
27
+ # The keys can be placed into douban.yaml or specified at runtime
28
+ # when you new a Douban::Authorize.
29
+ # Depencencies
30
+ # This library has some external dependencies:
31
+ # oauth ( http://oauth.googlecode.com/svn/code/ruby )
32
+ # You can install oauth via rubygem:
33
+ # Gem install oauth -y
34
+ #==Usage
35
+ # require "douban"
36
+ # client=Douban.authorize(apikey, secret)
37
+ # authorize_url=client.get_authorize_url
38
+ # NOTE:
39
+ # Permission to access a user's data is restricted -- you can't just access
40
+ # any user's data. In order to access a user's data, they need to have
41
+ # visit the authorize_url and press the 'agree' button .
42
+ # when user has press the 'agree' button ,we use client.auth to authorize.
43
+ # client=client.auth
44
+ # puts client.authoried? #return true or false
45
+ # Get users' info via his uid
46
+ # #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
47
+ # people = client.get_people('hooopo')
48
+ # people.class === People
49
+ # people.title === hooopo
50
+ # people.location === "长沙"
51
+ #==Install and Wiki
52
+ # http://code.google.com/p/doubanclient-ruby/
53
+
54
+
55
+ module Douban
56
+ autoload :Authorize, 'douban/authorize'
57
+ end
58
+ require 'douban/version'
@@ -1,1369 +1,1400 @@
1
- require 'oauth'
2
- require 'oauth/consumer'
3
- require 'rexml/document'
4
- require 'net/http'
5
-
6
- require 'douban/author'
7
- require 'douban/collection'
8
- require 'douban/event'
9
- require 'douban/mail'
10
- require 'douban/miniblog'
11
- require 'douban/miniblog_comments'
12
- require 'douban/note'
13
- require 'douban/people'
14
- require 'douban/recommendation'
15
- require 'douban/recommendation_comment'
16
- require 'douban/review'
17
- require 'douban/subject'
18
- require 'douban/tag'
19
-
20
- module Douban
21
- class Authorize
22
- attr_reader :api_key
23
- attr_reader :api_secret
24
- attr_reader :authorize_url
25
- attr_reader :consumer
26
-
27
- @@debug = false
28
-
29
- @@default_oauth_request_options = {
30
- :signature_method=>"HMAC-SHA1",
31
- :site=>"http://www.douban.com",
32
- :request_token_path=>"/service/auth/request_token",
33
- :access_token_path=>"/service/auth/access_token",
34
- :authorize_path=>"/service/auth/authorize",
35
- :scheme=>:header,
36
- :realm=>"http://www.example.com/"
37
- }
38
-
39
- @@default_oauth_access_options = {
40
- :site=>"http://api.douban.com",
41
- :scheme=>:header,
42
- :signature_method=>"HMAC-SHA1",
43
- :realm=>"http://www.example.com/"
44
- }
45
-
46
-
47
- def self.debug=(val)
48
- @@debug = val
49
- end
50
-
51
- def initialize(api_key, secret_key, options={})
52
- @api_key=api_key
53
- @secret_key=secret_key
54
- @oauth_request_option = @@default_oauth_request_options.merge(options)
55
- @oauth_access_option = @@default_oauth_access_options.merge(options)
56
- yield self if block_given?
57
- self
58
- end
59
-
60
- def authorized?
61
- ! @access_token.nil?
62
- end
63
-
64
- def get_authorize_url(oauth_callback=nil)
65
- oauth_callback ||= @oauth_request_option[:realm]
66
-
67
- @consumer=new_request_consumer
68
- @request_token=@consumer.get_request_token
69
- @authorize_url="#{@request_token.authorize_url}&oauth_callback=#{CGI.escape(oauth_callback)}"
70
- yield @request_token if block_given?
71
- @authorize_url
72
- end
73
-
74
- def auth
75
- begin
76
- @access_token=@request_token.get_access_token
77
- @access_token=OAuth::AccessToken.new(new_access_consumer,
78
- @access_token.token,
79
- @access_token.secret
80
- )
81
- rescue
82
- #raise $!
83
- ensure
84
- yield self if block_given?
85
- return self
86
- end
87
- end
88
- def get_people(uid="@me")
89
- resp=get("/people/#{u uid}")
90
- if resp.code=="200"
91
- atom=resp.body
92
- People.new(atom)
93
- else
94
- debug(resp)
95
- end
96
- end
97
-
98
- def get_friends(uid="@me",option={:start_index=>1,:max_results=>10})
99
- resp=get("/people/#{u uid.to_s}/friends?start-index=#{u option[:start_index]}&max-results=#{u option[:max_results]}")
100
- if resp.code=="200"
101
- friends=[]
102
- atom=resp.body
103
- doc=REXML::Document.new(atom)
104
- REXML::XPath.each(doc,"//entry") do |entry|
105
- friends << People.new(entry)
106
- end
107
- friends
108
- else
109
- debug(resp)
110
- end
111
- end
112
-
113
- def get_contacts(uid="@me",option={:start_index=>1,:max_results=>10})
114
- resp=get("/people/#{u uid.to_s}/contacts?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
115
- if resp.code=="200"
116
- contacts=[]
117
- atom=resp.body
118
- doc=REXML::Document.new(atom)
119
- REXML::XPath.each(doc,"//entry") do |entry|
120
- contacts << People.new(entry)
121
- end
122
- contacts
123
- else
124
- nil
125
- end
126
- end
127
-
128
-
129
- def search_people(q="",option={:start_index=>1,:max_results=>10})
130
- resp=get("/people?q=#{u(q.to_s)}&start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
131
- if resp.code=="200"
132
- results=[]
133
- atom=resp.body
134
- doc=REXML::Document.new(atom)
135
- REXML::XPath.each(doc,"//entry") do |entry|
136
- results << People.new(entry)
137
- end
138
- results
139
- else
140
- nil
141
- end
142
- end
143
-
144
- # 获取书籍信息
145
- # http://goo.gl/HaG5
146
- #
147
- # get_book(:isbn => isbn) => Book
148
- # get_book(:id => id) => Book
149
- # get_book(id) => Book
150
- # get_book(Book) => Book (refresh Book)
151
- def get_book(id)
152
- resp = case id
153
- when Book
154
- get("/book/subject/#{u id.subject_id}")
155
- when Hash
156
- if id[:isbn]
157
- get("/book/subject/isbn/#{u id[:isbn]}")
158
- elsif id[:id]
159
- get("/book/subject/#{u id[:id]}")
160
- else
161
- raise "Hash only support :isbn or :id"
162
- end
163
- else
164
- get("/book/subject/#{u id}")
165
- end
166
-
167
- if resp.code=="200"
168
- atom=resp.body
169
- Book.new(atom)
170
- else
171
- nil
172
- end
173
- end
174
-
175
- # 获取电影信息
176
- # http://goo.gl/2fZ4
177
- #
178
- # get_movie(:imdb => imdb) => Movie
179
- # get_movie(:id => id) => Movie
180
- # get_movie(id) => Movie
181
- # get_movie(Movie) => Movie (refresh Movie)
182
- def get_movie(id)
183
- resp = case id
184
- when Movie
185
- get("/movie/subject/#{u id.subject_id}")
186
- when Hash
187
- if id[:imdb]
188
- get("/movie/subject/imdb/#{u id[:imdb]}")
189
- elsif id[:id]
190
- get("/movie/subject/#{u id[:id]}")
191
- else
192
- raise "Hash only support :imdb or :id"
193
- end
194
- else
195
- get("/movie/subject/#{u id}")
196
- end
197
-
198
- if resp.code=="200"
199
- atom=resp.body
200
- Movie.new(atom)
201
- else
202
- nil
203
- end
204
- end
205
- def get_music(id=nil)
206
- resp=get("/music/subject/#{u(id.to_s)}")
207
- if resp.code=="200"
208
- atom=resp.body
209
- Music.new(atom)
210
- else
211
- nil
212
- end
213
- end
214
-
215
- # :call-seq:
216
- # search_book(:q => "search word") => [Book] or nil
217
- # search_book(:tag => "tag name") => [Book] or nil
218
- # search_book("search word") => [Book] or nil
219
- #
220
- # 搜索书籍
221
- #
222
- # http://goo.gl/rYDf
223
- #
224
- # * option
225
- # * q: query string
226
- # * tag:
227
- # * start_index:
228
- # * max_results:
229
- def search_book(*args)
230
- url = _subject_search_args_to_url(:book, *args)
231
-
232
- resp=get(url)
233
- if resp.code=="200"
234
- atom=resp.body
235
- doc=REXML::Document.new(atom)
236
- books=[]
237
- REXML::XPath.each(doc,"//entry") do |entry|
238
- books << Book.new(entry)
239
- end
240
- books
241
- else
242
- nil
243
- end
244
- end
245
-
246
- def search_movie(*args)
247
- url = _subject_search_args_to_url(:movie, *args)
248
-
249
- resp=get(url)
250
- if resp.code=="200"
251
- atom=resp.body
252
- doc=REXML::Document.new(atom)
253
- movies=[]
254
- REXML::XPath.each(doc,"//entry") do |entry|
255
- movies << Movie.new(entry)
256
- end
257
- movies
258
- else
259
- nil
260
- end
261
- end
262
- def search_music(tag="",option={:start_index=>1,:max_results=>10})
263
- url = _subject_search_args_to_url(:music, *args)
264
-
265
- resp=get(url)
266
- if resp.code=="200"
267
- atom=resp.body
268
- doc=REXML::Document.new(atom)
269
- music=[]
270
- REXML::XPath.each(doc,"//entry") do |entry|
271
- music << Music.new(entry)
272
- end
273
- music
274
- else
275
- nil
276
- end
277
- end
278
- def get_review(id="")
279
- resp=get("/review/#{u(id.to_s)}")
280
- if resp.code=="200"
281
- atom=resp.body
282
- Review.new(atom)
283
- else
284
- nil
285
- end
286
- end
287
- def get_user_reviews(user_id="@me",option={:start_index=>1,:max_results=>10,:orderby=>'score'})
288
- resp=get("/people/#{u(user_id.to_s)}/reviews?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}&orderby=#{option[:orderby]}")
289
- if resp.code=="200"
290
- atom=resp.body
291
- reviews=[]
292
- doc=REXML::Document.new(atom)
293
- REXML::XPath.each(doc,"//entry") do |entry|
294
- reviews<< Review.new(entry)
295
- end
296
- reviews
297
- else
298
- debug(resp)
299
- end
300
- end
301
- def get_movie_reviews(subject_id="",option={:start_index=>1,:max_results=>10,:orderby=>'score'})
302
- resp=get("/movie/subject/#{u(subject_id.to_s)}/reviews?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}&orderby=#{option[:orderby]}")
303
- if resp.code=="200"
304
- atom=resp.body
305
- reviews=[]
306
- doc=REXML::Document.new(atom)
307
- REXML::XPath.each(doc,"//entry") do |entry|
308
-
309
- reviews<< Review.new(entry)
310
- end
311
- reviews
312
- else
313
- nil
314
- end
315
- end
316
- def get_music_reviews(subject_id="",option={:start_index=>1,:max_results=>10,:orderby=>'score'})
317
- resp=get("/music/subject/#{u(subject_id.to_s)}/reviews?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}&orderby=#{option[:orderby]}")
318
- if resp.code=="200"
319
- atom=resp.body
320
- reviews=[]
321
- doc=REXML::Document.new(atom)
322
- REXML::XPath.each(doc,"//entry") do |entry|
323
- reviews<< Review.new(entry)
324
- end
325
- reviews
326
- else
327
- nil
328
- end
329
- end
330
- def get_book_reviews(subject_id="",option={:start_index=>1,:max_results=>10,:orderby=>'score'})
331
- resp=get("/book/subject/#{u(subject_id.to_s)}/reviews?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}&orderby=#{option[:orderby]}")
332
- if resp.code=="200"
333
- atom=resp.body
334
- reviews=[]
335
- doc=REXML::Document.new(atom)
336
- REXML::XPath.each(doc,"//entry") do |entry|
337
- reviews<< Review.new(entry)
338
- end
339
- reviews
340
- else
341
- nil
342
- end
343
- end
344
-
345
- def delete_review(review)
346
- review_id = case review
347
- when Review then review.review_id
348
- else review
349
- end
350
-
351
- resp=delete("/review/#{u(review_id.to_s)}")
352
- if resp.code=="200"
353
- true
354
- else
355
- false
356
- end
357
- end
358
-
359
- def create_review(subject_link="",title="",content="",rating=5)
360
- subject_link = subject_link.id if subject_link.kind_of?(Subject)
361
-
362
- entry=%Q{<?xml version='1.0' encoding='UTF-8'?>
363
- <entry xmlns:ns0="http://www.w3.org/2005/Atom">
364
- <db:subject xmlns:db="http://www.douban.com/xmlns/">
365
- <id>#{h subject_link}</id>
366
- </db:subject>
367
- <content>#{h content}</content>
368
- <gd:rating xmlns:gd="http://schemas.google.com/g/2005" value="#{h rating}" ></gd:rating>
369
- <title>#{h title}</title>
370
- </entry>
371
- }
372
- resp=post("/reviews",entry,{"Content-Type" => "application/atom+xml"})
373
- if resp.code=="201"
374
- Review.new(resp.body)
375
- else
376
- debug(resp)
377
- end
378
- end
379
-
380
- def modify_review(review, subject_link=nil,title="",content="",rating=5)
381
- review_id = case review
382
- when Review then review.review_id
383
- else review
384
- end
385
-
386
- subject_link = review.subject.id if subject_link.nil? and review.kind_of?(Review)
387
-
388
- entry=%Q{<?xml version='1.0' encoding='UTF-8'?>
389
- <entry xmlns:ns0="http://www.w3.org/2005/Atom">
390
- <id>http://api.douban.com/review/#{h review_id}</id>
391
- <db:subject xmlns:db="http://www.douban.com/xmlns/">
392
- <id>#{h subject_link}</id>
393
- </db:subject>
394
- <content>#{h content}</content>
395
- <gd:rating xmlns:gd="http://schemas.google.com/g/2005" value="#{h rating}" ></gd:rating>
396
- <title>#{h title}</title>
397
- </entry>
398
- }
399
- resp=put("/review/#{u(review_id)}",entry,{"Content-Type" => "application/atom+xml"})
400
- if resp.code=="202"
401
- Review.new(resp.body)
402
- else
403
- debug(resp)
404
- end
405
- end
406
- def get_collection(collection_id="")
407
- resp=get("/collection/#{u(collection_id.to_s)}")
408
- if resp.code=="200"
409
- atom=resp.body
410
- Collection.new(atom)
411
- else
412
- nil
413
- end
414
- end
415
- def get_user_collection(
416
- user_id="@me",
417
- option={
418
- :cat=>'',
419
- :tag=>'',
420
- :status=>'',
421
- :start_index=>1,
422
- :max_results=>10,
423
- :updated_max=>'',
424
- :updated_min=>''
425
- }
426
- )
427
- resp=get("/people/#{u(user_id.to_s)}/collection?cat=#{option[:cat]}&tag=#{option[:tag]}&status=#{option[:status]}&start-index=#{option[:start_index]}&max-results=#{option[:max_results]}&updated-max=#{option[:updated_max]}&updated-min=#{option[:updated_min]}")
428
- if resp.code=="200"
429
- atom=resp.body
430
- doc=REXML::Document.new(atom)
431
- author=REXML::XPath.first(doc,"//feed/author")
432
- author=Author.new(author.to_s) if author
433
- title=REXML::XPath.first(doc,"//feed/title")
434
- title=title.text if title
435
- collections=[]
436
- REXML::XPath.each(doc,"//entry") do |entry|
437
- collection=Collection.new(entry)
438
- collection.author=author
439
- collection.title=title
440
- collections<< collection
441
- end
442
- collections
443
- else
444
- nil
445
- end
446
- end
447
- def create_collection( subject_id="",content="",rating=5,status="",tag=[],option={ :privacy=>"public"})
448
- db_tag=""
449
- if tag.size==0
450
- db_tag='<db:tag name="" />'
451
- else
452
- tag.each do |t|
453
- db_tag+=%Q{<db:tag name="#{h t}" />}
454
- end
455
- end
456
- entry=%Q{<?xml version='1.0' encoding='UTF-8'?>
457
- <entry xmlns:ns0="http://www.w3.org/2005/Atom" xmlns:db="http://www.douban.com/xmlns/">
458
- <db:status>#{h status}</db:status>
459
- #{db_tag}
460
- <gd:rating xmlns:gd="http://schemas.google.com/g/2005" value="#{h rating}" />
461
- <content>#{h content}</content>
462
- <db:subject>
463
- <id>#{h subject_id}</id>
464
- </db:subject>
465
- <db:attribute name="privacy">#{h option[:privacy]}</db:attribute>
466
- </entry>
467
- }
468
- resp=post("/collection",entry,{"Content-Type"=>"application/atom+xml"})
469
- if resp.code=="201"
470
- Collection.new(resp.body)
471
- else
472
- debug(resp)
473
- end
474
- end
475
-
476
- def modify_collection(collection, subject_id="",content="",rating=5,status="",tag=[],option={:privacy=>"public"})
477
- collection_id = case collection
478
- when Collection then collection.collection_id
479
- else collection
480
- end
481
-
482
- subject_id = collection.subject.id if subject_id.nil? and collection.kind_of?(Collection)
483
-
484
- db_tag=""
485
- if tag.size==0
486
- db_tag='<db:tag name="" />'
487
- else
488
- tag.each do |t|
489
- db_tag+=%Q{<db:tag name="#{h t}" />}
490
- end
491
- end
492
- entry=%Q{<?xml version='1.0' encoding='UTF-8'?>
493
- <entry xmlns:ns0="http://www.w3.org/2005/Atom" xmlns:db="http://www.douban.com/xmlns/">
494
- <id>http://api.douban.com/collection/#{h collection_id}</id>
495
- <db:status>#{h status}</db:status>
496
-
497
- #{db_tag}
498
- <gd:rating xmlns:gd="http://schemas.google.com/g/2005" value="#{h rating}" />
499
- <content>#{h content}</content>
500
- <db:subject>
501
- <id>#{h subject_id}</id>
502
- </db:subject>
503
- <db:attribute name="privacy">#{h option[:privacy]}</db:attribute>
504
- </entry>
505
- }
506
- resp=put("/collection/#{u collection_id}",entry,{"Content-Type"=>"application/atom+xml"})
507
- # resp.code should be 202, but currently it's 200
508
- # http://www.douban.com/group/topic/12451628/
509
- if resp.code=="200" or resp.code == "202"
510
- Collection.new(resp.body)
511
- else
512
- debug(resp)
513
- end
514
- end
515
- def delete_collection(collection)
516
- collection_id = case collection
517
- when Collection then collection.collection_id
518
- else collection
519
- end
520
-
521
- resp=delete("/collection/#{u(collection_id.to_s)}")
522
- if resp.code=="200"
523
- true
524
- else
525
- false
526
- end
527
- end
528
- def get_user_miniblog(user_id="@me",option={:start_index=>1,:max_results=>10})
529
- resp=get("/people/#{u(user_id.to_s)}/miniblog?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
530
- if resp.code=="200"
531
- atom=resp.body
532
- doc=REXML::Document.new(atom)
533
- author=REXML::XPath.first(doc,"//feed/author")
534
- author=Author.new(author.to_s) if author
535
- miniblogs=[]
536
- REXML::XPath.each(doc,"//feed/entry") do |entry|
537
- miniblog=Miniblog.new(entry)
538
- miniblog.author=author
539
- miniblogs<< miniblog
540
- end
541
- miniblogs
542
- else
543
- nil
544
- end
545
- end
546
- def get_user_contact_miniblog(user_id="@me",option={:start_index=>1,:max_results=>10})
547
- resp=get("/people/#{u(user_id.to_s)}/miniblog/contacts?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
548
- if resp.code=="200"
549
- atom=resp.body
550
- doc=REXML::Document.new(atom)
551
- miniblogs=[]
552
- REXML::XPath.each(doc,"//feed/entry") do |entry|
553
- miniblog=Miniblog.new(entry)
554
- miniblogs<< miniblog
555
- end
556
- miniblogs
557
- else
558
- nil
559
- end
560
- end
561
- def create_miniblog(content="")
562
- entry=%Q{<?xml version='1.0' encoding='UTF-8'?>
563
- <entry xmlns:ns0="http://www.w3.org/2005/Atom" xmlns:db="http://www.douban.com/xmlns/">
564
- <content>#{h content}</content>
565
- </entry>}
566
- resp=post("/miniblog/saying",entry,{"Content-Type"=>"application/atom+xml"})
567
- if resp.code=="201"
568
- Miniblog.new(resp.body)
569
- else
570
- nil
571
- end
572
- end
573
- def delete_miniblog(miniblog_id="")
574
- resp=delete("/miniblog/#{u(miniblog_id.to_s)}")
575
- if resp.code=="200"
576
- true
577
- else
578
- false
579
- end
580
- end
581
-
582
- # :call-seq:
583
- # get_miniblog_comments(aMiniblog, options) => MiniblogComments or nil
584
- # get_miniblog_comments(miniblog_id, options) => MiniblogComments or nil
585
- #
586
- # 获取我说回复 (http://goo.gl/nTZK)
587
- def get_miniblog_comments(miniblog, options={})
588
- miniblog_id = case miniblog
589
- when Miniblog then miniblog.miniblog_id
590
- else miniblog
591
- end
592
-
593
- url = "/miniblog/#{u miniblog_id}/comments?"
594
- if options[:start_index]
595
- url << "start-index=#{u options[:start_index]}&"
596
- end
597
- if options[:max_results]
598
- url << "max-results=#{u options[:max_results]}&"
599
- end
600
-
601
- url = url[0..-2]
602
-
603
- resp = get(url)
604
- if resp.code == "200"
605
- MiniblogComments.new(resp.body)
606
- else
607
- debug(resp)
608
- end
609
- end
610
-
611
- # :call-seq:
612
- # create_miniblog_comment(aMiniblog, aString) => MiniblogComment or nil
613
- # create_miniblog_comment(obj, aString) => MiniblogComment or nil
614
- #
615
- # 回应我说 (http://goo.gl/j43Z)
616
- def create_miniblog_comment(miniblog, content)
617
- miniblog_id = case miniblog
618
- when Miniblog then miniblog.miniblog_id
619
- else miniblog
620
- end
621
- entry = %Q{<?xml version='1.0' encoding='UTF-8'?>
622
- <entry>
623
- <content>#{h content}</content>
624
- </entry>}
625
-
626
- resp = post("/miniblog/#{u miniblog_id}/comments", entry)
627
- if resp.code == "201"
628
- MiniblogComment.new(resp.body)
629
- else
630
- debug(resp)
631
- end
632
- end
633
-
634
- # :call-seq:
635
- # delete_miniblog_comment(aMiniblogComment) => true or false
636
- # delete_miniblog_comment(miniblog_id, comment_id) => true or false
637
- # 删除我说
638
- def delete_miniblog_comment(*args)
639
- if args.size == 1 and args[0].kind_of?(MiniblogComment)
640
- miniblog_id = args[0].miniblog_id
641
- comment_id = args[0].comment_id
642
- elsif args.size == 2
643
- miniblog_id = args[0].to_s
644
- comment_id = args[1].to_s
645
- else
646
- raise "unsupported argument error"
647
- end
648
-
649
- resp = delete("/miniblog/#{u miniblog_id}/comment/#{u comment_id}")
650
- if resp.code == "200"
651
- true
652
- else
653
- debug(resp, false)
654
- end
655
- end
656
-
657
- def get_note(note_id="")
658
- resp=get("/note/#{u(note_id.to_s)}")
659
- if resp.code=="200"
660
- atom=resp.body
661
- Note.new(atom)
662
- else
663
- nil
664
- end
665
- end
666
-
667
- def get_user_notes(user_id="@me",option={:start_index=>1,:max_results=>10})
668
- resp=get("/people/#{u(user_id.to_s)}/notes?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
669
- if resp.code=="200"
670
- atom=resp.body
671
- doc=REXML::Document.new(atom)
672
- author=REXML::XPath.first(doc,"//feed/author")
673
- author=Author.new(author.to_s) if author
674
- notes=[]
675
- REXML::XPath.each(doc,"//feed/entry") do |entry|
676
- note=Note.new(entry)
677
- note.author=author
678
- notes << note
679
- end
680
- notes
681
- else
682
- nil
683
- end
684
- end
685
-
686
- def create_note(title="",content="",option={:privacy=>"public",:can_reply=>"yes"})
687
- entry=%Q{<?xml version="1.0" encoding="UTF-8"?>
688
- <entry xmlns="http://www.w3.org/2005/Atom" xmlns:db="http://www.douban.com/xmlns/">
689
- <title>#{h title}</title>
690
- <content>#{h content}</content>
691
- <db:attribute name="privacy">#{h option[:privacy]}</db:attribute>
692
- <db:attribute name="can_reply">#{h option[:can_reply]}</db:attribute>
693
- </entry>
694
- }
695
- resp=post("/notes",entry,{"Content-Type"=>"application/atom+xml"})
696
- if resp.code=="201"
697
- Note.new(resp.body)
698
- else
699
- debug(resp)
700
- end
701
- end
702
-
703
- def delete_note(note_id="")
704
- note_id = note_id.note_id if note_id.kind_of?(Note)
705
-
706
- resp=delete("/note/#{u(note_id.to_s)}")
707
- if resp.code=="200"
708
- true
709
- else
710
- false
711
- end
712
- end
713
-
714
- def modify_note(note,title="",content="",option={:privacy=>"public",:can_reply=>"yes"})
715
- note_id = case note
716
- when Note then note.note_id
717
- else note
718
- end
719
-
720
- entry=%Q{<?xml version="1.0" encoding="UTF-8"?>
721
- <entry xmlns="http://www.w3.org/2005/Atom" xmlns:db="http://www.douban.com/xmlns/">
722
- <title>#{h title}</title>
723
- <content>#{h content}</content>
724
- <db:attribute name="privacy">#{h option[:privacy]}</db:attribute>
725
- <db:attribute name="can_reply">#{h option[:can_reply]}</db:attribute>
726
- </entry>
727
- }
728
- resp=put("/note/#{u(note_id.to_s)}",entry,{"Content-Type"=>"application/atom+xml"})
729
- if resp.code=="202"
730
- Note.new(resp.body)
731
- else
732
- debug(resp)
733
- end
734
- end
735
- def get_event(event_id="")
736
- resp=get("/event/#{u(event_id.to_s)}")
737
- if resp.code=="200"
738
- atom=resp.body
739
- Event.new(atom)
740
- else
741
- nil
742
- end
743
- end
744
-
745
- # <b>DEPRECATED:</b> Please use <tt>get_event_participant_people</tt> instead.
746
- def get_participant_people(*args)
747
- warn "[DEPRECATION] `get_participant_people` is deprecated. Please use `get_event_participant_people` instead."
748
- get_event_participant_people(*args)
749
- end
750
-
751
- def get_event_participant_people(event_id=nil,option={:start_index=>1,:max_results=>10})
752
- resp=get("/event/#{u(event_id.to_s)}/participants?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
753
- if resp.code=="200"
754
- people=[]
755
- atom=resp.body
756
- doc=REXML::Document.new(atom)
757
- REXML::XPath.each(doc,"//feed/entry") do |entry|
758
- people<< People.new(entry)
759
- end
760
- people
761
- else
762
- nil
763
- end
764
- end
765
-
766
- # <b>DEPRECATED:</b> Please use <tt>get_event_wisher_people</tt> instead.
767
- def get_wisher_people(*args)
768
- warn "[DEPRECATION] `get_wisher_people` is deprecated. Please use `get_event_wisher_people` instead."
769
- get_event_wisher_people(*args)
770
- end
771
-
772
- def get_event_wisher_people(event_id=nil,option={:start_index=>1,:max_results=>10})
773
- resp=get("/event/#{u(event_id.to_s)}/wishers?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
774
- if resp.code=="200"
775
- people=[]
776
- atom=resp.body
777
- doc=REXML::Document.new(atom)
778
- REXML::XPath.each(doc,"//feed/entry") do |entry|
779
- people<< People.new(entry)
780
- end
781
- people
782
- else
783
- nil
784
- end
785
- end
786
- def get_user_events(user_id="@me",option={:start_index=>1,:max_results=>10})
787
- resp=get("/people/#{u(user_id.to_s)}/events?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
788
- if resp.code=="200"
789
- events=[]
790
- atom=resp.body
791
- doc=REXML::Document.new(atom)
792
- REXML::XPath.each(doc,"//feed/entry") do |entry|
793
- events<< Event.new(entry)
794
- end
795
- events
796
- else
797
- nil
798
- end
799
- end
800
- def get_user_initiate_events(user_id="@me",option={:start_index=>1,:max_results=>10})
801
- resp=get("/people/#{u(user_id.to_s)}/events/initiate?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
802
- if resp.code=="200"
803
- events=[]
804
- atom=resp.body
805
- doc=REXML::Document.new(atom)
806
- REXML::XPath.each(doc,"//feed/entry") do |entry|
807
- events<< Event.new(entry)
808
- end
809
- events
810
- else
811
- nil
812
- end
813
- end
814
- def get_user_participate_events(user_id="@me",option={:start_index=>1,:max_results=>10})
815
- resp=get("/people/#{u(user_id.to_s)}/events/participate?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
816
- if resp.code=="200"
817
- events=[]
818
- atom=resp.body
819
- doc=REXML::Document.new(atom)
820
- REXML::XPath.each(doc,"//feed/entry") do |entry|
821
- events<< Event.new(entry)
822
- end
823
- events
824
- else
825
- nil
826
- end
827
- end
828
- def get_user_wish_events(user_id="@me",option={:start_index=>1,:max_results=>10})
829
- resp=get("/people/#{u(user_id.to_s)}/events/wish?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
830
- if resp.code=="200"
831
- events=[]
832
- atom=resp.body
833
- doc=REXML::Document.new(atom)
834
- REXML::XPath.each(doc,"//feed/entry") do |entry|
835
- events<< Event.new(entry)
836
- end
837
- events
838
- else
839
- nil
840
- end
841
- end
842
-
843
- def get_city_events(location_id=nil,option={:type=>"all",:start_index=>1,:max_results=>10})
844
- resp=get("/event/location/#{u(location_id.to_s)}?type=#{option[:type]}&start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
845
- if resp.code=="200"
846
- events=[]
847
- atom=resp.body
848
- doc=REXML::Document.new(atom)
849
- REXML::XPath.each(doc,"//feed/entry") do |entry|
850
- events<< Event.new(entry)
851
- end
852
- events
853
- else
854
- nil
855
- end
856
- end
857
- def search_events(q="",option={:location=>"all",:start_index=>1,:max_results=>10})
858
- resp=get("/events?q=#{u(q.to_s)}&location=#{option[:location]}&start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
859
- if resp.code=="200"
860
- events=[]
861
- atom=resp.body
862
- doc=REXML::Document.new(atom)
863
- REXML::XPath.each(doc,"//feed/entry") do |entry|
864
- events<< Event.new(entry)
865
- end
866
- events
867
- else
868
- nil
869
- end
870
- end
871
- def create_event(title="",content="",where="",option={:kind=>"party",:invite_only=>"no",:can_invite=>"yes",:when=>{"endTime"=>(Time.now+60*60*24*5).strftime("%Y-%m-%dT%H:%M:%S+08:00"),"startTime"=>Time.now.strftime("%Y-%m-%dT%H:%M:%S+08:00")}})
872
- entry=%Q{<?xml version="1.0" encoding="UTF-8"?>
873
- <entry xmlns="http://www.w3.org/2005/Atom" xmlns:db="http://www.douban.com/xmlns/" xmlns:gd="http://schemas.google.com/g/2005" xmlns:opensearch="http://a9.com/-/spec/opensearchrss/1.0/">
874
- <title>#{h title}</title>
875
- <category scheme="http://www.douban.com/2007#kind" term="http://www.douban.com/2007#event.#{option[:kind]}"/>
876
- <content>#{h content}</content>
877
- <db:attribute name="invite_only">#{h option[:invite_only]}</db:attribute>
878
- <db:attribute name="can_invite">#{h option[:can_invite]}</db:attribute>
879
- <gd:when endTime="#{h option[:when]["endTime"]}" startTime="#{h option[:when]["startTime"]}"/>
880
- <gd:where valueString="#{h where}" />
881
- </entry>
882
- }
883
- resp=post("/events",entry,{"Content-Type"=>"application/atom+xml"})
884
- if resp.code=="201"
885
- Event.new(resp.body)
886
- else
887
- debug(resp)
888
- end
889
- end
890
-
891
- def modify_event(event,title=nil,content=nil,where=nil,option=nil)
892
-
893
- event_id = case event
894
- when Event then event.event_id
895
- else event
896
- end
897
-
898
- option = {} if option.nil?
899
- option = {:kind=>"exhibit",
900
- :invite_only=>"no",
901
- :can_invite=>"yes",
902
- :when=>{"endTime"=>(Time.now+60*60*24*5).strftime("%Y-%m-%dT%H:%M:%S+08:00"),
903
- "startTime"=>Time.now.strftime("%Y-%m-%dT%H:%M:%S+08:00")}}.merge(option)
904
-
905
- entry=%Q{<?xml version="1.0" encoding="UTF-8"?>
906
- <entry xmlns="http://www.w3.org/2005/Atom" xmlns:db="http://www.douban.com/xmlns/" xmlns:gd="http://schemas.google.com/g/2005" xmlns:opensearch="http://a9.com/-/spec/opensearchrss/1.0/">
907
- <title>#{h title}</title>
908
- <category scheme="http://www.douban.com/2007#kind" term="http://www.douban.com/2007#event.#{option[:kind]}"/>
909
- <content>#{h content}</content>
910
- <db:attribute name="invite_only">#{h option[:invite_only]}</db:attribute>
911
- <db:attribute name="can_invite">#{h option[:can_invite]}</db:attribute>
912
- <gd:when endTime="#{h option[:when]["endTime"]}" startTime="#{h option[:when]["startTime"]}"/>
913
- <gd:where valueString="#{h where}" />
914
- </entry>
915
- }
916
- resp=put("/event/#{u(event_id)}",entry,{"Content-Type"=>"application/atom+xml"})
917
- if resp.code=="200"
918
- Event.new(resp.body)
919
- else
920
- debug(resp)
921
- end
922
- end
923
-
924
- def get_mail_inbox(option={:start_index=>1,:max_results=>10})
925
- resp=get("/doumail/inbox?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
926
- if resp.code=="200"
927
- mails=[]
928
- atom=resp.body
929
- doc=REXML::Document.new(atom)
930
- REXML::XPath.each(doc,"//entry") do |entry|
931
- mails << Mail.new(entry)
932
- end
933
- mails
934
- else
935
- nil
936
- end
937
- end
938
- def get_unread_mail(option={:start_index=>1,:max_results=>10})
939
- resp=get("/doumail/inbox/unread?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
940
- if resp.code=="200"
941
- mails=[]
942
- atom=resp.body
943
- doc=REXML::Document.new(atom)
944
- REXML::XPath.each(doc,"//entry") do |entry|
945
- mails << Mail.new(entry)
946
- end
947
- mails
948
- else
949
- nil
950
- end
951
- end
952
- def get_mail_outbox(option={:start_index=>1,:max_results=>10})
953
- resp=get("/doumail/outbox?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
954
- if resp.code=="200"
955
- mails=[]
956
- atom=resp.body
957
- doc=REXML::Document.new(atom)
958
- REXML::XPath.each(doc,"//entry") do |entry|
959
- mails << Mail.new(entry)
960
- end
961
- mails
962
- else
963
- nil
964
- end
965
- end
966
- def get_mail(mail_id="",keep_unread="false")
967
- resp=get("/doumail/#{u(mail_id.to_s)}?keep-unread=#{keep_unread}")
968
- if resp.code=="200"
969
- atom=resp.body
970
- Mail.new(atom)
971
- else
972
- nil
973
- end
974
- end
975
-
976
- # <b>DEPRECATED:</b> Please use <tt>send_mail</tt> instead.
977
- def create_mail(*args)
978
- warn "[DEPRECATION] `create_mail` is deprecated. Please use `send_mail` instead."
979
- send_mail(*args)
980
- end
981
-
982
- def send_mail(id="",title="",content="",captcha_token="",captcha_string="")
983
- if !(captcha_token.empty?&&captcha_string.empty?)
984
- entry=%Q(<?xml version="1.0" encoding="UTF-8"?>
985
- <entry xmlns="http://www.w3.org/2005/Atom" xmlns:db="http://www.douban.com/xmlns/" xmlns:gd="http://schemas.google.com/g/2005" xmlns:opensearch="http://a9.com/-/spec/opensearchrss/1.0/">
986
- <db:entity name="receiver"><uri>http://api.douban.com/people/#{h id}</uri></db:entity>
987
- <content>#{h content}</content>
988
- <title>#{h title}</title>
989
- <db:attribute name="captcha_token">#{h captcha_token}</db:attribute>
990
- <db:attribute name="captcha_string">#{h captcha_string}</db:attribute>
991
- </entry>)
992
- else
993
- entry=%Q(<?xml version="1.0" encoding="UTF-8"?>
994
- <entry xmlns="http://www.w3.org/2005/Atom" xmlns:db="http://www.douban.com/xmlns/" xmlns:gd="http://schemas.google.com/g/2005" xmlns:opensearch="http://a9.com/-/spec/opensearchrss/1.0/">
995
- <db:entity name="receiver">
996
- <uri>http://api.douban.com/people/#{h id}</uri></db:entity>
997
- <content>#{h content}</content>
998
- <title>#{h title}</title>
999
- </entry>)
1000
- end
1001
- resp=post("/doumails",entry,{"Content-Type"=>"application/atom+xml"})
1002
- if resp.code=="201"
1003
- true
1004
- elsif resp.code=="403"
1005
- hash={}
1006
- str=CGI.unescapeHTML(resp.body)
1007
- hash[:token]=str.scan(/^captcha_token=(.*?)&/).flatten.to_s
1008
- hash[:url]=str.scan(/captcha_url=(.*?)$/).flatten.to_s
1009
- hash
1010
- else
1011
- debug(resp)
1012
- end
1013
- end
1014
- def read_mail(mail_id="")
1015
- entry=%Q{<?xml version="1.0" encoding="UTF-8"?><entry xmlns="http://www.w3.org/2005/Atom" xmlns:db="http://www.douban.com/xmlns/" xmlns:gd="http://schemas.google.com/g/2005" xmlns:opensearch="http://a9.com/-/spec/opensearchrss/1.0/"><db:attribute name="unread">false</db:attribute></entry>}
1016
- resp=put("/doumail/#{mail_id.to_s}",entry,{"Content-Type" => "application/atom+xml"})
1017
- if resp.code=="202"
1018
- true
1019
- else
1020
- false
1021
- end
1022
- end
1023
- def delete_mail(mail_id="")
1024
- resp=delete("/doumail/#{mail_id.to_s}")
1025
- if resp.code=="200"
1026
- true
1027
- else
1028
- false
1029
- end
1030
- end
1031
- def read_mails(mail_ids=[])
1032
- entrys=""
1033
- mail_ids.each do |mail_id|
1034
- entrys +=%Q{<entry><id>http://api.douban.com/doumail/#{mail_id}</id><db:attribute name="unread">false</db:attribute></entry>}
1035
- end
1036
- feed=%Q{<?xml version="1.0" encoding="UTF-8"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:db="http://www.douban.com/xmlns/" xmlns:gd="http://schemas.google.com/g/2005" xmlns:opensearch="http://a9.com/-/spec/opensearchrss/1.0/">#{entrys}</feed>}
1037
- resp=put("/doumail/",feed,{"Content-Type" => "application/atom+xml"})
1038
- if resp.code=="202"
1039
- true
1040
- else
1041
- false
1042
- end
1043
- end
1044
- def delete_mails(mail_ids=[])
1045
- entrys=""
1046
- mail_ids.each do |mail_id|
1047
- entrys += %Q{<entry><id>http://api.douban.com/doumail/#{mail_id}</id></entry>}
1048
- end
1049
- feed=%Q{<?xml version="1.0" encoding="UTF-8"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:db="http://www.douban.com/xmlns/" xmlns:gd="http://schemas.google.com/g/2005" xmlns:opensearch="http://a9.com/-/spec/opensearchrss/1.0/">#{entrys}</feed>}
1050
- resp=post("/doumail/delete",feed,{"Content-Type" => "application/atom+xml"})
1051
- if resp.code=="202"
1052
- true
1053
- else
1054
- false
1055
- end
1056
- end
1057
-
1058
- def get_book_tags(subject_id="",flag=:book)
1059
- case flag
1060
- when :book
1061
- resp=get("/book/subject/#{subject_id}/tags")
1062
- when :music
1063
- resp=get("/music/subject/#{subject_id}/tags")
1064
- when :movie
1065
- resp=get("/movie/subject/#{subject_id}/tags")
1066
- end
1067
- if resp.code=="200"
1068
- tags=[]
1069
- atom=resp.body
1070
- doc=REXML::Document.new(atom)
1071
- REXML::XPath.each(doc,"//entry") do |entry|
1072
- tags << Tag.new(entry)
1073
- end
1074
- tags
1075
- else
1076
- nil
1077
- end
1078
- end
1079
- def get_user_tags(user_id="@me",flag=:book,option={:max_results=>10})
1080
- case flag
1081
- when :book
1082
- resp=get("/people/#{u user_id}/tags?cat=book&max-results=#{option[:max_results]}")
1083
- when :music
1084
- resp=get("/people/#{u user_id}/tags?cat=music&max-results=#{option[:max_results]}")
1085
- when :movie
1086
- resp=get("/people/#{u user_id}/tags?cat=movie&max-results=#{option[:max_results]}")
1087
- end
1088
- if resp.code=="200"
1089
- tags=[]
1090
- atom=resp.body
1091
- doc=REXML::Document.new(atom)
1092
- REXML::XPath.each(doc,"//entry") do |entry|
1093
- tags << Tag.new(entry)
1094
- end
1095
- tags
1096
- else
1097
- debug(resp)
1098
- end
1099
- end
1100
-
1101
- def delete_event(event_id="")
1102
- entry=%Q{<?xml version='1.0' encoding='UTF-8'?><entry xmlns:ns0="http://www.w3.org/2005/Atom" xmlns:db="http://www.douban.com/xmlns/"><content>sorry!!!</content></entry>}
1103
- resp=post("/event/#{u(event_id)}/delete",entry,{"Content-Type"=>"application/atom+xml"})
1104
- if resp.code=="200"
1105
- true
1106
- else
1107
- debug(resp, false)
1108
- end
1109
- end
1110
-
1111
- def request_token=(token)
1112
- unless token.kind_of? OAuth::RequestToken
1113
- token = OAuth::RequestToken.new(
1114
- new_request_consumer,
1115
- token.token,
1116
- token.secret)
1117
- end
1118
- @request_token = token
1119
- end
1120
-
1121
-
1122
- # :call-seq:
1123
- # request_token => OAuth::RequestToken
1124
- # request_token :as_token => OAuth::Token
1125
- #
1126
- # if you want to serialize request_token, use :as_token will be much shorter
1127
- def request_token(arg=nil)
1128
- if arg.nil?
1129
- @request_token
1130
- elsif arg == :as_token
1131
- @request_token.nil? ? nil :
1132
- OAuth::Token.new(@request_token.token, @request_token.secret)
1133
- else
1134
- raise ArgumentError
1135
- end
1136
- end
1137
-
1138
- def access_token=(token)
1139
- unless token.kind_of? OAuth::AccessToken
1140
- token = OAuth::AccessToken.new(
1141
- new_access_consumer,
1142
- token.token,
1143
- token.secret)
1144
- end
1145
- @access_token = token
1146
- end
1147
-
1148
- # :call-seq:
1149
- # access_token => OAuth::AccessToken
1150
- # access_token :as_token => OAuth::Token
1151
- #
1152
- # if you want to serialize access_token, use :as_token will be much shorter
1153
- def access_token(arg=nil)
1154
- if arg.nil?
1155
- @access_token
1156
- elsif arg == :as_token
1157
- @access_token.nil? ? nil :
1158
- OAuth::Token.new(@access_token.token, @access_token.secret)
1159
- else
1160
- raise ArgumentError
1161
- end
1162
- end
1163
-
1164
- def get_recommendation(id)
1165
- resp=get("/recommendation/#{u(id.to_s)}")
1166
- if resp.code=="200"
1167
- Recommendation.new(resp.body)
1168
- elsif resp.code == "404"
1169
- nil
1170
- else
1171
- debug(resp)
1172
- end
1173
- end
1174
-
1175
- def get_user_recommendations(user_id="@me",option={:start_index=>1,:max_results=>10})
1176
- resp=get("/people/#{u(user_id.to_s)}/recommendations?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
1177
- if resp.code == "200"
1178
- recommendations = []
1179
- doc=REXML::Document.new(resp.body)
1180
- REXML::XPath.each(doc,"//entry") do |entry|
1181
- recommendations << Recommendation.new(entry)
1182
- end
1183
- recommendations
1184
- else
1185
- debug(resp)
1186
- end
1187
- end
1188
-
1189
- def get_recommendation_comments(recommendation_id)
1190
- resp = get("/recommendation/#{u(recommendation_id)}/comments")
1191
- if resp.code == "200"
1192
- comments = []
1193
- doc=REXML::Document.new(resp.body)
1194
- REXML::XPath.each(doc, "//entry") do |entry|
1195
- comments << RecommendationComment.new(entry)
1196
- end
1197
- comments
1198
- else
1199
- debug(resp)
1200
- end
1201
- end
1202
-
1203
- def create_recommendation(subject, title, comment)
1204
- subject = subject.kind_of?(Douban::Subject) ? subject.id : subject
1205
- entry = %Q{<?xml version="1.0" encoding="UTF-8"?>
1206
- <entry xmlns="http://www.w3.org/2005/Atom"
1207
- xmlns:gd="http://schemas.google.com/g/2005"
1208
- xmlns:opensearch="http://a9.com/-/spec/opensearchrss/1.0/"
1209
- xmlns:db="http://www.douban.com/xmlns/">
1210
- <title>#{h title}</title>
1211
- <db:attribute name="comment">#{h comment}</db:attribute>
1212
- <link href="#{h subject}" rel="related" />
1213
- </entry>}
1214
- resp = post("/recommendations", entry, {"Content-Type"=>"application/atom+xml"})
1215
- if resp.code == '201'
1216
- Recommendation.new(resp.body)
1217
- else
1218
- debug(resp)
1219
- end
1220
- end
1221
-
1222
- def delete_recommendation(recommendation_id)
1223
- if recommendation_id.kind_of?(Douban::Recommendation)
1224
- recommendation_id = %r{/(\d+)$}.match(recommendation_id.id)[1]
1225
- end
1226
-
1227
- resp = delete("/recommendation/#{u recommendation_id}")
1228
- if resp.code == '200'
1229
- true
1230
- else
1231
- debug(resp, false)
1232
- end
1233
- end
1234
-
1235
- def create_recommendation_comment(recommendation, content)
1236
- recommendation_id = recommendation.kind_of?(Douban::Recommendation) \
1237
- ? recommendation.recommendation_id : recommendation
1238
- entry = %Q{<?xml version='1.0' encoding='UTF-8'?>
1239
- <entry>
1240
- <content>#{h content}</content>
1241
- </entry>}
1242
- resp = post("/recommendation/#{u recommendation_id}/comments", entry, {"Content-Type"=>"application/atom+xml"})
1243
- if resp.code == '201'
1244
- RecommendationComment.new(resp.body)
1245
- else
1246
- debug(resp)
1247
- end
1248
- end
1249
-
1250
- # delete_recommendation_comment(comment:RecommendationComment)
1251
- # delete_recommendation_comment(recommendation:Recommendation, comment_id:Integer)
1252
- # delete_recommendation_comment(recommendation_id:Integer, comment_id:Integer)
1253
- def delete_recommendation_comment(recommendation, comment_id=nil)
1254
- if comment_id.nil?
1255
- recommendation_id = recommendation.recommendation_id
1256
- comment_id = recommendation.comment_id
1257
- else
1258
- recommendation_id = recommendation.kind_of?(Douban::Recommendation) \
1259
- ? recommendation.recommendation_id : recommendation
1260
- end
1261
- resp = delete("/recommendation/#{u recommendation_id}/comment/#{u comment_id}")
1262
- if resp.code == '200'
1263
- true
1264
- else
1265
- debug(resp, false)
1266
- end
1267
- end
1268
-
1269
- # 验证Access Token是否可用
1270
- # http://goo.gl/8v8d
1271
- def verify_token
1272
- resp = get("/access_token/#{@access_token.token}")
1273
- if resp.code == "200"
1274
- true
1275
- elsif resp.code == "401"
1276
- false
1277
- else
1278
- debug(resp, false)
1279
- end
1280
- end
1281
-
1282
- # 注销一个Access Token
1283
- # http://goo.gl/0JAB
1284
- def delete_token
1285
- resp = delete("/access_token/#{@access_token.token}")
1286
- if resp.code == "200"
1287
- true
1288
- else
1289
- debug(resp, false)
1290
- end
1291
- end
1292
- alias_method :logout, :delete_token
1293
-
1294
- protected
1295
- def new_request_consumer
1296
- OAuth::Consumer.new(@api_key, @secret_key, @oauth_request_option)
1297
- end
1298
-
1299
- def new_access_consumer
1300
- OAuth::Consumer.new(@api_key, @secret_key, @oauth_access_option)
1301
- end
1302
-
1303
- def debug(resp, retval=nil)
1304
- if @@debug
1305
- p resp.code
1306
- p resp.body
1307
- end
1308
- retval
1309
- end
1310
-
1311
- def html_encode(o)
1312
- CGI.escapeHTML(o.to_s)
1313
- end
1314
- alias_method :h, :html_encode
1315
-
1316
- def url_encode(o)
1317
- CGI.escape(o.to_s)
1318
- end
1319
- alias_method :u, :url_encode
1320
-
1321
- def get(path,headers={})
1322
- @access_token.get(path,headers)
1323
- end
1324
- def post(path,data="",headers=nil)
1325
- headers ||= {"Content-Type" => "application/atom+xml"}
1326
- @access_token.post(path,data,headers)
1327
- end
1328
- def put(path,body="",headers=nil)
1329
- headers ||= {"Content-Type" => "application/atom+xml"}
1330
- @access_token.put(path,body,headers)
1331
- end
1332
- def delete(path,headers={})
1333
- @access_token.delete(path,headers)
1334
- end
1335
- def head(path,headers={})
1336
- @access_token.head(path,headers)
1337
- end
1338
-
1339
- def _subject_search_args_to_url(type, *args)
1340
- arg = args.shift
1341
-
1342
- option = case arg
1343
- when String
1344
- arg2 = args.shift
1345
- arg2 ||= {}
1346
- arg2.merge(:q => arg)
1347
- when Hash
1348
- arg
1349
- else
1350
- raise "unknown type for first arg: #{arg.class}"
1351
- end
1352
-
1353
- raise "extra argument" unless args.empty?
1354
-
1355
- if option[:q].nil? and option[:tag].nil?
1356
- raise "you must specify :q or :tag"
1357
- end
1358
-
1359
- url = "/#{type}/subjects?"
1360
- url << "q=#{u option[:q]}&" if option[:q]
1361
- url << "tag=#{u option[:tag]}&" if option[:tag]
1362
- url << "start_index=#{u option[:start_index]}&" if option[:start_index]
1363
- url << "max_results=#{u option[:max_results]}&" if option[:max_results]
1364
- url.slice!(-1)
1365
- url
1366
- end
1367
- end
1368
- end
1369
-
1
+ require 'oauth'
2
+ require 'oauth/consumer'
3
+ require 'rexml/document'
4
+ require 'net/http'
5
+
6
+ require 'douban/author'
7
+ require 'douban/collection'
8
+ require 'douban/event'
9
+ require 'douban/mail'
10
+ require 'douban/miniblog'
11
+ require 'douban/miniblog_comments'
12
+ require 'douban/note'
13
+ require 'douban/people'
14
+ require 'douban/recommendation'
15
+ require 'douban/recommendation_comment'
16
+ require 'douban/review'
17
+ require 'douban/subject'
18
+ require 'douban/tag'
19
+
20
+ module Douban
21
+ class Authorize
22
+ attr_reader :api_key
23
+ attr_reader :api_secret
24
+ attr_reader :authorize_url
25
+ attr_reader :consumer
26
+
27
+ @@debug = false
28
+
29
+ @@default_oauth_request_options = {
30
+ :signature_method=>"HMAC-SHA1",
31
+ :site=>"http://www.douban.com",
32
+ :request_token_path=>"/service/auth/request_token",
33
+ :access_token_path=>"/service/auth/access_token",
34
+ :authorize_path=>"/service/auth/authorize",
35
+ :scheme=>:header,
36
+ :realm=>"http://www.example.com/"
37
+ }
38
+
39
+ @@default_oauth_access_options = {
40
+ :site=>"http://api.douban.com",
41
+ :scheme=>:header,
42
+ :signature_method=>"HMAC-SHA1",
43
+ :realm=>"http://www.example.com/"
44
+ }
45
+
46
+
47
+ def self.debug=(val)
48
+ @@debug = val
49
+ end
50
+
51
+ def initialize(api_key, secret_key, options={})
52
+ @api_key=api_key
53
+ @secret_key=secret_key
54
+ @oauth_request_option = @@default_oauth_request_options.merge(options)
55
+ @oauth_access_option = @@default_oauth_access_options.merge(options)
56
+ yield self if block_given?
57
+ self
58
+ end
59
+
60
+ def authorized?
61
+ ! @access_token.nil?
62
+ end
63
+
64
+ def get_authorize_url(oauth_callback=nil)
65
+ oauth_callback ||= @oauth_request_option[:realm]
66
+
67
+ @consumer=new_request_consumer
68
+ @request_token=@consumer.get_request_token
69
+ @authorize_url="#{@request_token.authorize_url}&oauth_callback=#{CGI.escape(oauth_callback)}"
70
+ yield @request_token if block_given?
71
+ @authorize_url
72
+ end
73
+
74
+ def auth
75
+ begin
76
+ @access_token=@request_token.get_access_token
77
+ @access_token=OAuth::AccessToken.new(new_access_consumer,
78
+ @access_token.token,
79
+ @access_token.secret
80
+ )
81
+ rescue
82
+ #raise $!
83
+ ensure
84
+ yield self if block_given?
85
+ return self
86
+ end
87
+ end
88
+ def get_people(uid="@me")
89
+ resp=get("/people/#{u uid}")
90
+ if resp.code=="200"
91
+ atom=resp.body
92
+ People.new(atom)
93
+ else
94
+ debug(resp)
95
+ end
96
+ end
97
+
98
+ def get_friends(uid="@me",option={:start_index=>1,:max_results=>10})
99
+ resp=get("/people/#{u uid.to_s}/friends?start-index=#{u option[:start_index]}&max-results=#{u option[:max_results]}")
100
+ if resp.code=="200"
101
+ friends=[]
102
+ atom=resp.body
103
+ doc=REXML::Document.new(atom)
104
+ REXML::XPath.each(doc,"//entry") do |entry|
105
+ friends << People.new(entry)
106
+ end
107
+ friends
108
+ else
109
+ debug(resp)
110
+ end
111
+ end
112
+
113
+ def get_contacts(uid="@me",option={:start_index=>1,:max_results=>10})
114
+ resp=get("/people/#{u uid.to_s}/contacts?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
115
+ if resp.code=="200"
116
+ contacts=[]
117
+ atom=resp.body
118
+ doc=REXML::Document.new(atom)
119
+ REXML::XPath.each(doc,"//entry") do |entry|
120
+ contacts << People.new(entry)
121
+ end
122
+ contacts
123
+ else
124
+ nil
125
+ end
126
+ end
127
+
128
+
129
+ def search_people(q="",option={:start_index=>1,:max_results=>10})
130
+ resp=get("/people?q=#{u(q.to_s)}&start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
131
+ if resp.code=="200"
132
+ results=[]
133
+ atom=resp.body
134
+ doc=REXML::Document.new(atom)
135
+ REXML::XPath.each(doc,"//entry") do |entry|
136
+ results << People.new(entry)
137
+ end
138
+ results
139
+ else
140
+ nil
141
+ end
142
+ end
143
+
144
+ # 获取书籍信息
145
+ # http://goo.gl/HaG5
146
+ #
147
+ # get_book(:isbn => isbn) => Book
148
+ # get_book(:id => id) => Book
149
+ # get_book(id) => Book
150
+ # get_book(Book) => Book (refresh Book)
151
+ def get_book(id)
152
+ resp = case id
153
+ when Book
154
+ get("/book/subject/#{u id.subject_id}")
155
+ when Hash
156
+ if id[:isbn]
157
+ get("/book/subject/isbn/#{u id[:isbn]}")
158
+ elsif id[:id]
159
+ get("/book/subject/#{u id[:id]}")
160
+ else
161
+ raise "Hash only support :isbn or :id"
162
+ end
163
+ else
164
+ get("/book/subject/#{u id}")
165
+ end
166
+
167
+ if resp.code=="200"
168
+ atom=resp.body
169
+ Book.new(atom)
170
+ else
171
+ nil
172
+ end
173
+ end
174
+
175
+ # 获取电影信息
176
+ # http://goo.gl/2fZ4
177
+ #
178
+ # get_movie(:imdb => imdb) => Movie
179
+ # get_movie(:id => id) => Movie
180
+ # get_movie(id) => Movie
181
+ # get_movie(Movie) => Movie (refresh Movie)
182
+ def get_movie(id)
183
+ resp = case id
184
+ when Movie
185
+ get("/movie/subject/#{u id.subject_id}")
186
+ when Hash
187
+ if id[:imdb]
188
+ get("/movie/subject/imdb/#{u id[:imdb]}")
189
+ elsif id[:id]
190
+ get("/movie/subject/#{u id[:id]}")
191
+ else
192
+ raise "Hash only support :imdb or :id"
193
+ end
194
+ else
195
+ get("/movie/subject/#{u id}")
196
+ end
197
+
198
+ if resp.code=="200"
199
+ atom=resp.body
200
+ Movie.new(atom)
201
+ else
202
+ nil
203
+ end
204
+ end
205
+ def get_music(id=nil)
206
+ resp=get("/music/subject/#{u(id.to_s)}")
207
+ if resp.code=="200"
208
+ atom=resp.body
209
+ Music.new(atom)
210
+ else
211
+ nil
212
+ end
213
+ end
214
+
215
+ # :call-seq:
216
+ # search_book(:q => "search word") => [Book] or nil
217
+ # search_book(:tag => "tag name") => [Book] or nil
218
+ # search_book("search word") => [Book] or nil
219
+ #
220
+ # 搜索书籍
221
+ #
222
+ # http://goo.gl/rYDf
223
+ #
224
+ # * option
225
+ # * q: query string
226
+ # * tag:
227
+ # * start_index:
228
+ # * max_results:
229
+ def search_book(*args)
230
+ url = _subject_search_args_to_url(:book, *args)
231
+
232
+ resp=get(url)
233
+ if resp.code=="200"
234
+ atom=resp.body
235
+ doc=REXML::Document.new(atom)
236
+ books=[]
237
+ REXML::XPath.each(doc,"//entry") do |entry|
238
+ books << Book.new(entry)
239
+ end
240
+ books
241
+ else
242
+ nil
243
+ end
244
+ end
245
+
246
+ def search_movie(*args)
247
+ url = _subject_search_args_to_url(:movie, *args)
248
+
249
+ resp=get(url)
250
+ if resp.code=="200"
251
+ atom=resp.body
252
+ doc=REXML::Document.new(atom)
253
+ movies=[]
254
+ REXML::XPath.each(doc,"//entry") do |entry|
255
+ movies << Movie.new(entry)
256
+ end
257
+ movies
258
+ else
259
+ nil
260
+ end
261
+ end
262
+ def search_music(tag="",option={:start_index=>1,:max_results=>10})
263
+ url = _subject_search_args_to_url(:music, *args)
264
+
265
+ resp=get(url)
266
+ if resp.code=="200"
267
+ atom=resp.body
268
+ doc=REXML::Document.new(atom)
269
+ music=[]
270
+ REXML::XPath.each(doc,"//entry") do |entry|
271
+ music << Music.new(entry)
272
+ end
273
+ music
274
+ else
275
+ nil
276
+ end
277
+ end
278
+ def get_review(id="")
279
+ resp=get("/review/#{u(id.to_s)}")
280
+ if resp.code=="200"
281
+ atom=resp.body
282
+ Review.new(atom)
283
+ else
284
+ nil
285
+ end
286
+ end
287
+ def get_user_reviews(user_id="@me",option={:start_index=>1,:max_results=>10,:orderby=>'score'})
288
+ resp=get("/people/#{u(user_id.to_s)}/reviews?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}&orderby=#{option[:orderby]}")
289
+ if resp.code=="200"
290
+ atom=resp.body
291
+ reviews=[]
292
+ doc=REXML::Document.new(atom)
293
+ REXML::XPath.each(doc,"//entry") do |entry|
294
+ reviews<< Review.new(entry)
295
+ end
296
+ reviews
297
+ else
298
+ debug(resp)
299
+ end
300
+ end
301
+ def get_movie_reviews(subject_id="",option={:start_index=>1,:max_results=>10,:orderby=>'score'})
302
+ resp=get("/movie/subject/#{u(subject_id.to_s)}/reviews?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}&orderby=#{option[:orderby]}")
303
+ if resp.code=="200"
304
+ atom=resp.body
305
+ reviews=[]
306
+ doc=REXML::Document.new(atom)
307
+ REXML::XPath.each(doc,"//entry") do |entry|
308
+
309
+ reviews<< Review.new(entry)
310
+ end
311
+ reviews
312
+ else
313
+ nil
314
+ end
315
+ end
316
+ def get_music_reviews(subject_id="",option={:start_index=>1,:max_results=>10,:orderby=>'score'})
317
+ resp=get("/music/subject/#{u(subject_id.to_s)}/reviews?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}&orderby=#{option[:orderby]}")
318
+ if resp.code=="200"
319
+ atom=resp.body
320
+ reviews=[]
321
+ doc=REXML::Document.new(atom)
322
+ REXML::XPath.each(doc,"//entry") do |entry|
323
+ reviews<< Review.new(entry)
324
+ end
325
+ reviews
326
+ else
327
+ nil
328
+ end
329
+ end
330
+ def get_book_reviews(subject_id="",option={:start_index=>1,:max_results=>10,:orderby=>'score'})
331
+ resp=get("/book/subject/#{u(subject_id.to_s)}/reviews?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}&orderby=#{option[:orderby]}")
332
+ if resp.code=="200"
333
+ atom=resp.body
334
+ reviews=[]
335
+ doc=REXML::Document.new(atom)
336
+ REXML::XPath.each(doc,"//entry") do |entry|
337
+ reviews<< Review.new(entry)
338
+ end
339
+ reviews
340
+ else
341
+ nil
342
+ end
343
+ end
344
+
345
+ def delete_review(review)
346
+ review_id = case review
347
+ when Review then review.review_id
348
+ else review
349
+ end
350
+
351
+ resp=delete("/review/#{u(review_id.to_s)}")
352
+ if resp.code=="200"
353
+ true
354
+ else
355
+ false
356
+ end
357
+ end
358
+
359
+ def create_review(subject_link="",title="",content="",rating=5)
360
+ subject_link = subject_link.id if subject_link.kind_of?(Subject)
361
+
362
+ entry=%Q{<?xml version='1.0' encoding='UTF-8'?>
363
+ <entry xmlns:ns0="http://www.w3.org/2005/Atom">
364
+ <db:subject xmlns:db="http://www.douban.com/xmlns/">
365
+ <id>#{h subject_link}</id>
366
+ </db:subject>
367
+ <content>#{h content}</content>
368
+ <gd:rating xmlns:gd="http://schemas.google.com/g/2005" value="#{h rating}" ></gd:rating>
369
+ <title>#{h title}</title>
370
+ </entry>
371
+ }
372
+ resp=post("/reviews",entry,{"Content-Type" => "application/atom+xml"})
373
+ if resp.code=="201"
374
+ Review.new(resp.body)
375
+ else
376
+ debug(resp)
377
+ end
378
+ end
379
+
380
+ def modify_review(review, subject_link=nil,title="",content="",rating=5)
381
+ review_id = case review
382
+ when Review then review.review_id
383
+ else review
384
+ end
385
+
386
+ subject_link = review.subject.id if subject_link.nil? and review.kind_of?(Review)
387
+
388
+ entry=%Q{<?xml version='1.0' encoding='UTF-8'?>
389
+ <entry xmlns:ns0="http://www.w3.org/2005/Atom">
390
+ <id>http://api.douban.com/review/#{h review_id}</id>
391
+ <db:subject xmlns:db="http://www.douban.com/xmlns/">
392
+ <id>#{h subject_link}</id>
393
+ </db:subject>
394
+ <content>#{h content}</content>
395
+ <gd:rating xmlns:gd="http://schemas.google.com/g/2005" value="#{h rating}" ></gd:rating>
396
+ <title>#{h title}</title>
397
+ </entry>
398
+ }
399
+ resp=put("/review/#{u(review_id)}",entry,{"Content-Type" => "application/atom+xml"})
400
+ if resp.code=="202"
401
+ Review.new(resp.body)
402
+ else
403
+ debug(resp)
404
+ end
405
+ end
406
+ def get_collection(collection_id="")
407
+ resp=get("/collection/#{u(collection_id.to_s)}")
408
+ if resp.code=="200"
409
+ atom=resp.body
410
+ Collection.new(atom)
411
+ else
412
+ nil
413
+ end
414
+ end
415
+ def get_user_collection(
416
+ user_id="@me",
417
+ option={
418
+ :cat=>'',
419
+ :tag=>'',
420
+ :status=>'',
421
+ :start_index=>1,
422
+ :max_results=>10,
423
+ :updated_max=>'',
424
+ :updated_min=>''
425
+ }
426
+ )
427
+ resp=get("/people/#{u(user_id.to_s)}/collection?cat=#{option[:cat]}&tag=#{option[:tag]}&status=#{option[:status]}&start-index=#{option[:start_index]}&max-results=#{option[:max_results]}&updated-max=#{option[:updated_max]}&updated-min=#{option[:updated_min]}")
428
+ if resp.code=="200"
429
+ atom=resp.body
430
+ doc=REXML::Document.new(atom)
431
+ author=REXML::XPath.first(doc,"//feed/author")
432
+ author=Author.new(author.to_s) if author
433
+ title=REXML::XPath.first(doc,"//feed/title")
434
+ title=title.text if title
435
+ collections=[]
436
+ REXML::XPath.each(doc,"//entry") do |entry|
437
+ collection=Collection.new(entry)
438
+ collection.author=author
439
+ collection.title=title
440
+ collections<< collection
441
+ end
442
+ collections
443
+ else
444
+ nil
445
+ end
446
+ end
447
+ def create_collection( subject_id="",content="",rating=5,status="",tag=[],option={ :privacy=>"public"})
448
+ db_tag=""
449
+ if tag.size==0
450
+ db_tag='<db:tag name="" />'
451
+ else
452
+ tag.each do |t|
453
+ db_tag+=%Q{<db:tag name="#{h t}" />}
454
+ end
455
+ end
456
+ entry=%Q{<?xml version='1.0' encoding='UTF-8'?>
457
+ <entry xmlns:ns0="http://www.w3.org/2005/Atom" xmlns:db="http://www.douban.com/xmlns/">
458
+ <db:status>#{h status}</db:status>
459
+ #{db_tag}
460
+ <gd:rating xmlns:gd="http://schemas.google.com/g/2005" value="#{h rating}" />
461
+ <content>#{h content}</content>
462
+ <db:subject>
463
+ <id>#{h subject_id}</id>
464
+ </db:subject>
465
+ <db:attribute name="privacy">#{h option[:privacy]}</db:attribute>
466
+ </entry>
467
+ }
468
+ resp=post("/collection",entry,{"Content-Type"=>"application/atom+xml"})
469
+ if resp.code=="201"
470
+ Collection.new(resp.body)
471
+ else
472
+ debug(resp)
473
+ end
474
+ end
475
+
476
+ def modify_collection(collection, subject_id="",content="",rating=5,status="",tag=[],option={:privacy=>"public"})
477
+ collection_id = case collection
478
+ when Collection then collection.collection_id
479
+ else collection
480
+ end
481
+
482
+ subject_id = collection.subject.id if subject_id.nil? and collection.kind_of?(Collection)
483
+
484
+ db_tag=""
485
+ if tag.size==0
486
+ db_tag='<db:tag name="" />'
487
+ else
488
+ tag.each do |t|
489
+ db_tag+=%Q{<db:tag name="#{h t}" />}
490
+ end
491
+ end
492
+ entry=%Q{<?xml version='1.0' encoding='UTF-8'?>
493
+ <entry xmlns:ns0="http://www.w3.org/2005/Atom" xmlns:db="http://www.douban.com/xmlns/">
494
+ <id>http://api.douban.com/collection/#{h collection_id}</id>
495
+ <db:status>#{h status}</db:status>
496
+
497
+ #{db_tag}
498
+ <gd:rating xmlns:gd="http://schemas.google.com/g/2005" value="#{h rating}" />
499
+ <content>#{h content}</content>
500
+ <db:subject>
501
+ <id>#{h subject_id}</id>
502
+ </db:subject>
503
+ <db:attribute name="privacy">#{h option[:privacy]}</db:attribute>
504
+ </entry>
505
+ }
506
+ resp=put("/collection/#{u collection_id}",entry,{"Content-Type"=>"application/atom+xml"})
507
+ # resp.code should be 202, but currently it's 200
508
+ # http://www.douban.com/group/topic/12451628/
509
+ if resp.code=="200" or resp.code == "202"
510
+ Collection.new(resp.body)
511
+ else
512
+ debug(resp)
513
+ end
514
+ end
515
+ def delete_collection(collection)
516
+ collection_id = case collection
517
+ when Collection then collection.collection_id
518
+ else collection
519
+ end
520
+
521
+ resp=delete("/collection/#{u(collection_id.to_s)}")
522
+ if resp.code=="200"
523
+ true
524
+ else
525
+ false
526
+ end
527
+ end
528
+ def get_user_miniblog(user_id="@me",option={:start_index=>1,:max_results=>10})
529
+ resp=get("/people/#{u(user_id.to_s)}/miniblog?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
530
+ if resp.code=="200"
531
+ atom=resp.body
532
+ doc=REXML::Document.new(atom)
533
+ author=REXML::XPath.first(doc,"//feed/author")
534
+ author=Author.new(author.to_s) if author
535
+ miniblogs=[]
536
+ REXML::XPath.each(doc,"//feed/entry") do |entry|
537
+ miniblog=Miniblog.new(entry)
538
+ miniblog.author=author
539
+ miniblogs<< miniblog
540
+ end
541
+ miniblogs
542
+ else
543
+ nil
544
+ end
545
+ end
546
+ def get_user_contact_miniblog(user_id="@me",option={:start_index=>1,:max_results=>10})
547
+ resp=get("/people/#{u(user_id.to_s)}/miniblog/contacts?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
548
+ if resp.code=="200"
549
+ atom=resp.body
550
+ doc=REXML::Document.new(atom)
551
+ miniblogs=[]
552
+ REXML::XPath.each(doc,"//feed/entry") do |entry|
553
+ miniblog=Miniblog.new(entry)
554
+ miniblogs<< miniblog
555
+ end
556
+ miniblogs
557
+ else
558
+ nil
559
+ end
560
+ end
561
+ def create_miniblog(content="")
562
+ entry=%Q{<?xml version='1.0' encoding='UTF-8'?>
563
+ <entry xmlns:ns0="http://www.w3.org/2005/Atom" xmlns:db="http://www.douban.com/xmlns/">
564
+ <content>#{h content}</content>
565
+ </entry>}
566
+ resp=post("/miniblog/saying",entry,{"Content-Type"=>"application/atom+xml"})
567
+ if resp.code=="201"
568
+ Miniblog.new(resp.body)
569
+ else
570
+ nil
571
+ end
572
+ end
573
+ def delete_miniblog(miniblog_id="")
574
+ resp=delete("/miniblog/#{u(miniblog_id.to_s)}")
575
+ if resp.code=="200"
576
+ true
577
+ else
578
+ false
579
+ end
580
+ end
581
+
582
+ # :call-seq:
583
+ # get_miniblog_comments(aMiniblog, options) => MiniblogComments or nil
584
+ # get_miniblog_comments(miniblog_id, options) => MiniblogComments or nil
585
+ #
586
+ # 获取我说回复 (http://goo.gl/nTZK)
587
+ def get_miniblog_comments(miniblog, options={})
588
+ miniblog_id = case miniblog
589
+ when Miniblog then miniblog.miniblog_id
590
+ else miniblog
591
+ end
592
+
593
+ url = "/miniblog/#{u miniblog_id}/comments?"
594
+ if options[:start_index]
595
+ url << "start-index=#{u options[:start_index]}&"
596
+ end
597
+ if options[:max_results]
598
+ url << "max-results=#{u options[:max_results]}&"
599
+ end
600
+
601
+ url = url[0..-2]
602
+
603
+ resp = get(url)
604
+ if resp.code == "200"
605
+ MiniblogComments.new(resp.body)
606
+ else
607
+ debug(resp)
608
+ end
609
+ end
610
+
611
+ # :call-seq:
612
+ # create_miniblog_comment(aMiniblog, aString) => MiniblogComment or nil
613
+ # create_miniblog_comment(obj, aString) => MiniblogComment or nil
614
+ #
615
+ # 回应我说 (http://goo.gl/j43Z)
616
+ def create_miniblog_comment(miniblog, content)
617
+ miniblog_id = case miniblog
618
+ when Miniblog then miniblog.miniblog_id
619
+ else miniblog
620
+ end
621
+ entry = %Q{<?xml version='1.0' encoding='UTF-8'?>
622
+ <entry>
623
+ <content>#{h content}</content>
624
+ </entry>}
625
+
626
+ resp = post("/miniblog/#{u miniblog_id}/comments", entry)
627
+ if resp.code == "201"
628
+ MiniblogComment.new(resp.body)
629
+ else
630
+ debug(resp)
631
+ end
632
+ end
633
+
634
+ # :call-seq:
635
+ # delete_miniblog_comment(aMiniblogComment) => true or false
636
+ # delete_miniblog_comment(miniblog_id, comment_id) => true or false
637
+ # 删除我说
638
+ def delete_miniblog_comment(*args)
639
+ if args.size == 1 and args[0].kind_of?(MiniblogComment)
640
+ miniblog_id = args[0].miniblog_id
641
+ comment_id = args[0].comment_id
642
+ elsif args.size == 2
643
+ miniblog_id = args[0].to_s
644
+ comment_id = args[1].to_s
645
+ else
646
+ raise "unsupported argument error"
647
+ end
648
+
649
+ resp = delete("/miniblog/#{u miniblog_id}/comment/#{u comment_id}")
650
+ if resp.code == "200"
651
+ true
652
+ else
653
+ debug(resp, false)
654
+ end
655
+ end
656
+
657
+ def get_note(note_id="")
658
+ resp=get("/note/#{u(note_id.to_s)}")
659
+ if resp.code=="200"
660
+ atom=resp.body
661
+ Note.new(atom)
662
+ else
663
+ nil
664
+ end
665
+ end
666
+
667
+ def get_user_notes(user_id="@me",option={:start_index=>1,:max_results=>10})
668
+ resp=get("/people/#{u(user_id.to_s)}/notes?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
669
+ if resp.code=="200"
670
+ atom=resp.body
671
+ doc=REXML::Document.new(atom)
672
+ author=REXML::XPath.first(doc,"//feed/author")
673
+ author=Author.new(author.to_s) if author
674
+ notes=[]
675
+ REXML::XPath.each(doc,"//feed/entry") do |entry|
676
+ note=Note.new(entry)
677
+ note.author=author
678
+ notes << note
679
+ end
680
+ notes
681
+ else
682
+ nil
683
+ end
684
+ end
685
+
686
+ def create_note(title="",content="",option={:privacy=>"public",:can_reply=>"yes"})
687
+ entry=%Q{<?xml version="1.0" encoding="UTF-8"?>
688
+ <entry xmlns="http://www.w3.org/2005/Atom" xmlns:db="http://www.douban.com/xmlns/">
689
+ <title>#{h title}</title>
690
+ <content>#{h content}</content>
691
+ <db:attribute name="privacy">#{h option[:privacy]}</db:attribute>
692
+ <db:attribute name="can_reply">#{h option[:can_reply]}</db:attribute>
693
+ </entry>
694
+ }
695
+ resp=post("/notes",entry,{"Content-Type"=>"application/atom+xml"})
696
+ if resp.code=="201"
697
+ Note.new(resp.body)
698
+ else
699
+ debug(resp)
700
+ end
701
+ end
702
+
703
+ def delete_note(note_id="")
704
+ note_id = note_id.note_id if note_id.kind_of?(Note)
705
+
706
+ resp=delete("/note/#{u(note_id.to_s)}")
707
+ if resp.code=="200"
708
+ true
709
+ else
710
+ false
711
+ end
712
+ end
713
+
714
+ def modify_note(note,title="",content="",option={:privacy=>"public",:can_reply=>"yes"})
715
+ note_id = case note
716
+ when Note then note.note_id
717
+ else note
718
+ end
719
+
720
+ entry=%Q{<?xml version="1.0" encoding="UTF-8"?>
721
+ <entry xmlns="http://www.w3.org/2005/Atom" xmlns:db="http://www.douban.com/xmlns/">
722
+ <title>#{h title}</title>
723
+ <content>#{h content}</content>
724
+ <db:attribute name="privacy">#{h option[:privacy]}</db:attribute>
725
+ <db:attribute name="can_reply">#{h option[:can_reply]}</db:attribute>
726
+ </entry>
727
+ }
728
+ resp=put("/note/#{u(note_id.to_s)}",entry,{"Content-Type"=>"application/atom+xml"})
729
+ if resp.code=="202"
730
+ Note.new(resp.body)
731
+ else
732
+ debug(resp)
733
+ end
734
+ end
735
+ def get_event(event_id="")
736
+ resp=get("/event/#{u(event_id.to_s)}")
737
+ if resp.code=="200"
738
+ atom=resp.body
739
+ Event.new(atom)
740
+ else
741
+ nil
742
+ end
743
+ end
744
+
745
+ # <b>DEPRECATED:</b> Please use <tt>get_event_participant_people</tt> instead.
746
+ def get_participant_people(*args)
747
+ warn "[DEPRECATION] `get_participant_people` is deprecated. Please use `get_event_participant_people` instead."
748
+ get_event_participant_people(*args)
749
+ end
750
+
751
+ def get_event_participant_people(event_id=nil,option={:start_index=>1,:max_results=>10})
752
+ resp=get("/event/#{u(event_id.to_s)}/participants?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
753
+ if resp.code=="200"
754
+ people=[]
755
+ atom=resp.body
756
+ doc=REXML::Document.new(atom)
757
+ REXML::XPath.each(doc,"//feed/entry") do |entry|
758
+ people<< People.new(entry)
759
+ end
760
+ people
761
+ else
762
+ nil
763
+ end
764
+ end
765
+
766
+ # <b>DEPRECATED:</b> Please use <tt>get_event_wisher_people</tt> instead.
767
+ def get_wisher_people(*args)
768
+ warn "[DEPRECATION] `get_wisher_people` is deprecated. Please use `get_event_wisher_people` instead."
769
+ get_event_wisher_people(*args)
770
+ end
771
+
772
+ def get_event_wisher_people(event_id=nil,option={:start_index=>1,:max_results=>10})
773
+ resp=get("/event/#{u(event_id.to_s)}/wishers?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
774
+ if resp.code=="200"
775
+ people=[]
776
+ atom=resp.body
777
+ doc=REXML::Document.new(atom)
778
+ REXML::XPath.each(doc,"//feed/entry") do |entry|
779
+ people<< People.new(entry)
780
+ end
781
+ people
782
+ else
783
+ nil
784
+ end
785
+ end
786
+ def get_user_events(user_id="@me",option={:start_index=>1,:max_results=>10})
787
+ resp=get("/people/#{u(user_id.to_s)}/events?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
788
+ if resp.code=="200"
789
+ events=[]
790
+ atom=resp.body
791
+ doc=REXML::Document.new(atom)
792
+ REXML::XPath.each(doc,"//feed/entry") do |entry|
793
+ events<< Event.new(entry)
794
+ end
795
+ events
796
+ else
797
+ nil
798
+ end
799
+ end
800
+ def get_user_initiate_events(user_id="@me",option={:start_index=>1,:max_results=>10})
801
+ resp=get("/people/#{u(user_id.to_s)}/events/initiate?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
802
+ if resp.code=="200"
803
+ events=[]
804
+ atom=resp.body
805
+ doc=REXML::Document.new(atom)
806
+ REXML::XPath.each(doc,"//feed/entry") do |entry|
807
+ events<< Event.new(entry)
808
+ end
809
+ events
810
+ else
811
+ nil
812
+ end
813
+ end
814
+ def get_user_participate_events(user_id="@me",option={:start_index=>1,:max_results=>10})
815
+ resp=get("/people/#{u(user_id.to_s)}/events/participate?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
816
+ if resp.code=="200"
817
+ events=[]
818
+ atom=resp.body
819
+ doc=REXML::Document.new(atom)
820
+ REXML::XPath.each(doc,"//feed/entry") do |entry|
821
+ events<< Event.new(entry)
822
+ end
823
+ events
824
+ else
825
+ nil
826
+ end
827
+ end
828
+ def get_user_wish_events(user_id="@me",option={:start_index=>1,:max_results=>10})
829
+ resp=get("/people/#{u(user_id.to_s)}/events/wish?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
830
+ if resp.code=="200"
831
+ events=[]
832
+ atom=resp.body
833
+ doc=REXML::Document.new(atom)
834
+ REXML::XPath.each(doc,"//feed/entry") do |entry|
835
+ events<< Event.new(entry)
836
+ end
837
+ events
838
+ else
839
+ nil
840
+ end
841
+ end
842
+
843
+ def get_city_events(location_id=nil,option={:type=>"all",:start_index=>1,:max_results=>10})
844
+ resp=get("/event/location/#{u(location_id.to_s)}?type=#{option[:type]}&start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
845
+ if resp.code=="200"
846
+ events=[]
847
+ atom=resp.body
848
+ doc=REXML::Document.new(atom)
849
+ REXML::XPath.each(doc,"//feed/entry") do |entry|
850
+ events<< Event.new(entry)
851
+ end
852
+ events
853
+ else
854
+ nil
855
+ end
856
+ end
857
+ def search_events(q="",option={:location=>"all",:start_index=>1,:max_results=>10})
858
+ resp=get("/events?q=#{u(q.to_s)}&location=#{option[:location]}&start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
859
+ if resp.code=="200"
860
+ events=[]
861
+ atom=resp.body
862
+ doc=REXML::Document.new(atom)
863
+ REXML::XPath.each(doc,"//feed/entry") do |entry|
864
+ events<< Event.new(entry)
865
+ end
866
+ events
867
+ else
868
+ nil
869
+ end
870
+ end
871
+ def create_event(title="",content="",where="",option={:kind=>"party",:invite_only=>"no",:can_invite=>"yes",:when=>{"endTime"=>(Time.now+60*60*24*5).strftime("%Y-%m-%dT%H:%M:%S+08:00"),"startTime"=>Time.now.strftime("%Y-%m-%dT%H:%M:%S+08:00")}})
872
+ entry=%Q{<?xml version="1.0" encoding="UTF-8"?>
873
+ <entry xmlns="http://www.w3.org/2005/Atom" xmlns:db="http://www.douban.com/xmlns/" xmlns:gd="http://schemas.google.com/g/2005" xmlns:opensearch="http://a9.com/-/spec/opensearchrss/1.0/">
874
+ <title>#{h title}</title>
875
+ <category scheme="http://www.douban.com/2007#kind" term="http://www.douban.com/2007#event.#{option[:kind]}"/>
876
+ <content>#{h content}</content>
877
+ <db:attribute name="invite_only">#{h option[:invite_only]}</db:attribute>
878
+ <db:attribute name="can_invite">#{h option[:can_invite]}</db:attribute>
879
+ <gd:when endTime="#{h option[:when]["endTime"]}" startTime="#{h option[:when]["startTime"]}"/>
880
+ <gd:where valueString="#{h where}" />
881
+ </entry>
882
+ }
883
+ resp=post("/events",entry,{"Content-Type"=>"application/atom+xml"})
884
+ if resp.code=="201"
885
+ Event.new(resp.body)
886
+ else
887
+ debug(resp)
888
+ end
889
+ end
890
+
891
+ def modify_event(event,title=nil,content=nil,where=nil,option=nil)
892
+
893
+ event_id = case event
894
+ when Event then event.event_id
895
+ else event
896
+ end
897
+
898
+ option = {} if option.nil?
899
+ option = {:kind=>"exhibit",
900
+ :invite_only=>"no",
901
+ :can_invite=>"yes",
902
+ :when=>{"endTime"=>(Time.now+60*60*24*5).strftime("%Y-%m-%dT%H:%M:%S+08:00"),
903
+ "startTime"=>Time.now.strftime("%Y-%m-%dT%H:%M:%S+08:00")}}.merge(option)
904
+
905
+ entry=%Q{<?xml version="1.0" encoding="UTF-8"?>
906
+ <entry xmlns="http://www.w3.org/2005/Atom" xmlns:db="http://www.douban.com/xmlns/" xmlns:gd="http://schemas.google.com/g/2005" xmlns:opensearch="http://a9.com/-/spec/opensearchrss/1.0/">
907
+ <title>#{h title}</title>
908
+ <category scheme="http://www.douban.com/2007#kind" term="http://www.douban.com/2007#event.#{option[:kind]}"/>
909
+ <content>#{h content}</content>
910
+ <db:attribute name="invite_only">#{h option[:invite_only]}</db:attribute>
911
+ <db:attribute name="can_invite">#{h option[:can_invite]}</db:attribute>
912
+ <gd:when endTime="#{h option[:when]["endTime"]}" startTime="#{h option[:when]["startTime"]}"/>
913
+ <gd:where valueString="#{h where}" />
914
+ </entry>
915
+ }
916
+ resp=put("/event/#{u(event_id)}",entry,{"Content-Type"=>"application/atom+xml"})
917
+ if resp.code=="200"
918
+ Event.new(resp.body)
919
+ else
920
+ debug(resp)
921
+ end
922
+ end
923
+
924
+ def get_mail_inbox(option={:start_index=>1,:max_results=>10})
925
+ resp=get("/doumail/inbox?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
926
+ if resp.code=="200"
927
+ mails=[]
928
+ atom=resp.body
929
+ doc=REXML::Document.new(atom)
930
+ REXML::XPath.each(doc,"//entry") do |entry|
931
+ mails << Mail.new(entry)
932
+ end
933
+ mails
934
+ else
935
+ nil
936
+ end
937
+ end
938
+ def get_unread_mail(option={:start_index=>1,:max_results=>10})
939
+ resp=get("/doumail/inbox/unread?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
940
+ if resp.code=="200"
941
+ mails=[]
942
+ atom=resp.body
943
+ doc=REXML::Document.new(atom)
944
+ REXML::XPath.each(doc,"//entry") do |entry|
945
+ mails << Mail.new(entry)
946
+ end
947
+ mails
948
+ else
949
+ nil
950
+ end
951
+ end
952
+ def get_mail_outbox(option={:start_index=>1,:max_results=>10})
953
+ resp=get("/doumail/outbox?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
954
+ if resp.code=="200"
955
+ mails=[]
956
+ atom=resp.body
957
+ doc=REXML::Document.new(atom)
958
+ REXML::XPath.each(doc,"//entry") do |entry|
959
+ mails << Mail.new(entry)
960
+ end
961
+ mails
962
+ else
963
+ nil
964
+ end
965
+ end
966
+ def get_mail(mail_id="",keep_unread="false")
967
+ resp=get("/doumail/#{u(mail_id.to_s)}?keep-unread=#{keep_unread}")
968
+ if resp.code=="200"
969
+ atom=resp.body
970
+ Mail.new(atom)
971
+ else
972
+ nil
973
+ end
974
+ end
975
+
976
+ # <b>DEPRECATED:</b> Please use <tt>send_mail</tt> instead.
977
+ def create_mail(*args)
978
+ warn "[DEPRECATION] `create_mail` is deprecated. Please use `send_mail` instead."
979
+ send_mail(*args)
980
+ end
981
+
982
+ def send_mail(id="",title="",content="",captcha_token="",captcha_string="")
983
+ if !(captcha_token.empty?&&captcha_string.empty?)
984
+ entry=%Q(<?xml version="1.0" encoding="UTF-8"?>
985
+ <entry xmlns="http://www.w3.org/2005/Atom" xmlns:db="http://www.douban.com/xmlns/" xmlns:gd="http://schemas.google.com/g/2005" xmlns:opensearch="http://a9.com/-/spec/opensearchrss/1.0/">
986
+ <db:entity name="receiver"><uri>http://api.douban.com/people/#{h id}</uri></db:entity>
987
+ <content>#{h content}</content>
988
+ <title>#{h title}</title>
989
+ <db:attribute name="captcha_token">#{h captcha_token}</db:attribute>
990
+ <db:attribute name="captcha_string">#{h captcha_string}</db:attribute>
991
+ </entry>)
992
+ else
993
+ entry=%Q(<?xml version="1.0" encoding="UTF-8"?>
994
+ <entry xmlns="http://www.w3.org/2005/Atom" xmlns:db="http://www.douban.com/xmlns/" xmlns:gd="http://schemas.google.com/g/2005" xmlns:opensearch="http://a9.com/-/spec/opensearchrss/1.0/">
995
+ <db:entity name="receiver">
996
+ <uri>http://api.douban.com/people/#{h id}</uri></db:entity>
997
+ <content>#{h content}</content>
998
+ <title>#{h title}</title>
999
+ </entry>)
1000
+ end
1001
+ resp=post("/doumails",entry,{"Content-Type"=>"application/atom+xml"})
1002
+ if resp.code=="201"
1003
+ true
1004
+ elsif resp.code=="403"
1005
+ hash={}
1006
+ str=CGI.unescapeHTML(resp.body)
1007
+ hash[:token]=str.scan(/^captcha_token=(.*?)&/).flatten.to_s
1008
+ hash[:url]=str.scan(/captcha_url=(.*?)$/).flatten.to_s
1009
+ hash
1010
+ else
1011
+ debug(resp)
1012
+ end
1013
+ end
1014
+ def read_mail(mail_id="")
1015
+ entry=%Q{<?xml version="1.0" encoding="UTF-8"?><entry xmlns="http://www.w3.org/2005/Atom" xmlns:db="http://www.douban.com/xmlns/" xmlns:gd="http://schemas.google.com/g/2005" xmlns:opensearch="http://a9.com/-/spec/opensearchrss/1.0/"><db:attribute name="unread">false</db:attribute></entry>}
1016
+ resp=put("/doumail/#{mail_id.to_s}",entry,{"Content-Type" => "application/atom+xml"})
1017
+ if resp.code=="202"
1018
+ true
1019
+ else
1020
+ false
1021
+ end
1022
+ end
1023
+ def delete_mail(mail_id="")
1024
+ resp=delete("/doumail/#{mail_id.to_s}")
1025
+ if resp.code=="200"
1026
+ true
1027
+ else
1028
+ false
1029
+ end
1030
+ end
1031
+ def read_mails(mail_ids=[])
1032
+ entrys=""
1033
+ mail_ids.each do |mail_id|
1034
+ entrys +=%Q{<entry><id>http://api.douban.com/doumail/#{mail_id}</id><db:attribute name="unread">false</db:attribute></entry>}
1035
+ end
1036
+ feed=%Q{<?xml version="1.0" encoding="UTF-8"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:db="http://www.douban.com/xmlns/" xmlns:gd="http://schemas.google.com/g/2005" xmlns:opensearch="http://a9.com/-/spec/opensearchrss/1.0/">#{entrys}</feed>}
1037
+ resp=put("/doumail/",feed,{"Content-Type" => "application/atom+xml"})
1038
+ if resp.code=="202"
1039
+ true
1040
+ else
1041
+ false
1042
+ end
1043
+ end
1044
+ def delete_mails(mail_ids=[])
1045
+ entrys=""
1046
+ mail_ids.each do |mail_id|
1047
+ entrys += %Q{<entry><id>http://api.douban.com/doumail/#{mail_id}</id></entry>}
1048
+ end
1049
+ feed=%Q{<?xml version="1.0" encoding="UTF-8"?><feed xmlns="http://www.w3.org/2005/Atom" xmlns:db="http://www.douban.com/xmlns/" xmlns:gd="http://schemas.google.com/g/2005" xmlns:opensearch="http://a9.com/-/spec/opensearchrss/1.0/">#{entrys}</feed>}
1050
+ resp=post("/doumail/delete",feed,{"Content-Type" => "application/atom+xml"})
1051
+ if resp.code=="202"
1052
+ true
1053
+ else
1054
+ false
1055
+ end
1056
+ end
1057
+
1058
+ def get_book_tags(subject_id="",flag=:book)
1059
+ case flag
1060
+ when :book
1061
+ resp=get("/book/subject/#{subject_id}/tags")
1062
+ when :music
1063
+ resp=get("/music/subject/#{subject_id}/tags")
1064
+ when :movie
1065
+ resp=get("/movie/subject/#{subject_id}/tags")
1066
+ end
1067
+ if resp.code=="200"
1068
+ tags=[]
1069
+ atom=resp.body
1070
+ doc=REXML::Document.new(atom)
1071
+ REXML::XPath.each(doc,"//entry") do |entry|
1072
+ tags << Tag.new(entry)
1073
+ end
1074
+ tags
1075
+ else
1076
+ nil
1077
+ end
1078
+ end
1079
+ def get_user_tags(user_id="@me",flag=:book,option={:max_results=>10})
1080
+ case flag
1081
+ when :book
1082
+ resp=get("/people/#{u user_id}/tags?cat=book&max-results=#{option[:max_results]}")
1083
+ when :music
1084
+ resp=get("/people/#{u user_id}/tags?cat=music&max-results=#{option[:max_results]}")
1085
+ when :movie
1086
+ resp=get("/people/#{u user_id}/tags?cat=movie&max-results=#{option[:max_results]}")
1087
+ end
1088
+ if resp.code=="200"
1089
+ tags=[]
1090
+ atom=resp.body
1091
+ doc=REXML::Document.new(atom)
1092
+ REXML::XPath.each(doc,"//entry") do |entry|
1093
+ tags << Tag.new(entry)
1094
+ end
1095
+ tags
1096
+ else
1097
+ debug(resp)
1098
+ end
1099
+ end
1100
+
1101
+ def delete_event(event_id="")
1102
+ entry=%Q{<?xml version='1.0' encoding='UTF-8'?><entry xmlns:ns0="http://www.w3.org/2005/Atom" xmlns:db="http://www.douban.com/xmlns/"><content>sorry!!!</content></entry>}
1103
+ resp=post("/event/#{u(event_id)}/delete",entry,{"Content-Type"=>"application/atom+xml"})
1104
+ if resp.code=="200"
1105
+ true
1106
+ else
1107
+ debug(resp, false)
1108
+ end
1109
+ end
1110
+
1111
+ # @param token can be OAuth::RequestToken or OAuth::Token or Hash
1112
+ #
1113
+ # @see #request_token
1114
+ def request_token=(token)
1115
+ case token
1116
+ when OAuth::RequestToken
1117
+ @request_token = token
1118
+ when OAuth::Token
1119
+ @request_token = OAuth::RequestToken.new(
1120
+ new_request_consumer,
1121
+ token.token,
1122
+ token.secret)
1123
+ when Hash
1124
+ @request_token = OAuth::RequestToken.new(
1125
+ new_request_consumer,
1126
+ token[:token],
1127
+ token[:secret])
1128
+ else
1129
+ raise ArgumentError
1130
+ end
1131
+ end
1132
+
1133
+
1134
+ # :call-seq:
1135
+ # request_token => OAuth::RequestToken
1136
+ # request_token :as_token => OAuth::Token
1137
+ # request_token :as_hash => Hash
1138
+ #
1139
+ # if you want to serialize request_token, use :as_token or :as_hash
1140
+ def request_token(arg=nil)
1141
+ if arg.nil?
1142
+ @request_token
1143
+ elsif arg == :as_token
1144
+ @request_token.nil? ? nil :
1145
+ OAuth::Token.new(@request_token.token, @request_token.secret)
1146
+ elsif arg == :as_hash
1147
+ @request_token.nil? ? nil :
1148
+ {:token => @request_token.token,
1149
+ :secret => @request_token.secret}
1150
+ else
1151
+ raise ArgumentError
1152
+ end
1153
+ end
1154
+
1155
+ def access_token=(token)
1156
+ case token
1157
+ when OAuth::AccessToken
1158
+ @access_token = token
1159
+ when OAuth::Token
1160
+ @access_token = OAuth::AccessToken.new(
1161
+ new_access_consumer,
1162
+ token.token,
1163
+ token.secret)
1164
+ when Hash
1165
+ @access_token = OAuth::AccessToken.new(
1166
+ new_access_consumer,
1167
+ token[:token],
1168
+ token[:secret])
1169
+ else
1170
+ raise ArgumentError
1171
+ end
1172
+ end
1173
+
1174
+ # :call-seq:
1175
+ # access_token => OAuth::AccessToken
1176
+ # access_token :as_token => OAuth::Token
1177
+ # access_token :as_hash => Hash
1178
+ #
1179
+ # if you want to serialize access_token, use :as_token or :as_hash
1180
+ def access_token(arg=nil)
1181
+ if arg.nil?
1182
+ @access_token
1183
+ elsif arg == :as_token
1184
+ @access_token.nil? ? nil :
1185
+ OAuth::Token.new(@access_token.token, @access_token.secret)
1186
+ elsif arg == :as_hash
1187
+ @access_token.nil? ? nil :
1188
+ {:token => @access_token.token,
1189
+ :secret => @access_token.secret}
1190
+ else
1191
+ raise ArgumentError
1192
+ end
1193
+ end
1194
+
1195
+ def get_recommendation(id)
1196
+ resp=get("/recommendation/#{u(id.to_s)}")
1197
+ if resp.code=="200"
1198
+ Recommendation.new(resp.body)
1199
+ elsif resp.code == "404"
1200
+ nil
1201
+ else
1202
+ debug(resp)
1203
+ end
1204
+ end
1205
+
1206
+ def get_user_recommendations(user_id="@me",option={:start_index=>1,:max_results=>10})
1207
+ resp=get("/people/#{u(user_id.to_s)}/recommendations?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
1208
+ if resp.code == "200"
1209
+ recommendations = []
1210
+ doc=REXML::Document.new(resp.body)
1211
+ REXML::XPath.each(doc,"//entry") do |entry|
1212
+ recommendations << Recommendation.new(entry)
1213
+ end
1214
+ recommendations
1215
+ else
1216
+ debug(resp)
1217
+ end
1218
+ end
1219
+
1220
+ def get_recommendation_comments(recommendation_id)
1221
+ resp = get("/recommendation/#{u(recommendation_id)}/comments")
1222
+ if resp.code == "200"
1223
+ comments = []
1224
+ doc=REXML::Document.new(resp.body)
1225
+ REXML::XPath.each(doc, "//entry") do |entry|
1226
+ comments << RecommendationComment.new(entry)
1227
+ end
1228
+ comments
1229
+ else
1230
+ debug(resp)
1231
+ end
1232
+ end
1233
+
1234
+ def create_recommendation(subject, title, comment)
1235
+ subject = subject.kind_of?(Douban::Subject) ? subject.id : subject
1236
+ entry = %Q{<?xml version="1.0" encoding="UTF-8"?>
1237
+ <entry xmlns="http://www.w3.org/2005/Atom"
1238
+ xmlns:gd="http://schemas.google.com/g/2005"
1239
+ xmlns:opensearch="http://a9.com/-/spec/opensearchrss/1.0/"
1240
+ xmlns:db="http://www.douban.com/xmlns/">
1241
+ <title>#{h title}</title>
1242
+ <db:attribute name="comment">#{h comment}</db:attribute>
1243
+ <link href="#{h subject}" rel="related" />
1244
+ </entry>}
1245
+ resp = post("/recommendations", entry, {"Content-Type"=>"application/atom+xml"})
1246
+ if resp.code == '201'
1247
+ Recommendation.new(resp.body)
1248
+ else
1249
+ debug(resp)
1250
+ end
1251
+ end
1252
+
1253
+ def delete_recommendation(recommendation_id)
1254
+ if recommendation_id.kind_of?(Douban::Recommendation)
1255
+ recommendation_id = %r{/(\d+)$}.match(recommendation_id.id)[1]
1256
+ end
1257
+
1258
+ resp = delete("/recommendation/#{u recommendation_id}")
1259
+ if resp.code == '200'
1260
+ true
1261
+ else
1262
+ debug(resp, false)
1263
+ end
1264
+ end
1265
+
1266
+ def create_recommendation_comment(recommendation, content)
1267
+ recommendation_id = recommendation.kind_of?(Douban::Recommendation) \
1268
+ ? recommendation.recommendation_id : recommendation
1269
+ entry = %Q{<?xml version='1.0' encoding='UTF-8'?>
1270
+ <entry>
1271
+ <content>#{h content}</content>
1272
+ </entry>}
1273
+ resp = post("/recommendation/#{u recommendation_id}/comments", entry, {"Content-Type"=>"application/atom+xml"})
1274
+ if resp.code == '201'
1275
+ RecommendationComment.new(resp.body)
1276
+ else
1277
+ debug(resp)
1278
+ end
1279
+ end
1280
+
1281
+ # delete_recommendation_comment(comment:RecommendationComment)
1282
+ # delete_recommendation_comment(recommendation:Recommendation, comment_id:Integer)
1283
+ # delete_recommendation_comment(recommendation_id:Integer, comment_id:Integer)
1284
+ def delete_recommendation_comment(recommendation, comment_id=nil)
1285
+ if comment_id.nil?
1286
+ recommendation_id = recommendation.recommendation_id
1287
+ comment_id = recommendation.comment_id
1288
+ else
1289
+ recommendation_id = recommendation.kind_of?(Douban::Recommendation) \
1290
+ ? recommendation.recommendation_id : recommendation
1291
+ end
1292
+ resp = delete("/recommendation/#{u recommendation_id}/comment/#{u comment_id}")
1293
+ if resp.code == '200'
1294
+ true
1295
+ else
1296
+ debug(resp, false)
1297
+ end
1298
+ end
1299
+
1300
+ # 验证Access Token是否可用
1301
+ # http://goo.gl/8v8d
1302
+ def verify_token
1303
+ resp = get("/access_token/#{@access_token.token}")
1304
+ if resp.code == "200"
1305
+ true
1306
+ elsif resp.code == "401"
1307
+ false
1308
+ else
1309
+ debug(resp, false)
1310
+ end
1311
+ end
1312
+
1313
+ # 注销一个Access Token
1314
+ # http://goo.gl/0JAB
1315
+ def delete_token
1316
+ resp = delete("/access_token/#{@access_token.token}")
1317
+ if resp.code == "200"
1318
+ true
1319
+ else
1320
+ debug(resp, false)
1321
+ end
1322
+ end
1323
+ alias_method :logout, :delete_token
1324
+
1325
+ protected
1326
+ def new_request_consumer
1327
+ OAuth::Consumer.new(@api_key, @secret_key, @oauth_request_option)
1328
+ end
1329
+
1330
+ def new_access_consumer
1331
+ OAuth::Consumer.new(@api_key, @secret_key, @oauth_access_option)
1332
+ end
1333
+
1334
+ def debug(resp, retval=nil)
1335
+ if @@debug
1336
+ p resp.code
1337
+ p resp.body
1338
+ end
1339
+ retval
1340
+ end
1341
+
1342
+ def html_encode(o)
1343
+ CGI.escapeHTML(o.to_s)
1344
+ end
1345
+ alias_method :h, :html_encode
1346
+
1347
+ def url_encode(o)
1348
+ CGI.escape(o.to_s)
1349
+ end
1350
+ alias_method :u, :url_encode
1351
+
1352
+ def get(path,headers={})
1353
+ @access_token.get(path,headers)
1354
+ end
1355
+ def post(path,data="",headers=nil)
1356
+ headers ||= {"Content-Type" => "application/atom+xml"}
1357
+ @access_token.post(path,data,headers)
1358
+ end
1359
+ def put(path,body="",headers=nil)
1360
+ headers ||= {"Content-Type" => "application/atom+xml"}
1361
+ @access_token.put(path,body,headers)
1362
+ end
1363
+ def delete(path,headers={})
1364
+ @access_token.delete(path,headers)
1365
+ end
1366
+ def head(path,headers={})
1367
+ @access_token.head(path,headers)
1368
+ end
1369
+
1370
+ def _subject_search_args_to_url(type, *args)
1371
+ arg = args.shift
1372
+
1373
+ option = case arg
1374
+ when String
1375
+ arg2 = args.shift
1376
+ arg2 ||= {}
1377
+ arg2.merge(:q => arg)
1378
+ when Hash
1379
+ arg
1380
+ else
1381
+ raise "unknown type for first arg: #{arg.class}"
1382
+ end
1383
+
1384
+ raise "extra argument" unless args.empty?
1385
+
1386
+ if option[:q].nil? and option[:tag].nil?
1387
+ raise "you must specify :q or :tag"
1388
+ end
1389
+
1390
+ url = "/#{type}/subjects?"
1391
+ url << "q=#{u option[:q]}&" if option[:q]
1392
+ url << "tag=#{u option[:tag]}&" if option[:tag]
1393
+ url << "start_index=#{u option[:start_index]}&" if option[:start_index]
1394
+ url << "max_results=#{u option[:max_results]}&" if option[:max_results]
1395
+ url.slice!(-1)
1396
+ url
1397
+ end
1398
+ end
1399
+ end
1400
+