class DiasporaFederation::Discovery::XrdDocument

This class implements basic handling of XRD documents as far as it is necessary in the context of the protocols used with diaspora* federation.

It also implements handling of the JRD format, see {datatracker.ietf.org/doc/html/rfc6415#appendix-A RFC 6415, Appendix A} for a description of the JSON format.

@example Creating a XrdDocument

doc = XrdDocument.new
doc.expires = DateTime.new(2020, 1, 15, 0, 0, 1)
doc.subject = "http://example.tld/articles/11"
doc.aliases << "http://example.tld/cool_article"
doc.aliases << "http://example.tld/authors/2/articles/3"
doc.properties["http://x.example.tld/ns/version"] = "1.3"
doc.links << { rel: "author", type: "text/html", href: "http://example.tld/authors/2" }
doc.links << { rel: "copyright", template: "http://example.tld/copyright?id={uri}" }

doc.to_xml

@example Parsing a XrdDocument

data = XrdDocument.xml_data(xml_string)

@see docs.oasis-open.org/xri/xrd/v1.0/xrd-1.0.html Extensible Resource Descriptor (XRD) Version 1.0

Constants

DATETIME_FORMAT

format string for datetime (Expires element)

Link element attributes

NS
XMLNS

xml namespace url

Attributes

aliases[R]

@return [Array<String>] list of alias URIs

expires[RW]

The <Expires> element contains a time value which specifies the instant at and after which the document has expired and SHOULD NOT be used. @param [DateTime] value

properties[R]

@return [Hash<String => mixed>] list of properties. Hash key represents the

+type+ attribute, and the value is the element content
subject[RW]

The <Subject> element contains a URI value which identifies the resource described by this XRD. @param [String] value

Public Class Methods

json_data(jrd_doc) click to toggle source

Parse the JRD document from the given string and create a hash containing the extracted data with symbolized keys.

@param [String] jrd_doc JSON string @return [Hash] extracted data @raise [InvalidDocument] if the JRD is malformed

# File lib/diaspora_federation/discovery/xrd_document.rb, line 122
def self.json_data(jrd_doc)
  json_hash = JSON.parse(jrd_doc)

  {
    subject:    json_hash["subject"],
    expires:    (DateTime.strptime(json_hash["expires"], DATETIME_FORMAT) if json_hash.key?("expires")),
    aliases:    json_hash["aliases"],
    properties: json_hash["properties"],
    links:      symbolize_keys_for_links(json_hash["links"])
  }.compact
rescue JSON::JSONError => e
  raise InvalidDocument,
        "Not a JRD document: #{e.class}: #{e.message[0..255].encode(Encoding.default_external, undef: :replace)}"
end
new() click to toggle source
# File lib/diaspora_federation/discovery/xrd_document.rb, line 59
def initialize
  @aliases = []
  @links = []
  @properties = {}
end
xml_data(xrd_doc) click to toggle source

Parse the XRD document from the given string and create a hash containing the extracted data.

Small bonus: the hash structure that comes out of this method is the same as the one used to produce a JRD (JSON Resource Descriptor) or parsing it.

@param [String] xrd_doc XML string @return [Hash] extracted data @raise [InvalidDocument] if the XRD is malformed

# File lib/diaspora_federation/discovery/xrd_document.rb, line 100
def self.xml_data(xrd_doc)
  doc = parse_xrd_document(xrd_doc)

  {}.tap do |data|
    exp_elem = doc.at_xpath("xrd:XRD/xrd:Expires", NS)
    data[:expires] = DateTime.strptime(exp_elem.content, DATETIME_FORMAT) unless exp_elem.nil?

    subj_elem = doc.at_xpath("xrd:XRD/xrd:Subject", NS)
    data[:subject] = subj_elem.content unless subj_elem.nil?

    parse_aliases_from_xml_doc(doc, data)
    parse_properties_from_xml_doc(doc, data)
    parse_links_from_xml_doc(doc, data)
  end
end

Private Class Methods

parse_aliases_from_xml_doc(doc, data) click to toggle source
# File lib/diaspora_federation/discovery/xrd_document.rb, line 176
                     def self.parse_aliases_from_xml_doc(doc, data)
  aliases = []
  doc.xpath("xrd:XRD/xrd:Alias", NS).each do |node|
    aliases << node.content
  end
  data[:aliases] = aliases unless aliases.empty?
end
parse_properties_from_xml_doc(doc, data) click to toggle source
# File lib/diaspora_federation/discovery/xrd_document.rb, line 184
                     def self.parse_properties_from_xml_doc(doc, data)
  properties = {}
  doc.xpath("xrd:XRD/xrd:Property", NS).each do |node|
    properties[node[:type]] = node.children.empty? ? nil : node.content
  end
  data[:properties] = properties unless properties.empty?
end
parse_xrd_document(xrd_doc) click to toggle source
# File lib/diaspora_federation/discovery/xrd_document.rb, line 167
                     def self.parse_xrd_document(xrd_doc)
  raise ArgumentError unless xrd_doc.instance_of?(String)

  doc = Nokogiri::XML(xrd_doc)
  raise InvalidDocument, "Not an XRD document" if !doc.root || doc.root.name != "XRD"

  doc
end

Public Instance Methods

to_json(*_args) click to toggle source
# File lib/diaspora_federation/discovery/xrd_document.rb, line 81
def to_json(*_args)
  {
    subject:    subject,
    expires:    (expires.strftime(DATETIME_FORMAT) if expires.instance_of?(DateTime)),
    aliases:    (aliases if aliases.any?),
    properties: (properties if properties.any?),
    links:      (links if links.any?)
  }.compact
end
to_xml() click to toggle source

Generates an XML document from the current instance and returns it as string @return [String] XML document

# File lib/diaspora_federation/discovery/xrd_document.rb, line 67
def to_xml
  Nokogiri::XML::Builder.new(encoding: "UTF-8") {|xml|
    xml.XRD("xmlns" => XMLNS) {
      xml.Expires(expires.strftime(DATETIME_FORMAT)) if expires.instance_of?(DateTime)

      xml.Subject(subject) if !subject.nil? && !subject.empty?

      add_aliases_to(xml)
      add_properties_to(xml)
      add_links_to(xml)
    }
  }.to_xml
end

Private Instance Methods

add_aliases_to(xml) click to toggle source
# File lib/diaspora_federation/discovery/xrd_document.rb, line 143
def add_aliases_to(xml)
  aliases.each do |a|
    next if !a.instance_of?(String) || a.empty?

    xml.Alias(a.to_s)
  end
end
add_properties_to(xml) click to toggle source
# File lib/diaspora_federation/discovery/xrd_document.rb, line 151
def add_properties_to(xml)
  properties.each do |type, val|
    xml.Property(val.to_s, type: type)
  end
end