Class: Rack::SPARQL::ContentNegotiation

Inherits:
Object
  • Object
show all
Defined in:
vendor/bundler/ruby/2.5.0/bundler/gems/sparql-bb59900f675b/lib/rack/sparql/conneg.rb

Overview

Rack middleware for SPARQL content negotiation.

Uses HTTP Content Negotiation to find an appropriate RDF format to serialize any result with a body being RDF::Enumerable.

Override content negotiation by setting the :format option to #initialize.

This endpoint also serves the fuction of Rack::LinkedData, as it will serialize SPARQL results, which may be RDF Graphs

Constant Summary

VARY =
{'Vary' => 'Accept'}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(app, options = {}) ⇒ ContentNegotiation

Returns a new instance of ContentNegotiation

Parameters:

  • app (#call)
  • options (Hash{Symbol => Object}) (defaults to: {})

    Other options passed to writer.

Options Hash (options):

  • :format (RDF::Format, #to_sym)

    Specific RDF writer format to use



32
33
34
# File 'vendor/bundler/ruby/2.5.0/bundler/gems/sparql-bb59900f675b/lib/rack/sparql/conneg.rb', line 32

def initialize(app, options = {})
  @app, @options = app, options
end

Instance Attribute Details

#app#call (readonly)

Returns:



21
22
23
# File 'vendor/bundler/ruby/2.5.0/bundler/gems/sparql-bb59900f675b/lib/rack/sparql/conneg.rb', line 21

def app
  @app
end

#optionsHash{Symbol => Object} (readonly)

Returns:



25
26
27
# File 'vendor/bundler/ruby/2.5.0/bundler/gems/sparql-bb59900f675b/lib/rack/sparql/conneg.rb', line 25

def options
  @options
end

Instance Method Details

#accept_entry(entry) ⇒ Object (protected)



98
99
100
101
102
103
# File 'vendor/bundler/ruby/2.5.0/bundler/gems/sparql-bb59900f675b/lib/rack/sparql/conneg.rb', line 98

def accept_entry(entry)
  type, *options = entry.delete(' ').split(';')
  quality = 0 # we sort smallest first
  options.delete_if { |e| quality = 1 - e[2..-1].to_f if e.start_with? 'q=' }
  [type, [quality, type.count('*'), 1 - options.size]]
end

#call(env) ⇒ Array(Integer, Hash, #each)

Handles a Rack protocol request. Parses Accept header to find appropriate mime-type and sets content_type accordingly.

If result is RDF::Literal::Boolean, RDF::Query::Results, or RDF::Enumerable The result is serialized using SPARQL::Results

Inserts ordered content types into the environment as ORDERED_CONTENT_TYPES if an Accept header is present



47
48
49
50
51
52
53
54
55
56
57
# File 'vendor/bundler/ruby/2.5.0/bundler/gems/sparql-bb59900f675b/lib/rack/sparql/conneg.rb', line 47

def call(env)
  env['ORDERED_CONTENT_TYPES'] = parse_accept_header(env['HTTP_ACCEPT']) if env.has_key?('HTTP_ACCEPT')
  response = app.call(env)
  body = response[2].respond_to?(:body) ? response[2].body : response[2]
  case body
  when RDF::Enumerable, RDF::Query::Solutions, RDF::Literal::Boolean
    response[2] = body  # Put it back in the response, it might have been a proxy
    serialize(env, *response)
  else response
  end
end

#http_error(code, message = nil, headers = {}) ⇒ Array(Integer, Hash, #each) (protected)

Outputs an HTTP 4xx or 5xx response.

Parameters:

Returns:



121
122
123
124
# File 'vendor/bundler/ruby/2.5.0/bundler/gems/sparql-bb59900f675b/lib/rack/sparql/conneg.rb', line 121

def http_error(code, message = nil, headers = {})
  message = http_status(code) + (message.nil? ? "\n" : " (#{message})\n")
  [code, {'Content-Type' => 'text/plain; charset=utf-8'}.merge(headers), [message]]
end

#http_status(code) ⇒ String (protected)

Returns the standard HTTP status message for the given status code.

Parameters:

Returns:



131
132
133
# File 'vendor/bundler/ruby/2.5.0/bundler/gems/sparql-bb59900f675b/lib/rack/sparql/conneg.rb', line 131

def http_status(code)
  [code, Rack::Utils::HTTP_STATUS_CODES[code]].join(' ')
end

#not_acceptable(message = nil) ⇒ Array(Integer, Hash, #each) (protected)

Outputs an HTTP 406 Not Acceptable response.

Parameters:

  • message (String, #to_s) (defaults to: nil)

Returns:



110
111
112
# File 'vendor/bundler/ruby/2.5.0/bundler/gems/sparql-bb59900f675b/lib/rack/sparql/conneg.rb', line 110

def not_acceptable(message = nil)
  http_error(406, message, VARY)
end

#parse_accept_header(header) ⇒ Array<String> (protected)

Parses an HTTP Accept header, returning an array of MIME content types ordered by the precedence rules defined in HTTP/1.1 Section 14.1.

Parameters:

Returns:

See Also:



93
94
95
96
# File 'vendor/bundler/ruby/2.5.0/bundler/gems/sparql-bb59900f675b/lib/rack/sparql/conneg.rb', line 93

def parse_accept_header(header)
  entries = header.to_s.split(',')
  entries.map { |e| accept_entry(e) }.sort_by(&:last).map(&:first)
end

#serialize(env, status, headers, body) ⇒ Array(Integer, Hash, #each)

Serializes a SPARQL query result into a Rack protocol response using HTTP content negotiation rules or a specified Content-Type.

Parameters:

Returns:

Raises:



69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'vendor/bundler/ruby/2.5.0/bundler/gems/sparql-bb59900f675b/lib/rack/sparql/conneg.rb', line 69

def serialize(env, status, headers, body)
  begin
    serialize_options = {}
    serialize_options[:content_types] = env['ORDERED_CONTENT_TYPES'] if env['ORDERED_CONTENT_TYPES']
    serialize_options.merge!(@options)
    results = ::SPARQL.serialize_results(body, serialize_options)
    raise RDF::WriterError, "can't serialize results" unless results
    headers = headers.merge(VARY).merge('Content-Type' => results.content_type) # FIXME: don't overwrite existing Vary headers
    [status, headers, [results]]
  rescue RDF::WriterError => e
    # Use this instead of not_acceptable so that headers are not lost.
    http_error(406, e.message, headers.merge(VARY))
  end
end