discodactyl 0.3.0 → 0.4.1

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/Gemfile CHANGED
@@ -1,8 +1,3 @@
1
1
  source :rubygems
2
2
 
3
- gem 'actionpack', '~>2.3.8'
4
- gem 'feedzirra', '~>0.0.23'
5
- gem 'mofo', '~>0.2.16'
6
- gem 'nokogiri', '~>1.4.2'
7
- gem 'rake', '~>0.8.7'
8
- gem 'rr', '~>0.10.11'
3
+ gemspec
data/MANIFEST CHANGED
@@ -5,7 +5,7 @@ Gemfile
5
5
  INSTALL
6
6
  MANIFEST
7
7
  NEWS
8
- README
8
+ README.rdoc
9
9
  Rakefile
10
10
  TODO
11
11
  bin/webfinger
@@ -13,7 +13,13 @@ discodactyl.gemspec
13
13
  lib/discodactyl.rb
14
14
  lib/discodactyl/acct_uri.rb
15
15
  lib/discodactyl/host_meta.rb
16
+ lib/discodactyl/host_meta_jrd.rb
17
+ lib/discodactyl/jrd.rb
18
+ lib/discodactyl/jrd/document.rb
19
+ lib/discodactyl/jrd/link.rb
20
+ lib/discodactyl/known_rels.rb
16
21
  lib/discodactyl/link_header.rb
22
+ lib/discodactyl/log.rb
17
23
  lib/discodactyl/resource_discovery.rb
18
24
  lib/discodactyl/uri_template.rb
19
25
  lib/discodactyl/xrd.rb
@@ -22,6 +28,8 @@ lib/discodactyl/xrd/link.rb
22
28
  test/test_acct_uri.rb
23
29
  test/test_helper.rb
24
30
  test/test_host_meta.rb
31
+ test/test_host_meta_jrd.rb
32
+ test/test_jrd_document.rb
25
33
  test/test_link_header.rb
26
34
  test/test_resource_discovery.rb
27
35
  test/test_uri_template.rb
@@ -1,9 +1,10 @@
1
1
  = Discodactyl
2
2
 
3
- * http://dactylo.us
4
- * http://github.com/josephholsten/discodactyl
5
- * http://josephholsten.lighthouseapp.com/projects/36040-discodactyl
6
- * mailto:joseph@josephholsten.com
3
+ Code :: http://github.com/josephholsten/discodactyl
4
+ Bugs :: http://josephholsten.lighthouseapp.com/projects/36040-discodactyl
5
+ Tests :: http://discodactyl-ci.heroku.com/
6
+ Docs :: http://rdoc.info/projects/josephholsten/discodactyl
7
+ Author :: mailto:joseph@josephholsten.com
7
8
 
8
9
  == DESCRIPTION:
9
10
 
@@ -18,9 +19,22 @@ Discodactyl is an experimental toolkit for XRD service discovery documents and r
18
19
  * acct: URI parsing.
19
20
  * Minimal URI Template parsing and application.
20
21
  * Probably filled with bugs. Don't forget your bug powder.
22
+ * JRD parsing, querying.
23
+ * Yahoo 'webfinger' doesn't work. Sadly, they need to update their format to the most recent one.
21
24
 
22
25
  == SYNOPSIS:
23
26
 
27
+ For command-line fiends:
28
+
29
+ $ webfinger bradfitz@gmail.com
30
+ Profile: http://www.google.com/profiles/bradfitz
31
+ Profile data: http://www-opensocial.googleusercontent.com/api/people/115863474911002159675/
32
+ describedby: http://www.google.com/profiles/bradfitz
33
+ Contacts: http://www-opensocial.googleusercontent.com/api/people/
34
+ Status: Hah... about 4 hours ago
35
+
36
+ For ruby mongers:
37
+
24
38
  # All of WebFinger! In very few, if long and pointy, lines!
25
39
  require 'discodactyl'
26
40
 
@@ -46,7 +60,7 @@ This software is also available in a permissive license. Please contact Joseph H
46
60
 
47
61
  (The GNU Affero General Public License)
48
62
 
49
- Copyright (c) 2009, 2010 Joseph Anthony Pasquale Holsten
63
+ Copyright (c) 2009, 2010, 2011 Joseph Anthony Pasquale Holsten
50
64
 
51
65
  This program is free software: you can redistribute it and/or modify
52
66
  it under the terms of the GNU Affero General Public License as published by
data/TODO CHANGED
@@ -14,11 +14,28 @@
14
14
  * REXML support
15
15
  * collect common rel values into a enum module
16
16
  * put license info in every file
17
+ * find out if ruby has a default debug flag
18
+ * find well known uris via WellKnown.find(uri, :host_meta)
17
19
 
18
20
  Reference Specs
19
- * host meta http://tools.ietf.org/html/draft-hammer-hostmeta-05
21
+ * host meta http://tools.ietf.org/html/draft-hammer-hostmeta-13
20
22
  * webfinger
21
- * xrd http://www.oasis-open.org/committees/download.php/36542/xrd-1.0-wd15.html
22
- * lrdd http://tools.ietf.org/html/draft-hammer-discovery-04
23
- * link header http://tools.ietf.org/html/draft-nottingham-http-link-header-08
24
- * versioning-rels http://tools.ietf.org/html/draft-brown-versioning-link-relations-07
23
+ * xrd http://docs.oasis-open.org/xri/xrd/v1.0/xrd-1.0.html
24
+ * lrdd http://tools.ietf.org/html/draft-hammer-discovery-06
25
+ * link header http://tools.ietf.org/html/rfc5988
26
+ * versioning-rels http://tools.ietf.org/html/rfc5829
27
+
28
+ Interesting webfinger variants
29
+ * joseph@josephholsten.com
30
+ * romeda@gmail.com
31
+ * will@willmeyer.com
32
+ * dclinton@gmail.com
33
+ * bradfitz@gmail.com
34
+ * dclinton@yahoo.com
35
+ * beestage@yahoo.com
36
+ * gffletch@aol.com
37
+
38
+ other clients
39
+ * webfingerclient-dclinton.appspot.com/
40
+ * webfinger.org/lookup/darron@froese.org
41
+ * web.mailana.com/labs/findbyemail/
data/bin/webfinger CHANGED
@@ -7,14 +7,27 @@ require 'open-uri'
7
7
  require 'action_view'
