www-delicious 0.3.0 → 0.4.0

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