douban-ruby 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
Binary file
@@ -0,0 +1,40 @@
1
+ === 0.0.5 / 2010-07-04
2
+ * Douban::Authorize
3
+ * all modify_* functions will return an Object if success, otherwise nil. In
4
+ old version, it will return true or false.
5
+ * all create_* and delete_* will correctly escape the param for xml.
6
+ * all the functions return list will work correctly
7
+ * all the .rb files can be loaded. (you don't have to load from douban.rb)
8
+ * some bug fix.
9
+
10
+ === 0.0.4 / 2010-07-03
11
+ * Douban::Authorize
12
+ * all create_* functions (except create_mail) will return an Object if
13
+ success, otherwise nil, in old version, it will return true or false.
14
+ create_mail does not work like other functions, it's should be named
15
+ as send_mail.
16
+ * some refactors on the model classes.
17
+
18
+ === 0.0.3 / 2010-07-02
19
+ * Douban::Authorize
20
+ * recommendation apis added
21
+ * get_recommendation
22
+ * get_user_recommendations
23
+ * get_recommendation_comments
24
+ * create_recommendation
25
+ * delete_recommendation
26
+ * create_recommendation_comment
27
+ * delete_recommendation_comment
28
+
29
+ === 0.0.2 / 2010-07-01
30
+ * Douban.authorize no longer works, you need use Douban::Authorize.new
31
+ * Douban::Authorize
32
+ * you can restore request token with request_token=
33
+ * you can restore access token with access_token=
34
+ * create_miniblog will quote +content+ parameter
35
+ * create_miniblog will return a Douban::Miniblog if success, otherwise return nil
36
+ * add some test with rspec
37
+
38
+ === 0.0.1 / 2010-06-30
39
+
40
+ * import from http://code.google.com/p/doubanclient-ruby/
@@ -0,0 +1,34 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ examples/example.rb
6
+ lib/douban.rb
7
+ lib/douban/author.rb
8
+ lib/douban/authorize.rb
9
+ lib/douban/collection.rb
10
+ lib/douban/equal.rb
11
+ lib/douban/event.rb
12
+ lib/douban/mail.rb
13
+ lib/douban/miniblog.rb
14
+ lib/douban/note.rb
15
+ lib/douban/people.rb
16
+ lib/douban/recommendation.rb
17
+ lib/douban/recommendation_comment.rb
18
+ lib/douban/review.rb
19
+ lib/douban/subject.rb
20
+ lib/douban/tag.rb
21
+ spec/douban/author_spec.rb
22
+ spec/douban/authorize_spec.rb
23
+ spec/douban/collection_spec.rb
24
+ spec/douban/event_spec.rb
25
+ spec/douban/mail_spec.rb
26
+ spec/douban/miniblog_spec.rb
27
+ spec/douban/note_spec.rb
28
+ spec/douban/people_spec.rb
29
+ spec/douban/recommendation_comment_spec.rb
30
+ spec/douban/recommendation_spec.rb
31
+ spec/douban/review_spec.rb
32
+ spec/douban/tag_spec.rb
33
+ spec/douban_spec.rb
34
+ spec/spec_helper.rb
@@ -0,0 +1,25 @@
1
+ = Douban Ruby Client
2
+
3
+ http://github.com/lidaobing/douban-ruby
4
+
5
+ == Description
6
+
7
+ douban ruby client. including OAuth support.
8
+
9
+ Douban API reference: http://www.douban.com/service/apidoc/reference/
10
+
11
+ == Requirments
12
+
13
+ * oauth: sudo gem install oauth
14
+
15
+ == INSTALL:
16
+
17
+ sudo rake install_gem
18
+
19
+ == Usage:
20
+
21
+ http://github.com/lidaobing/douban-ruby/blob/master/examples/example.rb
22
+
23
+ == LICENSE:
24
+
25
+ licensed under Artistic License or GPL license.
@@ -0,0 +1,34 @@
1
+ require 'rubygems'
2
+ require 'hoe'
3
+ $:.unshift(File.dirname(__FILE__) + "/lib")
4
+ require 'douban'
5
+
6
+ ENV["SPEC_OPTS"] ||= "-f nested --color -b"
7
+
8
+ Hoe.spec 'douban-ruby' do
9
+ developer "Hoooopo", "hoooopo@gmail.com"
10
+ developer "LI Daobing", "lidaobing@gmail.com"
11
+ end
12
+
13
+ Hoe.plugin :minitest
14
+ Hoe.plugin :git
15
+ Hoe.plugin :gemcutter
16
+
17
+ desc "Simple require on packaged files to make sure they are all there"
18
+ task :verify => :package do
19
+ # An error message will be displayed if files are missing
20
+ if system %(ruby -e "require 'pkg/douban-#{Douban::VERSION}/lib/douban'")
21
+ puts "\nThe library files are present"
22
+ end
23
+ end
24
+
25
+ task :release => :verify
26
+
27
+ namespace :spec do
28
+ desc "Run specs with RCov"
29
+ Spec::Rake::SpecTask.new('rcov') do |t|
30
+ t.spec_files = FileList['spec/**/*_spec.rb' ]
31
+ t.rcov = true
32
+ t.rcov_opts = ['--exclude' , 'gems,spec' ]
33
+ end
34
+ end
@@ -0,0 +1,21 @@
1
+ require 'rubygems'
2
+ require 'douban'
3
+
4
+ class MyDouban < Douban::Authorize
5
+ def initialize
6
+ @apikey = '042bc009d7d4a04d0c83401d877de0e7'
7
+ @secret = 'a9bb2d7f8cc00110'
8
+ super(@apikey, @secret)
9
+ end
10
+
11
+ end
12
+
13
+ douban = MyDouban.new
14
+
15
+ print "please open #{douban.get_authorize_url}\n"
16
+ print "after login, press Enter to continue\n"
17
+
18
+ gets
19
+
20
+ douban.auth
21
+ print douban.get_people
@@ -0,0 +1,57 @@
1
+ #=douban.rb
2
+ #
3
+ # Copyright (c) 2008 Hooopo
4
+ # Written and maintained by Hooopo<hoooopo@gmail.com>
5
+ #
6
+ #
7
+ # This program is free software. You can re-distribute and/or
8
+ # modify this program under the same terms of ruby itself ---
9
+ # Ruby Distribution License or GNU General Public License.
10
+ #
11
+ # See Douban::Authorize for an overview and examples.
12
+ #
13
+ #== Douban API Client Based Ruby
14
+ # This is the Douban Ruby library, which communicates to the Douban
15
+ # API REST servers and converts Atom into proper Ruby objects.
16
+ # In order to use the library, you need to sign up for a Douban account and
17
+ # ensure you have the oauth and modules installed.
18
+ # Douban account and ApiKey,SecrectKey
19
+ # To get a Douban account,you need to sign up at:
20
+ # http://www.douban.com/register
21
+ # Once approved, you will need to create an application to get your api key and
22
+ # secret key at :
23
+ # http://www.douban.com/service/apikey/
24
+ # You can get apikey and secrectkey ,if you create an application.
25
+ # The keys can be placed into douban.yaml or specified at runtime
26
+ # when you new a Douban::Authorize.
27
+ # Depencencies
28
+ # This library has some external dependencies:
29
+ # oauth ( http://oauth.googlecode.com/svn/code/ruby )
30
+ # You can install oauth via rubygem:
31
+ # Gem install oauth -y
32
+ #==Usage
33
+ # require "douban"
34
+ # client=Douban.authorize(apikey, secret)
35
+ # authorize_url=client.get_authorize_url
36
+ # NOTE:
37
+ # Permission to access a user's data is restricted -- you can't just access
38
+ # any user's data. In order to access a user's data, they need to have
39
+ # visit the authorize_url and press the 'agree' button .
40
+ # when user has press the 'agree' button ,we use client.auth to authorize.
41
+ # client=client.auth
42
+ # puts client.authoried? #return true or false
43
+ # Get users' info via his uid
44
+ # #-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#-#
45
+ # people = client.get_people('hooopo')
46
+ # people.class === People
47
+ # people.title === hooopo
48
+ # people.location === "长沙"
49
+ #==Install and Wiki
50
+ # http://code.google.com/p/doubanclient-ruby/
51
+
52
+ require 'douban/authorize'
53
+
54
+ module Douban
55
+ VERSION = "0.0.5"
56
+ end
57
+
@@ -0,0 +1,41 @@
1
+ require 'rexml/document'
2
+ require 'douban/equal'
3
+
4
+ module Douban
5
+ class Author
6
+ include Douban::Equal
7
+
8
+ class << self
9
+ def attr_names
10
+ [
11
+ :uri,
12
+ :link,
13
+ :name
14
+ ]
15
+ end
16
+ end
17
+ attr_names.each do |attr|
18
+ attr_accessor attr
19
+ end
20
+ def initialize(atom=nil)
21
+ doc = case atom
22
+ when REXML::Document then atom.root
23
+ when REXML::Element then atom
24
+ when nil then nil
25
+ else REXML::Document.new(atom).root
26
+ end
27
+
28
+ unless doc.nil?
29
+ REXML::XPath.each(doc,"./link") do|link|
30
+ @link||={}
31
+ @link[link.attributes['rel']]=link.attributes['href']
32
+ end
33
+ name=REXML::XPath.first(doc,"./name")
34
+ @name=name.text if name
35
+ uri=REXML::XPath.first(doc,"./uri")
36
+ @uri=uri.text if uri
37
+ end
38
+ end
39
+ end
40
+ end
41
+
@@ -0,0 +1,1150 @@
1
+ require 'rubygems'
2
+ gem 'oauth'
3
+ require 'oauth'
4
+ require 'oauth/consumer'
5
+ require 'rexml/document'
6
+ require 'net/http'
7
+
8
+ require 'douban/author'
9
+ require 'douban/collection'
10
+ require 'douban/event'
11
+ require 'douban/mail'
12
+ require 'douban/miniblog'
13
+ require 'douban/note'
14
+ require 'douban/people'
15
+ require 'douban/recommendation'
16
+ require 'douban/recommendation_comment'
17
+ require 'douban/review'
18
+ require 'douban/subject'
19
+ require 'douban/tag'
20
+
21
+ module Douban
22
+ class Authorize
23
+ attr_reader :api_key
24
+ attr_reader :api_secret
25
+ attr_reader :authorize_url
26
+ attr_reader :consumer
27
+ attr_reader :request_token
28
+ attr_reader :access_token
29
+
30
+ @@debug = false
31
+
32
+ @@default_oauth_request_options = {
33
+ :signature_method=>"HMAC-SHA1",
34
+ :site=>"http://www.douban.com",
35
+ :request_token_path=>"/service/auth/request_token",
36
+ :access_token_path=>"/service/auth/access_token",
37
+ :authorize_path=>"/service/auth/authorize",
38
+ :scheme=>:header,
39
+ :realm=>"http://www.example.com/"
40
+ }
41
+
42
+ @@default_oauth_access_options = {
43
+ :site=>"http://api.douban.com",
44
+ :scheme=>:header,
45
+ :signature_method=>"HMAC-SHA1",
46
+ :realm=>"http://www.example.com/"
47
+ }
48
+
49
+
50
+ def self.debug=(val)
51
+ @@debug = val
52
+ end
53
+
54
+ def initialize(api_key, secret_key, options={})
55
+ @api_key=api_key
56
+ @secret_key=secret_key
57
+ @oauth_request_option = @@default_oauth_request_options.merge(options)
58
+ @oauth_access_option = @@default_oauth_access_options.merge(options)
59
+ yield self if block_given?
60
+ self
61
+ end
62
+
63
+ def authorized?
64
+ ! @access_token.nil?
65
+ end
66
+
67
+ def get_authorize_url(oauth_callback=nil)
68
+ oauth_callback ||= @oauth_request_option[:realm]
69
+
70
+ @consumer=new_request_consumer
71
+ @request_token=@consumer.get_request_token
72
+ @authorize_url="#{@request_token.authorize_url}&oauth_callback=#{CGI.escape(oauth_callback)}"
73
+ yield @request_token if block_given?
74
+ @authorize_url
75
+ end
76
+
77
+ def auth
78
+ begin
79
+ @access_token=@request_token.get_access_token
80
+ @access_token=OAuth::AccessToken.new(new_access_consumer,
81
+ @access_token.token,
82
+ @access_token.secret
83
+ )
84
+ rescue
85
+ #raise $!
86
+ ensure
87
+ yield self if block_given?
88
+ return self
89
+ end
90
+ end
91
+ def get_people(uid="@me")
92
+ resp=get("/people/#{u uid}")
93
+ if resp.code=="200"
94
+ atom=resp.body
95
+ People.new(atom)
96
+ else
97
+ nil
98
+ end
99
+ end
100
+
101
+ def get_friends(uid="@me",option={:start_index=>1,:max_results=>10})
102
+ resp=get("/people/#{u uid.to_s}/friends?start-index=#{u option[:start_index]}&max-results=#{u option[:max_results]}")
103
+ if resp.code=="200"
104
+ friends=[]
105
+ atom=resp.body
106
+ doc=REXML::Document.new(atom)
107
+ REXML::XPath.each(doc,"//entry") do |entry|
108
+ friends << People.new(entry)
109
+ end
110
+ friends
111
+ else
112
+ nil
113
+ end
114
+ end
115
+
116
+ def get_contacts(uid="@me",option={:start_index=>1,:max_results=>10})
117
+ resp=get("/people/#{u uid.to_s}/contacts?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
118
+ if resp.code=="200"
119
+ contacts=[]
120
+ atom=resp.body
121
+ doc=REXML::Document.new(atom)
122
+ REXML::XPath.each(doc,"//entry") do |entry|
123
+ contacts << People.new(entry)
124
+ end
125
+ contacts
126
+ else
127
+ nil
128
+ end
129
+ end
130
+
131
+
132
+ def search_people(q="",option={:start_index=>1,:max_results=>10})
133
+ resp=get("/people?q=#{u(q.to_s)}&start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
134
+ if resp.code=="200"
135
+ results=[]
136
+ atom=resp.body
137
+ doc=REXML::Document.new(atom)
138
+ REXML::XPath.each(doc,"//entry") do |entry|
139
+ results << People.new(entry)
140
+ end
141
+ results
142
+ else
143
+ nil
144
+ end
145
+ end
146
+ def get_book(id="")
147
+ if id.to_s.size >=10
148
+ resp=get("/book/subject/isbn/#{u(id.to_s)}")
149
+ else
150
+ resp=get("/book/subject/#{u(id.to_s)}")
151
+ end
152
+ if resp.code=="200"
153
+ atom=resp.body
154
+ Book.new(atom)
155
+ else
156
+ nil
157
+ end
158
+ end
159
+ def get_movie(id="")
160
+ if id.to_s=~/^tt\d+/
161
+ resp=get("/movie/subject/imdb/#{u(id.to_s)}")
162
+ else
163
+ resp=get("/movie/subject/#{u(id.to_s)}")
164
+ end
165
+ if resp.code=="200"
166
+ atom=resp.body
167
+ Movie.new(atom)
168
+ else
169
+ nil
170
+ end
171
+ end
172
+ def get_music(id=nil)
173
+ resp=get("/music/subject/#{u(id.to_s)}")
174
+ if resp.code=="200"
175
+ atom=resp.body
176
+ Music.new(atom)
177
+ else
178
+ nil
179
+ end
180
+ end
181
+ def search_book(tag="",option={:start_index=>1,:max_results=>10})
182
+ resp=get("/book/subjects?tag=#{u(tag.to_s)}&start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
183
+ if resp.code=="200"
184
+ atom=resp.body
185
+ doc=REXML::Document.new(atom)
186
+ books=[]
187
+ REXML::XPath.each(doc,"//entry") do |entry|
188
+ books << Book.new(entry)
189
+ end
190
+ books
191
+ else
192
+ nil
193
+ end
194
+
195
+ end
196
+ def search_movie(tag="",option={:start_index=>1,:max_results=>10})
197
+ resp=get("/movie/subjects?tag=#{u(tag.to_s)}&start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
198
+ if resp.code=="200"
199
+ atom=resp.body
200
+ doc=REXML::Document.new(atom)
201
+ movies=[]
202
+ REXML::XPath.each(doc,"//entry") do |entry|
203
+ movies << Movie.new(entry)
204
+ end
205
+ movies
206
+ else
207
+ nil
208
+ end
209
+ end
210
+ def search_music(tag="",option={:start_index=>1,:max_results=>10})
211
+ resp=get("/music/subjects?tag=#{u(tag)}&start-index=#{option['start-index']}&max-results=#{option['max-results']}")
212
+ if resp.code=="200"
213
+ atom=resp.body
214
+ doc=REXML::Document.new(atom)
215
+ music=[]
216
+ REXML::XPath.each(doc,"//entry") do |entry|
217
+ music << Music.new(entry)
218
+ end
219
+ music
220
+ else
221
+ nil
222
+ end
223
+ end
224
+ def get_review(id="")
225
+ resp=get("/review/#{u(id.to_s)}")
226
+ if resp.code=="200"
227
+ atom=resp.body
228
+ Review.new(atom)
229
+ else
230
+ nil
231
+ end
232
+ end
233
+ def get_user_reviews(user_id="@me",option={:start_index=>1,:max_results=>10,:orderby=>'score'})
234
+ resp=get("/people/#{u(user_id.to_s)}/reviews?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}&orderby=#{option[:orderby]}")
235
+ if resp.code=="200"
236
+ atom=resp.body
237
+ reviews=[]
238
+ doc=REXML::Document.new(atom)
239
+ REXML::XPath.each(doc,"//entry") do |entry|
240
+ reviews<< Review.new(entry)
241
+ end
242
+ reviews
243
+ else
244
+ debug(resp)
245
+ end
246
+ end
247
+ def get_movie_reviews(subject_id="",option={:start_index=>1,:max_results=>10,:orderby=>'score'})
248
+ resp=get("/movie/subject/#{u(subject_id.to_s)}/reviews?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}&orderby=#{option[:orderby]}")
249
+ if resp.code=="200"
250
+ atom=resp.body
251
+ reviews=[]
252
+ doc=REXML::Document.new(atom)
253
+ REXML::XPath.each(doc,"//entry") do |entry|
254
+
255
+ reviews<< Review.new(entry)
256
+ end
257
+ reviews
258
+ else
259
+ nil
260
+ end
261
+ end
262
+ def get_music_reviews(subject_id="",option={:start_index=>1,:max_results=>10,:orderby=>'score'})
263
+ resp=get("/music/subject/#{u(subject_id.to_s)}/reviews?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}&orderby=#{option[:orderby]}")
264
+ if resp.code=="200"
265
+ atom=resp.body
266
+ reviews=[]
267
+ doc=REXML::Document.new(atom)
268
+ REXML::XPath.each(doc,"//entry") do |entry|
269
+ reviews<< Review.new(entry)
270
+ end
271
+ reviews
272
+ else
273
+ nil
274
+ end
275
+ end
276
+ def get_book_reviews(subject_id="",option={:start_index=>1,:max_results=>10,:orderby=>'score'})
277
+ resp=get("/book/subject/#{u(subject_id.to_s)}/reviews?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}&orderby=#{option[:orderby]}")
278
+ if resp.code=="200"
279
+ atom=resp.body
280
+ reviews=[]
281
+ doc=REXML::Document.new(atom)
282
+ REXML::XPath.each(doc,"//entry") do |entry|
283
+ reviews<< Review.new(entry)
284
+ end
285
+ reviews
286
+ else
287
+ nil
288
+ end
289
+ end
290
+
291
+ def delete_review(review)
292
+ review_id = case review
293
+ when Review then review.review_id
294
+ else review
295
+ end
296
+
297
+ resp=delete("/review/#{u(review_id.to_s)}")
298
+ if resp.code=="200"
299
+ true
300
+ else
301
+ false
302
+ end
303
+ end
304
+
305
+ def create_review(subject_link="",title="",content="",rating=5)
306
+ subject_link = subject_link.id if subject_link.kind_of?(Subject)
307
+
308
+ entry=%Q{<?xml version='1.0' encoding='UTF-8'?>
309
+ <entry xmlns:ns0="http://www.w3.org/2005/Atom">
310
+ <db:subject xmlns:db="http://www.douban.com/xmlns/">
311
+ <id>#{h subject_link}</id>
312
+ </db:subject>
313
+ <content>#{h content}</content>
314
+ <gd:rating xmlns:gd="http://schemas.google.com/g/2005" value="#{h rating}" ></gd:rating>
315
+ <title>#{h title}</title>
316
+ </entry>
317
+ }
318
+ resp=post("/reviews",entry,{"Content-Type" => "application/atom+xml"})
319
+ if resp.code=="201"
320
+ Review.new(resp.body)
321
+ else
322
+ debug(resp)
323
+ end
324
+ end
325
+
326
+ def modify_review(review, subject_link=nil,title="",content="",rating=5)
327
+ review_id = case review
328
+ when Review then review.review_id
329
+ else review
330
+ end
331
+
332
+ subject_link = review.subject.id if subject_link.nil? and review.kind_of?(Review)
333
+
334
+ entry=%Q{<?xml version='1.0' encoding='UTF-8'?>
335
+ <entry xmlns:ns0="http://www.w3.org/2005/Atom">
336
+ <id>http://api.douban.com/review/#{h review_id}</id>
337
+ <db:subject xmlns:db="http://www.douban.com/xmlns/">
338
+ <id>#{h subject_link}</id>
339
+ </db:subject>
340
+ <content>#{h content}</content>
341
+ <gd:rating xmlns:gd="http://schemas.google.com/g/2005" value="#{h rating}" ></gd:rating>
342
+ <title>#{h title}</title>
343
+ </entry>
344
+ }
345
+ resp=put("/review/#{u(review_id)}",entry,{"Content-Type" => "application/atom+xml"})
346
+ if resp.code=="202"
347
+ Review.new(resp.body)
348
+ else
349
+ debug(resp)
350
+ end
351
+ end
352
+ def get_collection(collection_id="")
353
+ resp=get("/collection/#{u(collection_id.to_s)}")
354
+ if resp.code=="200"
355
+ atom=resp.body
356
+ Collection.new(atom)
357
+ else
358
+ nil
359
+ end
360
+ end
361
+ def get_user_collection(
362
+ user_id="@me",
363
+ option={
364
+ :cat=>'',
365
+ :tag=>'',
366
+ :status=>'',
367
+ :start_index=>1,
368
+ :max_results=>10,
369
+ :updated_max=>'',
370
+ :updated_min=>''
371
+ }
372
+ )
373
+ 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]}")
374
+ if resp.code=="200"
375
+ atom=resp.body
376
+ doc=REXML::Document.new(atom)
377
+ author=REXML::XPath.first(doc,"//feed/author")
378
+ author=Author.new(author.to_s) if author
379
+ title=REXML::XPath.first(doc,"//feed/title")
380
+ title=title.text if title
381
+ collections=[]
382
+ REXML::XPath.each(doc,"//entry") do |entry|
383
+ collection=Collection.new(entry)
384
+ collection.author=author
385
+ collection.title=title
386
+ collections<< collection
387
+ end
388
+ collections
389
+ else
390
+ nil
391
+ end
392
+ end
393
+ def create_collection( subject_id="",content="",rating=5,status="",tag=[],option={ :privacy=>"public"})
394
+ db_tag=""
395
+ if tag.size==0
396
+ db_tag='<db:tag name="" />'
397
+ else
398
+ tag.each do |t|
399
+ db_tag+=%Q{<db:tag name="#{h t}" />}
400
+ end
401
+ end
402
+ entry=%Q{<?xml version='1.0' encoding='UTF-8'?>
403
+ <entry xmlns:ns0="http://www.w3.org/2005/Atom" xmlns:db="http://www.douban.com/xmlns/">
404
+ <db:status>#{h status}</db:status>
405
+ #{db_tag}
406
+ <gd:rating xmlns:gd="http://schemas.google.com/g/2005" value="#{h rating}" />
407
+ <content>#{h content}</content>
408
+ <db:subject>
409
+ <id>#{h subject_id}</id>
410
+ </db:subject>
411
+ <db:attribute name="privacy">#{h option[:privacy]}</db:attribute>
412
+ </entry>
413
+ }
414
+ resp=post("/collection",entry,{"Content-Type"=>"application/atom+xml"})
415
+ if resp.code=="201"
416
+ Collection.new(resp.body)
417
+ else
418
+ debug(resp)
419
+ end
420
+ end
421
+
422
+ def modify_collection(collection, subject_id="",content="",rating=5,status="",tag=[],option={:privacy=>"public"})
423
+ collection_id = case collection
424
+ when Collection then collection.collection_id
425
+ else collection
426
+ end
427
+
428
+ subject_id = collection.subject.id if subject_id.nil? and collection.kind_of?(Collection)
429
+
430
+ db_tag=""
431
+ if tag.size==0
432
+ db_tag='<db:tag name="" />'
433
+ else
434
+ tag.each do |t|
435
+ db_tag+=%Q{<db:tag name="#{h t}" />}
436
+ end
437
+ end
438
+ entry=%Q{<?xml version='1.0' encoding='UTF-8'?>
439
+ <entry xmlns:ns0="http://www.w3.org/2005/Atom" xmlns:db="http://www.douban.com/xmlns/">
440
+ <id>http://api.douban.com/collection/#{h collection_id}</id>
441
+ <db:status>#{h status}</db:status>
442
+
443
+ #{db_tag}
444
+ <gd:rating xmlns:gd="http://schemas.google.com/g/2005" value="#{h rating}" />
445
+ <content>#{h content}</content>
446
+ <db:subject>
447
+ <id>#{h subject_id}</id>
448
+ </db:subject>
449
+ <db:attribute name="privacy">#{h option[:privacy]}</db:attribute>
450
+ </entry>
451
+ }
452
+ resp=put("/collection/#{u collection_id}",entry,{"Content-Type"=>"application/atom+xml"})
453
+ # resp.code should be 202, but currently it's 200
454
+ # http://www.douban.com/group/topic/12451628/
455
+ if resp.code=="200" or resp.code == "202"
456
+ Collection.new(resp.body)
457
+ else
458
+ debug(resp)
459
+ end
460
+ end
461
+ def delete_collection(collection)
462
+ collection_id = case collection
463
+ when Collection then collection.collection_id
464
+ else collection
465
+ end
466
+
467
+ resp=delete("/collection/#{u(collection_id.to_s)}")
468
+ if resp.code=="200"
469
+ true
470
+ else
471
+ false
472
+ end
473
+ end
474
+ def get_user_miniblog(user_id="@me",option={:start_index=>1,:max_results=>10})
475
+ resp=get("/people/#{u(user_id.to_s)}/miniblog?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
476
+ if resp.code=="200"
477
+ atom=resp.body
478
+ doc=REXML::Document.new(atom)
479
+ author=REXML::XPath.first(doc,"//feed/author")
480
+ author=Author.new(author.to_s) if author
481
+ miniblogs=[]
482
+ REXML::XPath.each(doc,"//feed/entry") do |entry|
483
+ miniblog=Miniblog.new(entry)
484
+ miniblog.author=author
485
+ miniblogs<< miniblog
486
+ end
487
+ miniblogs
488
+ else
489
+ nil
490
+ end
491
+ end
492
+ def get_user_contact_miniblog(user_id="@me",option={:start_index=>1,:max_results=>10})
493
+ resp=get("/people/#{u(user_id.to_s)}/miniblog/contacts?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
494
+ if resp.code=="200"
495
+ atom=resp.body
496
+ doc=REXML::Document.new(atom)
497
+ miniblogs=[]
498
+ REXML::XPath.each(doc,"//feed/entry") do |entry|
499
+ miniblog=Miniblog.new(entry)
500
+ miniblogs<< miniblog
501
+ end
502
+ miniblogs
503
+ else
504
+ nil
505
+ end
506
+ end
507
+ def create_miniblog(content="")
508
+ entry=%Q{<?xml version='1.0' encoding='UTF-8'?>
509
+ <entry xmlns:ns0="http://www.w3.org/2005/Atom" xmlns:db="http://www.douban.com/xmlns/">
510
+ <content>#{h content}</content>
511
+ </entry>}
512
+ resp=post("/miniblog/saying",entry,{"Content-Type"=>"application/atom+xml"})
513
+ if resp.code=="201"
514
+ Miniblog.new(resp.body)
515
+ else
516
+ nil
517
+ end
518
+ end
519
+ def delete_miniblog(miniblog_id="")
520
+ resp=delete("/miniblog/#{u(miniblog_id.to_s)}")
521
+ if resp.code=="200"
522
+ true
523
+ else
524
+ false
525
+ end
526
+ end
527
+ def get_note(note_id="")
528
+ resp=get("/note/#{u(note_id.to_s)}")
529
+ if resp.code=="200"
530
+ atom=resp.body
531
+ Note.new(atom)
532
+ else
533
+ nil
534
+ end
535
+ end
536
+
537
+ def get_user_notes(user_id="@me",option={:start_index=>1,:max_results=>10})
538
+ resp=get("/people/#{u(user_id.to_s)}/notes?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
539
+ if resp.code=="200"
540
+ atom=resp.body
541
+ doc=REXML::Document.new(atom)
542
+ author=REXML::XPath.first(doc,"//feed/author")
543
+ author=Author.new(author.to_s) if author
544
+ notes=[]
545
+ REXML::XPath.each(doc,"//feed/entry") do |entry|
546
+ note=Note.new(entry)
547
+ note.author=author
548
+ notes << note
549
+ end
550
+ notes
551
+ else
552
+ nil
553
+ end
554
+ end
555
+
556
+ def create_note(title="",content="",option={:privacy=>"public",:can_reply=>"yes"})
557
+ entry=%Q{<?xml version="1.0" encoding="UTF-8"?>
558
+ <entry xmlns="http://www.w3.org/2005/Atom" xmlns:db="http://www.douban.com/xmlns/">
559
+ <title>#{h title}</title>
560
+ <content>#{h content}</content>
561
+ <db:attribute name="privacy">#{h option[:privacy]}</db:attribute>
562
+ <db:attribute name="can_reply">#{h option[:can_reply]}</db:attribute>
563
+ </entry>
564
+ }
565
+ resp=post("/notes",entry,{"Content-Type"=>"application/atom+xml"})
566
+ if resp.code=="201"
567
+ Note.new(resp.body)
568
+ else
569
+ debug(resp)
570
+ end
571
+ end
572
+
573
+ def delete_note(note_id="")
574
+ note_id = note_id.note_id if note_id.kind_of?(Note)
575
+
576
+ resp=delete("/note/#{u(note_id.to_s)}")
577
+ if resp.code=="200"
578
+ true
579
+ else
580
+ false
581
+ end
582
+ end
583
+
584
+ def modify_note(note,title="",content="",option={:privacy=>"public",:can_reply=>"yes"})
585
+ note_id = case note
586
+ when Note then note.note_id
587
+ else note
588
+ end
589
+
590
+ entry=%Q{<?xml version="1.0" encoding="UTF-8"?>
591
+ <entry xmlns="http://www.w3.org/2005/Atom" xmlns:db="http://www.douban.com/xmlns/">
592
+ <title>#{h title}</title>
593
+ <content>#{h content}</content>
594
+ <db:attribute name="privacy">#{h option[:privacy]}</db:attribute>
595
+ <db:attribute name="can_reply">#{h option[:can_reply]}</db:attribute>
596
+ </entry>
597
+ }
598
+ resp=put("/note/#{u(note_id.to_s)}",entry,{"Content-Type"=>"application/atom+xml"})
599
+ if resp.code=="202"
600
+ Note.new(resp.body)
601
+ else
602
+ debug(resp)
603
+ end
604
+ end
605
+ def get_event(event_id="")
606
+ resp=get("/event/#{u(event_id.to_s)}")
607
+ if resp.code=="200"
608
+ atom=resp.body
609
+ Event.new(atom)
610
+ else
611
+ nil
612
+ end
613
+ end
614
+
615
+ # <b>DEPRECATED:</b> Please use <tt>get_event_participant_people</tt> instead.
616
+ def get_participant_people(*args)
617
+ warn "[DEPRECATION] `get_participant_people` is deprecated. Please use `get_event_participant_people` instead."
618
+ get_event_participant_people(*args)
619
+ end
620
+
621
+ def get_event_participant_people(event_id=nil,option={:start_index=>1,:max_results=>10})
622
+ resp=get("/event/#{u(event_id.to_s)}/participants?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
623
+ if resp.code=="200"
624
+ people=[]
625
+ atom=resp.body
626
+ doc=REXML::Document.new(atom)
627
+ REXML::XPath.each(doc,"//feed/entry") do |entry|
628
+ people<< People.new(entry)
629
+ end
630
+ people
631
+ else
632
+ nil
633
+ end
634
+ end
635
+
636
+ # <b>DEPRECATED:</b> Please use <tt>get_event_wisher_people</tt> instead.
637
+ def get_wisher_people(*args)
638
+ warn "[DEPRECATION] `get_wisher_people` is deprecated. Please use `get_event_wisher_people` instead."
639
+ get_event_wisher_people(*args)
640
+ end
641
+
642
+ def get_event_wisher_people(event_id=nil,option={:start_index=>1,:max_results=>10})
643
+ resp=get("/event/#{u(event_id.to_s)}/wishers?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
644
+ if resp.code=="200"
645
+ people=[]
646
+ atom=resp.body
647
+ doc=REXML::Document.new(atom)
648
+ REXML::XPath.each(doc,"//feed/entry") do |entry|
649
+ people<< People.new(entry)
650
+ end
651
+ people
652
+ else
653
+ nil
654
+ end
655
+ end
656
+ def get_user_events(user_id="@me",option={:start_index=>1,:max_results=>10})
657
+ resp=get("/people/#{u(user_id.to_s)}/events?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
658
+ if resp.code=="200"
659
+ events=[]
660
+ atom=resp.body
661
+ doc=REXML::Document.new(atom)
662
+ REXML::XPath.each(doc,"//feed/entry") do |entry|
663
+ events<< Event.new(entry)
664
+ end
665
+ events
666
+ else
667
+ nil
668
+ end
669
+ end
670
+ def get_user_initiate_events(user_id="@me",option={:start_index=>1,:max_results=>10})
671
+ resp=get("/people/#{u(user_id.to_s)}/events/initiate?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
672
+ if resp.code=="200"
673
+ events=[]
674
+ atom=resp.body
675
+ doc=REXML::Document.new(atom)
676
+ REXML::XPath.each(doc,"//feed/entry") do |entry|
677
+ events<< Event.new(entry)
678
+ end
679
+ events
680
+ else
681
+ nil
682
+ end
683
+ end
684
+ def get_user_participate_events(user_id="@me",option={:start_index=>1,:max_results=>10})
685
+ resp=get("/people/#{u(user_id.to_s)}/events/participate?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
686
+ if resp.code=="200"
687
+ events=[]
688
+ atom=resp.body
689
+ doc=REXML::Document.new(atom)
690
+ REXML::XPath.each(doc,"//feed/entry") do |entry|
691
+ events<< Event.new(entry)
692
+ end
693
+ events
694
+ else
695
+ nil
696
+ end
697
+ end
698
+ def get_user_wish_events(user_id="@me",option={:start_index=>1,:max_results=>10})
699
+ resp=get("/people/#{u(user_id.to_s)}/events/wish?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
700
+ if resp.code=="200"
701
+ events=[]
702
+ atom=resp.body
703
+ doc=REXML::Document.new(atom)
704
+ REXML::XPath.each(doc,"//feed/entry") do |entry|
705
+ events<< Event.new(entry)
706
+ end
707
+ events
708
+ else
709
+ nil
710
+ end
711
+ end
712
+
713
+ def get_city_events(location_id=nil,option={:type=>"all",:start_index=>1,:max_results=>10})
714
+ resp=get("/event/location/#{u(location_id.to_s)}?type=#{option[:type]}&start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
715
+ if resp.code=="200"
716
+ events=[]
717
+ atom=resp.body
718
+ doc=REXML::Document.new(atom)
719
+ REXML::XPath.each(doc,"//feed/entry") do |entry|
720
+ events<< Event.new(entry)
721
+ end
722
+ events
723
+ else
724
+ nil
725
+ end
726
+ end
727
+ def search_events(q="",option={:location=>"all",:start_index=>1,:max_results=>10})
728
+ resp=get("/events?q=#{u(q.to_s)}&location=#{option[:location]}&start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
729
+ if resp.code=="200"
730
+ events=[]
731
+ atom=resp.body
732
+ doc=REXML::Document.new(atom)
733
+ REXML::XPath.each(doc,"//feed/entry") do |entry|
734
+ events<< Event.new(entry)
735
+ end
736
+ events
737
+ else
738
+ nil
739
+ end
740
+ end
741
+ 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")}})
742
+ entry=%Q{<?xml version="1.0" encoding="UTF-8"?>
743
+ <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/">
744
+ <title>#{h title}</title>
745
+ <category scheme="http://www.douban.com/2007#kind" term="http://www.douban.com/2007#event.#{option[:kind]}"/>
746
+ <content>#{h content}</content>
747
+ <db:attribute name="invite_only">#{h option[:invite_only]}</db:attribute>
748
+ <db:attribute name="can_invite">#{h option[:can_invite]}</db:attribute>
749
+ <gd:when endTime="#{h option[:when]["endTime"]}" startTime="#{h option[:when]["startTime"]}"/>
750
+ <gd:where valueString="#{h where}" />
751
+ </entry>
752
+ }
753
+ resp=post("/events",entry,{"Content-Type"=>"application/atom+xml"})
754
+ if resp.code=="201"
755
+ Event.new(resp.body)
756
+ else
757
+ debug(resp)
758
+ end
759
+ end
760
+
761
+ def modify_event(event,title=nil,content=nil,where=nil,option=nil)
762
+
763
+ event_id = case event
764
+ when Event then event.event_id
765
+ else event
766
+ end
767
+
768
+ option = {} if option.nil?
769
+ option = {:kind=>"exhibit",
770
+ :invite_only=>"no",
771
+ :can_invite=>"yes",
772
+ :when=>{"endTime"=>(Time.now+60*60*24*5).strftime("%Y-%m-%dT%H:%M:%S+08:00"),
773
+ "startTime"=>Time.now.strftime("%Y-%m-%dT%H:%M:%S+08:00")}}.merge(option)
774
+
775
+ entry=%Q{<?xml version="1.0" encoding="UTF-8"?>
776
+ <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/">
777
+ <title>#{h title}</title>
778
+ <category scheme="http://www.douban.com/2007#kind" term="http://www.douban.com/2007#event.#{option[:kind]}"/>
779
+ <content>#{h content}</content>
780
+ <db:attribute name="invite_only">#{h option[:invite_only]}</db:attribute>
781
+ <db:attribute name="can_invite">#{h option[:can_invite]}</db:attribute>
782
+ <gd:when endTime="#{h option[:when]["endTime"]}" startTime="#{h option[:when]["startTime"]}"/>
783
+ <gd:where valueString="#{h where}" />
784
+ </entry>
785
+ }
786
+ resp=put("/event/#{u(event_id)}",entry,{"Content-Type"=>"application/atom+xml"})
787
+ if resp.code=="200"
788
+ Event.new(resp.body)
789
+ else
790
+ debug(resp)
791
+ end
792
+ end
793
+
794
+ def get_mail_inbox(option={:start_index=>1,:max_results=>10})
795
+ resp=get("/doumail/inbox?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
796
+ if resp.code=="200"
797
+ mails=[]
798
+ atom=resp.body
799
+ doc=REXML::Document.new(atom)
800
+ REXML::XPath.each(doc,"//entry") do |entry|
801
+ mails << Mail.new(entry)
802
+ end
803
+ mails
804
+ else
805
+ nil
806
+ end
807
+ end
808
+ def get_unread_mail(option={:start_index=>1,:max_results=>10})
809
+ resp=get("/doumail/inbox/unread?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
810
+ if resp.code=="200"
811
+ mails=[]
812
+ atom=resp.body
813
+ doc=REXML::Document.new(atom)
814
+ REXML::XPath.each(doc,"//entry") do |entry|
815
+ mails << Mail.new(entry)
816
+ end
817
+ mails
818
+ else
819
+ nil
820
+ end
821
+ end
822
+ def get_mail_outbox(option={:start_index=>1,:max_results=>10})
823
+ resp=get("/doumail/outbox?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
824
+ if resp.code=="200"
825
+ mails=[]
826
+ atom=resp.body
827
+ doc=REXML::Document.new(atom)
828
+ REXML::XPath.each(doc,"//entry") do |entry|
829
+ mails << Mail.new(entry)
830
+ end
831
+ mails
832
+ else
833
+ nil
834
+ end
835
+ end
836
+ def get_mail(mail_id="",keep_unread="false")
837
+ resp=get("/doumail/#{u(mail_id.to_s)}?keep-unread=#{keep_unread}")
838
+ if resp.code=="200"
839
+ atom=resp.body
840
+ Mail.new(atom)
841
+ else
842
+ nil
843
+ end
844
+ end
845
+
846
+ # <b>DEPRECATED:</b> Please use <tt>send_mail</tt> instead.
847
+ def create_mail(*args)
848
+ warn "[DEPRECATION] `create_mail` is deprecated. Please use `send_mail` instead."
849
+ send_mail(*args)
850
+ end
851
+
852
+ def send_mail(id="",title="",content="",captcha_token="",captcha_string="")
853
+ if !(captcha_token.empty?&&captcha_string.empty?)
854
+ entry=%Q(<?xml version="1.0" encoding="UTF-8"?>
855
+ <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/">
856
+ <db:entity name="receiver"><uri>http://api.douban.com/people/#{h id}</uri></db:entity>
857
+ <content>#{h content}</content>
858
+ <title>#{h title}</title>
859
+ <db:attribute name="captcha_token">#{h captcha_token}</db:attribute>
860
+ <db:attribute name="captcha_string">#{h captcha_string}</db:attribute>
861
+ </entry>)
862
+ else
863
+ entry=%Q(<?xml version="1.0" encoding="UTF-8"?>
864
+ <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/">
865
+ <db:entity name="receiver">
866
+ <uri>http://api.douban.com/people/#{h id}</uri></db:entity>
867
+ <content>#{h content}</content>
868
+ <title>#{h title}</title>
869
+ </entry>)
870
+ end
871
+ resp=post("/doumails",entry,{"Content-Type"=>"application/atom+xml"})
872
+ if resp.code=="201"
873
+ true
874
+ elsif resp.code=="403"
875
+ hash={}
876
+ str=CGI.unescapeHTML(resp.body)
877
+ hash[:token]=str.scan(/^captcha_token=(.*?)&/).flatten.to_s
878
+ hash[:url]=str.scan(/captcha_url=(.*?)$/).flatten.to_s
879
+ hash
880
+ else
881
+ debug(resp)
882
+ end
883
+ end
884
+ def read_mail(mail_id="")
885
+ 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>}
886
+ resp=put("/doumail/#{mail_id.to_s}",entry,{"Content-Type" => "application/atom+xml"})
887
+ if resp.code=="202"
888
+ true
889
+ else
890
+ false
891
+ end
892
+ end
893
+ def delete_mail(mail_id="")
894
+ resp=delete("/doumail/#{mail_id.to_s}")
895
+ if resp.code=="200"
896
+ true
897
+ else
898
+ false
899
+ end
900
+ end
901
+ def read_mails(mail_ids=[])
902
+ entrys=""
903
+ mail_ids.each do |mail_id|
904
+ entrys +=%Q{<entry><id>http://api.douban.com/doumail/#{mail_id}</id><db:attribute name="unread">false</db:attribute></entry>}
905
+ end
906
+ 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>}
907
+ resp=put("/doumail/",feed,{"Content-Type" => "application/atom+xml"})
908
+ if resp.code=="202"
909
+ true
910
+ else
911
+ false
912
+ end
913
+ end
914
+ def delete_mails(mail_ids=[])
915
+ entrys=""
916
+ mail_ids.each do |mail_id|
917
+ entrys += %Q{<entry><id>http://api.douban.com/doumail/#{mail_id}</id></entry>}
918
+ end
919
+ 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>}
920
+ resp=post("/doumail/delete",feed,{"Content-Type" => "application/atom+xml"})
921
+ if resp.code=="202"
922
+ true
923
+ else
924
+ false
925
+ end
926
+ end
927
+
928
+ def get_book_tags(subject_id="",flag=:book)
929
+ case flag
930
+ when :book
931
+ resp=get("/book/subject/#{subject_id}/tags")
932
+ when :music
933
+ resp=get("/music/subject/#{subject_id}/tags")
934
+ when :movie
935
+ resp=get("/movie/subject/#{subject_id}/tags")
936
+ end
937
+ if resp.code=="200"
938
+ tags=[]
939
+ atom=resp.body
940
+ doc=REXML::Document.new(atom)
941
+ REXML::XPath.each(doc,"//entry") do |entry|
942
+ tags << Tag.new(entry)
943
+ end
944
+ tags
945
+ else
946
+ nil
947
+ end
948
+ end
949
+ def get_user_tags(user_id="@me",flag=:book,option={:max_results=>10})
950
+ case flag
951
+ when :book
952
+ resp=get("/people/#{u user_id}/tags?cat=book&max-results=#{option[:max_results]}")
953
+ when :music
954
+ resp=get("/people/#{u user_id}/tags?cat=music&max-results=#{option[:max_results]}")
955
+ when :movie
956
+ resp=get("/people/#{u user_id}/tags?cat=movie&max-results=#{option[:max_results]}")
957
+ end
958
+ if resp.code=="200"
959
+ tags=[]
960
+ atom=resp.body
961
+ doc=REXML::Document.new(atom)
962
+ REXML::XPath.each(doc,"//entry") do |entry|
963
+ tags << Tag.new(entry)
964
+ end
965
+ tags
966
+ else
967
+ debug(resp)
968
+ end
969
+ end
970
+
971
+ def delete_event(event_id="")
972
+ 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>}
973
+ resp=post("/event/#{u(event_id)}/delete",entry,{"Content-Type"=>"application/atom+xml"})
974
+ if resp.code=="200"
975
+ true
976
+ else
977
+ debug(resp, false)
978
+ end
979
+ end
980
+
981
+ def request_token=(token)
982
+ unless token.kind_of? OAuth::RequestToken
983
+ token = OAuth::RequestToken.new(
984
+ new_request_consumer,
985
+ token.token,
986
+ token.secret)
987
+ end
988
+ @request_token = token
989
+ end
990
+
991
+ def access_token=(token)
992
+ unless token.kind_of? OAuth::AccessToken
993
+ token = OAuth::AccessToken.new(
994
+ new_access_consumer,
995
+ token.token,
996
+ token.secret)
997
+ end
998
+ @access_token = token
999
+ end
1000
+
1001
+ def get_recommendation(id)
1002
+ resp=get("/recommendation/#{u(id.to_s)}")
1003
+ if resp.code=="200"
1004
+ Recommendation.new(resp.body)
1005
+ elsif resp.code == "404"
1006
+ nil
1007
+ else
1008
+ debug(resp)
1009
+ end
1010
+ end
1011
+
1012
+ def get_user_recommendations(user_id="@me",option={:start_index=>1,:max_results=>10})
1013
+ resp=get("/people/#{u(user_id.to_s)}/recommendations?start-index=#{option[:start_index]}&max-results=#{option[:max_results]}")
1014
+ if resp.code == "200"
1015
+ recommendations = []
1016
+ doc=REXML::Document.new(resp.body)
1017
+ REXML::XPath.each(doc,"//entry") do |entry|
1018
+ recommendations << Recommendation.new(entry)
1019
+ end
1020
+ recommendations
1021
+ else
1022
+ debug(resp)
1023
+ end
1024
+ end
1025
+
1026
+ def get_recommendation_comments(recommendation_id)
1027
+ resp = get("/recommendation/#{u(recommendation_id)}/comments")
1028
+ if resp.code == "200"
1029
+ comments = []
1030
+ doc=REXML::Document.new(resp.body)
1031
+ REXML::XPath.each(doc, "//entry") do |entry|
1032
+ comments << RecommendationComment.new(entry)
1033
+ end
1034
+ comments
1035
+ else
1036
+ debug(resp)
1037
+ end
1038
+ end
1039
+
1040
+ def create_recommendation(subject, title, comment)
1041
+ subject = subject.kind_of?(Douban::Subject) ? subject.id : subject
1042
+ entry = %Q{<?xml version="1.0" encoding="UTF-8"?>
1043
+ <entry xmlns="http://www.w3.org/2005/Atom"
1044
+ xmlns:gd="http://schemas.google.com/g/2005"
1045
+ xmlns:opensearch="http://a9.com/-/spec/opensearchrss/1.0/"
1046
+ xmlns:db="http://www.douban.com/xmlns/">
1047
+ <title>#{h title}</title>
1048
+ <db:attribute name="comment">#{h comment}</db:attribute>
1049
+ <link href="#{h subject}" rel="related" />
1050
+ </entry>}
1051
+ resp = post("/recommendations", entry, {"Content-Type"=>"application/atom+xml"})
1052
+ if resp.code == '201'
1053
+ Recommendation.new(resp.body)
1054
+ else
1055
+ debug(resp)
1056
+ end
1057
+ end
1058
+
1059
+ def delete_recommendation(recommendation_id)
1060
+ if recommendation_id.kind_of?(Douban::Recommendation)
1061
+ recommendation_id = %r{/(\d+)$}.match(recommendation_id.id)[1]
1062
+ end
1063
+
1064
+ resp = delete("/recommendation/#{u recommendation_id}")
1065
+ if resp.code == '200'
1066
+ true
1067
+ else
1068
+ debug(resp, false)
1069
+ end
1070
+ end
1071
+
1072
+ def create_recommendation_comment(recommendation, content)
1073
+ recommendation_id = recommendation.kind_of?(Douban::Recommendation) \
1074
+ ? recommendation.recommendation_id : recommendation
1075
+ entry = %Q{<?xml version='1.0' encoding='UTF-8'?>
1076
+ <entry>
1077
+ <content>#{h content}</content>
1078
+ </entry>}
1079
+ resp = post("/recommendation/#{u recommendation_id}/comments", entry, {"Content-Type"=>"application/atom+xml"})
1080
+ if resp.code == '201'
1081
+ RecommendationComment.new(resp.body)
1082
+ else
1083
+ debug(resp)
1084
+ end
1085
+ end
1086
+
1087
+ # delete_recommendation_comment(comment:RecommendationComment)
1088
+ # delete_recommendation_comment(recommendation:Recommendation, comment_id:Integer)
1089
+ # delete_recommendation_comment(recommendation_id:Integer, comment_id:Integer)
1090
+ def delete_recommendation_comment(recommendation, comment_id=nil)
1091
+ if comment_id.nil?
1092
+ recommendation_id = recommendation.recommendation_id
1093
+ comment_id = recommendation.comment_id
1094
+ else
1095
+ recommendation_id = recommendation.kind_of?(Douban::Recommendation) \
1096
+ ? recommendation.recommendation_id : recommendation
1097
+ end
1098
+ resp = delete("/recommendation/#{u recommendation_id}/comment/#{u comment_id}")
1099
+ if resp.code == '200'
1100
+ true
1101
+ else
1102
+ debug(resp, false)
1103
+ end
1104
+ end
1105
+
1106
+ protected
1107
+ def new_request_consumer
1108
+ OAuth::Consumer.new(@api_key, @secret_key, @oauth_request_option)
1109
+ end
1110
+
1111
+ def new_access_consumer
1112
+ OAuth::Consumer.new(@api_key, @secret_key, @oauth_access_option)
1113
+ end
1114
+
1115
+ def debug(resp, retval=nil)
1116
+ if @@debug
1117
+ p resp.code
1118
+ p resp.body
1119
+ end
1120
+ retval
1121
+ end
1122
+
1123
+ def html_encode(o)
1124
+ CGI.escapeHTML(o.to_s)
1125
+ end
1126
+ alias_method :h, :html_encode
1127
+
1128
+ def url_encode(o)
1129
+ CGI.escape(o.to_s)
1130
+ end
1131
+ alias_method :u, :url_encode
1132
+
1133
+ def get(path,headers={})
1134
+ @access_token.get(path,headers)
1135
+ end
1136
+ def post(path,data="",headers={})
1137
+ @access_token.post(path,data,headers)
1138
+ end
1139
+ def put(path,body="",headers={})
1140
+ @access_token.put(path,body,headers)
1141
+ end
1142
+ def delete(path,headers={})
1143
+ @access_token.delete(path,headers)
1144
+ end
1145
+ def head(path,headers={})
1146
+ @access_token.head(path,headers)
1147
+ end
1148
+ end
1149
+ end
1150
+