douban-ruby 0.0.8 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+