roadforest 0.0.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/examples/file-management.rb +98 -0
- data/lib/roadforest/application/dispatcher.rb +54 -0
- data/lib/roadforest/application/parameters.rb +39 -0
- data/lib/roadforest/application/path-provider.rb +18 -0
- data/lib/roadforest/application/route-adapter.rb +24 -0
- data/lib/roadforest/application/services-host.rb +10 -0
- data/lib/roadforest/application.rb +42 -0
- data/lib/roadforest/blob-model.rb +56 -0
- data/lib/roadforest/content-handling/engine.rb +113 -0
- data/lib/roadforest/content-handling/media-type.rb +222 -0
- data/lib/roadforest/content-handling/type-handlers/jsonld.rb +172 -0
- data/lib/roadforest/http/adapters/excon.rb +47 -0
- data/lib/roadforest/http/graph-response.rb +20 -0
- data/lib/roadforest/http/graph-transfer.rb +112 -0
- data/lib/roadforest/http/message.rb +91 -0
- data/lib/roadforest/model.rb +151 -0
- data/lib/roadforest/models.rb +2 -0
- data/lib/roadforest/rdf/context-fascade.rb +25 -0
- data/lib/roadforest/rdf/document.rb +23 -0
- data/lib/roadforest/rdf/focus-list.rb +19 -0
- data/lib/roadforest/rdf/focus-wrapping.rb +30 -0
- data/lib/roadforest/rdf/graph-copier.rb +16 -0
- data/lib/roadforest/rdf/graph-focus.rb +95 -0
- data/lib/roadforest/rdf/graph-reading.rb +145 -0
- data/lib/roadforest/rdf/graph-store.rb +217 -0
- data/lib/roadforest/rdf/investigation.rb +90 -0
- data/lib/roadforest/rdf/normalization.rb +150 -0
- data/lib/roadforest/rdf/parcel.rb +47 -0
- data/lib/roadforest/rdf/post-focus.rb +35 -0
- data/lib/roadforest/rdf/resource-pattern.rb +60 -0
- data/lib/roadforest/rdf/resource-query.rb +58 -0
- data/lib/roadforest/rdf/source-rigor/credence/any.rb +9 -0
- data/lib/roadforest/rdf/source-rigor/credence/none-if-role-absent.rb +19 -0
- data/lib/roadforest/rdf/source-rigor/credence/role-if-available.rb +19 -0
- data/lib/roadforest/rdf/source-rigor/credence-annealer.rb +22 -0
- data/lib/roadforest/rdf/source-rigor/credence.rb +29 -0
- data/lib/roadforest/rdf/source-rigor/http-investigator.rb +20 -0
- data/lib/roadforest/rdf/source-rigor/investigator.rb +17 -0
- data/lib/roadforest/rdf/source-rigor/null-investigator.rb +10 -0
- data/lib/roadforest/rdf/source-rigor.rb +44 -0
- data/lib/roadforest/rdf/update-focus.rb +73 -0
- data/lib/roadforest/rdf/vocabulary.rb +11 -0
- data/lib/roadforest/rdf.rb +6 -0
- data/lib/roadforest/remote-host.rb +96 -0
- data/lib/roadforest/resource/handlers.rb +43 -0
- data/lib/roadforest/resource/http/form-parsing.rb +81 -0
- data/lib/roadforest/resource/rdf/leaf-item.rb +21 -0
- data/lib/roadforest/resource/rdf/list.rb +19 -0
- data/lib/roadforest/resource/rdf/parent-item.rb +26 -0
- data/lib/roadforest/resource/rdf/read-only.rb +100 -0
- data/lib/roadforest/resource/rdf.rb +4 -0
- data/lib/roadforest/resource/role/has-children.rb +22 -0
- data/lib/roadforest/resource/role/writable.rb +43 -0
- data/lib/roadforest/server.rb +3 -0
- data/lib/roadforest/test-support/dispatcher-facade.rb +77 -0
- data/lib/roadforest/test-support/http-client.rb +151 -0
- data/lib/roadforest/test-support/matchers.rb +67 -0
- data/lib/roadforest/test-support/remote-host.rb +23 -0
- data/lib/roadforest/test-support/trace-formatter.rb +140 -0
- data/lib/roadforest/test-support.rb +2 -0
- data/lib/roadforest/utility/class-registry.rb +49 -0
- data/lib/roadforest.rb +2 -0
- data/spec/client.rb +152 -0
- data/spec/credence-annealer.rb +44 -0
- data/spec/graph-copier.rb +87 -0
- data/spec/graph-store.rb +142 -0
- data/spec/media-types.rb +14 -0
- data/spec/rdf-parcel.rb +158 -0
- data/spec/update-focus.rb +117 -0
- data/spec_support/gem_test_suite.rb +0 -0
- metadata +241 -0
@@ -0,0 +1,81 @@
|
|
1
|
+
=begin
|
2
|
+
I think this code might be useful soon
|
3
|
+
module RoadForest
|
4
|
+
module ResourceMixin
|
5
|
+
module FormParsing
|
6
|
+
def self.content_types_accepted
|
7
|
+
[
|
8
|
+
["application/x-www-form-urlencoded", :handle_url_encoded_form],
|
9
|
+
["multipart/form-data", :handle_multipart_form]
|
10
|
+
]
|
11
|
+
end
|
12
|
+
|
13
|
+
def process_post
|
14
|
+
content_type = Webmachine::MediaType.parse(request.content_type)
|
15
|
+
if content_type.match?("application/x-www-form-urlencoded")
|
16
|
+
handle_url_encoded_form
|
17
|
+
elsif content_type.match?("multipart/form-data")
|
18
|
+
handle_multipart_form
|
19
|
+
else
|
20
|
+
false
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def parse_url_encoded_form
|
25
|
+
Hash[URI::decode_www_form(request.body)]
|
26
|
+
end
|
27
|
+
|
28
|
+
def parse_multipart_form
|
29
|
+
form_data = {}
|
30
|
+
reader = multipart_reader(request.content_type, form_data)
|
31
|
+
|
32
|
+
request.body.each do |chunk|
|
33
|
+
reader.write(chunk)
|
34
|
+
end
|
35
|
+
|
36
|
+
return form_data
|
37
|
+
end
|
38
|
+
|
39
|
+
def multipart_reader(content_type, form_data)
|
40
|
+
content_type = Webmachine::MediaType.parse(content_type)
|
41
|
+
reader = MultipartParser::Reader.new(content_type.params["boundary"])
|
42
|
+
reader.on_error do |message|
|
43
|
+
raise message
|
44
|
+
end
|
45
|
+
|
46
|
+
reader.on_part do |part|
|
47
|
+
case part.mime
|
48
|
+
when nil
|
49
|
+
form_data[part.name] = ""
|
50
|
+
part.on_data do |data|
|
51
|
+
form_data[part.name] << data
|
52
|
+
end
|
53
|
+
else
|
54
|
+
temp_file = Tempfile.new(part.filename)
|
55
|
+
|
56
|
+
form_data[part.name] = [part, temp_file]
|
57
|
+
|
58
|
+
part.on_data do |data|
|
59
|
+
temp_file.write(data)
|
60
|
+
end
|
61
|
+
|
62
|
+
part.on_end do
|
63
|
+
temp_file.close
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
return reader
|
69
|
+
end
|
70
|
+
|
71
|
+
def handle_url_encoded_form
|
72
|
+
handle_form(parse_url_encoded_form)
|
73
|
+
end
|
74
|
+
|
75
|
+
def handle_multipart_form
|
76
|
+
handle_form(parse_multipart_form)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
=end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'roadforest/resource/rdf/read-only'
|
2
|
+
require 'roadforest/resource/role/writable'
|
3
|
+
|
4
|
+
module RoadForest
|
5
|
+
module Resource
|
6
|
+
module RDF
|
7
|
+
#Used for a simple resource that has properties that can be updated, but
|
8
|
+
#doesn't have any children - for example a comment
|
9
|
+
class LeafItem < ReadOnly
|
10
|
+
register :leaf
|
11
|
+
|
12
|
+
include Role::Writable
|
13
|
+
|
14
|
+
def allowed_methods
|
15
|
+
super + Role::Writable.allowed_methods
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'roadforest/resource/rdf/read-only'
|
2
|
+
require 'roadforest/resource/role/has-children'
|
3
|
+
module RoadForest
|
4
|
+
module Resource
|
5
|
+
module RDF
|
6
|
+
#Used for a resource that simply represents a list of other resources
|
7
|
+
#without having any properties itself - a list of posts, for instance
|
8
|
+
class List < ReadOnly
|
9
|
+
register :list
|
10
|
+
|
11
|
+
include Role::HasChildren
|
12
|
+
|
13
|
+
def allowed_methods
|
14
|
+
super + Role::HasChildren.allowed_methods
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'roadforest/resource/rdf/read-only'
|
2
|
+
require 'roadforest/resource/role/writable'
|
3
|
+
require 'roadforest/resource/role/has-children'
|
4
|
+
module RoadForest
|
5
|
+
module Resource
|
6
|
+
module RDF
|
7
|
+
#Used for a resource that has properties itself that can be updated, and
|
8
|
+
#also has one or more kinds of dependent or subordinate resources - can new
|
9
|
+
#resources be created by posting to this one? e.g. a post, which can have
|
10
|
+
#comments
|
11
|
+
class ParentItem < ReadOnly
|
12
|
+
register :parent
|
13
|
+
|
14
|
+
include Role::Writable
|
15
|
+
include Role::HasChildren
|
16
|
+
|
17
|
+
def allowed_methods
|
18
|
+
(super + [Role::Writable, Role::HasChildren].inject([]) do |list, mod|
|
19
|
+
list + mod.allowed_methods
|
20
|
+
end).uniq
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'roadforest/resource/handlers'
|
2
|
+
require 'roadforest/application/parameters'
|
3
|
+
|
4
|
+
module RoadForest
|
5
|
+
module Resource
|
6
|
+
module RDF
|
7
|
+
#Used for a resource that presents a read-only representation
|
8
|
+
class ReadOnly < Webmachine::Resource
|
9
|
+
def self.register(method_name)
|
10
|
+
Handlers.register(method_name, self)
|
11
|
+
end
|
12
|
+
|
13
|
+
register :read_only
|
14
|
+
|
15
|
+
attr_accessor :model, :trace
|
16
|
+
|
17
|
+
### RoadForest interface
|
18
|
+
|
19
|
+
def params
|
20
|
+
params = Application::Parameters.new do |params|
|
21
|
+
params.path_info = @request.path_info
|
22
|
+
params.query_params = @request.query_params
|
23
|
+
params.path_tokens = @request.path_tokens
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def request_accept_header
|
28
|
+
request.headers["Accept"] || "*/*"
|
29
|
+
end
|
30
|
+
|
31
|
+
def response_content_type=(type)
|
32
|
+
response.headers["Content-Type"] = type
|
33
|
+
end
|
34
|
+
|
35
|
+
def response_body=(body)
|
36
|
+
response.body = body
|
37
|
+
end
|
38
|
+
|
39
|
+
def retrieve_model
|
40
|
+
absolutize(@model.canonical_host, @model.retrieve)
|
41
|
+
end
|
42
|
+
alias retreive_model retrieve_model
|
43
|
+
|
44
|
+
# def known_methods
|
45
|
+
# super + ["PATCH"]
|
46
|
+
# end
|
47
|
+
|
48
|
+
def model_supports(action)
|
49
|
+
@model.respond_to?(action)
|
50
|
+
end
|
51
|
+
### Webmachine interface
|
52
|
+
|
53
|
+
def trace?
|
54
|
+
!!@trace
|
55
|
+
end
|
56
|
+
|
57
|
+
#Overridden rather than metaprogram content type methods
|
58
|
+
def send(*args)
|
59
|
+
if args.length == 1 and not model.nil?
|
60
|
+
model.type_handling.fetch(args.first).call(self)
|
61
|
+
else
|
62
|
+
super
|
63
|
+
end
|
64
|
+
rescue KeyError
|
65
|
+
super
|
66
|
+
end
|
67
|
+
|
68
|
+
def method(name)
|
69
|
+
if model.nil?
|
70
|
+
super
|
71
|
+
else
|
72
|
+
model.type_handling.fetch(name).method(:call)
|
73
|
+
end
|
74
|
+
rescue KeyError
|
75
|
+
super
|
76
|
+
end
|
77
|
+
|
78
|
+
def content_types_provided
|
79
|
+
model.type_handling.renderers.type_map
|
80
|
+
end
|
81
|
+
|
82
|
+
def resource_exists?
|
83
|
+
@model.exists?
|
84
|
+
end
|
85
|
+
|
86
|
+
def generate_etag
|
87
|
+
@model.etag
|
88
|
+
end
|
89
|
+
|
90
|
+
def last_modified
|
91
|
+
@model.last_modified
|
92
|
+
end
|
93
|
+
|
94
|
+
def expires
|
95
|
+
@model.expires
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module RoadForest
|
2
|
+
module Resource
|
3
|
+
module Role
|
4
|
+
module HasChildren
|
5
|
+
def self.allowed_methods
|
6
|
+
%w[POST]
|
7
|
+
end
|
8
|
+
|
9
|
+
def post_is_create
|
10
|
+
false
|
11
|
+
end
|
12
|
+
|
13
|
+
def process_post
|
14
|
+
parser = model.type_handling.choose_parser(request.content_type || 'application/octet-stream')
|
15
|
+
|
16
|
+
parser.add_child(self)
|
17
|
+
return true
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'roadforest/resource/role/has-children'
|
2
|
+
|
3
|
+
module RoadForest
|
4
|
+
module Resource
|
5
|
+
module Role
|
6
|
+
module Writable
|
7
|
+
class IncludeOrder < StandardError; end
|
8
|
+
|
9
|
+
def self.allowed_methods
|
10
|
+
%w[POST PUT DELETE]
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.included(mod)
|
14
|
+
if mod.ancestors.include?(HasChildren)
|
15
|
+
#Might regret this later - some kind of "I promise to fix it?"
|
16
|
+
raise IncludeOrder, "Writable has to be included before HasChildren"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def post_is_create
|
21
|
+
true
|
22
|
+
end
|
23
|
+
|
24
|
+
def content_types_accepted
|
25
|
+
model.type_handling.parsers.type_map
|
26
|
+
end
|
27
|
+
|
28
|
+
def request_body
|
29
|
+
@request.body
|
30
|
+
end
|
31
|
+
|
32
|
+
def known_content_type(content_type)
|
33
|
+
content_type = Webmachine::MediaType.parse(content_type)
|
34
|
+
content_types_accepted.any?{|ct, _| content_type.match?(ct)}
|
35
|
+
end
|
36
|
+
|
37
|
+
def delete_resource
|
38
|
+
@model.delete(params)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'roadforest/test-support/trace-formatter'
|
2
|
+
require 'webmachine/headers'
|
3
|
+
require 'webmachine/request'
|
4
|
+
require 'webmachine/response'
|
5
|
+
require 'webmachine/decision/flow'
|
6
|
+
module RoadForest
|
7
|
+
module TestSupport
|
8
|
+
class DispatcherFacade < BasicObject
|
9
|
+
def initialize(dispatcher)
|
10
|
+
@dispatcher = dispatcher
|
11
|
+
end
|
12
|
+
|
13
|
+
def method_missing(method, *args, &block)
|
14
|
+
@dispatcher.__send__(method, *args, &block)
|
15
|
+
end
|
16
|
+
|
17
|
+
def dispatch(request, response)
|
18
|
+
if resource = @dispatcher.find_resource(request, response)
|
19
|
+
FSM.new(resource, request, response).run
|
20
|
+
else
|
21
|
+
Webmachine.render_error(404, request, response)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class FSM < ::Webmachine::Decision::FSM
|
27
|
+
def self.trace_on
|
28
|
+
unless ancestors.include? Webmachine::Trace::FSM
|
29
|
+
include Webmachine::Trace::FSM
|
30
|
+
end
|
31
|
+
Webmachine::Trace.trace_store = :memory
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.dump_trace
|
35
|
+
puts trace_dump
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.trace_dump
|
39
|
+
Webmachine::Trace.traces.map do |trace|
|
40
|
+
TraceFormatter.new(Webmachine::Trace.fetch(trace))
|
41
|
+
end.join("\n")
|
42
|
+
end
|
43
|
+
|
44
|
+
#Um, actually *don't* handle exceptions
|
45
|
+
def handle_exceptions
|
46
|
+
yield.tap do |result|
|
47
|
+
#p result #ok
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def initialize_tracing
|
52
|
+
return if self.class.ancestors.include? Webmachine::Trace::FSM
|
53
|
+
super
|
54
|
+
end
|
55
|
+
|
56
|
+
def run
|
57
|
+
state = Webmachine::Decision::Flow::START
|
58
|
+
trace_request(request)
|
59
|
+
loop do
|
60
|
+
trace_decision(state)
|
61
|
+
result = handle_exceptions { send(state) }
|
62
|
+
case result
|
63
|
+
when Fixnum # Response code
|
64
|
+
respond(result)
|
65
|
+
break
|
66
|
+
when Symbol # Next state
|
67
|
+
state = result
|
68
|
+
else # You bwoke it
|
69
|
+
raise InvalidResource, t('fsm_broke', :state => state, :result => result.inspect)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
ensure
|
73
|
+
trace_response(response)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
require 'addressable/uri'
|
2
|
+
require 'roadforest/test-support/dispatcher-facade'
|
3
|
+
module RoadForest
|
4
|
+
module TestSupport
|
5
|
+
class HTTPClient
|
6
|
+
def initialize(app, url)
|
7
|
+
@app = app
|
8
|
+
@default_url = Addressable::URI.parse(url)
|
9
|
+
@exchanges = []
|
10
|
+
@dispatcher = DispatcherFacade.new(@app.dispatcher)
|
11
|
+
end
|
12
|
+
attr_reader :exchanges
|
13
|
+
|
14
|
+
def inspect
|
15
|
+
"#<#{self.class.name}:#{"%0xd" % object_id} #{exchanges.length} exchanges>"
|
16
|
+
end
|
17
|
+
|
18
|
+
def do_request(request)
|
19
|
+
uri = request.url
|
20
|
+
|
21
|
+
uri = Addressable::URI.parse(uri)
|
22
|
+
uri = @default_url.join(uri)
|
23
|
+
|
24
|
+
exchange = Exchange.new
|
25
|
+
|
26
|
+
exchange.method = request.method
|
27
|
+
exchange.uri = uri
|
28
|
+
exchange.body = request.body
|
29
|
+
exchange.dispatcher = @dispatcher
|
30
|
+
|
31
|
+
@exchanges << exchange
|
32
|
+
|
33
|
+
exchange.header('Host', [uri.host, uri.port].compact.join(':'))
|
34
|
+
exchange.header('Accept', '*/*')
|
35
|
+
request.headers.each do |name, value|
|
36
|
+
exchange.header(name, value)
|
37
|
+
end
|
38
|
+
|
39
|
+
yield exchange if block_given?
|
40
|
+
|
41
|
+
#puts; puts "#{__FILE__}:#{__LINE__} => #{(request).inspect}"
|
42
|
+
|
43
|
+
exchange.do_request
|
44
|
+
|
45
|
+
response = HTTP::Response.new
|
46
|
+
response.headers = exchange.response.headers.dup
|
47
|
+
response.status = exchange.response.code
|
48
|
+
response.body_string = exchange.response.body
|
49
|
+
|
50
|
+
#puts; puts "#{__FILE__}:#{__LINE__} => #{(response).inspect}"
|
51
|
+
return response
|
52
|
+
end
|
53
|
+
|
54
|
+
class Exchange
|
55
|
+
def initialize
|
56
|
+
@uri = nil
|
57
|
+
@method = nil
|
58
|
+
@headers = Webmachine::Headers.new
|
59
|
+
@query_params = {}
|
60
|
+
@req = nil
|
61
|
+
@res = nil
|
62
|
+
end
|
63
|
+
attr_accessor :uri, :method, :body, :dispatcher
|
64
|
+
attr_reader :headers, :query_params
|
65
|
+
|
66
|
+
# Returns the request object.
|
67
|
+
def request
|
68
|
+
@req || webmachine_test_error('No request object yet. Issue a request first.')
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns the response object after a request has been made.
|
72
|
+
def response
|
73
|
+
@res || webmachine_test_error('No response yet. Issue a request first!')
|
74
|
+
end
|
75
|
+
|
76
|
+
# Set a single header for the next request.
|
77
|
+
def header(name, value)
|
78
|
+
@headers[name] = value
|
79
|
+
end
|
80
|
+
|
81
|
+
def headers=(hash)
|
82
|
+
hash.each do |key, value|
|
83
|
+
header(key, value)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def query_param(name, value)
|
88
|
+
@query_params[name] = value
|
89
|
+
end
|
90
|
+
|
91
|
+
def query_params=(hash)
|
92
|
+
hash.each do |key, value|
|
93
|
+
query_param(key, value)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def do_request
|
98
|
+
self.uri = Addressable::URI.parse(uri)
|
99
|
+
uri.query_values = (uri.query_values || {}).merge(query_params)
|
100
|
+
|
101
|
+
|
102
|
+
@req = Webmachine::Request.new(method, uri, headers, RequestBody.new(body))
|
103
|
+
@res = Webmachine::Response.new
|
104
|
+
|
105
|
+
dispatcher.dispatch(@req, @res)
|
106
|
+
|
107
|
+
return @res
|
108
|
+
end
|
109
|
+
|
110
|
+
class RequestBody
|
111
|
+
# @return the request from Mongrel
|
112
|
+
|
113
|
+
# @param request the request from Mongrel
|
114
|
+
def initialize(body)
|
115
|
+
@raw_body = body
|
116
|
+
end
|
117
|
+
|
118
|
+
def body
|
119
|
+
@body =
|
120
|
+
case @raw_body
|
121
|
+
when IO, StringIO
|
122
|
+
@raw_body.rewind
|
123
|
+
@raw_body.read
|
124
|
+
when String
|
125
|
+
@raw_body
|
126
|
+
else
|
127
|
+
raise "Can't handle body type: #{@raw_body.class}"
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
|
132
|
+
# @return [String] the request body as a string
|
133
|
+
def to_s
|
134
|
+
body
|
135
|
+
end
|
136
|
+
|
137
|
+
# @yield [chunk]
|
138
|
+
# @yieldparam [String] chunk a chunk of the request body
|
139
|
+
def each(&block)
|
140
|
+
yield(body)
|
141
|
+
end
|
142
|
+
end # class RequestBody
|
143
|
+
|
144
|
+
private
|
145
|
+
def webmachine_test_error(msg)
|
146
|
+
raise Webmachine::Test::Error.new(msg)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module RoadForest
|
2
|
+
module Testing
|
3
|
+
class MatchesQuery
|
4
|
+
def initialize(&block)
|
5
|
+
@query = ::RDF::Query.new(&block)
|
6
|
+
end
|
7
|
+
|
8
|
+
def matches?(actual)
|
9
|
+
@actual = actual
|
10
|
+
solutions = @query.execute(actual)
|
11
|
+
not solutions.empty?
|
12
|
+
end
|
13
|
+
|
14
|
+
def failure_message_for_should
|
15
|
+
"expected #{@query.inspect} to return solutions on #{@actual.inspect}, but didn't"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class ListEquivalence
|
20
|
+
def initialize(expected)
|
21
|
+
@expected = expected
|
22
|
+
end
|
23
|
+
|
24
|
+
def matches?(actual)
|
25
|
+
@actual = actual
|
26
|
+
@actual_extra = @actual - @expected
|
27
|
+
@expected_extra = @expected - @actual
|
28
|
+
@actual_extra.empty? and @expected_extra.empty?
|
29
|
+
end
|
30
|
+
|
31
|
+
def failure_message_for_should
|
32
|
+
"expected #{@actual.inspect} to have the same elements as #{@expected.inspect}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class StatementsFromGraph
|
37
|
+
def initialize(graph)
|
38
|
+
@graph = graph
|
39
|
+
end
|
40
|
+
|
41
|
+
def that_match_query(query)
|
42
|
+
@graph.query(query).to_a
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
module HelperMethods
|
47
|
+
def statements_from_graph(graph)
|
48
|
+
StatementsFromGraph.new(graph)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
module MatcherMethods
|
53
|
+
def match_query(&block)
|
54
|
+
MatchesQuery.new(&block)
|
55
|
+
end
|
56
|
+
|
57
|
+
def be_equivalent_to(list)
|
58
|
+
ListEquivalence.new(list)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
RSpec::configure do |config|
|
65
|
+
config.include RoadForest::Testing::MatcherMethods
|
66
|
+
config.include RoadForest::Testing::HelperMethods
|
67
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'roadforest/rdf/graph-store'
|
2
|
+
require 'roadforest/remote-host'
|
3
|
+
require 'roadforest/test-support/http-client'
|
4
|
+
module RoadForest::TestSupport
|
5
|
+
class RemoteHost < ::RoadForest::RemoteHost
|
6
|
+
def initialize(app)
|
7
|
+
@app = app
|
8
|
+
super(app.canonical_host)
|
9
|
+
end
|
10
|
+
|
11
|
+
def build_graph_store
|
12
|
+
RoadForest::RDF::GraphStore.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def http_client
|
16
|
+
@http_client ||= HTTPClient.new(@app, @url)
|
17
|
+
end
|
18
|
+
|
19
|
+
def http_exchanges
|
20
|
+
http_client.exchanges
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|