blogmarks 0.1.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/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:
|