8
8
  require 'mofo'
9
9
  require 'feedzirra'
10
+ require 'logger'
11
+
12
+ log = Logger.new(STDOUT)
13
+ Discodactyl.log = log
10
14
 
11
15
  if ARGV.length == 0
12
16
  puts "Usage: #{__FILE__} user@host"
13
17
  exit 0
14
18
  end
15
19
 
20
+ if ARGV[0].match /^-d/
21
+ log.level = Logger::DEBUG
22
+ # ARGV.unshift
23
+ ARGV.shift
24
+ else
25
+ log.level = Logger::WARN
26
+ end
27
+
16
28
  # parse identifier
17
- acct = URI::ACCT.parse(ARGV[0])
29
+ raw_acct = ARGV[0]
30
+ acct = URI::ACCT.parse(raw_acct)
18
31
 
19
32
  # Perform LRDD discovery on acct with the webfinger relation
20
33
  finger_rel = "lrdd"
@@ -22,41 +35,73 @@ begin
22
35
  webfinger_uris = Discodactyl::ResourceDiscovery.get_uris_by_rel(acct, finger_rel, 'uri' => acct)
23
36
  rescue Discodactyl::HostMetaHTTPError => error
24
37
  if error.io.status[1] == 'Not Found'
25
- abort "No host meta webfinger information found at #{error.io.base_uri}"
38
+ abort "No host meta webfinger information found at #{error.io.base_uri}. Is the file there?"
26
39
  else
27
40
  raise
28
41
  end
42
+ rescue Discodactyl::HostMetaSocketError => error
43
+ abort "Could not connect to #{error.host}:#{error.port}. Is your internet connection working?"
29
44
  end
30
45
 
31
46
  abort "URI didn't have any linked webfinger URIs" if webfinger_uris.empty?
32
47
 
33
48
  # retrieve user disco doc
34
- disco_res = open(webfinger_uris.first)
49
+ begin
50
+ disco_uri = webfinger_uris.first
51
+ disco_res = open(disco_uri)
52
+ rescue OpenURI::HTTPError
53
+ abort "Couldn't open the disco doc at #{disco_uri}. Is your host-meta pointed at the right place?"
54
+ end
35
55
 
36
56
  # parse disco doc
37
- disco = Discodactyl::XRD::Document.parse(disco_res)
57
+ begin
58
+ disco = Discodactyl::JRD::Document.parse(disco_res)
59
+ log.debug "disco doc <#{webfinger_uris.first}>:\n#{disco.inspect}\n"
60
+ rescue Discodactyl::JRD::ParseError => e
61
+ log.debug "disco doc doesn't seem to be a JRD #{e.message}"
62
+ disco_res.rewind
63
+ disco = Discodactyl::XRD::Document.parse(disco_res)
64
+ log.debug "disco doc <#{webfinger_uris.first}>:\n#{disco}\n"
65
+ end
66
+
67
+ log.debug "links: #{disco.links.map{|r| [r.rel, r.type, r.href]}.inspect}"
68
+ log.debug "rels: #{disco.rels.inspect}"
69
+
70
+ if disco.subject
71
+ log.debug "subject: #{disco.subject}"
72
+ unless acct.to_s == disco.subject.to_s
73
+ abort "Disco doc's subject <#{disco.subject}> doesn't match account we're webfingering <#{acct}>"
74
+ end
75
+ else
76
+ log.warn("Disco doc didn't list a subject. Assuming everything is well.")
77
+ end
38
78
 
39
79
  hcard_uri = disco.uris_by_rel('http://microformats.org/profile/hcard').first
40
- hcard = hCard.find(:first => hcard_uri)
80
+ hcard = hCard.find(:first => hcard_uri) rescue
41
81
  if hcard
42
82
  puts "Name: #{hcard.fn.gsub(/\s+/, ' ')}" if hcard.properties.include?('fn')
43
83
  puts "Title: #{hcard.title}" if hcard.properties.include?('title')
44
84
  puts "Organization: #{hcard.org}" if hcard.properties.include?('org')
45
85
  end
46
86
 
47
- openid = disco.uris_by_rel('http://specs.openid.net/auth/').first
48
- puts "OpenID: #{openid}" if openid
49
-
50
- profile = disco.uris_by_rel('http://webfinger.net/rel/profile-page').first
51
- puts "Profile: #{profile}" if profile
87
+ links_with_titles = disco.links.select{|l| l.title.present? }
88
+ links_with_titles.each do |link|
89
+ puts "#{link.title}: #{link.to_uri}"
90
+ end
52
91
 
53
- contacts = disco.uris_by_rel('http://portablecontacts.net/spec/1.0').first
54
- puts "Contacts: #{contacts}" if contacts
92
+ Discodactyl::KNOWN_RELS.each do |name, rel|
93
+ uri = disco.uris_by_rel(rel).first
94
+ puts "#{name}: #{uri}" if uri
95
+ end
55
96
 
56
97
  activities = disco.uris_by_rel('http://schemas.google.com/g/2010#updates-from').first
57
98
  if activities
58
99
  feed = Feedzirra::Feed.fetch_and_parse(activities)
59
100
  entry = feed.entries.first
