www-delicious 0.1.0 → 0.2.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.
- 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
|