www-delicious 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,17 @@
1
1
  = Changelog
2
2
 
3
3
 
4
+ == Release 0.4.0
5
+
6
+ * FIXED: A trivial bug causes the test `test_request_waits_necessary_time_between_requests` to fail in case the subsequent request is sent exactly 1 second after the prior one.
7
+
8
+ * FIXED: Object#blank? is always redefined regardless already defined before.
9
+
10
+ * CHANGED: Removed dependency from Echoe.
11
+
12
+ * REMOVED: Removed old setup.rb installation method.
13
+
14
+
4
15
  == Release 0.3.0
5
16
 
6
17
  * FIXED: Compatibility fixes for Ruby 1.9. WWW::Delicious is now 100% compatible with 1.9. You should remember to define the proper content encoding with magic comments when working with UTF-8/MultiByte XML or Ruby files, see http://redmine.ruby-lang.org/wiki/ruby-19/ScriptEncoding (closes #142).
@@ -2,7 +2,7 @@
2
2
 
3
3
  (The MIT License)
4
4
 
5
- Copyright (c) 2008-2009 Simone Carletti <weppos@weppos.net>
5
+ Copyright (c) 2008-2010 Simone Carletti <weppos@weppos.net>
6
6
 
7
7
  Permission is hereby granted, free of charge, to any person obtaining