60
- include ActionView::Helpers::DateHelper
61
- puts "Status: #{Loofah::Helpers.strip_tags(entry.content)} #{time_ago_in_words(entry.published)} ago"
101
+ if entry
102
+ include ActionView::Helpers::DateHelper
103
+ silence_warnings do
104
+ puts "Status: #{Loofah::Helpers.strip_tags(entry.content)} #{time_ago_in_words(entry.published)} ago"
105
+ end
106
+ end
62
107
  end
data/discodactyl.gemspec CHANGED
@@ -7,17 +7,19 @@ Gem::Specification.new do |spec|
7
7
  spec.email = "joseph@josephholsten.com"
8
8
  spec.homepage = "http://dactylo.us"
9
9
  spec.description = %q{Discodactyl is an experimental toolkit for XRD service discovery documents and related protocols. It includes implementations of XRD URITemplate Link-Patterns, basic site-meta support, HTTP Link header parsing, acct: URIs and a webfinger poking stick.}
10
- spec.extra_rdoc_files = %w[ AUTHORS CHANGELOG COPYING INSTALL NEWS README TODO ]
10
+ spec.extra_rdoc_files = %w[ AUTHORS CHANGELOG COPYING INSTALL NEWS README.rdoc TODO ]
11
11
  spec.rdoc_options << "--charset=UTF-8" <<
12
12
  "--title" << "Discodactyl Documentation" <<
13
- "--main" << "README"
13
+ "--main" << "README.rdoc"
14
14
  spec.version = version
15
15
  spec.summary = spec.description.split(/\.\s+/).first
16
16
  spec.files = File.read("MANIFEST").split(/\r?\n\r?/)
17
17
  spec.executables = spec.files.grep(/^bin/) { |f| File.basename(f) }
18
18
  spec.test_files = spec.files.grep(/^test\/.*test_.*\.rb$/)
19
- spec.add_runtime_dependency 'nokogiri', '~>1.4.0'
20
- spec.add_runtime_dependency 'actionpack', '~>2.3.0'
19
+ spec.add_runtime_dependency 'nokogiri', '~>1.4.2'
20
+ spec.add_runtime_dependency 'actionpack', '~>3.0.0'
21
21
  spec.add_runtime_dependency 'feedzirra', '~>0.0.23'
22
- spec.add_runtime_dependency 'mofo', '~>0.2.0'
22
+ spec.add_runtime_dependency 'mofo', '~>0.2.16'
23
+ spec.add_development_dependency 'rr', '~>0.10.11'
24
+ spec.add_development_dependency 'rake', '~>0.8.7'
23
25
  end
@@ -13,7 +13,9 @@ module URI # :nodoc:
13
13
  /^(acct:)?(.*)$/ =~ str
14
14
  scheme = 'acct'
15
15
  opaque = $2
16
- self.new(scheme, nil, nil, nil, nil, nil, opaque, nil, nil)
16
+ acct = self.new(scheme, nil, nil, nil, nil, nil, opaque, nil, nil)
17
+ Discodactyl.log.debug("Parsed #{str.inspect} into URI #{acct.inspect}") if Discodactyl.log
18
+ acct
17
19
  end
18
20
 
19
21
 
@@ -1,5 +1,6 @@
1
1
  require 'discodactyl/xrd'
2
2
  require 'open-uri'
3
+ require 'socket'
3
4
 
4
5
  module Discodactyl # :nodoc
5
6
  # Convienient access to host metadata and individual resources controlled by
@@ -15,6 +16,7 @@ module Discodactyl # :nodoc
15
16
  # Take a URI and retrieve its HostMeta document
16
17
  def from_uri(uri)
17
18
  uri = get_uri_from_uri(uri)
19
+ Discodactyl.log.debug("got xrd host-meta uri: #{uri}") if Discodactyl.log
18
20
  begin
19
21
  raw = uri.open
20
22
  rescue OpenURI::HTTPError => error
@@ -24,6 +26,8 @@ module Discodactyl # :nodoc
24
26
  else
25
27
  raise
26
28
  end
29
+ rescue ::SocketError => error
30
+ raise HostMetaSocketError.new(error.message, uri.host, uri.port)
27
31
  end
28
32
  self.parse raw
29
33
  end
@@ -31,4 +35,12 @@ module Discodactyl # :nodoc
31
35
  end
32
36
  class HostMetaHTTPError < OpenURI::HTTPError # :nodoc:
33
37
  end
38
+ class HostMetaSocketError < StandardError # :nodoc:
39
+ attr_accessor :host, :port
40
+ def initialize(msg = nil, host = nil, port = nil)
41
+ message = msg
42
+ @host = host
43
+ @port = port
44
+ end
45
+ end
34
46
  end
