module Krikri::LDP::Resource

Implements basic LDP CRUD operations. Requires an implementation of `#rdf_subject` returning an `RDF::URI`. The resource idenitified by the URI must conform to LDP Resource's interaction patterns.

@example implementing a resource

class MyResource
  include Krikri::LDP::Resource

  def rdf_subject
    @rdf_subject ||= RDF::URI('http://example.com/ldp/a/resource/path')
  end
end

@note Ideally, this is a general purpose LDP resource. However some HTTP

PUT creation behavior may be specific to Marmotta. This avoids the need to
interact directly with the owning container, but is a less generalized 
implementation.

@see www.w3.org/TR/ldp/#h-ldpr-resource LDP Resource interaction

patterns.

@see wiki.apache.org/marmotta/LDPImplementationReport for

information about Marmotta's PUT.

Public Instance Methods

delete!(headers = {}) click to toggle source

Sends DELETE request to the resource's rdf_subject via ldp_connection. Headers can be passed in. Default HTTP headers are:

If-Match: "#{etag}" (uses idempotent put if an Entity Tag is cached)
# File lib/krikri/ldp/resource.rb, line 132
def delete!(headers = {})
  raise "Cannot delete #{rdf_subject}, does not exist." unless exist?
  headers['If-Match'] ||= etag
  response = make_request(:delete, nil, headers)
  @http_headers = nil
  response
end
etag() click to toggle source

@return [String] the current cached HTTP ETag for the resource

# File lib/krikri/ldp/resource.rb, line 50
def etag
  http_head['etag'] if exists?
end
exist?()
Alias for: exists?
exists?() click to toggle source

@return [Boolean] true if the LDP server already knows about the resource,

otherwise false.
# File lib/krikri/ldp/resource.rb, line 94
def exists?
  return true if http_head
  false
rescue Faraday::ResourceNotFound
  false
rescue Faraday::ClientError => e
  return false if !e.response.nil? && e.response[:status] == 410
  raise e
end
Also aliased as: exist?
get(headers = {}, force = false) click to toggle source

Sends a GET request to rdf_subject and caches the headers and body. Executes lazily unless `force` parameter is `true`, using cached values if present.

@param headers [Hash<String, String>] a hash of HTTP headers;

e.g. {'Content-Type' => 'text/plain'}.

@param force [Boolean] force request if true

@raise (see make_request) @return [Faraday::Response] the server's response

# File lib/krikri/ldp/resource.rb, line 84
def get(headers = {}, force = false)
  return @http_cache if @http_cache && !force
  response = make_request(:get, nil, headers)
  @http_headers = response.env['response_headers']
  @http_cache = response
end
http_head(force = false) click to toggle source

Sends a HEAD request to rdf_subject and caches the headers. Executes lazily unless `force` parameter is `true`, using cached values if present.

@param force [Boolean] force request if true

@raise (see make_request) @return [Hash<String, String>] a hash of HTTP headers

# File lib/krikri/ldp/resource.rb, line 68
def http_head(force = false)
  return @http_headers if @http_headers && !force
  @http_headers = make_request(:head).env['response_headers']
end
ldp_connection() click to toggle source

@return [Faraday::Connection] a connection to the configured LDP endpoint

# File lib/krikri/ldp/resource.rb, line 33
def ldp_connection
  @ldp_conn ||= Faraday.new(ldp_ns) do |conn|
    conn.request :retry, max: 4, interval: 0.025,
                 interval_randomness: 0.5, backoff_factor: 2,
                 exceptions: [Faraday::ConnectionFailed,
                              'Errno::ETIMEDOUT',
                              'Timeout::Error',
                              'Error::TimeoutError',
                              Faraday::TimeoutError]
    conn.use Faraday::Response::RaiseError
    conn.use FaradayMiddleware::FollowRedirects, limit: 3
    conn.adapter Faraday.default_adapter
  end
end
modified_date() click to toggle source

@return [String] the current cached Last-Modified date for the resource

# File lib/krikri/ldp/resource.rb, line 56
def modified_date
  http_head['last-modified'] if exists?
end
save(body = nil, headers = {}) click to toggle source

Sends PUT request to the resource's rdf_subject via ldp_connection. A body and headers can be passed in. Default HTTP headers are:

Content-Type: 'text/turtle' (i.e. creates an LDP-RS)
If-Match: "#{etag}" (uses idempotent put if an Entity Tag is cached)

@param body [#to_s] the request body. @param headers [Hash<String, String>] a hash of HTTP headers;

e.g. {'Content-Type' => 'text/plain'}.

@raise (see make_request) @return [Faraday::Response] the server's response

# File lib/krikri/ldp/resource.rb, line 118
def save(body = nil, headers = {})
  headers['Content-Type'] ||= default_content_type
  headers['If-Match'] ||= etag if exists?
  response = make_request(:put, body, headers)
  @http_headers = response.env['response_headers']
  response
end

Private Instance Methods

default_content_type() click to toggle source
# File lib/krikri/ldp/resource.rb, line 163
def default_content_type
  'text/turtle'
end
ldp_ns() click to toggle source
# File lib/krikri/ldp/resource.rb, line 169
def ldp_ns
  Krikri::Settings['marmotta']['ldp']
end
make_request(method, body = nil, headers = {}) click to toggle source

Lightly wraps Faraday to manage requests of various types, their bodies and headers.

@param method [Symbol] HTTP method/verb. @param body [#to_s] the request body. @param headers [Hash<String, String>] a hash of HTTP headers;

e.g. {'Content-Type' => 'text/plain'}.

@raise [Faraday::ClientError] if the server responds with an error status.

Faraday::ClientError#response contains the full response.

@return [Faraday::Response] the server's response

# File lib/krikri/ldp/resource.rb, line 154
def make_request(method, body = nil, headers = {})
  validate_subject
  ldp_connection.send(method) do |request|
    request.url rdf_subject
    request.headers = headers if headers
    request.body = body
  end
end
validate_namespace() click to toggle source
# File lib/krikri/ldp/resource.rb, line 190
def validate_namespace
  raise "#{self.class} requires an rdf_subject in #{ldp_ns}, but "\
  "got #{rdf_subject}." unless rdf_subject.to_s.starts_with? ldp_ns
end
validate_not_nil() click to toggle source
# File lib/krikri/ldp/resource.rb, line 180
def validate_not_nil
  raise "#{self.class} requires a URI rdf_subject, but got nil." if
    rdf_subject.nil?
end
validate_not_node() click to toggle source
# File lib/krikri/ldp/resource.rb, line 185
def validate_not_node
  raise "#{self.class} requires a URI rdf_subject, but got a node." if
    rdf_subject.respond_to?(:node?) && rdf_subject.node?
end
validate_subject() click to toggle source
# File lib/krikri/ldp/resource.rb, line 173
def validate_subject
  validate_not_nil
  validate_not_node
  validate_namespace
  true
end