blogmarks 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +7 -0
- data/MIT-LICENSE +20 -0
- data/README +44 -0
- data/lib/blogmarks.rb +29 -0
- data/lib/blogmarks/class_attribute_accessors.rb +67 -0
- data/lib/blogmarks/client.rb +301 -0
- data/lib/blogmarks/feed.rb +98 -0
- data/lib/blogmarks/mark.rb +136 -0
- data/lib/blogmarks/tag.rb +106 -0
- data/test/test_blogmarks_client.rb +63 -0
- data/test/test_blogmarks_entry.rb +163 -0
- metadata +70 -0
data/CHANGES
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2006 Jonathan Tron
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
= Project: Builder
|
2
|
+
|
3
|
+
== Goal
|
4
|
+
|
5
|
+
Provide a simple way to interact with BlogMarks.net online bookmarking system.
|
6
|
+
|
7
|
+
== Classes
|
8
|
+
|
9
|
+
BlogMarks::Client:: Client to interact with BlogMarks.net API
|
10
|
+
BlogMarks::Tag:: Define a Tag
|
11
|
+
BlogMarks::Mark:: Define a Mark
|
12
|
+
BlogMarks::Feed:: Define a Feed
|
13
|
+
|
14
|
+
== Usage
|
15
|
+
|
16
|
+
# Accessing last blogmarks :
|
17
|
+
client = BlogMarks::Client.new('username', 'mypassword')
|
18
|
+
client.find_marks # Without other options, the api will return the last 30 marks (subject to change)
|
19
|
+
# then get the default feed and iterate over fetched mark
|
20
|
+
client.feed.marks.each do |mark|
|
21
|
+
puts "Mark Link : " + mark.link_related
|
22
|
+
puts "Mark Summary : " + mark.summary
|
23
|
+
end
|
24
|
+
|
25
|
+
or
|
26
|
+
|
27
|
+
# Accessing YOUR last blogmarks :
|
28
|
+
client = BlogMarks::Client.new('username', 'mypassword')
|
29
|
+
client.find_my_marks # Without other options, the api will return your last 30 marks (subject to change)
|
30
|
+
|
31
|
+
or
|
32
|
+
|
33
|
+
# Accessing only the 10 latest blogmarks
|
34
|
+
client = BlogMarks::Client.new('username', 'mypassword')
|
35
|
+
client.find_marks( :last => 10 )
|
36
|
+
|
37
|
+
TODO : Show some example to post/update/edit blogmarks and tags
|
38
|
+
|
39
|
+
== Contact
|
40
|
+
|
41
|
+
Author:: Jonathan Tron
|
42
|
+
Email:: jonathan@tron.name
|
43
|
+
Home Page:: http://projects.tron.name/ruby/blogmarks/
|
44
|
+
License:: MIT Licence (http://www.opensource.org/licenses/mit-license.html)
|
data/lib/blogmarks.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#/bin/env ruby
|
2
|
+
#--
|
3
|
+
# Copyright (c) 2004 Jonathan Tron
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
# a copy of this software and associated documentation files (the
|
7
|
+
# "Software"), to deal in the Software without restriction, including
|
8
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
# the following conditions:
|
12
|
+
#
|
13
|
+
# The above copyright notice and this permission notice shall be
|
14
|
+
# included in all copies or substantial portions of the Software.
|
15
|
+
#
|
16
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
23
|
+
#++
|
24
|
+
|
25
|
+
require File.dirname(__FILE__) + '/blogmarks/class_attribute_accessors'
|
26
|
+
require File.dirname(__FILE__) + '/blogmarks/feed'
|
27
|
+
require File.dirname(__FILE__) + '/blogmarks/tag'
|
28
|
+
require File.dirname(__FILE__) + '/blogmarks/mark'
|
29
|
+
require File.dirname(__FILE__) + '/blogmarks/client'
|
@@ -0,0 +1,67 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2005 David Heinemeier Hansson
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
#++
|
23
|
+
|
24
|
+
# Extends the class object with class and instance accessors for class attributes,
|
25
|
+
# just like the native attr* accessors for instance attributes.
|
26
|
+
class Class # :nodoc:
|
27
|
+
def cattr_reader(*syms)
|
28
|
+
syms.flatten.each do |sym|
|
29
|
+
class_eval(<<-EOS, __FILE__, __LINE__)
|
30
|
+
unless defined? @@#{sym}
|
31
|
+
@@#{sym} = nil
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.#{sym}
|
35
|
+
@@#{sym}
|
36
|
+
end
|
37
|
+
|
38
|
+
def #{sym}
|
39
|
+
@@#{sym}
|
40
|
+
end
|
41
|
+
EOS
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def cattr_writer(*syms)
|
46
|
+
syms.flatten.each do |sym|
|
47
|
+
class_eval(<<-EOS, __FILE__, __LINE__)
|
48
|
+
unless defined? @@#{sym}
|
49
|
+
@@#{sym} = nil
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.#{sym}=(obj)
|
53
|
+
@@#{sym} = obj
|
54
|
+
end
|
55
|
+
|
56
|
+
def #{sym}=(obj)
|
57
|
+
@@#{sym} = obj
|
58
|
+
end
|
59
|
+
EOS
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def cattr_accessor(*syms)
|
64
|
+
cattr_reader(*syms)
|
65
|
+
cattr_writer(*syms)
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,301 @@
|
|
1
|
+
#/bin/env ruby
|
2
|
+
#--
|
3
|
+
# Copyright (c) 2004 Jonathan Tron
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
# a copy of this software and associated documentation files (the
|
7
|
+
# "Software"), to deal in the Software without restriction, including
|
8
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
# the following conditions:
|
12
|
+
#
|
13
|
+
# The above copyright notice and this permission notice shall be
|
14
|
+
# included in all copies or substantial portions of the Software.
|
15
|
+
#
|
16
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
23
|
+
#++
|
24
|
+
|
25
|
+
# Standard ruby libs
|
26
|
+
require 'uri'
|
27
|
+
require 'net/http'
|
28
|
+
require 'base64'
|
29
|
+
require 'digest/md5'
|
30
|
+
require 'digest/sha1'
|
31
|
+
require 'rexml/document'
|
32
|
+
|
33
|
+
# Gem dependencies
|
34
|
+
require 'rubygems'
|
35
|
+
require 'builder'
|
36
|
+
|
37
|
+
# Inner dependencies
|
38
|
+
require File.dirname(__FILE__) + '/feed'
|
39
|
+
|
40
|
+
module BlogMarks
|
41
|
+
|
42
|
+
class Client
|
43
|
+
# API URL to interact with BlogMarks.net
|
44
|
+
cattr_accessor :api_url
|
45
|
+
@@api_url = 'http://api.blogmarks.net'
|
46
|
+
# User agent to report to blogmarks server
|
47
|
+
cattr_reader :user_agent
|
48
|
+
@@user_agent = 'Ruby BlogmarksClient Library'
|
49
|
+
# Text used to compute the nonce for authentication
|
50
|
+
cattr_accessor :private_text
|
51
|
+
@@private_text = 'Ipsum Dolor Sit Amet'
|
52
|
+
|
53
|
+
# Store the fetched Feed
|
54
|
+
attr_reader :feed
|
55
|
+
# User login
|
56
|
+
attr_accessor :user
|
57
|
+
# User password
|
58
|
+
attr_accessor :password
|
59
|
+
|
60
|
+
# Blogmarks client constructor
|
61
|
+
def initialize( user, password)
|
62
|
+
@user = user
|
63
|
+
@password = password
|
64
|
+
@feed = nil
|
65
|
+
end
|
66
|
+
|
67
|
+
# Add a Mark
|
68
|
+
def add_mark( mark )
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
# Update a Mark
|
73
|
+
def update_mark( mark )
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
# Delete a Mark
|
78
|
+
def delete_mark( mark )
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
# Find some marks in public marks
|
83
|
+
#
|
84
|
+
# This function accept an options hash, where options can be :
|
85
|
+
#
|
86
|
+
# * <tt>:last</tt> <i>integer from -1 to ...</i>
|
87
|
+
# Limit number of results (default: 30)
|
88
|
+
# * <tt>:offset</tt> <i>positive integer</i>
|
89
|
+
# First result offset, for paging... (default: 0)
|
90
|
+
# * <tt>:search</tt> <i>string</i>
|
91
|
+
# Searches for string in blogmarks (title, desc) + tags
|
92
|
+
# * <tt>:tags</tt> <i>string</i>
|
93
|
+
# Searches for requested public tags
|
94
|
+
# * <tt>:private_tags</tt> <i>string</i>
|
95
|
+
# Recherche les blogmarks contenant les tags privés indiqués (*).
|
96
|
+
# * <tt>:related</tt> <i>regexp</i>
|
97
|
+
# ???
|
98
|
+
# * <tt>:author</tt> <i>string <user></i>
|
99
|
+
# Searches only in <user>'s marks
|
100
|
+
# * <tt>:private</tt> <i>true | false</i>
|
101
|
+
# If true, also search in private marks (default: false)
|
102
|
+
# * <tt>:month</tt> <i>integer 01 to 12</i>
|
103
|
+
# if used with year, search only in marks posted durint that month
|
104
|
+
# * <tt>:year</tt> <i>integer YYYY</i>
|
105
|
+
# Restrict search to marks posted during that year
|
106
|
+
# * <tt>:level</tt> <i>positive integer <level></i>
|
107
|
+
# Restrict search to marks posted by at least <level> users
|
108
|
+
# * <tt>:order_by</tt> <i>issued|modified|created|popularity</i>
|
109
|
+
# Results sorting mode (default: issued)
|
110
|
+
# * <tt>:order_type</tt> <i>asc|desc</i>
|
111
|
+
# Results dorting direction (default: desc)
|
112
|
+
def find_marks( options={} )
|
113
|
+
url = build_url( "#{api_url}/marks", options )
|
114
|
+
|
115
|
+
res = send_request(url)
|
116
|
+
|
117
|
+
parse_xml_response(res)
|
118
|
+
end
|
119
|
+
|
120
|
+
# Find some marks in my marks
|
121
|
+
#
|
122
|
+
# This function is an alias to find_mark which set the :author option to current user in options hash.
|
123
|
+
# For accepted options see #find_marks
|
124
|
+
def find_my_marks( options={} )
|
125
|
+
options[:author] = @user
|
126
|
+
find_marks(options)
|
127
|
+
end
|
128
|
+
|
129
|
+
protected
|
130
|
+
|
131
|
+
def send_request( url, verb='GET', content=nil) #:nodoc:
|
132
|
+
nonce = get_nonce
|
133
|
+
creation_timestamp = Time.new.strftime("%Y-%m-%d\T%H:%M:%S\Z")
|
134
|
+
password_hash = Digest::MD5.hexdigest(password)
|
135
|
+
password_digest = Base64.encode64(Digest::SHA1.hexdigest("#{nonce}#{creation_timestamp}#{password_hash}"))
|
136
|
+
|
137
|
+
uri = URI.parse(url)
|
138
|
+
req = nil
|
139
|
+
|
140
|
+
case verb
|
141
|
+
when 'POST'
|
142
|
+
req = Net::HTTP::Post.new(uri.request_uri)
|
143
|
+
when 'PUT'
|
144
|
+
req = Net::HTTP::Put.new(uri.request_uri)
|
145
|
+
when 'DELETE'
|
146
|
+
req = Net::HTTP::Delete.new(uri.request_uri)
|
147
|
+
else
|
148
|
+
req = Net::HTTP::Get.new(uri.request_uri)
|
149
|
+
end
|
150
|
+
|
151
|
+
req['X-WSSE'] = "UsernameToken Username=\"#{user}\", PasswordDigest=\"#{password_digest}\", Nonce=\"#{nonce}\", Created=\"#{creation_timestamp}\""
|
152
|
+
req['User-Agent'] = user_agent
|
153
|
+
|
154
|
+
res = Net::HTTP.new( uri.host, uri.port ).start do | http |
|
155
|
+
http.request(req)
|
156
|
+
end
|
157
|
+
|
158
|
+
case verb
|
159
|
+
when 'POST'
|
160
|
+
return true if res.kind_of? Net::HTTPCreated
|
161
|
+
when 'PUT'
|
162
|
+
return true if res.kind_of? Net::HTTPResetContent
|
163
|
+
when 'DELETE'
|
164
|
+
return true if res.kind_of? Net::HTTPOK
|
165
|
+
when 'GET'
|
166
|
+
return res.body if res.kind_of? Net::HTTPOK
|
167
|
+
end
|
168
|
+
|
169
|
+
# If reached we have an error
|
170
|
+
handle_error(res)
|
171
|
+
end
|
172
|
+
|
173
|
+
# Build an xml entry element to use during Mark post from params
|
174
|
+
#--
|
175
|
+
# TODO : Remove/Modify this method as we now pass a Mark Object to POST/DELETE/UPDATE method
|
176
|
+
#++
|
177
|
+
def params_to_xml( params={} ) #:nodoc:
|
178
|
+
|
179
|
+
return 'No Link' if !params.has_key? :url
|
180
|
+
return 'No Title' if !params.has_key? :title
|
181
|
+
|
182
|
+
buffer = ""
|
183
|
+
xm = Builder::XmlMarkup.new( :target => buffer )
|
184
|
+
|
185
|
+
xm.instruct!
|
186
|
+
|
187
|
+
xm.entry {
|
188
|
+
|
189
|
+
xm.title(params[:title])
|
190
|
+
|
191
|
+
xm.link( "rel" => "related", "href" => "#{params[:url]}")
|
192
|
+
|
193
|
+
xm.summary( "#{params[:description]}" ) if params.has_key? :description
|
194
|
+
|
195
|
+
xm.published( "0000-00-00" ) if params.has_key?(:private) && params[:private]
|
196
|
+
|
197
|
+
xm.published( Time.new.strftime("%Y-%m-%d\T%H:%M:%S\Z") ) if params.has_key?(:private) && !params[:private]
|
198
|
+
|
199
|
+
params[:tags].each do |tag|
|
200
|
+
xm.category( "term" => "#{api_url}/tags/", "sheme" => "#{tag}") if !tag.empty?
|
201
|
+
end if params.has_key? :tags
|
202
|
+
|
203
|
+
params[:private_tags].each do |tag|
|
204
|
+
xm.category( "term" => "#{api_url}/tags/user/#{user}/", "sheme" => "#{tag}") if !tag.empty?
|
205
|
+
end if params.has_key? :private_tags
|
206
|
+
|
207
|
+
xm.link( "rel" => "via", "href" => "#{params[:via]}" ) if params.has_key? :via
|
208
|
+
}
|
209
|
+
|
210
|
+
buffer
|
211
|
+
end
|
212
|
+
|
213
|
+
# Parse raw xml data and if successful build a Feed from it
|
214
|
+
def parse_xml_response( xml ) #:nodoc:
|
215
|
+
xml_doc = create_xml_document( xml )
|
216
|
+
|
217
|
+
# If we successfuly build an REXML document, let's begin the work :)
|
218
|
+
if !xml_doc.nil?
|
219
|
+
@feed = BlogMarks::Feed.build_from_xml_element(xml_doc.root)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
# Get a unique nonce to use for authentication
|
224
|
+
# As specified in API Spec,
|
225
|
+
# nonce is a string containing "timestamp SHA1(timestamp:private_text)"
|
226
|
+
def get_nonce
|
227
|
+
timestamp = Time.new.utc.strftime("%Y-%m-%d\T%H:%M:%S\Z")
|
228
|
+
temp = Digest::SHA1.hexdigest("#{timestamp}:#{private_text}")
|
229
|
+
return "#{timestamp} #{temp}"
|
230
|
+
end
|
231
|
+
|
232
|
+
# Build an URI with querystring based on params as in BlogMarks API Spec
|
233
|
+
# * Multiple values for a param will be delimited by a space
|
234
|
+
# * Multi-words values will be double-quoted
|
235
|
+
def build_url( ressource, params={} ) #:nodoc:
|
236
|
+
querystring = ""
|
237
|
+
|
238
|
+
params.each do |index, param|
|
239
|
+
querystring << "&" unless querystring.empty?
|
240
|
+
|
241
|
+
# Handle params composed of multiple value
|
242
|
+
if param.kind_of? Array
|
243
|
+
# Let's our query play nicely with tags composed of multiple word
|
244
|
+
param.collect! do |el|
|
245
|
+
if el =~ /\s+/
|
246
|
+
"\"#{el}\""
|
247
|
+
else
|
248
|
+
el
|
249
|
+
end
|
250
|
+
end
|
251
|
+
param = param.join(' ')
|
252
|
+
end
|
253
|
+
|
254
|
+
querystring << "#{index}=#{param}"
|
255
|
+
end
|
256
|
+
|
257
|
+
|
258
|
+
ressource << "?" << querystring if !querystring.empty?
|
259
|
+
|
260
|
+
return URI.escape(ressource)
|
261
|
+
end
|
262
|
+
|
263
|
+
# Handle error when occurs
|
264
|
+
# Raise a simple message explaining error
|
265
|
+
#--
|
266
|
+
# TODO : add a simple subclass to base Exception with neat message access
|
267
|
+
#++
|
268
|
+
def handle_error( response_error )
|
269
|
+
message = ''
|
270
|
+
message = "HTTP authentication failed" if response_error.kind_of? Net::HTTPUnauthorized
|
271
|
+
message = "Requested URI does not reference a ressource or search returned no results." if response_error.kind_of? Net::HTTPNotFound
|
272
|
+
message = "Server-side error occured" if response_error.kind_of? Net::HTTPInternalServerError
|
273
|
+
|
274
|
+
if response_error.kind_of? Net::HTTPInternalServerError
|
275
|
+
xml_doc = create_xml_document( response_error )
|
276
|
+
unless xml_doc.nil?
|
277
|
+
raise(message + "\n" + xml_doc.root.text)
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
# Parse a string containing raw xml data and return its REXML::Document representation or nil if failed
|
283
|
+
def create_xml_document( xml )
|
284
|
+
xml_doc = nil
|
285
|
+
begin
|
286
|
+
begin
|
287
|
+
# Let's try to parse the xml
|
288
|
+
xml_doc = REXML::Document.new( xml, :ignore_whitespace_nodes => :all )
|
289
|
+
rescue Object
|
290
|
+
# If error occurs try to repair it with the htree
|
291
|
+
xml_doc = REXML::HTree.parse( xml ).to_rexml
|
292
|
+
end
|
293
|
+
rescue Object
|
294
|
+
xml_doc = nil
|
295
|
+
end
|
296
|
+
|
297
|
+
return xml_doc
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
#/bin/env ruby
|
2
|
+
#--
|
3
|
+
# Copyright (c) 2004 Jonathan Tron
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
# a copy of this software and associated documentation files (the
|
7
|
+
# "Software"), to deal in the Software without restriction, including
|
8
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
# the following conditions:
|
12
|
+
#
|
13
|
+
# The above copyright notice and this permission notice shall be
|
14
|
+
# included in all copies or substantial portions of the Software.
|
15
|
+
#
|
16
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
23
|
+
#++
|
24
|
+
|
25
|
+
require 'uri'
|
26
|
+
require 'rexml/document'
|
27
|
+
require File.dirname(__FILE__) + '/mark'
|
28
|
+
|
29
|
+
module BlogMarks
|
30
|
+
|
31
|
+
class Feed
|
32
|
+
# Feed title
|
33
|
+
attr_reader :title
|
34
|
+
# Blogmarks.net corresponding feed page
|
35
|
+
attr_reader :link_alternate
|
36
|
+
# Previous Feed URI
|
37
|
+
attr_reader :link_prev
|
38
|
+
# Next Feed URI
|
39
|
+
attr_reader :link_next
|
40
|
+
# Feed update date
|
41
|
+
attr_reader :updated
|
42
|
+
|
43
|
+
# Feed Marks
|
44
|
+
attr_reader :marks
|
45
|
+
|
46
|
+
# Create a feed from params
|
47
|
+
#
|
48
|
+
# params hash can contains any of the object vars
|
49
|
+
def initialize( params={} )
|
50
|
+
@title = params[:title] || nil
|
51
|
+
@alternate = params[:link_alternate] || nil
|
52
|
+
@link_prev = params[:link_prev] || nil
|
53
|
+
@link_next = params[:link_next] || nil
|
54
|
+
@updated = params[:updated] || nil
|
55
|
+
@marks = params[:marks] || []
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
class << self # Class methods
|
60
|
+
|
61
|
+
# Parse an REXML::Element corresponding to the feed
|
62
|
+
def build_from_xml_element( feed )
|
63
|
+
# We need a valide REXML::Element parameters :D
|
64
|
+
return nil unless feed.kind_of?(REXML::Element)
|
65
|
+
|
66
|
+
# Let's build the params to build a BlogMarks::Feed
|
67
|
+
params = {}
|
68
|
+
# Iterate over child nodes
|
69
|
+
feed.each_element do |element|
|
70
|
+
case element.name
|
71
|
+
when 'head'
|
72
|
+
element.each_element do |headElement|
|
73
|
+
case headElement.name
|
74
|
+
when 'title'
|
75
|
+
params[:title] = headElement.text
|
76
|
+
when 'updated'
|
77
|
+
params[:updated] = Time.parse(headElement.text)
|
78
|
+
when 'link'
|
79
|
+
params[:link_alternate] = URI.parse( headElement.attributes['href'] ) if headElement.attributes['rel'] == 'alternate'
|
80
|
+
params[:link_prev] = URI.parse( headElement.attributes['href'] ) if headElement.attributes['rel'] == 'prev'
|
81
|
+
params[:link_next] = URI.parse( headElement.attributes['href'] ) if headElement.attributes['rel'] == 'next'
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
when 'entry'
|
86
|
+
params[:marks] = [] unless params.has_key?(:marks)
|
87
|
+
params[:marks] << BlogMarks::Mark.build_from_xml_element(element)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Create and return a new Feed constructs using params hash
|
92
|
+
return Feed.new(params)
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
#/bin/env ruby
|
2
|
+
#--
|
3
|
+
# Copyright (c) 2004 Jonathan Tron
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
# a copy of this software and associated documentation files (the
|
7
|
+
# "Software"), to deal in the Software without restriction, including
|
8
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
# the following conditions:
|
12
|
+
#
|
13
|
+
# The above copyright notice and this permission notice shall be
|
14
|
+
# included in all copies or substantial portions of the Software.
|
15
|
+
#
|
16
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
23
|
+
#++
|
24
|
+
|
25
|
+
require 'uri'
|
26
|
+
require 'rexml/document'
|
27
|
+
|
28
|
+
module BlogMarks
|
29
|
+
|
30
|
+
class Mark
|
31
|
+
# Mark Id
|
32
|
+
attr_reader :id
|
33
|
+
# Mark Title
|
34
|
+
attr_accessor :title
|
35
|
+
# Mark summary
|
36
|
+
attr_accessor :summary
|
37
|
+
# Mark's url
|
38
|
+
attr_accessor :link_related
|
39
|
+
# Mark's html representation
|
40
|
+
attr_reader :link_alternate
|
41
|
+
# Mark's site screenshot
|
42
|
+
attr_reader :link_image
|
43
|
+
# Mark's last update date
|
44
|
+
attr_reader :updated
|
45
|
+
# Mark's published date
|
46
|
+
attr_accessor :published
|
47
|
+
# Mark's author
|
48
|
+
attr_accessor :author
|
49
|
+
# Mark's edit
|
50
|
+
attr_reader :edit
|
51
|
+
# Mark's creation date
|
52
|
+
attr_accessor :bm_created
|
53
|
+
# Mark's public tags
|
54
|
+
attr_accessor :public_tags
|
55
|
+
# Mark's private tags
|
56
|
+
attr_accessor :private_tags
|
57
|
+
|
58
|
+
# Create a mark from params
|
59
|
+
#
|
60
|
+
# params hash can contains any of the object vars, but :title and :link_related is mandatory
|
61
|
+
def initialize( params={} )
|
62
|
+
@id = params[:id] || nil
|
63
|
+
@title = params[:title] || raise( ArgumentError, ":title can't be empty")
|
64
|
+
@link_related = params[:link_related] || raise( ArgumentError, ":link_related can't be empty")
|
65
|
+
@link_alternate = params[:link_alternate] || nil
|
66
|
+
@link_image = params[:link_image] || nil
|
67
|
+
@summary = params[:summary] || nil
|
68
|
+
@updated = params[:updated] || nil
|
69
|
+
@published = params[:published] || nil
|
70
|
+
@author = params[:author] || nil
|
71
|
+
@edit = params[:edit] || nil
|
72
|
+
@bm_created = params[:bm_created] || nil
|
73
|
+
@public_tags = params[:public_tags] || []
|
74
|
+
@private_tags = params[:private_tags] || []
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
# Return an xml representation of the mark according to BlogMarks.net Specs
|
79
|
+
def to_xml
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
class << self # Class methods
|
84
|
+
|
85
|
+
# Parse an REXML::Element corresponding to the entry
|
86
|
+
def build_from_xml_element( entry )
|
87
|
+
# We need a valide REXML::Element parameters :D
|
88
|
+
return nil unless entry.kind_of?(REXML::Element)
|
89
|
+
|
90
|
+
# Let's build the params to build a BlogMarks::Mark
|
91
|
+
params = {}
|
92
|
+
|
93
|
+
# Iterate over child nodes
|
94
|
+
entry.each_element do |element|
|
95
|
+
case element.name
|
96
|
+
when 'id'
|
97
|
+
params[:id] = element.text
|
98
|
+
when 'title'
|
99
|
+
params[:title] = element.text
|
100
|
+
when 'updated'
|
101
|
+
params[:updated] = Time.parse(element.text)
|
102
|
+
when 'published'
|
103
|
+
params[:published] = Time.parse(element.text)
|
104
|
+
when 'author'
|
105
|
+
params[:author] = element.elements['name'].text
|
106
|
+
when 'edit'
|
107
|
+
params[:edit] = URI.parse(element.text)
|
108
|
+
when 'bm:created'
|
109
|
+
params[:bm_created] = Time.parse(element.text)
|
110
|
+
when 'summary'
|
111
|
+
params[:summary] = element.text
|
112
|
+
when 'link'
|
113
|
+
params[:link_related] = URI.parse( element.attributes['href'] ) if element.attributes['rel'] == 'related'
|
114
|
+
params[:link_alernate] = URI.parse( element.attributes['href'] ) if element.attributes['rel'] == 'alternate'
|
115
|
+
params[:link_image] = URI.parse( element.attributes['href'] ) if element.attributes['rel'] == 'image'
|
116
|
+
when 'category'
|
117
|
+
if element.attributes['term'] == 'http://api.blogmarks.net/tags'
|
118
|
+
params[:public_tags] = [] unless params.has_key?(:public_tags)
|
119
|
+
params[:public_tags] << element.attributes['label']
|
120
|
+
end
|
121
|
+
|
122
|
+
if element.attributes['term'] != 'http://api.blogmarks.net/tags'
|
123
|
+
params[:private_tags] = [] unless params.has_key?(:private_tags)
|
124
|
+
params[:private_tags] << element.attributes['label']
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# Create and return a new Mark constructs using params hash
|
130
|
+
return Mark.new(params)
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
#/bin/env ruby
|
2
|
+
#--
|
3
|
+
# Copyright (c) 2004 Jonathan Tron
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
# a copy of this software and associated documentation files (the
|
7
|
+
# "Software"), to deal in the Software without restriction, including
|
8
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
# the following conditions:
|
12
|
+
#
|
13
|
+
# The above copyright notice and this permission notice shall be
|
14
|
+
# included in all copies or substantial portions of the Software.
|
15
|
+
#
|
16
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
23
|
+
#++
|
24
|
+
|
25
|
+
require 'uri'
|
26
|
+
require 'rexml/document'
|
27
|
+
|
28
|
+
module BlogMarks
|
29
|
+
|
30
|
+
class Tag
|
31
|
+
# Tag id
|
32
|
+
attr_reader :id
|
33
|
+
# Tag html representation uri
|
34
|
+
attr_reader :link_alternate
|
35
|
+
# Tag image uri
|
36
|
+
attr_reader :link_image
|
37
|
+
# Tag edit uri
|
38
|
+
attr_reader :edit
|
39
|
+
# Tag title
|
40
|
+
attr_accessor :title
|
41
|
+
# Tag summary
|
42
|
+
attr_accessor :summary
|
43
|
+
# Tag publish date
|
44
|
+
attr_accessor :published
|
45
|
+
# Tag creation date
|
46
|
+
attr_reader :bm_created
|
47
|
+
# Tag update date
|
48
|
+
attr_reader :updated
|
49
|
+
|
50
|
+
# Create a tag from params
|
51
|
+
#
|
52
|
+
# params hash can contains any of the object vars, but :title is mandatory
|
53
|
+
def initialize( params={} )
|
54
|
+
@id = params[:id] || nil
|
55
|
+
@title = params[:title] || raise( ArgumentError, ":title can't be empty")
|
56
|
+
@link_alternate = params[:link_alternate] || nil
|
57
|
+
@link_image = params[:link_image] || nil
|
58
|
+
@summary = params[:summary] || nil
|
59
|
+
@updated = params[:updated] || nil
|
60
|
+
@published = params[:published] || nil
|
61
|
+
@edit = params[:edit] || nil
|
62
|
+
@bm_created = params[:bm_created] || nil
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
class << self # Class methods
|
67
|
+
|
68
|
+
# Parse an REXML::Element corresponding to the tag
|
69
|
+
def build_from_xml_element( tag )
|
70
|
+
# We need a valide REXML::Element parameters :D
|
71
|
+
return nil unless tag.kind_of?(REXML::Element)
|
72
|
+
|
73
|
+
# Let's build the params to build a BlogMarks::Tag
|
74
|
+
params = {}
|
75
|
+
|
76
|
+
# Iterate over child nodes
|
77
|
+
tag.each_element do |element|
|
78
|
+
case element.name
|
79
|
+
when 'id'
|
80
|
+
params[:id] = element.text
|
81
|
+
when 'title'
|
82
|
+
params[:title] = element.text
|
83
|
+
when 'updated'
|
84
|
+
params[:updated] = Time.parse(element.text)
|
85
|
+
when 'published'
|
86
|
+
params[:published] = Time.parse(element.text)
|
87
|
+
when 'edit'
|
88
|
+
params[:edit] = URI.parse(element.text)
|
89
|
+
when 'bm:created'
|
90
|
+
params[:bm_created] = Time.parse(element.text)
|
91
|
+
when 'summary'
|
92
|
+
params[:summary] = element.text
|
93
|
+
when 'link'
|
94
|
+
params[:link_alternate] = URI.parse( element.attributes['href'] ) if element.attributes['rel'] == 'alternate'
|
95
|
+
params[:link_image] = URI.parse( element.attributes['href'] ) if element.attributes['rel'] == 'image'
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Create and return a new Tag constructs using params hash
|
100
|
+
return Tag.new(params)
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
#/bin/env ruby
|
2
|
+
#--
|
3
|
+
# Copyright (c) 2004 Jonathan Tron
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
# a copy of this software and associated documentation files (the
|
7
|
+
# "Software"), to deal in the Software without restriction, including
|
8
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
# the following conditions:
|
12
|
+
#
|
13
|
+
# The above copyright notice and this permission notice shall be
|
14
|
+
# included in all copies or substantial portions of the Software.
|
15
|
+
#
|
16
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
23
|
+
#++
|
24
|
+
|
25
|
+
require 'test/unit'
|
26
|
+
require 'blogmarks'
|
27
|
+
|
28
|
+
## This test should use a mock object to simulate the server response
|
29
|
+
## I'm not too familiar with this so it will come later :(
|
30
|
+
class TestClient < Test::Unit::TestCase
|
31
|
+
|
32
|
+
def setup
|
33
|
+
@client = BlogMarks::Client.new('', '')
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_create
|
37
|
+
assert_not_nil(@client)
|
38
|
+
assert_instance_of(BlogMarks::Client, @client)
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_find_marks
|
42
|
+
@client.find_marks
|
43
|
+
assert_not_nil(@client.feed)
|
44
|
+
assert(@client.feed.marks.size > 0)
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_find_my_marks
|
48
|
+
@client.find_my_marks
|
49
|
+
assert_not_nil(@client.feed)
|
50
|
+
assert(@client.feed.marks.size > 0)
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_find_marks_limited_to_5
|
54
|
+
@client.find_marks( :last => 5 )
|
55
|
+
assert_not_nil(@client.feed)
|
56
|
+
assert(@client.feed.marks.size <= 5)
|
57
|
+
end
|
58
|
+
|
59
|
+
##
|
60
|
+
def test_find_marks_begining_at_5
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,163 @@
|
|
1
|
+
#/bin/env ruby
|
2
|
+
#--
|
3
|
+
# Copyright (c) 2004 Jonathan Tron
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
# a copy of this software and associated documentation files (the
|
7
|
+
# "Software"), to deal in the Software without restriction, including
|
8
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
# the following conditions:
|
12
|
+
#
|
13
|
+
# The above copyright notice and this permission notice shall be
|
14
|
+
# included in all copies or substantial portions of the Software.
|
15
|
+
#
|
16
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
23
|
+
#++
|
24
|
+
|
25
|
+
require 'test/unit'
|
26
|
+
require 'uri'
|
27
|
+
require 'rexml/document'
|
28
|
+
require 'blogmarks'
|
29
|
+
|
30
|
+
class TestMark < Test::Unit::TestCase
|
31
|
+
|
32
|
+
def setup
|
33
|
+
@mark = BlogMarks::Mark.new( :id => 'tag:blogmarks.net,2005:marks,432',
|
34
|
+
:url => URI.parse('http://jonathan.tron.name'),
|
35
|
+
:title => "Jonathan's DevLog",
|
36
|
+
:summary => "Test",
|
37
|
+
:link_related => URI.parse("http://jonathan.tron.name"),
|
38
|
+
:link_alternate => URI.parse("http://jonathan.tron.name"),
|
39
|
+
:link_image => URI.parse("http://jonathan.tron.name/images/test.png"),
|
40
|
+
:updated => Time.new,
|
41
|
+
:published => Time.new,
|
42
|
+
:author => 'Jonathan',
|
43
|
+
:edit => URI.parse("http://api.blogmarks.net/marks/432"),
|
44
|
+
:bm_created => Time.new,
|
45
|
+
:public_tags => %w(blog dev multi\ word),
|
46
|
+
:private_tags => %w(test unit) )
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_create_error
|
50
|
+
assert_raise( ArgumentError ) {
|
51
|
+
BlogMarks::Mark.new()
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_create
|
56
|
+
assert_not_nil @mark
|
57
|
+
assert_instance_of(BlogMarks::Mark, @mark)
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_id
|
61
|
+
assert_equal('tag:blogmarks.net,2005:marks,432', @mark.id)
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_title
|
65
|
+
assert_equal("Jonathan's DevLog", @mark.title)
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_link_related
|
69
|
+
assert_instance_of(URI::HTTP, @mark.link_related)
|
70
|
+
assert_equal("http://jonathan.tron.name", @mark.link_related.to_s)
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_link_alternate
|
74
|
+
assert_instance_of(URI::HTTP, @mark.link_alternate)
|
75
|
+
assert_equal("http://jonathan.tron.name", @mark.link_alternate.to_s)
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_link_image
|
79
|
+
assert_instance_of(URI::HTTP, @mark.link_image)
|
80
|
+
assert_equal("http://jonathan.tron.name/images/test.png", @mark.link_image.to_s)
|
81
|
+
end
|
82
|
+
|
83
|
+
def test_updated
|
84
|
+
assert_instance_of(Time, @mark.updated)
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_published
|
88
|
+
assert_instance_of(Time, @mark.published)
|
89
|
+
end
|
90
|
+
|
91
|
+
def test_author
|
92
|
+
assert_equal('Jonathan', @mark.author)
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_edit
|
96
|
+
assert_instance_of(URI::HTTP, @mark.edit)
|
97
|
+
assert_equal('http://api.blogmarks.net/marks/432', @mark.edit.to_s )
|
98
|
+
end
|
99
|
+
|
100
|
+
def test_bm_created
|
101
|
+
assert_instance_of(Time, @mark.bm_created)
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_public_tags
|
105
|
+
assert_instance_of(Array, @mark.public_tags)
|
106
|
+
assert_equal(3, @mark.public_tags.size)
|
107
|
+
|
108
|
+
assert_equal('blog', @mark.public_tags[0] )
|
109
|
+
assert_equal('dev', @mark.public_tags[1])
|
110
|
+
assert_equal("multi word", @mark.public_tags[2])
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_private_tags
|
114
|
+
assert_instance_of(Array, @mark.private_tags)
|
115
|
+
assert_equal(2, @mark.private_tags.size)
|
116
|
+
|
117
|
+
assert_equal('test', @mark.private_tags[0] )
|
118
|
+
assert_equal('unit', @mark.private_tags[1])
|
119
|
+
end
|
120
|
+
|
121
|
+
def test_build_from_xml
|
122
|
+
xml = <<-ENTRY
|
123
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
124
|
+
<feed version="draft-ietf-atompub-format-05:do not deploy" xmlns="http://purl.org/atom/ns#draft-ietf-atompub-format-05" xmlns:bm="http://api.blogmarks.net/ns#">
|
125
|
+
<head>
|
126
|
+
<title>Last public marks from user Jonathan</title>
|
127
|
+
<link rel="alternate" type="application/xhtml+xml" href="http://blogmarks.net/user/Jonathan"/>
|
128
|
+
<updated>2006-01-26T21:58:24Z</updated>
|
129
|
+
</head>
|
130
|
+
<entry>
|
131
|
+
<id>tag:blogmarks.net,2006:marks,323785</id>
|
132
|
+
<title type="TEXT">Don't Meet Your Heroes - The CSS & Web Standards News Compilation Site - DMYH</title>
|
133
|
+
<link rel="related" href="http://www.dontmeetyourheroes.com/index.php" type="text/html"/>
|
134
|
+
<link rel="alternate" href="http://blogmarks.net/user/Jonathan/archives/2006/01/#mark323785" type="application/xhtml+xml" title="Don't Meet Your Heroes - The CSS & Web Standards News Compilation Site - DMYH"/>
|
135
|
+
<link rel="via" href="http://blogmarks.net/user/qoodoll/archives/2006/01/#mark323762" type="text/html"/>
|
136
|
+
<link rel="image" href="http://www.blogmarks.net/screenshots/2006/01/26/1d8ec2781448c994f7e537f3b6d9a1a8.png" type="image/png"/>
|
137
|
+
<updated>2006-01-26T21:58:24Z</updated>
|
138
|
+
<published>2006-01-26T21:58:24Z</published>
|
139
|
+
<author><name>Jonathan</name></author>
|
140
|
+
<category term="http://api.blogmarks.net/tags" sheme="/web" label="web"/>
|
141
|
+
<category term="http://api.blogmarks.net/tags" sheme="/css" label="css"/>
|
142
|
+
<category term="http://api.blogmarks.net/tags" sheme="/site" label="site"/>
|
143
|
+
<category term="http://api.blogmarks.net/tags" sheme="/news" label="news"/>
|
144
|
+
<edit>http://api.blogmarks.net/atom/marks/323785</edit>
|
145
|
+
<bm:created>2006-01-26T21:58:24Z</bm:created>
|
146
|
+
</entry>
|
147
|
+
</feed>
|
148
|
+
ENTRY
|
149
|
+
|
150
|
+
xml_doc = REXML::Document.new( xml, :ignore_whitespace_nodes => :all )
|
151
|
+
|
152
|
+
mark = nil
|
153
|
+
xml_doc.root.elements.each( "/feed/entry" ) do |entry|
|
154
|
+
begin
|
155
|
+
mark = BlogMarks::Mark.build_from_xml_element(entry)
|
156
|
+
rescue
|
157
|
+
puts 'Error'
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
assert_instance_of(BlogMarks::Mark, mark)
|
162
|
+
end
|
163
|
+
end
|
metadata
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.8.11
|
3
|
+
specification_version: 1
|
4
|
+
name: blogmarks
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.1.0
|
7
|
+
date: 2006-01-31 00:00:00 +01:00
|
8
|
+
summary: Library to access BlogMarks.net bookmarks.
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: jonathan@tron.name
|
12
|
+
homepage: http://projets.tron.name/ruby/blogmarks
|
13
|
+
rubyforge_project:
|
14
|
+
description: BlogMarks provide a simple way to access BlogMarks.net bookmarks system.
|
15
|
+
autorequire: builder
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
authors:
|
29
|
+
- Jonathan Tron
|
30
|
+
files:
|
31
|
+
- lib/blogmarks.rb
|
32
|
+
- lib/blogmarks/class_attribute_accessors.rb
|
33
|
+
- lib/blogmarks/client.rb
|
34
|
+
- lib/blogmarks/feed.rb
|
35
|
+
- lib/blogmarks/mark.rb
|
36
|
+
- lib/blogmarks/tag.rb
|
37
|
+
- test/test_blogmarks_client.rb
|
38
|
+
- test/test_blogmarks_entry.rb
|
39
|
+
- README
|
40
|
+
- CHANGES
|
41
|
+
- MIT-LICENSE
|
42
|
+
test_files:
|
43
|
+
- test/test_blogmarks_client.rb
|
44
|
+
- test/test_blogmarks_entry.rb
|
45
|
+
rdoc_options:
|
46
|
+
- --title
|
47
|
+
- BlogMarks -- Access your Marks
|
48
|
+
- --main
|
49
|
+
- README
|
50
|
+
- --line-numbers
|
51
|
+
extra_rdoc_files:
|
52
|
+
- README
|
53
|
+
- CHANGES
|
54
|
+
- MIT-LICENSE
|
55
|
+
executables: []
|
56
|
+
|
57
|
+
extensions: []
|
58
|
+
|
59
|
+
requirements: []
|
60
|
+
|
61
|
+
dependencies:
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: builder
|
64
|
+
version_requirement:
|
65
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 1.2.4
|
70
|
+
version:
|