@@ -0,0 +1,35 @@
1
+ require 'open-uri'
2
+ require 'active_support'
3
+ require 'socket'
4
+ require "discodactyl/uri_template"
5
+ require "discodactyl/jrd"
6
+ require 'discodactyl/log'
7
+
8
+ module Discodactyl
9
+ class HostMetaJRD < Discodactyl::JRD::Document
10
+ # Take a URI and return the URI for its HostMeta document
11
+ def self.get_uri_from_uri(uri)
12
+ host = uri.respond_to?('host') ? uri.host : uri
13
+ URI.parse('http://'+ host + '/.well-known/host-meta.json')
14
+ end
15
+
16
+ # Take a URI and retrieve its HostMeta document
17
+ def self.from_uri(uri)
18
+ uri = get_uri_from_uri(uri)
19
+ Discodactyl.log.debug("got jrd host-meta uri: #{uri}") if Discodactyl.log
20
+ begin
21
+ raw = uri.open
22
+ rescue OpenURI::HTTPError => error
23
+ if error.io.status[1] == 'Not Found'
24
+ error.io.base_uri = uri
25
+ raise HostMetaHTTPError.new(error.message, error.io)
26
+ else
27
+ raise
28
+ end
29
+ rescue ::SocketError => error
30
+ raise HostMetaSocketError.new(error.message, uri.host, uri.port)
31
+ end
32
+ self.parse raw
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,49 @@
1
+ require 'nokogiri'
2
+ require 'active_support'
3
+ require 'discodactyl/jrd/link'
4
+ require "discodactyl/uri_template"
5
+
6
+ module Discodactyl # :nodoc:
7
+ module JRD # :nodoc:
8
+ class Document < Hash
9
+ def self.parse(raw)
10
+ begin
11
+ Discodactyl.log.debug("parsing jrd: #{raw}") if Discodactyl.log
12
+ decoded = ActiveSupport::JSON.decode(raw)
13
+ rescue => e
14
+ raise ParseError.new(e.message)
15
+ end
16
+ self.[](decoded)
17
+ end
18
+
19
+ def uris_by_rel(rel, params={})
20
+ links_by_rel(rel).map do |link|
21
+ link.href || link.template.to_uri(params)
22
+ end
23
+ end
24
+
25
+ def links_by_rel(rel)
26
+ if self.has_key? 'links' and self['links'].has_key? rel
27
+ self['links'][rel].map{|link| Link.parse(link.merge('rel'=>rel)) }
28
+ else
29
+ []
30
+ end
31
+ end
32
+
33
+ def links
34
+ rels.map do |rel, links|
35
+ links.map {|link| Link.parse(link.merge('rel' => rel)) }
36
+ end.flatten
37
+ end
38
+
39
+ def rels
40
+ self['links'] || []
41
+ end
42
+
43
+ def subject
44
+ self['subject']
45
+ end
46
+ end
47
+ class ParseError < StandardError; end
48
+ end
49
+ end
@@ -0,0 +1,25 @@
1
+ require 'discodactyl/uri_template'
2
+
3
+ module Discodactyl # :nodoc:
4
+ module JRD # :nodoc:
5
+ class Link < Struct.new(:href, :template, :rel, :title, :type)
6
+ attr_accessor :raw
7
+ class << self
8
+ def parse(element)
9
+ self.new.tap do |link|
10
+ begin
11
+ link.rel = element['rel']
12
+ link.type = element['type']
13
+ link.href = element['href']
14
+ link.title = element['title']
15
+ link.template = URITemplate.new(element['template']) unless link.href
16
+ link.raw = element
17
+ rescue
18
+ raise "Couldn't parse #{link} into a JRD Link"
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,2 @@
1
+ require 'discodactyl/jrd/document'
2
+ require 'discodactyl/jrd/link'
@@ -0,0 +1,12 @@
1
+ module Discodactyl # :nodoc:
2
+ KNOWN_RELS = {
3
+ 'OpenID' => 'http://specs.openid.net/auth/',
4
+ 'OpenID Provider' => 'http://specs.openid.net/auth/2.0/provider',
5
+ 'OpenID Provider' => 'openid2.provider',
6
+ "Profile" => 'http://webfinger.net/rel/profile-page',
7
+ "Profile data" => 'http://portablecontacts.net/spec/1.0#me',
8
+ "Contacts" => 'http://portablecontacts.net/spec/1.0',
9
+ "describedby" => 'describedby', # from POWDER
10
+ "Webfinger/LRDD" => 'lrdd'
11
+ }
12
+ end
@@ -1,4 +1,4 @@
1
- require 'active_support/core_ext/object/misc'
1
+ # require 'active_support/core_ext/object/misc'
2
2
 
3
3
  module Discodactyl # :nodoc:
4
4
  # Access to web links stored in HTTP Link header-fields. See also:
@@ -14,7 +14,7 @@ module Discodactyl # :nodoc:
14
14
  # link[:href] # '/'
15
15
  # link[:rel] # ['http://example.net/foo']
16
16
  def parse(string)
17
- returning(params = LinkHeader.new) do
17
+ LinkHeader.new.tap do |params|
18
18
  if string =~ /^<([^>]+)>(.*)$/
19
19
  params[:href] = $1
20
20
  $2.split(/;\s*/).each do |part|
@@ -0,0 +1,5 @@
1
+ require 'active_support/core_ext/module/attribute_accessors'
2
+
3
+ module Discodactyl # :nodoc:
4
+ mattr_accessor :log
5
+ end
@@ -1,6 +1,7 @@
1
1
  require 'open-uri'
2
2
  require 'nokogiri'
3
3
  require 'discodactyl/link_header'
4
+ require 'discodactyl/log'
4
5
 
5
6
  module Discodactyl # :nodoc:
6
7
  class ResourceDiscovery
@@ -20,6 +21,7 @@ module Discodactyl # :nodoc:
20
21
  def get_uris_by_rel(uri, rel, params = {})
21
22
  begin
22
23
  uri = URI.parse(uri.to_s) unless uri.respond_to?('open')
24
+ Discodactyl.log.debug "Getting uris for uri #{uri.inspect} by rel #{rel.inspect} with params #{params.inspect}" if Discodactyl.log
23
25
  resource = uri.open
24
26
  rescue OpenURI::HTTPError => e
25
27
  status = e.io.status[0] # => 3xx, 4xx, or 5xx
@@ -61,9 +63,28 @@ module Discodactyl # :nodoc:
61
63
  end
62
64
  end
63
65
  unless uris