8
8
  a copy of this software and associated documentation files (the
@@ -1,19 +1,13 @@
1
1
  = WWW::Delicious
2
2
 
3
- WWW::Delicious is a Ruby client for http://del.icio.us XML API.
3
+ WWW::Delicious is a Ruby client for {delicious.com}[http://delicious.com] XML API.
4
4
 
5
5
  It provides both read and write functionality. You can read user Posts, Tags
6
6
  and Bundles but you can create new Posts, Tags and Bundles as well.
7
7
 
8
+ WWW::Delicious maps all the original del.icio.us API calls and provides some additional convenient methods to perform common tasks. For a full API overview, visit the official {Delicious API documentation}[http://delicious.com/help/api].
8
9
 
9
- == Overview
10
-
11
- WWW::Delicious maps all the original del.icio.us API calls and provides some
12
- additional convenient methods to perform common tasks.
13
- Please read the official documentation (http://del.icio.us/help/api/)
14
- to learn more about del.icio.us API.
15
-
16
- WWW::Delicious is 100% compatible with all del.icio.us API constraints,
10
+ WWW::Delicious is compatible with all {delicious.com}[http://delicious.com] API constraints,
17
11
  including the requirement to set a valid user agent or wait at least
18
12
  one second between queries.
19
13
  Basically, the main benefit from using this library is that you don't need
@@ -21,36 +15,28 @@ to take care of all these low level details, if you don't want:
21
15
  WWW::Delicious will try to give you the most with less efforts.
22
16
 
23
17
 
24
- == Dependencies
25
-
26
- * Ruby >= 1.8.6 (not tested with previous versions)
18
+ == Requirements
27
19
 
28
- As of release 0.3.0, WWW::Delicious is compatible with Ruby 1.9.1.
20
+ * Ruby >= 1.8.6 or Ruby 1.9.x
29
21
 
30
22
 
31
23
  == Download and Installation
32
24
 
33
- RubyGems[http://rubyforge.org/projects/rubygems/] is the preferred install method.
34
- To get the latest version, simply type the following instruction into your command prompt:
25
+ This library is intended to be installed as a Gem.
35
26
 
36
- $ sudo gem install www-delicious
37
-
38
- Depending on your system, you might need su privileges.
39
-
40
- To install the library manually, downlad the latest version from
41
- navigate to the root library directory and enter:
27
+ $ gem install www-delicious
42
28
 
43
- $ sudo ruby setup.rb
29
+ You might need administrator privileges on your system to install it.
44
30
 
45
- If you need the latest development version you can download the source code
46
- from one of the GIT repositories listed above.
31
+ If you need the latest development version you can download the
32
+ {source code}[http://github.com/weppos/www-delicious/] from GitHub.
47
33
  Beware that the code might not be as stable as the official release.
48
34
 
49
35
 
50
36
  == Getting Started
51
37
 
52
- In order to use this library you need a valid del.icio.us account.
53
- Go to http://del.icio.us/ and register for a new account if you don't already have one.
38
+ In order to use this library you need a valid Delicious account.
39
+ Go to http://delicious.com and register for a new account if you don't already have one.
54
40
 
55
41
  Then create a valid instance of WWW::Delicious providing your account credentials.
56
42
 
@@ -171,16 +157,16 @@ You can also create new bundles or delete existing ones.
171
157
  d.bundles_delete('OldBundle')
172
158
 
173
159
 
174
- == Author
160
+ == Credits
175
161
 
176
- {Simone Carletti}[http://www.simonecarletti.com/] <weppos@weppos.net>
162
+ Author:: {Simone Carletti}[http://www.simonecarletti.com/] <weppos@weppos.net>
177
163
 
178
164
 
179
165
  == Resources
180
166
 
181
- * {Homepage}[http://code.simonecarletti.com/www-delicious]
182
- * {API}[http://www-delicious.rubyforge.org/]
183
- * {GitHub}[http://github.com/weppos/www-delicious/]
167
+ * {Homepage}[http://www.simonecarletti.com/code/www-delicious]
168
+ * {Repository}[http://github.com/weppos/www-delicious/]
169
+ * {API Documentation}[http://www.simonecarletti.com/code/www-delicious/api/] (RDoc)
184
170
  * {RubyForge}[http://rubyforge.org/projects/www-delicious/]
185
171
 
186
172
 
@@ -188,7 +174,7 @@ You can also create new bundles or delete existing ones.
188
174
 
189
175
  Feel free to email {Simone Carletti}[mailto:weppos@weppos.net] with any questions or feedback.
190
176
 
191
- Please use the {Ticket System}[http://code.simonecarletti.com/projects/show/www-delicious] to submit bug reports or feature request.
177
+ Please use the {Ticket System}[http://github.com/weppos/www-delicious/issues] to submit bug reports or feature request.
192
178
 
193
179
 
194
180
  == Changelog
@@ -198,5 +184,4 @@ See the CHANGELOG.rdoc file for details.
198
184
 
199
185
  == License
200
186
 
201
- Copyright (c) 2008-2009 Simone Carletti, WWW::Delicious is released under the MIT license.
202
-
187
+ Copyright (c) 2008-2010 Simone Carletti, WWW::Delicious is released under the MIT license.
@@ -1,8 +1,8 @@
1
- #
1
+ #
2
2
  # = WWW::Delicious
3
3
  #
4
4
  # Ruby client for del.icio.us API.
5
- #
5
+ #
6
6
  #
7
7
  # Category:: WWW
8
8
  # Package:: WWW::Delicious
@@ -10,7 +10,7 @@
10
10
  # License:: MIT License
11
11
  #
12
12
  #--
13
- # SVN: $Id$
13
+ #
14
14
  #++
15
15
 
16
16
 
@@ -29,68 +29,69 @@ module WWW #:nodoc:
29
29
 
30
30
  #
31
31
  # = WWW::Delicious
32
- #
33
- # WWW::Delicious is a Ruby client for http://del.icio.us XML API.
34
- #
35
- # It provides both read and write functionalities.
36
- # You can read user Posts, Tags and Bundles
32
+ #
33
+ # WWW::Delicious is a Ruby client for http://delicious.com XML API.
34
+ #
35
+ # It provides both read and write functionalities.
36
+ # You can read user Posts, Tags and Bundles
37
37
  # but you can create new Posts, Tags and Bundles as well.
38
38
  #
39
39
  #
40
40
  # == Basic Usage
41
- #
41
+ #
42
42
  # The following is just a basic demonstration of the main features.
43
43
  # See the README file for a deeper explanation about how to get the best
44
44
  # from WWW::Delicious library.
45
- #
45
+ #
46
46
  # The examples in this page make the following assumptions
47
47
  # * you have a valid del.icio.us account
48
48
  # * +username+ is your account username
49
49
  # * +password+ is your account password
50
- #
50
+ #
51
51
  # In order to make a query you first need to create
52
52
  # a new WWW::Delicious instance as follows:
53
53
  #
54
54
  # require 'www/delicious'
55
- #
55
+ #
56
56
  # username = 'my delicious username'
57
57
  # password = 'my delicious password'
58
58
  #
59
59
  # d = WWW::Delicious.new(username, password)
60
- #
60
+ #
61
61
  # The constructor accepts some additional options.
62
62
  # For instance, if you want to customize the user agent:
63
- #
63
+ #
64
64
  # d = WWW::Delicious.new(username, password, :user_agent => 'FooAgent')
65
- #
65
+ #
66
66
  # Now you can use any of the API methods available.
67
- #
67
+ #
68
68
  # For example, you may want to know when your account was last updated
69
69
  # to check whether someone else made some changes on behalf of you:
70
- #
70
+ #
71
71
  # datetime = d.update # => Wed Mar 12 08:41:20 UTC 2008
72
- #
72
+ #
73
73
  # Because the answer is a valid +Time+ instance, you can format it with +strftime+.
74
- #
74
+ #
75
75
  # datetime = d.update # => Wed Mar 12 08:41:20 UTC 2008
76
76
  # datetime.strftime('%Y') # => 2008
77
77
  #
78
78
  class Delicious
79
-
80
- NAME = 'WWW::Delicious'
81
- GEM = 'www-delicious'
82
- AUTHOR = 'Simone Carletti <weppos@weppos.net>'
83
-
79
+
80
+ NAME = "WWW::Delicious"
81
+ GEM = "www-delicious"
82
+ AUTHORS = ["Simone Carletti <weppos@weppos.net>"]
83
+
84
+
84
85
  # del.icio.us account username
85
86
  attr_reader :username
86
-
87
+
87
88
  # del.icio.us account password
88
89
  attr_reader :password
89
-
90
+
90
91
  # base URI for del.icio.us API
91
92
  attr_reader :base_uri
92
93
 
93
-
94
+
94
95
  # API Base URL
95
96
  API_BASE_URI = 'https://api.del.icio.us'
96
97
 
@@ -121,306 +122,306 @@ module WWW #:nodoc:
121
122
  API_PATH_POSTS_ADD = '/v1/posts/add';
122
123
  # API Path Delete Post
123
124
  API_PATH_POSTS_DELETE = '/v1/posts/delete';
124
-
125
+
125
126
  # Time to wait before sending a new request, in seconds
126
127
  SECONDS_BEFORE_NEW_REQUEST = 1
127
-
128
+
128
129
  # Time converter converts a Time instance into the format
129
130
  # requested by Delicious API
130
- TIME_CONVERTER = lambda { |time| time.iso8601() }
131
-
132
-
133
- #
134
- # Constructs a new <tt>WWW::Delicious</tt> object
131
+ TIME_CONVERTER = lambda { |time| time.iso8601 }
132
+
133
+
134
+ #
135
+ # Constructs a new <tt>WWW::Delicious</tt> object
135
136
  # with given +username+ and +password+.
136
- #
137
+ #
137
138
  # # create a new object with username 'user' and password 'psw
138
139
  # obj = WWW::Delicious('user', 'psw')
139
140
  # # => self
140
- #
141
+ #
141
142
  # If a block is given, the instance is passed to the block
142
143
  # but this method always returns the instance itself.
143
- #
144
+ #
144
145
  # WWW::Delicious('user', 'psw') do |d|
145
146
  # d.update() # => Fri May 02 18:02:48 UTC 2008
146
147
  # end
147
148
  # # => self
148
- #
149
+ #
149
150
  # You can also specify some additional options, including a custom user agent
150
- # or the base URI for del.icio.us API.
151
- #
151
+ # or the base URI for delicious.com API.
152
+ #
152
153
  # WWW::Delicious('user', 'psw', :base_uri => 'https://ma.gnolia.com/api/mirrord') do |d|
153
154
  # # the following call is mirrored by ma.gnolia
154
155
  # d.update() # => Fri May 02 18:02:48 UTC 2008
155
156
  # end
156
157
  # # => self
157
- #
158
+ #
158
159
  # === Options
159
160
  # This class accepts a Hash with additional options.
160
161
  # Here's the list of valid keys:
161
162
  #
162
163
  # <tt>:user_agent</tt>:: User agent to display in HTTP requests.
163
164
  # <tt>:base_uri</tt>:: The base URI to del.icio.us API.
164
- #
165
+ #
165
166
  def initialize(username, password, options = {}, &block) # :yields: delicious
166
167
  @username, @password = username.to_s, password.to_s
167
-
168
+
168
169
  # set API base URI
169
170
  @base_uri = URI.parse(options[:base_uri] || API_BASE_URI)
170
-
171
+
171
172
  init_user_agent(options)
172
173
  init_http_client(options)
173
-
174
+
174
175
  yield self if block_given?
175
176
  self # ensure to always return self even if block is given
176
177
  end
177
-
178
-
179
- #
178
+
179
+
180
+ #
180
181
  # Returns the reference to current <tt>@http_client</tt>.
181
182
  # The http is always valid unless it has been previously set to +nil+.
182
- #
183
+ #
183
184
  # # nil client
184
185
  # obj.http_client # => nil
185
- #
186
+ #
186
187
  # # valid client
187
188
  # obj.http_client # => Net::HTTP
188
- #
189
+ #
189
190
  def http_client()
190
191
  return @http_client
191
192
  end
192
193
 
193
- #
194
+ #
194
195
  # Sets the internal <tt>@http_client</tt> to +client+.
195
- #
196
+ #
196
197
  # # nil client
197
198
  # obj.http_client = nil
198
- #
199
+ #
199
200
  # # http client
200
201
  # obj.http_client = Net::HTTP.new()
201
- #
202
+ #
202
203
  # # invalid client
203
204
  # obj.http_client = 'foo' # => ArgumentError
204
- #
205
+ #
205
206
  def http_client=(client)
206
207
  unless client.kind_of?(Net::HTTP) or client.nil?
207
208
  raise ArgumentError, "`client` expected to be a kind of `Net::HTTP`, `#{client.class}` given"
208
209
  end
209
210
  @http_client = client
210
211
  end
211
-
212
+
212
213
  # Returns current user agent string.
213
214
  def user_agent()
214
215
  return @headers['User-Agent']
215
216
  end
216
-
217
-
218
- #
217
+
218
+
219
+ #
219
220
  # Returns true if given account credentials are valid.
220
- #
221
+ #
221
222
  # d = WWW::Delicious.new('username', 'password')
222
223
  # d.valid_account? # => true
223
- #
224
+ #
224
225
  # d = WWW::Delicious.new('username', 'invalid_password')
225
226
  # d.valid_account? # => false
226
- #
227
+ #
227
228
  # This method is not "exception safe".
228
229
  # It doesn't return false if an HTTP error or any kind of other error occurs,
229
230
  # it raises back the exception to the caller instead.
230
- #
231
- #
231
+ #
232
+ #
232
233
  # Raises:: WWW::Delicious::Error
233
234
  # Raises:: WWW::Delicious::HTTPError
234
235
  # Raises:: WWW::Delicious::ResponseError
235
- #
236
+ #
236
237
  def valid_account?
237
238
  update()
238
239
  return true
239
240
  rescue HTTPError => e
240
241
  return false if e.message =~ /invalid username or password/i
241
- raise
242
+ raise
242
243
  end
243
244
 
244
- #
245
+ #
245
246
  # Checks to see when a user last posted an item
246
247
  # and returns the last update +Time+ for the user.
247
- #
248
+ #
248
249
  # d.update() # => Fri May 02 18:02:48 UTC 2008
249
- #
250
- #
250
+ #
251
+ #
251
252
  # Raises:: WWW::Delicious::Error
252
253
  # Raises:: WWW::Delicious::HTTPError
253
254
  # Raises:: WWW::Delicious::ResponseError
254
- #
255
+ #
255
256
  def update()
256
257
  response = request(API_PATH_UPDATE)
257
258
  return parse_update_response(response.body)
258
259
  end
259
-
260
- #
260
+
261
+ #
261
262
  # Retrieves all of a user's bundles
262
263
  # and returns an array of <tt>WWW::Delicious::Bundle</tt>.
263
- #
264
+ #
264
265
  # d.bundles_all() # => [#<WWW::Delicious::Bundle>, #<WWW::Delicious::Bundle>, ...]
265
266
  # d.bundles_all() # => []
266
- #
267
- #
267
+ #
268
+ #
268
269
  # Raises:: WWW::Delicious::Error
269
270
  # Raises:: WWW::Delicious::HTTPError
270
271
  # Raises:: WWW::Delicious::ResponseError
271
- #
272
+ #
272
273
  def bundles_all()
273
274
  response = request(API_PATH_BUNDLES_ALL)
274
275
  return parse_bundle_collection(response.body)
275
276
  end
276
-
277
- #
278
- # Assignes a set of tags to a single bundle,
277
+
278
+ #
279
+ # Assignes a set of tags to a single bundle,
279
280
  # wipes away previous settings for bundle.
280
- #
281
+ #
281
282
  # # create from a bundle
282
283
  # d.bundles_set(WWW::Delicious::Bundle.new('MyBundle'), %w(foo bar))
283
- #
284
+ #
284
285
  # # create from a string
285
286
  # d.bundles_set('MyBundle', %w(foo bar))
286
- #
287
- #
287
+ #
288
+ #
288
289
  # Raises:: WWW::Delicious::Error
289
290
  # Raises:: WWW::Delicious::HTTPError
290
291
  # Raises:: WWW::Delicious::ResponseError
291
- #
292
+ #
292
293
  def bundles_set(bundle_or_name, tags = [])
293
294
  params = prepare_bundles_set_params(bundle_or_name, tags)
294
295
  response = request(API_PATH_BUNDLES_SET, params)
295
296
  return parse_and_eval_execution_response(response.body)
296
297
  end
297
-
298
- #
298
+
299
+ #
299
300
  # Deletes +bundle_or_name+ bundle from del.icio.us.
300
- # +bundle_or_name+ can be either a WWW::Delicious::Bundle instance
301
+ # +bundle_or_name+ can be either a WWW::Delicious::Bundle instance
301
302
  # or a string with the name of the bundle.
302
- #
303
+ #
303
304
  # This method doesn't care whether the exists.
304
305
  # If not, the execution will silently return without rising any error.
305
- #
306
+ #
306
307
  # # delete from a bundle
307
308
  # d.bundles_delete(WWW::Delicious::Bundle.new('MyBundle'))
308
- #
309
+ #
309
310
  # # delete from a string
310
311
  # d.bundles_delete('MyBundle', %w(foo bar))
311
- #
312
- #
312
+ #
313
+ #
313
314
  # Raises:: WWW::Delicious::Error
314
315
  # Raises:: WWW::Delicious::HTTPError
315
316
  # Raises:: WWW::Delicious::ResponseError
316
- #
317
+ #
317
318
  def bundles_delete(bundle_or_name)
318
319
  params = prepare_bundles_delete_params(bundle_or_name)
319
320
  response = request(API_PATH_BUNDLES_DELETE, params)
320
321
  return parse_and_eval_execution_response(response.body)
321
322
  end
322
-
323
- #
323
+
324
+ #
324
325
  # Retrieves the list of tags and number of times used by the user
325
326
  # and returns an array of <tt>WWW::Delicious::Tag</tt>.
326
- #
327
+ #
327
328
  # d.tags_get() # => [#<WWW::Delicious::Tag>, #<WWW::Delicious::Tag>, ...]
328
329
  # d.tags_get() # => []
329
- #
330
- #
330
+ #
331
+ #
331
332
  # Raises:: WWW::Delicious::Error
332
333
  # Raises:: WWW::Delicious::HTTPError
333
334
  # Raises:: WWW::Delicious::ResponseError
334
- #
335
+ #
335
336
  def tags_get()
336
337
  response = request(API_PATH_TAGS_GET)
337
338
  return parse_tag_collection(response.body)
338
339
  end
339
-
340
- #
340
+
341
+ #
341
342
  # Renames an existing tag with a new tag name.
342
- #
343
+ #
343
344
  # # rename from a tag
344
345
  # d.bundles_set(WWW::Delicious::Tag.new('old'), WWW::Delicious::Tag.new('new'))
345
- #
346
+ #
346
347
  # # rename from a string
347
348
  # d.bundles_set('old', 'new')
348
- #
349
- #
349
+ #
350
+ #
350
351
  # Raises:: WWW::Delicious::Error
351
352
  # Raises:: WWW::Delicious::HTTPError
352
353
  # Raises:: WWW::Delicious::ResponseError
353
- #
354
+ #
354
355
  def tags_rename(from_name_or_tag, to_name_or_tag)
355
356
  params = prepare_tags_rename_params(from_name_or_tag, to_name_or_tag)
356
357
  response = request(API_PATH_TAGS_RENAME, params)
357
358
  return parse_and_eval_execution_response(response.body)
358
359
  end
359
-
360
- #
360
+
361
+ #
361
362
  # Returns an array of <tt>WWW::Delicious::Post</tt> matching +options+.
362
363
  # If no option is given, the last post is returned.
363
364
  # If no date or url is given, most recent date will be used.
364
- #
365
+ #
365
366
  # d.posts_get() # => [#<WWW::Delicious::Post>, #<WWW::Delicious::Post>, ...]
366
367
  # d.posts_get() # => []
367
- #
368
+ #
368
369
  # # get all posts tagged with ruby
369
370
  # d.posts_get(:tag => WWW::Delicious::Tag.new('ruby))
370
- #
371
+ #
371
372
  # # get all posts matching URL 'http://www.simonecarletti.com'
372
373
  # d.posts_get(:url => URI.parse('http://www.simonecarletti.com'))
373
- #
374
+ #
374
375
  # # get all posts tagged with ruby and matching URL 'http://www.simonecarletti.com'
375
376
  # d.posts_get(:tag => WWW::Delicious::Tag.new('ruby),
376
377
  # :url => URI.parse('http://www.simonecarletti.com'))
377
- #
378
- #
378
+ #
379
+ #
379
380
  # === Options
380
381
  # <tt>:tag</tt>:: a tag to filter by. It can be either a <tt>WWW::Delicious::Tag</tt> or a +String+.
381
382
  # <tt>:dt</tt>:: a +Time+ with a date to filter by.
382
383
  # <tt>:url</tt>:: a valid URI to filter by. It can be either an instance of +URI+ or a +String+.
383
- #
384
+ #
384
385
  # Raises:: WWW::Delicious::Error
385
386
  # Raises:: WWW::Delicious::HTTPError
386
387
  # Raises:: WWW::Delicious::ResponseError
387
- #
388
+ #
388
389
  def posts_get(options = {})
389
390
  params = prepare_posts_params(options.clone, [:dt, :tag, :url])
390
391
  response = request(API_PATH_POSTS_GET, params)
391
392
  return parse_post_collection(response.body)
392
393
  end
393
394
 
394
- #
395
+ #
395
396
  # Returns a list of the most recent posts, filtered by argument.
396
- #
397
+ #
397
398
  # # get the most recent posts
398
399
  # d.posts_recent()
399
- #
400
+ #
400
401
  # # get the 10 most recent posts
401
402
  # d.posts_recent(:count => 10)
402
- #
403
- #
403
+ #
404
+ #
404
405
  # === Options
405
406
  # <tt>:tag</tt>:: a tag to filter by. It can be either a <tt>WWW::Delicious::Tag</tt> or a +String+.
406
407
  # <tt>:count</tt>:: number of items to retrieve. (default: 15, maximum: 100).
407
- #
408
+ #
408
409
  def posts_recent(options = {})
409
410
  params = prepare_posts_params(options.clone, [:count, :tag])
410
411
  response = request(API_PATH_POSTS_RECENT, params)
411
412
  return parse_post_collection(response.body)
412
413
  end
413
-
414
- #
414
+
415
+ #
415
416
  # Returns a list of all posts, filtered by argument.
416
- #
417
+ #
417
418
  # # get all (this is a very expensive query)
418
419
  # d.posts_all
419
- #
420
+ #
420
421
  # # get all posts matching ruby
421
422
  # d.posts_all(:tag => WWW::Delicious::Tag.new('ruby'))
422
- #
423
- #
423
+ #
424
+ #
424
425
  # === Options
425
426
  # <tt>:tag</tt>:: a tag to filter by. It can be either a <tt>WWW::Delicious::Tag</tt> or a +String+.
426
427
  #
@@ -432,16 +433,16 @@ module WWW #:nodoc:
432
433
 
433
434
  #
434
435
  # Returns a list of dates with the number of posts at each date.
435
- #
436
+ #
436
437
  # # get number of posts per date
437
438
  # d.posts_dates
438
439
  # # => { '2008-05-05' => 12, '2008-05-06' => 3, ... }
439
- #
440
+ #
440
441
  # # get number posts per date tagged as ruby
441
442
  # d.posts_dates(:tag => WWW::Delicious::Tag.new('ruby'))
442
443
  # # => { '2008-05-05' => 10, '2008-05-06' => 3, ... }
443
- #
444
- #
444
+ #
445
+ #
445
446
  # === Options
446
447
  # <tt>:tag</tt>:: a tag to filter by. It can be either a <tt>WWW::Delicious::Tag</tt> or a +String+.
447
448
  #
@@ -456,13 +457,13 @@ module WWW #:nodoc:
456
457
  # +post_or_values+ can be either a +WWW::Delicious::Post+ instance
457
458
  # or a Hash of params. This method accepts all params available
458
459
  # to initialize a new +WWW::Delicious::Post+.
459
- #
460
+ #
460
461
  # # add a post from WWW::Delicious::Post
461
462
  # d.posts_add(WWW::Delicious::Post.new(:url => 'http://www.foobar.com', :title => 'Hello world!'))
462
- #
463
+ #
463
464
  # # add a post from values
464
465
  # d.posts_add(:url => 'http://www.foobar.com', :title => 'Hello world!')
465
- #
466
+ #
466
467
  #
467
468
  def posts_add(post_or_values)
468
469
  params = prepare_param_post(post_or_values).to_params
@@ -473,16 +474,16 @@ module WWW #:nodoc:
473
474
  #
474
475
  # Deletes the post matching given +url+ from del.icio.us.
475
476
  # +url+ can be either an URI instance or a string representation of a valid URL.
476
- #
477
+ #
477
478
  # This method doesn't care whether a post with given +url+ exists.
478
479
  # If not, the execution will silently return without rising any error.
479
- #
480
+ #
480
481
  # # delete a post from URI
481
482
  # d.post_delete(URI.parse('http://www.foobar.com/'))
482
- #
483
+ #
483
484
  # # delete a post from a string
484
485
  # d.post_delete('http://www.foobar.com/')
485
- #
486
+ #
486
487
  #
487
488
  def posts_delete(url)
488
489
  params = prepare_posts_params({:url => url}, [:url])
@@ -490,9 +491,9 @@ module WWW #:nodoc:
490
491
  return parse_and_eval_execution_response(response.body)
491
492
  end
492
493
 
493
-
494
+
494
495
  protected
495
-
496
+
496
497
  # Initializes the HTTP client.
497
498
  # It automatically enable +use_ssl+ flag according to +@base_uri+ scheme.
498
499
  def init_http_client(options)
@@ -501,64 +502,64 @@ module WWW #:nodoc:
501
502
  http.verify_mode = OpenSSL::SSL::VERIFY_NONE # FIXME: not 100% supported
502
503
  self.http_client = http
503
504
  end
504
-
505
+
505
506
  # Initializes user agent value for HTTP requests.
506
507
  def init_user_agent(options)
507
508
  user_agent = options[:user_agent] || default_user_agent()
508
509
  @headers ||= {}
509
510
  @headers['User-Agent'] = user_agent
510
511
  end
511
-
512
- #
512
+
513
+ #
513
514
  # Creates and returns the default user agent string.
514
- #
515
+ #
515
516
  # By default, the user agent is composed by the following schema:
516
517
  # <tt>NAME/VERSION (Ruby/RUBY_VERSION)</tt>
517
- #
518
+ #
518
519
  # * +NAME+ is the constant representing this library name
519
520
  # * +VERSION+ is the constant representing current library version
520
521
  # * +RUBY_VERSION+ is the version of Ruby interpreter the library is interpreted by
521
- #
522
+ #
522
523
  # default_user_agent
523
524
  # # => WWW::Delicious/0.1.0 (Ruby/1.8.6)
524
- #
525
+ #
525
526
  def default_user_agent
526
527
  return "#{NAME}/#{VERSION} (Ruby/#{RUBY_VERSION})"
527
528
  end
528
-
529
-
530
- #
529
+
530
+
531
+ #
531
532
  # Composes an HTTP query string from an hash of +options+.
532
533
  # The result is URI encoded.
533
- #
534
+ #
534
535
  # http_build_query(:foo => 'baa', :bar => 'boo')
535
536
  # # => foo=baa&bar=boo
536
- #
537
+ #
537
538
  def http_build_query(params = {})
538
- return params.collect do |k,v|
539
+ return params.collect do |k,v|
539
540
  "#{URI.encode(k.to_s)}=#{URI.encode(v.to_s)}" unless v.nil?
540
541
  end.compact.join('&')
541
542
  end
542
-
543
- #
543
+
544
+ #
544
545
  # Sends an HTTP GET request to +path+ and appends given +params+.
545
- #
546
+ #
546
547
  # This method is 100% compliant with Delicious API reference.
547
548
  # It waits at least 1 second between each HTTP request and
548
549
  # provides an identifiable user agent by default,
549
- # or the custom user agent set by +user_agent+ option
550
- # when this istance has been created.
551
- #
550
+ # or the custom user agent set by +user_agent+ option
551
+ # when this instance has been created.
552
+ #
552
553
  # request('/v1/api/path', :foo => 1, :bar => 2)
553
554
  # # => sends a GET request to /v1/api/path?foo=1&bar=2
554
- #
555
+ #
555
556
  def request(path, params = {})
556
557
  raise Error, 'Invalid HTTP Client' unless http_client
557
558
  wait_before_new_request
558
-
559
+
559
560
  uri = @base_uri.merge(path)
560
561
  uri.query = http_build_query(params) unless params.empty?
561
-
562
+
562
563
  begin
563
564
  @last_request = Time.now # see #wait_before_new_request
564
565
  @last_request_uri = uri # useful for debug
@@ -566,7 +567,7 @@ module WWW #:nodoc:
566
567
  rescue => e # catch EOFError, SocketError and more
567
568
  raise HTTPError, e.message
568
569
  end
569
-
570
+
570
571
  case response
571
572
  when Net::HTTPSuccess
572
573
  return response
@@ -579,7 +580,7 @@ module WWW #:nodoc:
579
580
  raise HTTPError, "HTTP #{response.code}: #{response.message}"
580
581
  end
581
582
  end
582
-
583
+
583
584
  # Makes the real HTTP request to given +uri+ and returns the +response+.
584
585
  # This method exists basically to simplify unit testing with mocha.
585
586
  def make_request(uri)
@@ -589,55 +590,55 @@ module WWW #:nodoc:
589
590
  http.request(req)
590
591
  end
591
592
  end
592
-
593
- #
594
- # Delicious API reference requests to wait AT LEAST ONE SECOND
593
+
594
+ #
595
+ # Delicious API reference requests to wait AT LEAST ONE SECOND
595
596
  # between queries or the client is likely to get automatically throttled.
596
- #
597
+ #
597
598
  # This method calculates the difference between current time
598
599
  # and the last request time and wait for the necessary time to meet
599
600
  # SECONDS_BEFORE_NEW_REQUEST requirement.
600
- #
601
+ #
601
602
  # The difference is not rounded. If you only have to wait for 0.034 seconds
602
603
  # then your don't have to wait 0 or 1 seconds, but 0.034 seconds!
603
- #
604
+ #
604
605
  def wait_before_new_request
605
606
  return unless @last_request # this is the first request
606
607
  # puts "Last request at #{TIME_CONVERTER.call(@last_request)}" if debug?
607
608
  diff = Time.now - @last_request
608
609
  if diff < SECONDS_BEFORE_NEW_REQUEST
609
610
  # puts "Sleeping for #{diff} before new request..." if debug?
610
- sleep(SECONDS_BEFORE_NEW_REQUEST - diff)
611
+ sleep(SECONDS_BEFORE_NEW_REQUEST - diff)
611
612
  end
612
613
  end
613
-
614
-
615
- #
614
+
615
+
616
+ #
616
617
  # Parses the response <tt>body</tt> and runs a common set of validators.
617
618
  # Returns <tt>body</tt> as parsed REXML::Document on success.
618
- #
619
+ #
619
620
  # Raises:: WWW::Delicious::ResponseError in case of invalid response.
620
- #
621
+ #
621
622
  def parse_and_validate_response(body, options = {})
622
623
  dom = REXML::Document.new(body)
623
-
624
+
624
625
  if (value = options[:root_name]) && dom.root.name != value
625
626
  raise ResponseError, "Invalid response, root node is not `#{value}`"
626
627
  end
627
628
  if (value = options[:root_text]) && dom.root.text != value
628
629
  raise ResponseError, value
629
630
  end
630
-
631
+
631
632
  return dom
632
633
  end
633
-
634
- #
634
+
635
+ #
635
636
  # Parses and evaluates the response returned by an execution,
636
637
  # usually an update/delete/insert operation.
637
- #
638
+ #
638
639
  # Raises:: WWW::Delicious::ResponseError in case of invalid response
639
640
  # Raises:: WWW::Delicious::Error in case of execution error
640
- #
641
+ #
641
642
  def parse_and_eval_execution_response(body)
642
643
  dom = parse_and_validate_response(body, :root_name => 'result')
643
644
  response = dom.root.if_attribute_value(:code)
@@ -645,53 +646,53 @@ module WWW #:nodoc:
645
646
  raise Error, "Invalid response, #{response}" unless %w(done ok).include?(response)
646
647
  true
647
648
  end
648
-
649
+
649
650
  # Parses the response of an Update request
650
651
  # and returns the update Timestamp.
651
652
  def parse_update_response(body)
652
653
  dom = parse_and_validate_response(body, :root_name => 'update')
653
654
  dom.root.if_attribute_value(:time) { |v| Time.parse(v) }
654
655
  end
655
-
656
+
656
657
  # Parses a response containing a collection of Bundles
657
658
  # and returns an array of <tt>WWW::Delicious::Bundle</tt>.
658
659
  def parse_bundle_collection(body)
659
660
  dom = parse_and_validate_response(body, :root_name => 'bundles')
660
661
  dom.root.elements.collect('bundle') { |xml| Bundle.from_rexml(xml) }
661
662
  end
662
-
663
+
663
664
  # Parses a response containing a collection of Tags
664
665
  # and returns an array of <tt>WWW::Delicious::Tag</tt>.
665
666
  def parse_tag_collection(body)
666
667
  dom = parse_and_validate_response(body, :root_name => 'tags')
667
668
  dom.root.elements.collect('tag') { |xml| Tag.from_rexml(xml) }
668
669
  end
669
-
670
+
670
671
  # Parses a response containing a collection of Posts
671
672
  # and returns an array of <tt>WWW::Delicious::Post</tt>.
672
673
  def parse_post_collection(body)
673
674
  dom = parse_and_validate_response(body, :root_name => 'posts')
674
675
  dom.root.elements.collect('post') { |xml| Post.from_rexml(xml) }
675
676
  end
676
-
677
+
677
678
  # Parses the response of a <tt>posts_dates</tt> request
678
679
  # and returns a +Hash+ of date => count.
679
680
  def parse_posts_dates_response(body)
680
681
  dom = parse_and_validate_response(body, :root_name => 'dates')
681
682
  return dom.root.get_elements('date').inject({}) do |collection, xml|
682
- date = xml.if_attribute_value(:date)
683
+ date = xml.if_attribute_value(:date)
683
684
  count = xml.if_attribute_value(:count)
684
685
  collection.merge({ date => count })
685
686
  end
686
687
  end
687
-
688
-
689
- #
688
+
689
+
690
+ #
690
691
  # Prepares the params for a `bundles_set` call
691
692
  # and returns a Hash with the params ready for the HTTP request.
692
- #
693
+ #
693
694
  # Raises:: WWW::Delicious::Error
694
- #
695
+ #
695
696
  def prepare_bundles_set_params(name_or_bundle, tags = [])
696
697
  bundle = prepare_param_bundle(name_or_bundle, tags) do |b|
697
698
  raise Error, "Bundle name is empty" if b.name.empty?
@@ -699,46 +700,46 @@ module WWW #:nodoc:
699
700
  end
700
701
  return { :bundle => bundle.name, :tags => bundle.tags.join(' ') }
701
702
  end
702
-
703
- #
703
+
704
+ #
704
705
  # Prepares the params for a `bundles_set` call
705
706
  # and returns a Hash with the params ready for the HTTP request.
706
- #
707
+ #
707
708
  # Raises:: WWW::Delicious::Error
708
- #
709
+ #
709
710
  def prepare_bundles_delete_params(name_or_bundle)
710
711
  bundle = prepare_param_bundle(name_or_bundle) do |b|
711
712
  raise Error, "Bundle name is empty" if b.name.empty?
712
713
  end
713
714
  return { :bundle => bundle.name }
714
715
  end
715
-
716
- #
716
+
717
+ #
717
718
  # Prepares the params for a `tags_rename` call
718
719
  # and returns a Hash with the params ready for the HTTP request.
719
- #
720
+ #
720
721
  # Raises:: WWW::Delicious::Error
721
- #
722
+ #
722
723
  def prepare_tags_rename_params(from_name_or_tag, to_name_or_tag)
723
724
  from, to = [from_name_or_tag, to_name_or_tag].collect do |v|
724
725
  prepare_param_tag(v)
725
726
  end
726
727
  return { :old => from, :new => to }
727
728
  end
728
-
729
- #
729
+
730
+ #
730
731
  # Prepares the params for a `post_*` call
731
732
  # and returns a Hash with the params ready for the HTTP request.
732
- #
733
+ #
733
734
  # Raises:: WWW::Delicious::Error
734
- #
735
+ #
735
736
  def prepare_posts_params(params, allowed_params = [])
736
737
  compare_params(params, allowed_params)
737
-
738
+
738
739
  # we don't need to check whether the following parameters
739
740
  # are valid for this request because compare_params
740
741
  # would raise if an invalid param is supplied
741
-
742
+
742
743
  params[:tag] = prepare_param_tag(params[:tag]) if params[:tag]
743
744
  params[:dt] = TIME_CONVERTER.call(params[:dt]) if params[:dt]
744
745
  params[:url] = URI.parse(params[:url]) if params[:url]
@@ -748,18 +749,18 @@ module WWW #:nodoc:
748
749
  else
749
750
  15 # default value
750
751
  end
751
-
752
+
752
753
  return params
753
754
  end
754
-
755
-
756
- #
755
+
756
+
757
+ #
757
758
  # Prepares the +post+ param for an API request.
758
- #
759
+ #
759
760
  # Creates and returns a <tt>WWW::Delicious::Post</tt> instance from <tt>post_or_values</tt>.
760
761
  # <tt>post_or_values</tt> can be either an Hash with post attributes
761
762
  # or a <tt>WWW::Delicious::Post</tt> instance.
762
- #
763
+ #
763
764
  def prepare_param_post(post_or_values, &block)
764
765
  post = case post_or_values
765
766
  when WWW::Delicious::Post
@@ -769,20 +770,20 @@ module WWW #:nodoc:
769
770
  else
770
771
  raise ArgumentError, 'Expected `args` to be `WWW::Delicious::Post` or `Hash`'
771
772
  end
772
-
773
+
773
774
  yield(post) if block_given?
774
775
  # TODO: validate post with post.validate!
775
776
  raise ArgumentError, 'Both `url` and `title` are required' unless post.api_valid?
776
777
  post
777
778
  end
778
-
779
- #
779
+
780
+ #
780
781
  # Prepares the +bundle+ param for an API request.
781
- #
782
+ #
782
783
  # Creates and returns a <tt>WWW::Delicious::Bundle</tt> instance from <tt>name_or_bundle</tt>.
783
784
  # <tt>name_or_bundle</tt> can be either a string holding bundle name
784
785
  # or a <tt>WWW::Delicious::Bundle</tt> instance.
785
- #
786
+ #
786
787
  def prepare_param_bundle(name_or_bundle, tags = [], &block) # :yields: bundle
787
788
  bundle = case name_or_bundle
788
789
  when WWW::Delicious::Bundle
@@ -790,19 +791,19 @@ module WWW #:nodoc:
790
791
  else
791
792
  Bundle.new(:name => name_or_bundle, :tags => tags)
792
793
  end
793
-
794
+
794
795
  yield(bundle) if block_given?
795
796
  # TODO: validate bundle with bundle.validate!
796
797
  bundle
797
798
  end
798
-
799
- #
799
+
800
+ #
800
801
  # Prepares the +tag+ param for an API request.
801
- #
802
+ #
802
803
  # Creates and returns a <tt>WWW::Delicious::Tag</tt> instance from <tt>name_or_tag</tt>.
803
804
  # <tt>name_or_tag</tt> can be either a string holding tag name
804
805
  # or a <tt>WWW::Delicious::Tag</tt> instance.
805
- #
806
+ #
806
807
  def prepare_param_tag(name_or_tag, &block) # :yields: tag
807
808
  tag = case name_or_tag
808
809
  when WWW::Delicious::Tag
@@ -810,62 +811,62 @@ module WWW #:nodoc:
810
811
  else
811
812
  Tag.new(:name => name_or_tag.to_s)
812
813
  end
813
-
814
+
814
815
  yield(tag) if block_given?
815
816
  # TODO: validate tag with tag.validate!
816
817
  raise "Invalid `tag` value supplied" unless tag.api_valid?
817
818
  tag
818
819
  end
819
-
820
- #
820
+
821
+ #
821
822
  # Checks whether user given +params+ are valid against a defined collection of +valid_params+.
822
- #
823
+ #
823
824
  # === Examples
824
- #
825
+ #
825
826
  # params = {:foo => 1, :bar => 2}
826
827
  #
827
828
  # compare_params(params, [:foo, :bar])
828
829
  # # => valid
829
- #
830
+ #
830
831
  # compare_params(params, [:foo, :bar, :baz])
831
832
  # # => raises
832
- #
833
+ #
833
834
  # compare_params(params, [:foo])
834
835
  # # => raises
835
- #
836
+ #
836
837
  # Raises:: WWW::Delicious::Error
837
- #
838
+ #
838
839
  def compare_params(params, valid_params)
839
840
  raise ArgumentError, "Expected `params` to be a kind of `Hash`" unless params.kind_of?(Hash)
840
841
  raise ArgumentError, "Expected `valid_params` to be a kind of `Array`" unless valid_params.kind_of?(Array)
841
-
842
+
842
843
  # compute options difference
843
844
  difference = params.keys - valid_params
844
845
  raise Error, "Invalid params: `#{difference.join('`, `')}`" unless difference.empty?
845
846
  end
846
-
847
-
847
+
848
+
848
849
  module XMLUtils #:nodoc:
849
-
850
+
850
851
  #
851
852
  # Returns the +xmlattr+ attribute value for current <tt>REXML::Element</tt>.
852
- #
853
+ #
853
854
  # If block is given and attribute value is not nil,
854
855
  # the content of the block is executed.
855
- #
856
+ #
856
857
  # === Examples
857
- #
858
+ #
858
859
  # dom = REXML::Document.new('<a name="1"><b>foo</b><b>bar</b></a>')
859
- #
860
+ #
860
861
  # dom.root.if_attribute_value(:name)
861
862
  # # => "1"
862
- #
863
+ #
863
864
  # dom.root.if_attribute_value(:name) { |v| v.to_i }
864
865
  # # => 1
865
- #
866
+ #
866
867
  # dom.root.if_attribute_value(:foo)
867
868
  # # => nil
868
- #
869
+ #
869
870
  # dom.root.if_attribute_value(:name) { |v| v.to_i }
870
871
  # # => nil
871
872
  #
@@ -878,7 +879,7 @@ module WWW #:nodoc:
878
879
  value = yield value if !value.nil? and block_given?
879
880
  value
880
881
  end
881
-
882
+
882
883
  #
883
884
  # Returns the value of +expression+ child of this element, if it exists.
884
885
  # If blog is given, block is called on +expression+ element value
@@ -891,7 +892,7 @@ module WWW #:nodoc:
891
892
  value
892
893
  end
893
894
  end
894
-
895
+
895
896
  #
896
897
  # Executes the content of +block+ on +expression+
897
898
  # child of this element, if it exists.
@@ -905,7 +906,7 @@ module WWW #:nodoc:
905
906
  nil
906
907
  end
907
908
  end
908
-
909
+
909
910
  end # XMLUtils
910
911
 
911
912
  end
@@ -913,24 +914,24 @@ end
913
914
 
914
915
 
915
916
  class Object
916
-
917
+
917
918
  # An object is blank if it's false, empty, or a whitespace string.
918
919
  # For example, "", " ", +nil+, [], and {} are blank.
919
- #
920
+ #
920
921
  # This simplifies
921
- #
922
+ #
922
923
  # if !address.nil? && !address.empty?
923
- #
924
+ #
924
925
  # to
925
- #
926
+ #
926
927
  # if !address.blank?
927
928
  #
928
929
  # Object#blank? comes from the GEM ActiveSupport 2.1.
929
- #
930
- def blank?
930
+ #
931
+ def blank?
931
932
  respond_to?(:empty?) ? empty? : !self
932
- end unless Object.method_defined? :blanks?
933
-
933
+ end unless Object.method_defined? :blank?
934
+
934
935
  end
935
936
 
936
937