www-delicious 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +41 -0
- data/{MIT-LICENSE → MIT-LICENSE.rdoc} +0 -0
- data/Manifest +49 -0
- data/{README → README.rdoc} +13 -14
- data/Rakefile +36 -118
- data/TODO +4 -0
- data/lib/www/delicious.rb +527 -446
- data/lib/www/delicious/bundle.rb +41 -22
- data/lib/www/delicious/element.rb +72 -0
- data/lib/www/delicious/errors.rb +2 -2
- data/lib/www/delicious/post.rb +71 -61
- data/lib/www/delicious/tag.rb +43 -62
- data/lib/www/delicious/version.rb +1 -1
- data/test/fixtures/net_response_invalid_account.yml +25 -0
- data/test/fixtures/net_response_success.yml +23 -0
- data/test/helper.rb +19 -79
- data/test/test_all.rb +17 -0
- data/test/test_offline.rb +17 -0
- data/test/test_online.rb +19 -0
- data/test/testcases/element/bundle.xml +1 -0
- data/test/testcases/element/invalid_root.xml +2 -0
- data/test/testcases/element/post.xml +2 -0
- data/test/testcases/element/post_unshared.xml +2 -0
- data/test/testcases/element/tag.xml +1 -0
- data/test/testcases/response/bundles_all.xml +5 -0
- data/test/testcases/response/bundles_all_empty.xml +2 -0
- data/test/testcases/response/bundles_delete.xml +2 -0
- data/test/testcases/response/bundles_set.xml +2 -0
- data/test/testcases/response/bundles_set_error.xml +2 -0
- data/test/testcases/response/posts_add.xml +2 -0
- data/test/testcases/response/posts_all.xml +12 -0
- data/test/testcases/response/posts_dates.xml +14 -0
- data/test/testcases/response/posts_dates_with_tag.xml +14 -0
- data/test/testcases/response/posts_delete.xml +2 -0
- data/test/testcases/response/posts_get.xml +7 -0
- data/test/testcases/response/posts_get_with_tag.xml +6 -0
- data/test/testcases/response/posts_recent.xml +19 -0
- data/test/testcases/response/posts_recent_with_tag.xml +19 -0
- data/test/testcases/response/tags_get.xml +5 -0
- data/test/testcases/response/tags_get_empty.xml +2 -0
- data/test/testcases/response/tags_rename.xml +2 -0
- data/test/testcases/response/update.delicious1.xml +2 -0
- data/test/testcases/response/update.xml +3 -0
- data/test/unit/bundle_test.rb +62 -0
- data/test/unit/delicious_test.rb +186 -238
- data/test/unit/online/online_test.rb +147 -0
- data/test/unit/post_test.rb +67 -0
- data/test/unit/tag_test.rb +68 -0
- data/www-delicious.gemspec +146 -0
- metadata +85 -31
- data/CHANGELOG +0 -5
- data/test/unit/delicious_bundle_test.rb +0 -90
- data/test/unit/delicious_online_test.rb +0 -143
- data/test/unit/delicious_post_test.rb +0 -102
- data/test/unit/delicious_tag_test.rb +0 -140
data/CHANGELOG.rdoc
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
= Changelog
|
2
|
+
|
3
|
+
|
4
|
+
== Release 0.2.0
|
5
|
+
|
6
|
+
* ADDED: :base_uri initialization option allows to create a new instance specifying a custom base_uri for all API calls. This is useful, for example, if you want to use ma.gno.lia Mirror'd APIs (http://wiki.ma.gnolia.com/Mirror%27d_API) instead the del.icio.us one (thanks to Jörg Battermann).
|
7
|
+
|
8
|
+
* ADDED: two new REXML::Element core extension elements to enhance interaction with node elements.
|
9
|
+
|
10
|
+
* FIXED: a wrong indentation in README file causes all list items to be rendered as source code.
|
11
|
+
|
12
|
+
* FIXED: Missing WWW::Delicious::Bundle#to_s method causes a class ID representation to be returned.
|
13
|
+
|
14
|
+
* FIXED: Missing unit tests for post_ calls (closes #18).
|
15
|
+
|
16
|
+
* FIXED: Added test for `shared` Post attribute and fixed an issue with duplicate `replace` method definition (closes #11).
|
17
|
+
|
18
|
+
* CHANGED: improved documentation and added more examples (closes #21).
|
19
|
+
|
20
|
+
* CHANGED: REXML::Element#attribute_value core extension has been renamed to REXML::Element#if_attribute_value.
|
21
|
+
|
22
|
+
* CHANGED: Renamed TESTCASE_PATH to TESTCASES_PATH.
|
23
|
+
|
24
|
+
* CHANGED: WWW::Delicious::Tag, WWW::Delicious::Bundle, WWW::Delicious::Post now extend WWW::Delicious::Element. Simplified classes.
|
25
|
+
|
26
|
+
* CHANGED: WWW::Delicious::Tag#to_s always returns a string even if name is nil.
|
27
|
+
|
28
|
+
* CHANGED: WWW::Delicious::Tag :count attribute is now stored and returned as Fixnum instead of String.
|
29
|
+
|
30
|
+
* CHANGED: Unit test reorganization (closes #22).
|
31
|
+
|
32
|
+
* CHANGED: Simplified and tidyfied test system with Mocha (closes #19).
|
33
|
+
|
34
|
+
* CHANGED: Various internal API methods have been renamed for coherence with their new scope.
|
35
|
+
|
36
|
+
* CHANGED: Integrated Echoe, cleaned Rakefile (closes #23).
|
37
|
+
|
38
|
+
|
39
|
+
== Release 0.1.0 (2008-05-11)
|
40
|
+
|
41
|
+
* Initial public release.
|
File without changes
|
data/Manifest
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
CHANGELOG.rdoc
|
2
|
+
lib/www/delicious/bundle.rb
|
3
|
+
lib/www/delicious/element.rb
|
4
|
+
lib/www/delicious/errors.rb
|
5
|
+
lib/www/delicious/post.rb
|
6
|
+
lib/www/delicious/tag.rb
|
7
|
+
lib/www/delicious/version.rb
|
8
|
+
lib/www/delicious.rb
|
9
|
+
MIT-LICENSE.rdoc
|
10
|
+
Rakefile
|
11
|
+
README.rdoc
|
12
|
+
setup.rb
|
13
|
+
test/fixtures/net_response_invalid_account.yml
|
14
|
+
test/fixtures/net_response_success.yml
|
15
|
+
test/helper.rb
|
16
|
+
test/test_all.rb
|
17
|
+
test/test_offline.rb
|
18
|
+
test/test_online.rb
|
19
|
+
test/testcases/element/bundle.xml
|
20
|
+
test/testcases/element/invalid_root.xml
|
21
|
+
test/testcases/element/post.xml
|
22
|
+
test/testcases/element/post_unshared.xml
|
23
|
+
test/testcases/element/tag.xml
|
24
|
+
test/testcases/response/bundles_all.xml
|
25
|
+
test/testcases/response/bundles_all_empty.xml
|
26
|
+
test/testcases/response/bundles_delete.xml
|
27
|
+
test/testcases/response/bundles_set.xml
|
28
|
+
test/testcases/response/bundles_set_error.xml
|
29
|
+
test/testcases/response/posts_add.xml
|
30
|
+
test/testcases/response/posts_all.xml
|
31
|
+
test/testcases/response/posts_dates.xml
|
32
|
+
test/testcases/response/posts_dates_with_tag.xml
|
33
|
+
test/testcases/response/posts_delete.xml
|
34
|
+
test/testcases/response/posts_get.xml
|
35
|
+
test/testcases/response/posts_get_with_tag.xml
|
36
|
+
test/testcases/response/posts_recent.xml
|
37
|
+
test/testcases/response/posts_recent_with_tag.xml
|
38
|
+
test/testcases/response/tags_get.xml
|
39
|
+
test/testcases/response/tags_get_empty.xml
|
40
|
+
test/testcases/response/tags_rename.xml
|
41
|
+
test/testcases/response/update.delicious1.xml
|
42
|
+
test/testcases/response/update.xml
|
43
|
+
test/unit/bundle_test.rb
|
44
|
+
test/unit/delicious_test.rb
|
45
|
+
test/unit/online/online_test.rb
|
46
|
+
test/unit/post_test.rb
|
47
|
+
test/unit/tag_test.rb
|
48
|
+
TODO
|
49
|
+
Manifest
|
data/{README → README.rdoc}
RENAMED
@@ -23,7 +23,7 @@ WWW::Delicious will try to give you the most with less efforts.
|
|
23
23
|
|
24
24
|
== Dependencies
|
25
25
|
|
26
|
-
|
26
|
+
* Ruby 1.8.6
|
27
27
|
|
28
28
|
|
29
29
|
== Source
|
@@ -176,15 +176,22 @@ You can also create new bundles or delete existing ones.
|
|
176
176
|
|
177
177
|
== Documentation
|
178
178
|
|
179
|
-
Visit the website[http://code.simonecarletti.com/] for the full documentation
|
179
|
+
Visit the website[http://code.simonecarletti.com/www-delicious] for the full documentation
|
180
180
|
and more examples.
|
181
181
|
|
182
182
|
|
183
|
+
== Author
|
184
|
+
|
185
|
+
* {Simone Carletti}[http://www.simonecarletti.com/] <weppos@weppos.net>
|
186
|
+
|
187
|
+
If you like this software, please {recommend me}[http://www.workingwithrails.com/person/11967-simone-carletti] at Working with Rails.
|
188
|
+
|
189
|
+
|
183
190
|
== Website and Project Home
|
184
191
|
|
185
|
-
|
186
|
-
|
187
|
-
|
192
|
+
* {Project Homepage}[http://code.simonecarletti.com/www-delicious]
|
193
|
+
* {At GitHub}[http://github.com/weppos/www-delicious/]
|
194
|
+
* {At RubyForge}[http://rubyforge.org/projects/www-delicious/]
|
188
195
|
|
189
196
|
|
190
197
|
== FeedBack and Bug reports
|
@@ -193,15 +200,7 @@ Feel free to email {Simone Carletti}[mailto:weppos@weppos.net]
|
|
193
200
|
with any questions or feedback.
|
194
201
|
|
195
202
|
Please submit your bug reports to the Redmine installation for WWW::Delicious
|
196
|
-
available at http://code.simonecarletti.com
|
197
|
-
|
198
|
-
|
199
|
-
== TODO
|
200
|
-
|
201
|
-
* allow Tags and Bundles to be passed as params for API calls
|
202
|
-
* allow Tags, Bundles and Posts to be used for API call (no longer readonly)
|
203
|
-
* improve the way new posts are created and updated
|
204
|
-
* more (see issue tracker on the website)
|
203
|
+
available at http://code.simonecarletti.com/www-delicious.
|
205
204
|
|
206
205
|
|
207
206
|
== Changelog
|
data/Rakefile
CHANGED
@@ -1,137 +1,55 @@
|
|
1
1
|
require 'rubygems'
|
2
|
-
require '
|
3
|
-
require 'rake/testtask'
|
4
|
-
require 'rake/rdoctask'
|
2
|
+
require 'echoe'
|
5
3
|
|
6
4
|
$LOAD_PATH.unshift(File.dirname(__FILE__) + "/lib")
|
7
5
|
require 'www/delicious'
|
8
6
|
|
9
|
-
|
7
|
+
|
10
8
|
# Common package properties
|
11
|
-
PKG_NAME
|
9
|
+
PKG_NAME = ENV['PKG_NAME'] || WWW::Delicious::GEM
|
12
10
|
PKG_VERSION = ENV['PKG_VERSION'] || WWW::Delicious::VERSION
|
13
11
|
PKG_SUMMARY = "Ruby client for del.icio.us API."
|
14
|
-
PKG_FILES
|
12
|
+
PKG_FILES = FileList.new("{lib,test}/**/*.rb") do |fl|
|
15
13
|
fl.exclude 'TODO'
|
16
|
-
fl.include %w(README CHANGELOG MIT-LICENSE)
|
14
|
+
fl.include %w(README.rdoc CHANGELOG.rdoc MIT-LICENSE.rdoc)
|
17
15
|
fl.include %w(Rakefile setup.rb)
|
18
16
|
end
|
19
17
|
RUBYFORGE_PROJECT = 'www-delicious'
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
18
|
+
|
19
|
+
if ENV['SNAPSHOT'].to_i == 1
|
20
|
+
PKG_VERSION << "." << Time.now.utc.strftime("%Y%m%d%H%M%S")
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
Echoe.new(PKG_NAME, PKG_VERSION) do |p|
|
25
|
+
p.author = "Simone Carletti"
|
26
|
+
p.email = "weppos@weppos.net"
|
27
|
+
p.summary = PKG_SUMMARY
|
28
|
+
p.description = <<-EOF
|
29
|
+
WWW::Delicious is a del.icio.us API client implemented in Ruby. \
|
30
|
+
It provides access to all available del.icio.us API queries \
|
31
|
+
and returns the original XML response as a friendly Ruby object.
|
32
|
+
EOF
|
33
|
+
p.url = "http://code.simonecarletti.com/www-delicious"
|
34
|
+
p.project = RUBYFORGE_PROJECT
|
35
|
+
|
36
|
+
p.need_zip = true
|
37
|
+
p.rcov_options = ["-x Rakefile -x mocha -x rcov"]
|
38
|
+
p.rdoc_pattern = /^(lib|CHANGELOG.rdoc|README.rdoc)/
|
39
|
+
|
40
|
+
p.development_dependencies = ["rake >=0.8",
|
41
|
+
"echoe >=3",
|
42
|
+
"mocha >=0.9"]
|
32
43
|
end
|
33
44
|
|
34
45
|
|
35
|
-
#
|
36
|
-
# task::
|
37
|
-
# :rcov
|
38
|
-
# desc::
|
39
|
-
# Create code coverage report.
|
40
|
-
#
|
41
46
|
begin
|
42
|
-
require '
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
t.test_files = FileList["test/unit/*.rb"]
|
48
|
-
t.output_dir = "coverage"
|
49
|
-
t.verbose = true
|
47
|
+
require 'code_statistics'
|
48
|
+
desc "Show library's code statistics"
|
49
|
+
task :stats do
|
50
|
+
CodeStatistics.new(["WWW::Delicious", "lib"],
|
51
|
+
["Tests", "test"]).to_s
|
50
52
|
end
|
51
53
|
rescue LoadError
|
52
|
-
puts "
|
53
|
-
end
|
54
|
-
|
55
|
-
|
56
|
-
#
|
57
|
-
# task::
|
58
|
-
# :rdoc
|
59
|
-
# desc::
|
60
|
-
# Generate RDoc documentation.
|
61
|
-
#
|
62
|
-
desc "Generate RDoc documentation"
|
63
|
-
Rake::RDocTask.new(:rdoc) do |rdoc|
|
64
|
-
rdoc.rdoc_dir = 'doc'
|
65
|
-
rdoc.title = "#{PKG_NAME} -- #{PKG_SUMMARY}"
|
66
|
-
rdoc.main = "README"
|
67
|
-
rdoc.options << "--inline-source" << "--line-numbers"
|
68
|
-
rdoc.options << '--charset' << 'utf-8'
|
69
|
-
rdoc.rdoc_files.include("README", "CHANGELOG", "MIT-LICENSE")
|
70
|
-
rdoc.rdoc_files.include("lib/**/*.rb")
|
71
|
-
end
|
72
|
-
|
73
|
-
|
74
|
-
unless defined?(Gem)
|
75
|
-
puts "Package Target requires RubyGEMs"
|
76
|
-
else
|
77
|
-
|
78
|
-
# Package requirements
|
79
|
-
GEM_SPEC = Gem::Specification.new do |s|
|
80
|
-
|
81
|
-
s.name = PKG_NAME
|
82
|
-
s.version = PKG_VERSION
|
83
|
-
s.summary = PKG_SUMMARY
|
84
|
-
s.description = <<-EOF
|
85
|
-
WWW::Delicious is a del.icio.us API client implemented in Ruby. \
|
86
|
-
It provides access to all available del.icio.us API queries \
|
87
|
-
and returns the original XML response as a friendly Ruby object.
|
88
|
-
EOF
|
89
|
-
s.platform = Gem::Platform::RUBY
|
90
|
-
s.rubyforge_project = RUBYFORGE_PROJECT
|
91
|
-
|
92
|
-
s.required_ruby_version = '>= 1.8.6'
|
93
|
-
s.add_dependency('rake', '>= 0.7.3')
|
94
|
-
|
95
|
-
s.files = PKG_FILES.to_a()
|
96
|
-
|
97
|
-
s.has_rdoc = true
|
98
|
-
s.rdoc_options << "--title" << "#{s.name} -- #{s.summary}"
|
99
|
-
s.rdoc_options << "--inline-source" << "--line-numbers"
|
100
|
-
s.rdoc_options << "--main" << "README"
|
101
|
-
s.rdoc_options << '--charset' << 'utf-8'
|
102
|
-
s.extra_rdoc_files = %w(README CHANGELOG MIT-LICENSE)
|
103
|
-
|
104
|
-
s.test_files = FileList["test/unit/*.rb"]
|
105
|
-
|
106
|
-
s.author = "Simone Carletti"
|
107
|
-
s.email = "weppos@weppos.net"
|
108
|
-
s.homepage = "http://code.simonecarletti.com/www-delicious"
|
109
|
-
|
110
|
-
end
|
111
|
-
|
112
|
-
#
|
113
|
-
# task::
|
114
|
-
# :gem
|
115
|
-
# desc::
|
116
|
-
# Generate the GEM package and all stuff.
|
117
|
-
#
|
118
|
-
Rake::GemPackageTask.new(GEM_SPEC) do |p|
|
119
|
-
p.gem_spec = GEM_SPEC
|
120
|
-
p.need_tar = true
|
121
|
-
p.need_zip = true
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
|
126
|
-
#
|
127
|
-
# task::
|
128
|
-
# :clean
|
129
|
-
# desc::
|
130
|
-
# Clean up generated directories and files.
|
131
|
-
#
|
132
|
-
desc "Clean up generated directories and files"
|
133
|
-
task :clean do
|
134
|
-
rm_rf "pkg"
|
135
|
-
rm_rf "doc"
|
136
|
-
rm_rf "coverage"
|
54
|
+
puts "CodeStatistics (Rails) is not available"
|
137
55
|
end
|
data/TODO
ADDED
data/lib/www/delicious.rb
CHANGED
@@ -94,6 +94,9 @@ module WWW #:nodoc:
|
|
94
94
|
|
95
95
|
# del.icio.us account password
|
96
96
|
attr_reader :password
|
97
|
+
|
98
|
+
# base URI for del.icio.us API
|
99
|
+
attr_reader :base_uri
|
97
100
|
|
98
101
|
|
99
102
|
# API Base URL
|
@@ -135,8 +138,7 @@ module WWW #:nodoc:
|
|
135
138
|
TIME_CONVERTER = lambda { |time| time.iso8601() }
|
136
139
|
|
137
140
|
|
138
|
-
|
139
|
-
#
|
141
|
+
#
|
140
142
|
# Constructs a new <tt>WWW::Delicious</tt> object
|
141
143
|
# with given +username+ and +password+.
|
142
144
|
#
|
@@ -151,19 +153,29 @@ module WWW #:nodoc:
|
|
151
153
|
# d.update() # => Fri May 02 18:02:48 UTC 2008
|
152
154
|
# end
|
153
155
|
# # => self
|
156
|
+
#
|
157
|
+
# You can also specify some additional options, including a custom user agent
|
158
|
+
# or the base URI for del.icio.us API.
|
159
|
+
#
|
160
|
+
# WWW::Delicious('user', 'psw', :base_uri => 'https://ma.gnolia.com/api/mirrord') do |d|
|
161
|
+
# # the following call is mirrored by ma.gnolia
|
162
|
+
# d.update() # => Fri May 02 18:02:48 UTC 2008
|
163
|
+
# end
|
164
|
+
# # => self
|
154
165
|
#
|
155
166
|
# === Options
|
156
|
-
# This class accepts
|
167
|
+
# This class accepts a Hash with additional options.
|
157
168
|
# Here's the list of valid keys:
|
158
169
|
#
|
159
170
|
# <tt>:user_agent</tt>:: User agent to display in HTTP requests.
|
171
|
+
# <tt>:base_uri</tt>:: The base URI to del.icio.us API.
|
160
172
|
#
|
161
173
|
def initialize(username, password, options = {}, &block) # :yields: delicious
|
162
174
|
@username, @password = username.to_s, password.to_s
|
163
|
-
|
175
|
+
|
164
176
|
# set API base URI
|
165
|
-
@base_uri = URI.parse(API_BASE_URI)
|
166
|
-
|
177
|
+
@base_uri = URI.parse(options[:base_uri] || API_BASE_URI)
|
178
|
+
|
167
179
|
init_user_agent(options)
|
168
180
|
init_http_client(options)
|
169
181
|
|
@@ -171,9 +183,8 @@ module WWW #:nodoc:
|
|
171
183
|
self # ensure to always return self even if block is given
|
172
184
|
end
|
173
185
|
|
174
|
-
|
175
|
-
|
176
|
-
#
|
186
|
+
|
187
|
+
#
|
177
188
|
# Returns the reference to current <tt>@http_client</tt>.
|
178
189
|
# The http is always valid unless it has been previously set to +nil+.
|
179
190
|
#
|
@@ -182,15 +193,14 @@ module WWW #:nodoc:
|
|
182
193
|
#
|
183
194
|
# # valid client
|
184
195
|
# obj.http_client # => Net::HTTP
|
185
|
-
#
|
196
|
+
#
|
186
197
|
def http_client()
|
187
198
|
return @http_client
|
188
199
|
end
|
189
200
|
|
190
|
-
|
191
|
-
#
|
201
|
+
#
|
192
202
|
# Sets the internal <tt>@http_client</tt> to +client+.
|
193
|
-
#
|
203
|
+
#
|
194
204
|
# # nil client
|
195
205
|
# obj.http_client = nil
|
196
206
|
#
|
@@ -206,18 +216,14 @@ module WWW #:nodoc:
|
|
206
216
|
end
|
207
217
|
@http_client = client
|
208
218
|
end
|
209
|
-
|
210
|
-
public
|
211
|
-
#
|
219
|
+
|
212
220
|
# Returns current user agent string.
|
213
|
-
#
|
214
221
|
def user_agent()
|
215
222
|
return @headers['User-Agent']
|
216
223
|
end
|
217
224
|
|
218
225
|
|
219
|
-
|
220
|
-
#
|
226
|
+
#
|
221
227
|
# Returns true if given account credentials are valid.
|
222
228
|
#
|
223
229
|
# d = WWW::Delicious.new('username', 'password')
|
@@ -230,10 +236,11 @@ module WWW #:nodoc:
|
|
230
236
|
# It doesn't return false if an HTTP error or any kind of other error occurs,
|
231
237
|
# it raises back the exception to the caller instead.
|
232
238
|
#
|
239
|
+
#
|
233
240
|
# Raises:: WWW::Delicious::Error
|
234
241
|
# Raises:: WWW::Delicious::HTTPError
|
235
242
|
# Raises:: WWW::Delicious::ResponseError
|
236
|
-
#
|
243
|
+
#
|
237
244
|
def valid_account?
|
238
245
|
update()
|
239
246
|
return true
|
@@ -242,8 +249,7 @@ module WWW #:nodoc:
|
|
242
249
|
raise
|
243
250
|
end
|
244
251
|
|
245
|
-
|
246
|
-
#
|
252
|
+
#
|
247
253
|
# Checks to see when a user last posted an item
|
248
254
|
# and returns the last update +Time+ for the user.
|
249
255
|
#
|
@@ -253,14 +259,13 @@ module WWW #:nodoc:
|
|
253
259
|
# Raises:: WWW::Delicious::Error
|
254
260
|
# Raises:: WWW::Delicious::HTTPError
|
255
261
|
# Raises:: WWW::Delicious::ResponseError
|
256
|
-
#
|
262
|
+
#
|
257
263
|
def update()
|
258
264
|
response = request(API_PATH_UPDATE)
|
259
265
|
return parse_update_response(response.body)
|
260
266
|
end
|
261
267
|
|
262
|
-
|
263
|
-
#
|
268
|
+
#
|
264
269
|
# Retrieves all of a user's bundles
|
265
270
|
# and returns an array of <tt>WWW::Delicious::Bundle</tt>.
|
266
271
|
#
|
@@ -271,14 +276,13 @@ module WWW #:nodoc:
|
|
271
276
|
# Raises:: WWW::Delicious::Error
|
272
277
|
# Raises:: WWW::Delicious::HTTPError
|
273
278
|
# Raises:: WWW::Delicious::ResponseError
|
274
|
-
#
|
279
|
+
#
|
275
280
|
def bundles_all()
|
276
281
|
response = request(API_PATH_BUNDLES_ALL)
|
277
|
-
return
|
282
|
+
return parse_bundle_collection(response.body)
|
278
283
|
end
|
279
284
|
|
280
|
-
|
281
|
-
#
|
285
|
+
#
|
282
286
|
# Assignes a set of tags to a single bundle,
|
283
287
|
# wipes away previous settings for bundle.
|
284
288
|
#
|
@@ -292,16 +296,20 @@ module WWW #:nodoc:
|
|
292
296
|
# Raises:: WWW::Delicious::Error
|
293
297
|
# Raises:: WWW::Delicious::HTTPError
|
294
298
|
# Raises:: WWW::Delicious::ResponseError
|
295
|
-
#
|
299
|
+
#
|
296
300
|
def bundles_set(bundle_or_name, tags = [])
|
297
301
|
params = prepare_bundles_set_params(bundle_or_name, tags)
|
298
302
|
response = request(API_PATH_BUNDLES_SET, params)
|
299
303
|
return parse_and_eval_execution_response(response.body)
|
300
304
|
end
|
301
305
|
|
302
|
-
|
303
|
-
#
|
304
|
-
#
|
306
|
+
#
|
307
|
+
# Deletes +bundle_or_name+ bundle from del.icio.us.
|
308
|
+
# +bundle_or_name+ can be either a WWW::Delicious::Bundle instance
|
309
|
+
# or a string with the name of the bundle.
|
310
|
+
#
|
311
|
+
# This method doesn't care whether the exists.
|
312
|
+
# If not, the execution will silently return without rising any error.
|
305
313
|
#
|
306
314
|
# # delete from a bundle
|
307
315
|
# d.bundles_delete(WWW::Delicious::Bundle.new('MyBundle'))
|
@@ -313,47 +321,69 @@ module WWW #:nodoc:
|
|
313
321
|
# Raises:: WWW::Delicious::Error
|
314
322
|
# Raises:: WWW::Delicious::HTTPError
|
315
323
|
# Raises:: WWW::Delicious::ResponseError
|
316
|
-
#
|
324
|
+
#
|
317
325
|
def bundles_delete(bundle_or_name)
|
318
326
|
params = prepare_bundles_delete_params(bundle_or_name)
|
319
327
|
response = request(API_PATH_BUNDLES_DELETE, params)
|
320
328
|
return parse_and_eval_execution_response(response.body)
|
321
329
|
end
|
322
330
|
|
323
|
-
|
324
|
-
#
|
331
|
+
#
|
325
332
|
# Retrieves the list of tags and number of times used by the user
|
326
333
|
# and returns an array of <tt>WWW::Delicious::Tag</tt>.
|
327
334
|
#
|
335
|
+
# d.tags_get() # => [#<WWW::Delicious::Tag>, #<WWW::Delicious::Tag>, ...]
|
336
|
+
# d.tags_get() # => []
|
337
|
+
#
|
338
|
+
#
|
328
339
|
# Raises:: WWW::Delicious::Error
|
329
340
|
# Raises:: WWW::Delicious::HTTPError
|
330
341
|
# Raises:: WWW::Delicious::ResponseError
|
331
|
-
#
|
342
|
+
#
|
332
343
|
def tags_get()
|
333
344
|
response = request(API_PATH_TAGS_GET)
|
334
|
-
return
|
345
|
+
return parse_tag_collection(response.body)
|
335
346
|
end
|
336
347
|
|
337
|
-
|
338
|
-
#
|
348
|
+
#
|
339
349
|
# Renames an existing tag with a new tag name.
|
340
350
|
#
|
351
|
+
# # rename from a tag
|
352
|
+
# d.bundles_set(WWW::Delicious::Tag.new('old'), WWW::Delicious::Tag.new('new'))
|
353
|
+
#
|
354
|
+
# # rename from a string
|
355
|
+
# d.bundles_set('old', 'new')
|
356
|
+
#
|
357
|
+
#
|
341
358
|
# Raises:: WWW::Delicious::Error
|
342
359
|
# Raises:: WWW::Delicious::HTTPError
|
343
360
|
# Raises:: WWW::Delicious::ResponseError
|
344
|
-
#
|
361
|
+
#
|
345
362
|
def tags_rename(from_name_or_tag, to_name_or_tag)
|
346
363
|
params = prepare_tags_rename_params(from_name_or_tag, to_name_or_tag)
|
347
364
|
response = request(API_PATH_TAGS_RENAME, params)
|
348
365
|
return parse_and_eval_execution_response(response.body)
|
349
366
|
end
|
350
367
|
|
351
|
-
|
352
|
-
#
|
368
|
+
#
|
353
369
|
# Returns an array of <tt>WWW::Delicious::Post</tt> matching +options+.
|
354
370
|
# If no option is given, the last post is returned.
|
355
371
|
# If no date or url is given, most recent date will be used.
|
356
372
|
#
|
373
|
+
# d.posts_get() # => [#<WWW::Delicious::Post>, #<WWW::Delicious::Post>, ...]
|
374
|
+
# d.posts_get() # => []
|
375
|
+
#
|
376
|
+
# # get all posts tagged with ruby
|
377
|
+
# d.posts_get(:tag => WWW::Delicious::Tag.new('ruby))
|
378
|
+
#
|
379
|
+
# # get all posts matching URL 'http://www.simonecarletti.com'
|
380
|
+
# d.posts_get(:url => URI.parse('http://www.simonecarletti.com'))
|
381
|
+
#
|
382
|
+
# # get all posts tagged with ruby and matching URL 'http://www.simonecarletti.com'
|
383
|
+
# d.posts_get(:tag => WWW::Delicious::Tag.new('ruby),
|
384
|
+
# :url => URI.parse('http://www.simonecarletti.com'))
|
385
|
+
#
|
386
|
+
#
|
357
387
|
# === Options
|
358
388
|
# <tt>:tag</tt>:: a tag to filter by. It can be either a <tt>WWW::Delicious::Tag</tt> or a +String+.
|
359
389
|
# <tt>:dt</tt>:: a +Time+ with a date to filter by.
|
@@ -362,30 +392,42 @@ module WWW #:nodoc:
|
|
362
392
|
# Raises:: WWW::Delicious::Error
|
363
393
|
# Raises:: WWW::Delicious::HTTPError
|
364
394
|
# Raises:: WWW::Delicious::ResponseError
|
365
|
-
#
|
395
|
+
#
|
366
396
|
def posts_get(options = {})
|
367
397
|
params = prepare_posts_params(options.clone, [:dt, :tag, :url])
|
368
398
|
response = request(API_PATH_POSTS_GET, params)
|
369
|
-
return
|
399
|
+
return parse_post_collection(response.body)
|
370
400
|
end
|
371
401
|
|
372
|
-
|
373
|
-
#
|
402
|
+
#
|
374
403
|
# Returns a list of the most recent posts, filtered by argument.
|
375
404
|
#
|
405
|
+
# # get the most recent posts
|
406
|
+
# d.posts_recent()
|
407
|
+
#
|
408
|
+
# # get the 10 most recent posts
|
409
|
+
# d.posts_recent(:count => 10)
|
410
|
+
#
|
411
|
+
#
|
376
412
|
# === Options
|
377
413
|
# <tt>:tag</tt>:: a tag to filter by. It can be either a <tt>WWW::Delicious::Tag</tt> or a +String+.
|
378
414
|
# <tt>:count</tt>:: number of items to retrieve. (default: 15, maximum: 100).
|
379
|
-
#
|
415
|
+
#
|
380
416
|
def posts_recent(options = {})
|
381
417
|
params = prepare_posts_params(options.clone, [:count, :tag])
|
382
418
|
response = request(API_PATH_POSTS_RECENT, params)
|
383
|
-
return
|
419
|
+
return parse_post_collection(response.body)
|
384
420
|
end
|
385
|
-
|
386
|
-
|
387
|
-
#
|
388
|
-
#
|
421
|
+
|
422
|
+
#
|
423
|
+
# Returns a list of all posts, filtered by argument.
|
424
|
+
#
|
425
|
+
# # get all (this is a very expensive query)
|
426
|
+
# d.posts_all
|
427
|
+
#
|
428
|
+
# # get all posts matching ruby
|
429
|
+
# d.posts_all(:tag => WWW::Delicious::Tag.new('ruby'))
|
430
|
+
#
|
389
431
|
#
|
390
432
|
# === Options
|
391
433
|
# <tt>:tag</tt>:: a tag to filter by. It can be either a <tt>WWW::Delicious::Tag</tt> or a +String+.
|
@@ -393,13 +435,21 @@ module WWW #:nodoc:
|
|
393
435
|
def posts_all(options = {})
|
394
436
|
params = prepare_posts_params(options.clone, [:tag])
|
395
437
|
response = request(API_PATH_POSTS_ALL, params)
|
396
|
-
return
|
438
|
+
return parse_post_collection(response.body)
|
397
439
|
end
|
398
440
|
|
399
|
-
public
|
400
441
|
#
|
401
442
|
# Returns a list of dates with the number of posts at each date.
|
402
443
|
#
|
444
|
+
# # get number of posts per date
|
445
|
+
# d.posts_dates
|
446
|
+
# # => { '2008-05-05' => 12, '2008-05-06' => 3, ... }
|
447
|
+
#
|
448
|
+
# # get number posts per date tagged as ruby
|
449
|
+
# d.posts_dates(:tag => WWW::Delicious::Tag.new('ruby'))
|
450
|
+
# # => { '2008-05-05' => 10, '2008-05-06' => 3, ... }
|
451
|
+
#
|
452
|
+
#
|
403
453
|
# === Options
|
404
454
|
# <tt>:tag</tt>:: a tag to filter by. It can be either a <tt>WWW::Delicious::Tag</tt> or a +String+.
|
405
455
|
#
|
@@ -409,24 +459,38 @@ module WWW #:nodoc:
|
|
409
459
|
return parse_posts_dates_response(response.body)
|
410
460
|
end
|
411
461
|
|
412
|
-
public
|
413
462
|
#
|
414
463
|
# Add a post to del.icio.us.
|
464
|
+
# +post_or_values+ can be either a +WWW::Delicious::Post+ instance
|
465
|
+
# or a Hash of params. This method accepts all params available
|
466
|
+
# to initialize a new +WWW::Delicious::Post+.
|
467
|
+
#
|
468
|
+
# # add a post from WWW::Delicious::Post
|
469
|
+
# d.posts_add(WWW::Delicious::Post.new(:url => 'http://www.foobar.com', :title => 'Hello world!'))
|
470
|
+
#
|
471
|
+
# # add a post from values
|
472
|
+
# d.posts_add(:url => 'http://www.foobar.com', :title => 'Hello world!')
|
473
|
+
#
|
415
474
|
#
|
416
475
|
def posts_add(post_or_values)
|
417
|
-
params =
|
476
|
+
params = prepare_param_post(post_or_values).to_params
|
418
477
|
response = request(API_PATH_POSTS_ADD, params)
|
419
478
|
return parse_and_eval_execution_response(response.body)
|
420
479
|
end
|
421
480
|
|
422
|
-
public
|
423
481
|
#
|
424
|
-
# Deletes
|
482
|
+
# Deletes the post matching given +url+ from del.icio.us.
|
483
|
+
# +url+ can be either an URI instance or a string representation of a valid URL.
|
484
|
+
#
|
485
|
+
# This method doesn't care whether a post with given +url+ exists.
|
486
|
+
# If not, the execution will silently return without rising any error.
|
487
|
+
#
|
488
|
+
# # delete a post from URI
|
489
|
+
# d.post_delete(URI.parse('http://www.foobar.com/'))
|
490
|
+
#
|
491
|
+
# # delete a post from a string
|
492
|
+
# d.post_delete('http://www.foobar.com/')
|
425
493
|
#
|
426
|
-
# === Params
|
427
|
-
# url::
|
428
|
-
# the url of the item.
|
429
|
-
# It can be either an +URI+ or a +String+.
|
430
494
|
#
|
431
495
|
def posts_delete(url)
|
432
496
|
params = prepare_posts_params({:url => url}, [:url])
|
@@ -436,431 +500,448 @@ module WWW #:nodoc:
|
|
436
500
|
|
437
501
|
|
438
502
|
protected
|
439
|
-
#
|
440
|
-
# Initializes HTTP client.
|
441
|
-
#
|
442
|
-
def init_http_client(options)
|
443
|
-
http = Net::HTTP.new(@base_uri.host, 443)
|
444
|
-
http.use_ssl = true if @base_uri.scheme == "https"
|
445
|
-
http.verify_mode = OpenSSL::SSL::VERIFY_NONE # FIXME: not 100% supported
|
446
|
-
self.http_client = http
|
447
|
-
end
|
448
|
-
|
449
|
-
protected
|
450
|
-
#
|
451
|
-
# Initializes user agent value for HTTP requests.
|
452
|
-
#
|
453
|
-
def init_user_agent(options)
|
454
|
-
user_agent = options[:user_agent] || default_user_agent()
|
455
|
-
@headers ||= {}
|
456
|
-
@headers['User-Agent'] = user_agent
|
457
|
-
end
|
458
|
-
|
459
|
-
protected
|
460
|
-
#
|
461
|
-
# Creates and returns the default user agent string.
|
462
|
-
#
|
463
|
-
# By default, the user agent is composed by the following schema:
|
464
|
-
# <tt>NAME/VERSION (Ruby/RUBY_VERSION)</tt>
|
465
|
-
#
|
466
|
-
# * +NAME+ is the constant representing this library name
|
467
|
-
# * +VERSION+ is the constant representing current library version
|
468
|
-
# * +RUBY_VERSION+ is the version of Ruby interpreter the library is interpreted by
|
469
|
-
#
|
470
|
-
def default_user_agent()
|
471
|
-
return "#{NAME}/#{VERSION} (Ruby/#{RUBY_VERSION})"
|
472
|
-
end
|
473
|
-
|
474
|
-
|
475
|
-
protected
|
476
|
-
#
|
477
|
-
# Composes an HTTP query string from an hash of +options+.
|
478
|
-
#
|
479
|
-
def http_build_query(params = {})
|
480
|
-
return params.collect do |k,v|
|
481
|
-
"#{URI.encode(k.to_s)}=#{URI.encode(v.to_s)}" unless v.nil?
|
482
|
-
end.compact.join('&')
|
483
|
-
end
|
484
503
|
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
504
|
+
# Initializes the HTTP client.
|
505
|
+
# It automatically enable +use_ssl+ flag according to +@base_uri+ scheme.
|
506
|
+
def init_http_client(options)
|
507
|
+
http = Net::HTTP.new(@base_uri.host, 443)
|
508
|
+
http.use_ssl = true if @base_uri.scheme == "https"
|
509
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE # FIXME: not 100% supported
|
510
|
+
self.http_client = http
|
511
|
+
end
|
512
|
+
|
513
|
+
# Initializes user agent value for HTTP requests.
|
514
|
+
def init_user_agent(options)
|
515
|
+
user_agent = options[:user_agent] || default_user_agent()
|
516
|
+
@headers ||= {}
|
517
|
+
@headers['User-Agent'] = user_agent
|
518
|
+
end
|
519
|
+
|
520
|
+
#
|
521
|
+
# Creates and returns the default user agent string.
|
522
|
+
#
|
523
|
+
# By default, the user agent is composed by the following schema:
|
524
|
+
# <tt>NAME/VERSION (Ruby/RUBY_VERSION)</tt>
|
525
|
+
#
|
526
|
+
# * +NAME+ is the constant representing this library name
|
527
|
+
# * +VERSION+ is the constant representing current library version
|
528
|
+
# * +RUBY_VERSION+ is the version of Ruby interpreter the library is interpreted by
|
529
|
+
#
|
530
|
+
# default_user_agent
|
531
|
+
# # => WWW::Delicious/0.1.0 (Ruby/1.8.6)
|
532
|
+
#
|
533
|
+
def default_user_agent
|
534
|
+
return "#{NAME}/#{VERSION} (Ruby/#{RUBY_VERSION})"
|
535
|
+
end
|
536
|
+
|
537
|
+
|
538
|
+
#
|
539
|
+
# Composes an HTTP query string from an hash of +options+.
|
540
|
+
# The result is URI encoded.
|
541
|
+
#
|
542
|
+
# http_build_query(:foo => 'baa', :bar => 'boo')
|
543
|
+
# # => foo=baa&bar=boo
|
544
|
+
#
|
545
|
+
def http_build_query(params = {})
|
546
|
+
return params.collect do |k,v|
|
547
|
+
"#{URI.encode(k.to_s)}=#{URI.encode(v.to_s)}" unless v.nil?
|
548
|
+
end.compact.join('&')
|
549
|
+
end
|
550
|
+
|
551
|
+
#
|
552
|
+
# Sends an HTTP GET request to +path+ and appends given +params+.
|
553
|
+
#
|
554
|
+
# This method is 100% compliant with Delicious API reference.
|
555
|
+
# It waits at least 1 second between each HTTP request and
|
556
|
+
# provides an identifiable user agent by default,
|
557
|
+
# or the custom user agent set by +user_agent+ option
|
558
|
+
# when this istance has been created.
|
559
|
+
#
|
560
|
+
# request('/v1/api/path', :foo => 1, :bar => 2)
|
561
|
+
# # => sends a GET request to /v1/api/path?foo=1&bar=2
|
562
|
+
#
|
563
|
+
def request(path, params = {})
|
564
|
+
raise Error, 'Invalid HTTP Client' unless http_client
|
565
|
+
wait_before_new_request
|
566
|
+
|
567
|
+
uri = @base_uri.merge(path)
|
568
|
+
uri.query = http_build_query(params) unless params.empty?
|
569
|
+
|
570
|
+
begin
|
571
|
+
@last_request = Time.now # see #wait_before_new_request
|
572
|
+
@last_request_uri = uri # useful for debug
|
573
|
+
response = make_request(uri)
|
574
|
+
rescue => e # catch EOFError, SocketError and more
|
575
|
+
raise HTTPError, e.message
|
576
|
+
end
|
577
|
+
|
578
|
+
case response
|
579
|
+
when Net::HTTPSuccess
|
580
|
+
return response
|
581
|
+
when Net::HTTPUnauthorized # 401
|
582
|
+
raise HTTPError, 'Invalid username or password'
|
583
|
+
when Net::HTTPServiceUnavailable # 503
|
584
|
+
raise HTTPError, 'You have been throttled.' +
|
585
|
+
'Please ensure you are waiting at least one second before each request.'
|
586
|
+
else
|
587
|
+
raise HTTPError, "HTTP #{response.code}: #{response.message}"
|
588
|
+
end
|
589
|
+
end
|
590
|
+
|
591
|
+
# Makes the real HTTP request to given +uri+ and returns the +response+.
|
592
|
+
# This method exists basically to simplify unit testing with mocha.
|
593
|
+
def make_request(uri)
|
594
|
+
http_client.start do |http|
|
506
595
|
req = Net::HTTP::Get.new(uri.request_uri, @headers)
|
507
596
|
req.basic_auth(@username, @password)
|
508
597
|
http.request(req)
|
509
598
|
end
|
510
|
-
rescue => e # catch EOFError, SocketError and more
|
511
|
-
raise HTTPError, e.message
|
512
599
|
end
|
513
|
-
|
514
|
-
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
600
|
+
|
601
|
+
#
|
602
|
+
# Delicious API reference requests to wait AT LEAST ONE SECOND
|
603
|
+
# between queries or the client is likely to get automatically throttled.
|
604
|
+
#
|
605
|
+
# This method calculates the difference between current time
|
606
|
+
# and the last request time and wait for the necessary time to meet
|
607
|
+
# SECONDS_BEFORE_NEW_REQUEST requirement.
|
608
|
+
#
|
609
|
+
# The difference is not rounded. If you only have to wait for 0.034 seconds
|
610
|
+
# then your don't have to wait 0 or 1 seconds, but 0.034 seconds!
|
611
|
+
#
|
612
|
+
def wait_before_new_request
|
613
|
+
return unless @last_request # this is the first request
|
614
|
+
# puts "Last request at #{TIME_CONVERTER.call(@last_request)}" if debug?
|
615
|
+
diff = Time.now - @last_request
|
616
|
+
if diff < SECONDS_BEFORE_NEW_REQUEST
|
617
|
+
# puts "Sleeping for #{diff} before new request..." if debug?
|
618
|
+
sleep(SECONDS_BEFORE_NEW_REQUEST - diff)
|
619
|
+
end
|
524
620
|
end
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
|
536
|
-
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
# puts "Sleeping for #{diff} before new request..." if debug?
|
545
|
-
sleep(SECONDS_BEFORE_NEW_REQUEST - diff)
|
621
|
+
|
622
|
+
|
623
|
+
#
|
624
|
+
# Parses the response <tt>body</tt> and runs a common set of validators.
|
625
|
+
# Returns <tt>body</tt> as parsed REXML::Document on success.
|
626
|
+
#
|
627
|
+
# Raises:: WWW::Delicious::ResponseError in case of invalid response.
|
628
|
+
#
|
629
|
+
def parse_and_validate_response(body, options = {})
|
630
|
+
dom = REXML::Document.new(body)
|
631
|
+
|
632
|
+
if (value = options[:root_name]) && dom.root.name != value
|
633
|
+
raise ResponseError, "Invalid response, root node is not `#{value}`"
|
634
|
+
end
|
635
|
+
if (value = options[:root_text]) && dom.root.text != value
|
636
|
+
raise ResponseError, value
|
637
|
+
end
|
638
|
+
|
639
|
+
return dom
|
546
640
|
end
|
547
|
-
end
|
548
|
-
|
549
|
-
|
550
|
-
protected
|
551
|
-
#
|
552
|
-
# Parses the response +body+ and runs a common set of validators.
|
553
|
-
#
|
554
|
-
def parse_and_validate_response(body, options = {})
|
555
|
-
dom = REXML::Document.new(body)
|
556
641
|
|
557
|
-
|
558
|
-
|
642
|
+
#
|
643
|
+
# Parses and evaluates the response returned by an execution,
|
644
|
+
# usually an update/delete/insert operation.
|
645
|
+
#
|
646
|
+
# Raises:: WWW::Delicious::ResponseError in case of invalid response
|
647
|
+
# Raises:: WWW::Delicious::Error in case of execution error
|
648
|
+
#
|
649
|
+
def parse_and_eval_execution_response(body)
|
650
|
+
dom = parse_and_validate_response(body, :root_name => 'result')
|
651
|
+
response = dom.root.if_attribute_value(:code)
|
652
|
+
response = dom.root.text if response.nil?
|
653
|
+
raise Error, "Invalid response, #{response}" unless %w(done ok).include?(response)
|
654
|
+
true
|
559
655
|
end
|
560
|
-
|
561
|
-
|
656
|
+
|
657
|
+
# Parses the response of an Update request
|
658
|
+
# and returns the update Timestamp.
|
659
|
+
def parse_update_response(body)
|
660
|
+
dom = parse_and_validate_response(body, :root_name => 'update')
|
661
|
+
dom.root.if_attribute_value(:time) { |v| Time.parse(v) }
|
562
662
|
end
|
563
|
-
|
564
|
-
return dom
|
565
|
-
end
|
566
|
-
|
567
|
-
protected
|
568
|
-
#
|
569
|
-
# Parses and evaluates the response returned by an execution,
|
570
|
-
# usually an update/delete/insert operation.
|
571
|
-
#
|
572
|
-
def parse_and_eval_execution_response(body)
|
573
|
-
dom = parse_and_validate_response(body, :root_name => 'result')
|
574
|
-
|
575
|
-
rsp = dom.root.attribute_value(:code)
|
576
|
-
rsp = dom.root.text if rsp.nil?
|
577
|
-
raise Error, "Invalid response, #{rsp}" unless %w(done ok).include?(rsp)
|
578
|
-
end
|
579
|
-
|
580
|
-
protected
|
581
|
-
#
|
582
|
-
# Parses the response of an 'update' request.
|
583
|
-
#
|
584
|
-
def parse_update_response(body)
|
585
|
-
dom = parse_and_validate_response(body, :root_name => 'update')
|
586
|
-
return dom.root.attribute_value(:time) { |v| Time.parse(v) }
|
587
|
-
end
|
588
|
-
|
589
|
-
protected
|
590
|
-
#
|
591
|
-
# Parses the response of a 'bundles_all' request
|
592
|
-
# and returns an array of <tt>WWW::Delicious::Bundle</tt>.
|
593
|
-
#
|
594
|
-
def parse_bundles_all_response(body)
|
595
|
-
dom = parse_and_validate_response(body, :root_name => 'bundles')
|
596
|
-
bundles = []
|
597
663
|
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
604
|
-
# Parses the response of a 'tags_get' request
|
605
|
-
# and returns an array of <tt>WWW::Delicious::Tag</tt>.
|
606
|
-
#
|
607
|
-
def parse_tags_get_response(body)
|
608
|
-
dom = parse_and_validate_response(body, :root_name => 'tags')
|
609
|
-
tags = []
|
664
|
+
# Parses a response containing a collection of Bundles
|
665
|
+
# and returns an array of <tt>WWW::Delicious::Bundle</tt>.
|
666
|
+
def parse_bundle_collection(body)
|
667
|
+
dom = parse_and_validate_response(body, :root_name => 'bundles')
|
668
|
+
dom.root.elements.collect('bundle') { |xml| Bundle.from_rexml(xml) }
|
669
|
+
end
|
610
670
|
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
# Parses a response containing a list of Posts
|
618
|
-
# and returns an array of <tt>WWW::Delicious::Post</tt>.
|
619
|
-
#
|
620
|
-
def parse_posts_response(body)
|
621
|
-
dom = parse_and_validate_response(body, :root_name => 'posts')
|
622
|
-
posts = []
|
671
|
+
# Parses a response containing a collection of Tags
|
672
|
+
# and returns an array of <tt>WWW::Delicious::Tag</tt>.
|
673
|
+
def parse_tag_collection(body)
|
674
|
+
dom = parse_and_validate_response(body, :root_name => 'tags')
|
675
|
+
dom.root.elements.collect('tag') { |xml| Tag.from_rexml(xml) }
|
676
|
+
end
|
623
677
|
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
#
|
630
|
-
# Parses the response of a 'posts_dates' request
|
631
|
-
# and returns an +Hash+ of date => count.
|
632
|
-
#
|
633
|
-
def parse_posts_dates_response(body)
|
634
|
-
dom = parse_and_validate_response(body, :root_name => 'dates')
|
635
|
-
results = {}
|
636
|
-
|
637
|
-
dom.root.elements.each('date') do |xml|
|
638
|
-
date = xml.attribute_value(:date)
|
639
|
-
count = xml.attribute_value(:count).to_i()
|
640
|
-
results[date] = count
|
678
|
+
# Parses a response containing a collection of Posts
|
679
|
+
# and returns an array of <tt>WWW::Delicious::Post</tt>.
|
680
|
+
def parse_post_collection(body)
|
681
|
+
dom = parse_and_validate_response(body, :root_name => 'posts')
|
682
|
+
dom.root.elements.collect('post') { |xml| Post.from_rexml(xml) }
|
641
683
|
end
|
642
|
-
|
643
|
-
|
644
|
-
|
645
|
-
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
652
|
-
#
|
653
|
-
# Raises::
|
654
|
-
#
|
655
|
-
def prepare_bundles_set_params(name_or_bundle, tags = [])
|
656
|
-
bundle = prepare_param_bundle(name_or_bundle, tags) do |b|
|
657
|
-
raise Error, "Bundle name is empty" if b.name.empty?
|
658
|
-
raise Error, "Bundle must contain at least one tag" if b.tags.empty?
|
684
|
+
|
685
|
+
# Parses the response of a <tt>posts_dates</tt> request
|
686
|
+
# and returns a +Hash+ of date => count.
|
687
|
+
def parse_posts_dates_response(body)
|
688
|
+
dom = parse_and_validate_response(body, :root_name => 'dates')
|
689
|
+
return dom.root.get_elements('date').inject({}) do |collection, xml|
|
690
|
+
date = xml.if_attribute_value(:date)
|
691
|
+
count = xml.if_attribute_value(:count)
|
692
|
+
collection.merge({ date => count })
|
693
|
+
end
|
659
694
|
end
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
|
674
|
-
# Raises::
|
675
|
-
#
|
676
|
-
def prepare_bundles_delete_params(name_or_bundle)
|
677
|
-
bundle = prepare_param_bundle(name_or_bundle) do |b|
|
678
|
-
raise Error, "Bundle name is empty" if b.name.empty?
|
695
|
+
|
696
|
+
|
697
|
+
#
|
698
|
+
# Prepares the params for a `bundles_set` call
|
699
|
+
# and returns a Hash with the params ready for the HTTP request.
|
700
|
+
#
|
701
|
+
# Raises:: WWW::Delicious::Error
|
702
|
+
#
|
703
|
+
def prepare_bundles_set_params(name_or_bundle, tags = [])
|
704
|
+
bundle = prepare_param_bundle(name_or_bundle, tags) do |b|
|
705
|
+
raise Error, "Bundle name is empty" if b.name.empty?
|
706
|
+
raise Error, "Bundle must contain at least one tag" if b.tags.empty?
|
707
|
+
end
|
708
|
+
return { :bundle => bundle.name, :tags => bundle.tags.join(' ') }
|
679
709
|
end
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
def prepare_tags_rename_params(from_name_or_tag, to_name_or_tag)
|
693
|
-
from, to = [from_name_or_tag, to_name_or_tag].collect do |v|
|
694
|
-
prepare_param_tag(v)
|
710
|
+
|
711
|
+
#
|
712
|
+
# Prepares the params for a `bundles_set` call
|
713
|
+
# and returns a Hash with the params ready for the HTTP request.
|
714
|
+
#
|
715
|
+
# Raises:: WWW::Delicious::Error
|
716
|
+
#
|
717
|
+
def prepare_bundles_delete_params(name_or_bundle)
|
718
|
+
bundle = prepare_param_bundle(name_or_bundle) do |b|
|
719
|
+
raise Error, "Bundle name is empty" if b.name.empty?
|
720
|
+
end
|
721
|
+
return { :bundle => bundle.name }
|
695
722
|
end
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
def prepare_posts_params(params, allowed_params = [])
|
709
|
-
compare_params(params, allowed_params)
|
710
|
-
|
711
|
-
# we don't need to check whether the following parameters
|
712
|
-
# are valid for this request because compare_params
|
713
|
-
# would raise if an invalid param is supplied
|
714
|
-
|
715
|
-
params[:tag] = prepare_param_tag(params[:tag]) if params[:tag]
|
716
|
-
params[:dt] = TIME_CONVERTER.call(params[:dt]) if params[:dt]
|
717
|
-
params[:url] = URI.parse(params[:url]) if params[:url]
|
718
|
-
params[:count] = if value = params[:count]
|
719
|
-
raise Error, 'Expected `count` <= 100' if value.to_i() > 100 # requirement
|
720
|
-
value.to_i()
|
721
|
-
else
|
722
|
-
15 # default value
|
723
|
+
|
724
|
+
#
|
725
|
+
# Prepares the params for a `tags_rename` call
|
726
|
+
# and returns a Hash with the params ready for the HTTP request.
|
727
|
+
#
|
728
|
+
# Raises:: WWW::Delicious::Error
|
729
|
+
#
|
730
|
+
def prepare_tags_rename_params(from_name_or_tag, to_name_or_tag)
|
731
|
+
from, to = [from_name_or_tag, to_name_or_tag].collect do |v|
|
732
|
+
prepare_param_tag(v)
|
733
|
+
end
|
734
|
+
return { :old => from, :new => to }
|
723
735
|
end
|
724
736
|
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
|
737
|
-
|
738
|
-
|
739
|
-
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
737
|
+
#
|
738
|
+
# Prepares the params for a `post_*` call
|
739
|
+
# and returns a Hash with the params ready for the HTTP request.
|
740
|
+
#
|
741
|
+
# Raises:: WWW::Delicious::Error
|
742
|
+
#
|
743
|
+
def prepare_posts_params(params, allowed_params = [])
|
744
|
+
compare_params(params, allowed_params)
|
745
|
+
|
746
|
+
# we don't need to check whether the following parameters
|
747
|
+
# are valid for this request because compare_params
|
748
|
+
# would raise if an invalid param is supplied
|
749
|
+
|
750
|
+
params[:tag] = prepare_param_tag(params[:tag]) if params[:tag]
|
751
|
+
params[:dt] = TIME_CONVERTER.call(params[:dt]) if params[:dt]
|
752
|
+
params[:url] = URI.parse(params[:url]) if params[:url]
|
753
|
+
params[:count] = if value = params[:count]
|
754
|
+
raise Error, 'Expected `count` <= 100' if value.to_i() > 100 # requirement
|
755
|
+
value.to_i
|
756
|
+
else
|
757
|
+
15 # default value
|
758
|
+
end
|
759
|
+
|
760
|
+
return params
|
747
761
|
end
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
762
|
+
|
763
|
+
|
764
|
+
#
|
765
|
+
# Prepares the +post+ param for an API request.
|
766
|
+
#
|
767
|
+
# Creates and returns a <tt>WWW::Delicious::Post</tt> instance from <tt>post_or_values</tt>.
|
768
|
+
# <tt>post_or_values</tt> can be either an Hash with post attributes
|
769
|
+
# or a <tt>WWW::Delicious::Post</tt> instance.
|
770
|
+
#
|
771
|
+
def prepare_param_post(post_or_values, &block)
|
772
|
+
post = case post_or_values
|
773
|
+
when WWW::Delicious::Post
|
774
|
+
post_or_values
|
775
|
+
when Hash
|
776
|
+
Post.new(post_or_values)
|
777
|
+
else
|
778
|
+
raise ArgumentError, 'Expected `args` to be `WWW::Delicious::Post` or `Hash`'
|
779
|
+
end
|
780
|
+
|
781
|
+
yield(post) if block_given?
|
782
|
+
# TODO: validate post with post.validate!
|
783
|
+
raise ArgumentError, 'Both `url` and `title` are required' unless post.api_valid?
|
784
|
+
post
|
766
785
|
end
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
772
|
-
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
786
|
+
|
787
|
+
#
|
788
|
+
# Prepares the +bundle+ param for an API request.
|
789
|
+
#
|
790
|
+
# Creates and returns a <tt>WWW::Delicious::Bundle</tt> instance from <tt>name_or_bundle</tt>.
|
791
|
+
# <tt>name_or_bundle</tt> can be either a string holding bundle name
|
792
|
+
# or a <tt>WWW::Delicious::Bundle</tt> instance.
|
793
|
+
#
|
794
|
+
def prepare_param_bundle(name_or_bundle, tags = [], &block) # :yields: bundle
|
795
|
+
bundle = case name_or_bundle
|
796
|
+
when WWW::Delicious::Bundle
|
797
|
+
name_or_bundle
|
798
|
+
else
|
799
|
+
Bundle.new(:name => name_or_bundle, :tags => tags)
|
800
|
+
end
|
801
|
+
|
802
|
+
yield(bundle) if block_given?
|
803
|
+
# TODO: validate bundle with bundle.validate!
|
804
|
+
bundle
|
785
805
|
end
|
786
806
|
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
807
|
+
#
|
808
|
+
# Prepares the +tag+ param for an API request.
|
809
|
+
#
|
810
|
+
# Creates and returns a <tt>WWW::Delicious::Tag</tt> instance from <tt>name_or_tag</tt>.
|
811
|
+
# <tt>name_or_tag</tt> can be either a string holding tag name
|
812
|
+
# or a <tt>WWW::Delicious::Tag</tt> instance.
|
813
|
+
#
|
814
|
+
def prepare_param_tag(name_or_tag, &block) # :yields: tag
|
815
|
+
tag = case name_or_tag
|
816
|
+
when WWW::Delicious::Tag
|
817
|
+
name_or_tag
|
818
|
+
else
|
819
|
+
Tag.new(:name => name_or_tag.to_s)
|
820
|
+
end
|
821
|
+
|
822
|
+
yield(tag) if block_given?
|
823
|
+
# TODO: validate tag with tag.validate!
|
824
|
+
raise "Invalid `tag` value supplied" unless tag.api_valid?
|
825
|
+
tag
|
826
|
+
end
|
827
|
+
|
828
|
+
#
|
829
|
+
# Checks whether user given +params+ are valid against a defined collection of +valid_params+.
|
830
|
+
#
|
831
|
+
# === Examples
|
832
|
+
#
|
833
|
+
# params = {:foo => 1, :bar => 2}
|
834
|
+
#
|
835
|
+
# compare_params(params, [:foo, :bar])
|
836
|
+
# # => valid
|
837
|
+
#
|
838
|
+
# compare_params(params, [:foo, :bar, :baz])
|
839
|
+
# # => raises
|
840
|
+
#
|
841
|
+
# compare_params(params, [:foo])
|
842
|
+
# # => raises
|
843
|
+
#
|
844
|
+
# Raises:: WWW::Delicious::Error
|
845
|
+
#
|
846
|
+
def compare_params(params, valid_params)
|
847
|
+
raise ArgumentError, "Expected `params` to be a kind of `Hash`" unless params.kind_of?(Hash)
|
848
|
+
raise ArgumentError, "Expected `valid_params` to be a kind of `Array`" unless valid_params.kind_of?(Array)
|
849
|
+
|
850
|
+
# compute options difference
|
851
|
+
difference = params.keys - valid_params
|
852
|
+
raise Error, "Invalid params: `#{difference.join('`, `')}`" unless difference.empty?
|
853
|
+
end
|
792
854
|
|
793
|
-
protected
|
794
|
-
#
|
795
|
-
# Checks whether user given params are valid against valid params.
|
796
|
-
#
|
797
|
-
# === Params
|
798
|
-
# params::
|
799
|
-
# an +Hash+ with user given params to validate
|
800
|
-
# valid_params::
|
801
|
-
# an +Array+ of valid params keys to check against
|
802
|
-
#
|
803
|
-
# === Examples
|
804
|
-
#
|
805
|
-
# params = {:foo => 1, :bar => 2}
|
806
|
-
#
|
807
|
-
# compare_params(params, [:foo, :bar])
|
808
|
-
# # => valid
|
809
|
-
#
|
810
|
-
# compare_params(params, [:foo, :bar, :baz])
|
811
|
-
# # => raises
|
812
|
-
#
|
813
|
-
# compare_params(params, [:foo])
|
814
|
-
# # => raises
|
815
|
-
#
|
816
|
-
# Raises:: WWW::Delicious::Error
|
817
|
-
#
|
818
|
-
def compare_params(params, valid_params)
|
819
|
-
raise ArgumentError, "Expected `params` to be a kind of `Hash`" unless params.kind_of?(Hash)
|
820
|
-
raise ArgumentError, "Expected `valid_params` to be a kind of `Array`" unless valid_params.kind_of?(Array)
|
821
|
-
|
822
|
-
# compute options difference
|
823
|
-
difference = params.keys - valid_params
|
824
|
-
raise Error,
|
825
|
-
"Invalid params: `#{difference.join('`, `')}`" unless difference.empty?
|
826
|
-
end
|
827
|
-
|
828
855
|
|
829
856
|
module XMLUtils #:nodoc:
|
830
|
-
|
831
|
-
public
|
857
|
+
|
832
858
|
#
|
833
|
-
# Returns the +xmlattr+ attribute value for
|
859
|
+
# Returns the +xmlattr+ attribute value for current <tt>REXML::Element</tt>.
|
834
860
|
#
|
835
|
-
# If block is given and
|
861
|
+
# If block is given and attribute value is not nil,
|
836
862
|
# the content of the block is executed.
|
837
863
|
#
|
838
|
-
# ===
|
839
|
-
#
|
840
|
-
#
|
841
|
-
#
|
842
|
-
#
|
843
|
-
#
|
844
|
-
#
|
845
|
-
#
|
846
|
-
#
|
864
|
+
# === Examples
|
865
|
+
#
|
866
|
+
# dom = REXML::Document.new('<a name="1"><b>foo</b><b>bar</b></a>')
|
867
|
+
#
|
868
|
+
# dom.root.if_attribute_value(:name)
|
869
|
+
# # => "1"
|
870
|
+
#
|
871
|
+
# dom.root.if_attribute_value(:name) { |v| v.to_i }
|
872
|
+
# # => 1
|
873
|
+
#
|
874
|
+
# dom.root.if_attribute_value(:foo)
|
875
|
+
# # => nil
|
876
|
+
#
|
877
|
+
# dom.root.if_attribute_value(:name) { |v| v.to_i }
|
878
|
+
# # => nil
|
847
879
|
#
|
848
|
-
def
|
849
|
-
value = if attr = self.attribute(xmlattr.to_s
|
850
|
-
attr.value
|
880
|
+
def if_attribute_value(xmlattr, &block) #:nodoc:
|
881
|
+
value = if attr = self.attribute(xmlattr.to_s)
|
882
|
+
attr.value
|
851
883
|
else
|
852
884
|
nil
|
853
885
|
end
|
854
886
|
value = yield value if !value.nil? and block_given?
|
855
|
-
|
887
|
+
value
|
856
888
|
end
|
857
|
-
|
858
|
-
|
889
|
+
|
890
|
+
#
|
891
|
+
# Returns the value of +expression+ child of this element, if it exists.
|
892
|
+
# If blog is given, block is called on +expression+ element value
|
893
|
+
# and the result is returned.
|
894
|
+
#
|
895
|
+
def if_element_value(expression, &block)
|
896
|
+
if_element(expression) do |element|
|
897
|
+
value = element.text
|
898
|
+
value = yield value if block_given?
|
899
|
+
value
|
900
|
+
end
|
901
|
+
end
|
902
|
+
|
903
|
+
#
|
904
|
+
# Executes the content of +block+ on +expression+
|
905
|
+
# child of this element, if it exists.
|
906
|
+
# Returns the result or +nil+ if +xmlelement+ doesn't exist.
|
907
|
+
#
|
908
|
+
def if_element(expression, &block)
|
909
|
+
raise LocalJumpError, "no block given" unless block_given?
|
910
|
+
if element = self.elements[expression.to_s]
|
911
|
+
yield element
|
912
|
+
else
|
913
|
+
nil
|
914
|
+
end
|
915
|
+
end
|
916
|
+
|
917
|
+
end # XMLUtils
|
859
918
|
|
860
919
|
end
|
861
920
|
end
|
862
921
|
|
863
922
|
|
923
|
+
class Object
|
924
|
+
|
925
|
+
# An object is blank if it's false, empty, or a whitespace string.
|
926
|
+
# For example, "", " ", +nil+, [], and {} are blank.
|
927
|
+
#
|
928
|
+
# This simplifies
|
929
|
+
#
|
930
|
+
# if !address.nil? && !address.empty?
|
931
|
+
#
|
932
|
+
# to
|
933
|
+
#
|
934
|
+
# if !address.blank?
|
935
|
+
#
|
936
|
+
# Object#blank? comes from the GEM ActiveSupport 2.1.
|
937
|
+
#
|
938
|
+
def blank?
|
939
|
+
respond_to?(:empty?) ? empty? : !self
|
940
|
+
end unless Object.method_defined? :blanks?
|
941
|
+
|
942
|
+
end
|
943
|
+
|
944
|
+
|
864
945
|
module REXML # :nodoc:
|
865
946
|
class Element < Parent # :nodoc:
|
866
947
|
include WWW::Delicious::XMLUtils
|