64
- host_meta = Discodactyl::HostMeta.from_uri uri
66
+ Discodactyl.log.debug("getting jrd host-meta") if Discodactyl.log
67
+ begin
68
+ host_meta = Discodactyl::HostMetaJRD.from_uri uri
69
+ rescue => error
70
+ Discodactyl.log.debug("Error parsing jrd host-meta: #{error}") if Discodactyl.log
71
+ end
72
+ Discodactyl.log.debug("finding uris in jrd host-meta") if Discodactyl.log
73
+ uris = host_meta.uris_by_rel(rel, params) if host_meta
74
+ end
75
+
76
+ unless uris
77
+ Discodactyl.log.debug("Getting xrd host-meta") if Discodactyl.log
78
+ begin
79
+ host_meta = Discodactyl::HostMeta.from_uri uri
80
+ rescue => error
81
+ Discodactyl.log.debug("Error getting xrd host-meta: #{error}") if Discodactyl.log
82
+ raise
83
+ end
84
+ Discodactyl.log.debug("finding uris in host-meta") if Discodactyl.log
65
85
  uris = host_meta.uris_by_rel(rel, params)
66
86
  end
87
+ Discodactyl.log.debug "URIs: #{uris.inspect}" if Discodactyl.log
67
88
  uris
68
89
  end
69
90
 
@@ -8,6 +8,7 @@ module XRD # :nodoc:
8
8
  class << self
9
9
  def parse(string)
10
10
  raw = Nokogiri::XML(string)
11
+ Discodactyl.log.debug("parsing xrd: #{raw}") if Discodactyl.log
11
12
  doc = self.new
12
13
 
13
14
  doc.raw = raw
@@ -29,7 +30,8 @@ module XRD # :nodoc:
29
30
  end
30
31
 
31
32
  def links_by_rel(rel)
32
- linkelems_by_rel(rel).map {|e| Link.parse(e) }
33
+ # linkelems_by_rel(rel).map {|e| Link.parse(e) }
34
+ find_all_links_by_rel(rel)
33
35
  end
34
36
 
35
37
  def uris_by_rel(rel, params = {})
@@ -54,6 +56,10 @@ module XRD # :nodoc:
54
56
  def find_link_by_id(link_id)
55
57
  links.find {|link| link.id == link_id}
56
58
  end
59
+
60
+ def find_all_links_by_rel(rel)
61
+ links.find_all {|link| link.rel == rel }
62
+ end
57
63
 
58
64
  def ids
59
65
  links.map(&:id).reject(&:nil?)
@@ -62,6 +68,10 @@ module XRD # :nodoc:
62
68
  def to_s
63
69
  raw.to_s
64
70
  end
71
+
72
+ def rels
73
+ links.map(&:rel).reject(&:nil?).uniq
74
+ end
65
75
 
66
76
  def generate_tag_uri
67
77
  scheme = 'tag'
@@ -70,6 +80,10 @@ module XRD # :nodoc:
70
80
  specific = "/xrd/link/#{rand(2**10)}"
71
81
  "#{scheme}:#{authority},#{date}:#{specific}"
72
82
  end
83
+
84
+ def subject
85
+ raw.xpath('/xrd:XRD/xrd:Subject', XMLNS).text
86
+ end
73
87
  end
74
88
  end
75
89
  end
@@ -1,26 +1,25 @@
1
1
  require 'nokogiri'
2
- require 'active_support/core_ext/object/misc'
3
2
  require 'discodactyl/uri_template'
4
3
 
5
4
  module Discodactyl # :nodoc:
6
5
  module XRD # :nodoc:
7
- class Link
6
+ class Link < Struct.new(:href, :template, :rel, :title, :type)
7
+ attr_accessor :raw
8
8
  class << self
9
9
  def parse(element)
10
- returning(link = self.new) do
10
+ self.new.tap do |link|
11
11
  link.rel = element['rel']
12
12
  link.type = element['type']
13
13
  link.href = element['href']
14
+ link.title = element['title']
14
15
  link.template = URITemplate.new(element['template']) unless link.href
15
16
  link.raw = element
16
17
  end
17
18
  end
18
19
  end
19
20
 
20
- attr_accessor :href, :template, :rel, :type, :raw
21
-
22
21
  def to_uri(params = {})
23
- @href || @template.to_uri(params)
22
+ href || template.to_uri(params)
24
23
  end
25
24
 
26
25
  def id
data/lib/discodactyl.rb CHANGED
@@ -1,9 +1,13 @@
1
1
  require 'discodactyl/acct_uri'
2
2
  require 'discodactyl/host_meta'
3
+ require 'discodactyl/host_meta_jrd'
4
+ require 'discodactyl/jrd'
5
+ require 'discodactyl/known_rels'
6
+ require 'discodactyl/log'
3
7
  require 'discodactyl/resource_discovery'
4
8
  require 'discodactyl/uri_template'
5
9
  require 'discodactyl/xrd'
6
10
 
7
11
  module Discodactyl # :nodoc:
8
- VERSION = '0.3.0'
12
+ VERSION = '0.4.1'
9
13
  end
