douban-ruby 0.0.5

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.
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
+