@@ -0,0 +1,63 @@
1
+ #!/usr/bin/env ruby -w
2
+ libdir = File.expand_path('../../lib', __FILE__)
3
+ $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
4
+
5
+ require "test/unit"
6
+ require "discodactyl/host_meta_jrd"
7
+ require 'discodactyl/acct_uri'
8
+
9
+ class TestHostMetaJRD < Test::Unit::TestCase
10
+ def test_get_uri_from_host
11
+ uri = 'host.example'
12
+ expected = URI.parse 'http://host.example/.well-known/host-meta.json'
13
+ assert_equal expected, Discodactyl::HostMetaJRD.get_uri_from_uri(uri)
14
+ end
15
+
16
+ def test_get_uri_from_http
17
+ uri = URI.parse 'http://host.example/some/path'
18
+ expected = URI.parse 'http://host.example/.well-known/host-meta.json'
19
+ assert_equal expected, Discodactyl::HostMetaJRD.get_uri_from_uri(uri)
20
+ end
21
+
22
+ def test_get_uri_from_acct
23
+ uri = URI.parse 'acct:user@host.example'
24
+ expected = URI.parse 'http://host.example/.well-known/host-meta.json'
25
+ assert_equal expected, Discodactyl::HostMetaJRD.get_uri_from_uri(uri)
26
+ end
27
+
28
+ def test_from_uri
29
+ uri = URI.parse 'acct:josephholsten@localhost'
30
+ assert_equal({"links"=>{"lrdd"=>[{"href"=>"http://localhost/discovery.jrd"}]}}, Discodactyl::HostMetaJRD.from_uri(uri))
31
+ end
32
+
33
+ def test_parse
34
+ json = <<JSON
35
+ { "links": {
36
+ "lrdd": [{
37
+ "href":"http://host.example/discovery.jrd" }]}}
38
+ JSON
39
+ assert_equal({"links"=>{"lrdd"=>[{"href"=>"http://host.example/discovery.jrd"}]}}, Discodactyl::HostMetaJRD.parse(json))
40
+ end
41
+
42
+ def test_links_by_rel
43
+ jrd = Discodactyl::HostMetaJRD[{"links"=>{"lrdd"=>[{"href"=>"http://host.example/discovery.jrd"}]}}]
44
+ expected = [Discodactyl::JRD::Link.parse({'href'=>'http://host.example/discovery.jrd', 'rel' => 'lrdd'})]
45
+ assert_equal(expected, jrd.links_by_rel('lrdd'))
46
+ end
47
+
48
+ # links by rel when links is missing, when rel is missing
49
+ def test_links_by_rel_when_missing
50
+ jrd = Discodactyl::HostMetaJRD[{}]
51
+ assert_equal([], jrd.links_by_rel('lrdd'))
52
+ end
53
+
54
+ def test_uris_by_rel
55
+ jrd = Discodactyl::HostMetaJRD[{"links"=>{"lrdd"=>[{"href"=>"http://host.example/discovery.jrd"}]}}]
56
+ assert_equal(['http://host.example/discovery.jrd'], jrd.uris_by_rel('lrdd'))
57
+ end
58
+
59
+ def test_uris_by_rel_with_template
60
+ jrd = Discodactyl::HostMetaJRD[{"links"=>{"lrdd"=>[{"template"=>"http://host.example/{id}"}]}}]
61
+ assert_equal(['http://host.example/foo'], jrd.uris_by_rel('lrdd', 'id' => 'foo'))
62
+ end
63
+ end
@@ -0,0 +1,142 @@
1
+ #!/usr/bin/env ruby -w
2
+ libdir = File.expand_path('../../lib', __FILE__)
3
+ $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
4
+ testdir = File.expand_path('../../test', __FILE__)
5
+ $LOAD_PATH.unshift(testdir) unless $LOAD_PATH.include?(testdir)
6
+
7
+ require 'test_helper'
8
+ require 'discodactyl/jrd/document'
9
+
10
+ class TestJRDParsing < Test::Unit::TestCase
11
+ def setup
12
+ @minimal_jrd_string =<<eos
13
+ {
14
+ "subject":"http://host.example/"
15
+ }
16
+ eos
17
+
18
+ # @full_jrd_string =<<eos
19
+ # {"subject":"http://host.example/","links":{"describedby":[{"href":"http://host.example/endpoint"},{"template":"http://host.example/descriptor?q={%id}"}],"webfinger":[{"href":"http://host.example/endpoint"}],"feed":[{"template":"http://host.example/descriptor?q={%id}"}]}}
20
+ # eos
21
+ # <"{\"subject\":\"http://host.example/\",\"links\":{\"describedby\":[{\"href\":\"http://host.example/endpoint\"},{\"template\":\"http://host.example/descriptor?q={%id}\"}],\"webfinger\":[{\"href\":\"http://host.example/endpoint\"}],\"feed\":[{\"template\":\"http://host.example/descriptor?q={%id}\"}]}}\n"> expected but was
22
+ # <"{\"subject\":\"http://host.example/\",\"links\":{\"webfinger\":[{\"href\":\"http://host.example/endpoint\"}],\"describedby\":[{\"href\":\"http://host.example/endpoint\"},{\"template\":\"http://host.example/descriptor?q={%id}\"}],\"feed\":[{\"template\":\"http://host.example/descriptor?q={%id}\"}]}}">.
23
+
24
+ @full_jrd_string =<<eos
25
+ {"subject":"http://host.example/","links":{"webfinger":[{"href":"http://host.example/endpoint"}],"describedby":[{"href":"http://host.example/endpoint"},{"template":"http://host.example/descriptor?q={%id}"}],"feed":[{"template":"http://host.example/descriptor?q={%id}"}]}}
26
+ eos
27
+
28
+ @raw_link_without_id = '<Link rel="http://oexchange.org/spec/0.8/rel/user-target" type="application/xrd+xml" href="http://www.example.com/linkeater/oexchange.xrd"/>'
29
+
30
+ @raw_link_without_id = '{"http://oexchange.org/spec/0.8/rel/user-target":[{"type":"application/xrd+xml", "href":"http://www.example.com/linkeater/oexchange.xrd"}]}'
31
+ @raw_link_with_id = '{"http://oexchange.org/spec/0.8/rel/user-target":[{"id":"foo","type":"application/xrd+xml", "href":"http://www.example.com/linkeater/oexchange.xrd"}]}'
32
+
33
+ @jrd = Discodactyl::JRD::Document.parse(@full_jrd_string)
34
+ end
35
+ def test_parse
36
+ doc = Discodactyl::JRD::Document.parse(@full_jrd_string)
37
+ assert_not_nil(doc)
38
+ end
39
+ def test_parse_links
40
+ assert_equal(4, @jrd.links.length)
41
+ end
42
+
43
+ # def test_linkelems_by_rel
44
+ # link_elems = @jrd.linkelems_by_rel 'describedby'
45
+ #
46
+ # assert_length(2, link_elems)
47
+ #
48
+ # assert_equal 'http://host.example/endpoint', link_elems[0]['href']
49
+ # assert_equal 'http://host.example/descriptor?q={%id}', link_elems[1]['template']
50
+ # end
51
+
52
+ # def test_linkelems_by_rel_with_multiple_rels
53
+ # link_elems = @jrd.linkelems_by_rel 'describedby'
54
+ #
55
+ # assert_length(2, link_elems)
56
+ #
57
+ # assert_equal 'http://host.example/endpoint', link_elems[0]['href']
58
+ # assert_equal 'http://host.example/descriptor?q={%id}', link_elems[1]['template']
59
+ # end
60
+
61
+ def test_uris_by_rel
62
+ uris = @jrd.uris_by_rel 'describedby', 'id' => 'bradfitz@gmail.com'
63
+
64
+ assert_length(2, uris)
65
+
66
+ assert_equal 'http://host.example/endpoint', uris[0]
67
+ assert_equal 'http://host.example/descriptor?q=bradfitz@gmail.com', uris[1]
68
+ end
69
+
70
+ def test_links_by_rel
71
+ links = @jrd.links_by_rel('feed')
72
+
73
+ assert_length(1, links)
74
+ expected = Discodactyl::URITemplate.new('http://host.example/descriptor?q={%id}')
75
+ assert_equal(expected, links[0].template)
76
+ end
77
+
78
+ def test_uris_by_rel
79
+ links = @jrd.uris_by_rel('feed', 'id' => 'dclinton@gmail.com')
80
+
81
+ assert_length(1, links)
82
+ assert_include?('http://host.example/descriptor?q=dclinton@gmail.com', links)
83
+ end
84
+
85
+ # def test_update
86
+ # jrd_str =<<eos
87
+ # <?xml version=\"1.0\"?>
88
+ # <XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
89
+ # <Subject>http://host.example/</Subject>
90
+ # <Link rel="webfinger" href="http://host.example/endpoint"/>
91
+ # <Link rel="describedby" href="http://host.example/endpoint"/>
92
+ # <Link rel="feed" template="http://host.example/descriptor?q={%id}"/>
93
+ # <Link rel="describedby" template="http://host.example/descriptor?q={%id}"/>
94
+ # <Link href="http://www.example.com/linkeater/oexchange.xrd" rel="http://oexchange.org/spec/0.8/rel/user-target" type="application/xrd+xml"/>
95
+ # </XRD>
96
+ # eos
97
+ # doc = Discodactyl::JRD::Document.parse jrd_str
98
+ #
99
+ # link = "<Link rel='http://oexchange.org/spec/0.8/rel/user-target' type='application/xrd+xml' href='updated' />"
100
+ #
101
+ # doc.raw.xpath('//xrd:Link',Discodactyl::XRD::XMLNS).after(link)
102
+ #
103
+ # expected =<<eos
104
+ # <?xml version=\"1.0\"?>
105
+ # <XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
106
+ # <Subject>http://host.example/</Subject>
107
+ # <Link rel="webfinger" href="http://host.example/endpoint"/>
108
+ # <Link rel="describedby" href="http://host.example/endpoint"/>
109
+ # <Link rel="feed" template="http://host.example/descriptor?q={%id}"/>
110
+ # <Link rel="describedby" template="http://host.example/descriptor?q={%id}"/>
111
+ # <Link href="updated" rel="http://oexchange.org/spec/0.8/rel/user-target" type="application/xrd+xml"/>
112
+ # </XRD>
113
+ # eos
114
+ #
115
+ # assert_equal expected, doc.to_s
116
+ # end
117
+
118
+ # def test_delete
119
+ # xrd_str =<<eos
120
+ # <?xml version=\"1.0\"?>
121
+ # <XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
122
+ # <Subject>http://host.example/</Subject>
123
+ # <Link xml:id='1' href="http://example.com/" rel="user-target" type="application/xrd+xml"/>
124
+ # </XRD>
125
+ # eos
126
+ # doc = Discodactyl::XRD::Document.parse xrd_str
127
+ #
128
+ # link = "<Link rel='http://oexchange.org/spec/0.8/rel/user-target' type='application/xrd+xml' href='updated' />"
129
+ #
130
+ # doc.raw.xpath('//xrd:Link',Discodactyl::XRD::XMLNS).after(link)
131
+ #
132
+ # expected =<<eos
133
+ # <?xml version=\"1.0\"?>
134
+ # <XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
135
+ # <Subject>http://host.example/</Subject>
136
+ # </XRD>
137
+ # eos
138
+ #
139
+ # assert_equal expected, doc.to_s
140
+ # assert !doc.include?(link)
141
+ # end
142
+ end
metadata CHANGED
@@ -1,12 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: discodactyl
3
3
  version: !ruby/object:Gem::Version
4
- prerelease: false
4
+ hash: 13
5
+ prerelease:
5
6
  segments:
6
7
  - 0
7
- - 3
8
- - 0
9
- version: 0.3.0
8
+ - 4
9
+ - 1
10
+ version: 0.4.1
10
11
  platform: ruby
11
12
  authors:
12
13
  - Joseph Anthony Pasquale Holsten
@@ -14,44 +15,50 @@ autorequire:
14
15
  bindir: bin
15
16
  cert_chain: []
16
17
 
17
- date: 2010-07-08 00:00:00 -05:00
18
+ date: 2011-03-14 00:00:00 -05:00
18
19
  default_executable:
19
20
  dependencies:
20
21
  - !ruby/object:Gem::Dependency
21
22
  name: nokogiri
22
23
  prerelease: false
23
24
  requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
24
26
  requirements:
25
27
  - - ~>
26
28
  - !ruby/object:Gem::Version
29
+ hash: 3
27
30
  segments:
28
31
  - 1
29
32
  - 4
30
- - 0
31
- version: 1.4.0
33
+ - 2
34
+ version: 1.4.2
32
35
  type: :runtime
33
36
  version_requirements: *id001
34
37
  - !ruby/object:Gem::Dependency
35
38
  name: actionpack
36
39
  prerelease: false
37
40
  requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
38
42
  requirements:
39
43
  - - ~>
40
44
  - !ruby/object:Gem::Version
45
+ hash: 7
41
46
  segments:
42
- - 2
43
47
  - 3
44
48
  - 0
45
- version: 2.3.0
49
+ - 0
50
+ version: 3.0.0
46
51
  type: :runtime
47
52
  version_requirements: *id002
48
53
  - !ruby/object:Gem::Dependency
49
54
  name: feedzirra
50
55
  prerelease: false
51
56
  requirement: &id003 !ruby/object:Gem::Requirement
57
+ none: false
52
58
  requirements:
53
59
  - - ~>
54
60
  - !ruby/object:Gem::Version
61
+ hash: 49
55
62
  segments:
56
63
  - 0
57
64
  - 0
@@ -63,16 +70,50 @@ dependencies:
63
70
  name: mofo
64
71
  prerelease: false
65
72
  requirement: &id004 !ruby/object:Gem::Requirement
73
+ none: false
66
74
  requirements:
67
75
  - - ~>
68
76
  - !ruby/object:Gem::Version
77
+ hash: 55
69
78
  segments:
70
79
  - 0
71
80
  - 2
72
- - 0
73
- version: 0.2.0
81
+ - 16
82
+ version: 0.2.16
74
83
  type: :runtime
75
84
  version_requirements: *id004
85
+ - !ruby/object:Gem::Dependency
86
+ name: rr
87
+ prerelease: false
88
+ requirement: &id005 !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ hash: 33
94
+ segments:
95
+ - 0
96
+ - 10
97
+ - 11
98
+ version: 0.10.11
99
+ type: :development
100
+ version_requirements: *id005
101
+ - !ruby/object:Gem::Dependency
102
+ name: rake
103
+ prerelease: false
104
+ requirement: &id006 !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ~>
108
+ - !ruby/object:Gem::Version
109
+ hash: 49
110
+ segments:
111
+ - 0
112
+ - 8
113
+ - 7
114
+ version: 0.8.7
115
+ type: :development
116
+ version_requirements: *id006
76
117
  description: "Discodactyl is an experimental toolkit for XRD service discovery documents and related protocols. It includes implementations of XRD URITemplate Link-Patterns, basic site-meta support, HTTP Link header parsing, acct: URIs and a webfinger poking stick."
77
118
  email: joseph@josephholsten.com
78
119
  executables:
@@ -85,7 +126,7 @@ extra_rdoc_files:
85
126
  - COPYING
86
127
  - INSTALL
87
128
  - NEWS
88
- - README
129
+ - README.rdoc
89
130
  - TODO
90
131
  files:
91
132
  - AUTHORS
@@ -95,7 +136,7 @@ files:
95
136
  - INSTALL
96
137
  - MANIFEST
97
138
  - NEWS
98
- - README
139
+ - README.rdoc
99
140
  - Rakefile
100
141
  - TODO
101
142
  - bin/webfinger
@@ -103,7 +144,13 @@ files:
103
144
  - lib/discodactyl.rb
104
145
  - lib/discodactyl/acct_uri.rb
105
146
  - lib/discodactyl/host_meta.rb
147
+ - lib/discodactyl/host_meta_jrd.rb
148
+ - lib/discodactyl/jrd.rb
149
+ - lib/discodactyl/jrd/document.rb
150
+ - lib/discodactyl/jrd/link.rb
151
+ - lib/discodactyl/known_rels.rb
106
152
  - lib/discodactyl/link_header.rb
153
+ - lib/discodactyl/log.rb
107
154
  - lib/discodactyl/resource_discovery.rb
108
155
  - lib/discodactyl/uri_template.rb
109
156
  - lib/discodactyl/xrd.rb
@@ -112,6 +159,8 @@ files:
112
159
  - test/test_acct_uri.rb
113
160
  - test/test_helper.rb
114
161
  - test/test_host_meta.rb
162
+ - test/test_host_meta_jrd.rb
163
+ - test/test_jrd_document.rb
115
164
  - test/test_link_header.rb
116
165
  - test/test_resource_discovery.rb
117
166
  - test/test_uri_template.rb
@@ -128,27 +177,31 @@ rdoc_options:
128
177
  - --title
129
178
  - Discodactyl Documentation
130
179
  - --main
131
- - README
180
+ - README.rdoc
132
181
  require_paths:
133
182
  - lib
134
183
  required_ruby_version: !ruby/object:Gem::Requirement
184
+ none: false
135
185
  requirements:
136
186
  - - ">="
137
187
  - !ruby/object:Gem::Version
188
+ hash: 3
138
189
  segments:
139
190
  - 0
140
191
  version: "0"
141
192
  required_rubygems_version: !ruby/object:Gem::Requirement
193
+ none: false
142
194
  requirements:
143
195
  - - ">="
144
196
  - !ruby/object:Gem::Version
197
+ hash: 3
145
198
  segments:
146
199
  - 0
147
200
  version: "0"
148
201
  requirements: []
149
202
 
150
203
  rubyforge_project:
151
- rubygems_version: 1.3.6
204
+ rubygems_version: 1.5.0
152
205
  signing_key:
153
206
  specification_version: 3
154
207
  summary: Discodactyl is an experimental toolkit for XRD service discovery documents and related protocols
@@ -156,6 +209,8 @@ test_files:
156
209
  - test/test_acct_uri.rb
157
210
  - test/test_helper.rb
158
211
  - test/test_host_meta.rb
212
+ - test/test_host_meta_jrd.rb
213
+ - test/test_jrd_document.rb
159
214
  - test/test_link_header.rb
160
215
  - test/test_resource_discovery.rb
161
216
  - test/test_uri_template.rb