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.
Files changed (71) hide show
  1. data/examples/file-management.rb +98 -0
  2. data/lib/roadforest/application/dispatcher.rb +54 -0
  3. data/lib/roadforest/application/parameters.rb +39 -0
  4. data/lib/roadforest/application/path-provider.rb +18 -0
  5. data/lib/roadforest/application/route-adapter.rb +24 -0
  6. data/lib/roadforest/application/services-host.rb +10 -0
  7. data/lib/roadforest/application.rb +42 -0
  8. data/lib/roadforest/blob-model.rb +56 -0
  9. data/lib/roadforest/content-handling/engine.rb +113 -0
  10. data/lib/roadforest/content-handling/media-type.rb +222 -0
  11. data/lib/roadforest/content-handling/type-handlers/jsonld.rb +172 -0
  12. data/lib/roadforest/http/adapters/excon.rb +47 -0
  13. data/lib/roadforest/http/graph-response.rb +20 -0
  14. data/lib/roadforest/http/graph-transfer.rb +112 -0
  15. data/lib/roadforest/http/message.rb +91 -0
  16. data/lib/roadforest/model.rb +151 -0
  17. data/lib/roadforest/models.rb +2 -0
  18. data/lib/roadforest/rdf/context-fascade.rb +25 -0
  19. data/lib/roadforest/rdf/document.rb +23 -0
  20. data/lib/roadforest/rdf/focus-list.rb +19 -0
  21. data/lib/roadforest/rdf/focus-wrapping.rb +30 -0
  22. data/lib/roadforest/rdf/graph-copier.rb +16 -0
  23. data/lib/roadforest/rdf/graph-focus.rb +95 -0
  24. data/lib/roadforest/rdf/graph-reading.rb +145 -0
  25. data/lib/roadforest/rdf/graph-store.rb +217 -0
  26. data/lib/roadforest/rdf/investigation.rb +90 -0
  27. data/lib/roadforest/rdf/normalization.rb +150 -0
  28. data/lib/roadforest/rdf/parcel.rb +47 -0
  29. data/lib/roadforest/rdf/post-focus.rb +35 -0
  30. data/lib/roadforest/rdf/resource-pattern.rb +60 -0
  31. data/lib/roadforest/rdf/resource-query.rb +58 -0
  32. data/lib/roadforest/rdf/source-rigor/credence/any.rb +9 -0
  33. data/lib/roadforest/rdf/source-rigor/credence/none-if-role-absent.rb +19 -0
  34. data/lib/roadforest/rdf/source-rigor/credence/role-if-available.rb +19 -0
  35. data/lib/roadforest/rdf/source-rigor/credence-annealer.rb +22 -0
  36. data/lib/roadforest/rdf/source-rigor/credence.rb +29 -0
  37. data/lib/roadforest/rdf/source-rigor/http-investigator.rb +20 -0
  38. data/lib/roadforest/rdf/source-rigor/investigator.rb +17 -0
  39. data/lib/roadforest/rdf/source-rigor/null-investigator.rb +10 -0
  40. data/lib/roadforest/rdf/source-rigor.rb +44 -0
  41. data/lib/roadforest/rdf/update-focus.rb +73 -0
  42. data/lib/roadforest/rdf/vocabulary.rb +11 -0
  43. data/lib/roadforest/rdf.rb +6 -0
  44. data/lib/roadforest/remote-host.rb +96 -0
  45. data/lib/roadforest/resource/handlers.rb +43 -0
  46. data/lib/roadforest/resource/http/form-parsing.rb +81 -0
  47. data/lib/roadforest/resource/rdf/leaf-item.rb +21 -0
  48. data/lib/roadforest/resource/rdf/list.rb +19 -0
  49. data/lib/roadforest/resource/rdf/parent-item.rb +26 -0
  50. data/lib/roadforest/resource/rdf/read-only.rb +100 -0
  51. data/lib/roadforest/resource/rdf.rb +4 -0
  52. data/lib/roadforest/resource/role/has-children.rb +22 -0
  53. data/lib/roadforest/resource/role/writable.rb +43 -0
  54. data/lib/roadforest/server.rb +3 -0
  55. data/lib/roadforest/test-support/dispatcher-facade.rb +77 -0
  56. data/lib/roadforest/test-support/http-client.rb +151 -0
  57. data/lib/roadforest/test-support/matchers.rb +67 -0
  58. data/lib/roadforest/test-support/remote-host.rb +23 -0
  59. data/lib/roadforest/test-support/trace-formatter.rb +140 -0
  60. data/lib/roadforest/test-support.rb +2 -0
  61. data/lib/roadforest/utility/class-registry.rb +49 -0
  62. data/lib/roadforest.rb +2 -0
  63. data/spec/client.rb +152 -0
  64. data/spec/credence-annealer.rb +44 -0
  65. data/spec/graph-copier.rb +87 -0
  66. data/spec/graph-store.rb +142 -0
  67. data/spec/media-types.rb +14 -0
  68. data/spec/rdf-parcel.rb +158 -0
  69. data/spec/update-focus.rb +117 -0
  70. data/spec_support/gem_test_suite.rb +0 -0
  71. metadata +241 -0
@@ -0,0 +1,172 @@
1
+ #@require 'rdf/rdfa' #XXX Otherwise json-ld grabs RDFa documents. Awaiting fix
2
+ #upstream
3
+ require 'json/ld'
4
+
5
+ module RoadForest
6
+ module MediaType
7
+ module Handlers
8
+ module Wrap
9
+ class Wrapper
10
+ def initialize(type, handler)
11
+ @type = type
12
+ @handler = handler
13
+ end
14
+ attr_reader :type, :handler
15
+
16
+ def local_to_network(network)
17
+ @handler.local_to_network(network)
18
+ end
19
+ alias from_graph local_to_network
20
+
21
+ def network_to_local(source)
22
+ @handler.network_to_local(source)
23
+ end
24
+ alias to_graph network_to_local
25
+ end
26
+
27
+ class Render < Wrapper
28
+ def call(resource)
29
+ @handler.render_for(resource)
30
+ end
31
+
32
+ def content_type_header
33
+ @type.content_type_header
34
+ end
35
+ end
36
+
37
+ class Parse < Wrapper
38
+ def call(resource)
39
+ @handler.parse_for(resource)
40
+ end
41
+
42
+ def add_child(resource)
43
+ @handler.add_child_to(resource)
44
+ end
45
+ end
46
+ end
47
+
48
+ class Handler
49
+ def network_to_local(network)
50
+ return network
51
+ end
52
+
53
+ def local_to_network(local)
54
+ return local
55
+ end
56
+
57
+ def parse_for(resource)
58
+ source = resource.request_body
59
+ input_data = network_to_local(source)
60
+ model = resource.model
61
+
62
+ update_model(model, input_data)
63
+
64
+ renderer = model.type_handling.choose_renderer(resource.request_accept_header)
65
+ body = renderer.local_to_network(model.response_data)
66
+
67
+ build_response(resource)
68
+ end
69
+
70
+ def render_for(resource)
71
+ model = resource.model
72
+ output_data = get_output(model)
73
+ local_to_network(output_data)
74
+ end
75
+
76
+ def add_child_to(resource)
77
+ model = resource.model
78
+ source = resource.request_body
79
+ input_data = network_to_local(source)
80
+
81
+ child_for_model(resource.model, input_data)
82
+
83
+ build_response(resource)
84
+ end
85
+
86
+ def build_response(resource)
87
+ model = resource.model
88
+
89
+ renderer = model.type_handling.choose_renderer(resource.request_accept_header)
90
+ body = renderer.local_to_network(model.response_data)
91
+
92
+ resource.response_content_type = renderer.content_type_header
93
+ resource.response_body = body
94
+ if model.response_location
95
+ resource.redirect_to(model.response_location)
96
+ end
97
+ end
98
+
99
+ def child_for_model(model, data)
100
+ model.add_child(data)
101
+ model.processed
102
+ end
103
+
104
+ def update_model(model, input_data)
105
+ result = model.update(input_data)
106
+ model.response_data = result
107
+ model.processed
108
+ result
109
+ end
110
+
111
+ def get_output(model)
112
+ result = model.retrieve
113
+ model.response_data = result
114
+ model.processed
115
+ result
116
+ end
117
+ end
118
+
119
+ require 'roadforest/rdf/normalization'
120
+ class RDFHandler < Handler
121
+ include RDF::Normalization
122
+
123
+ def get_output(model)
124
+ graph = super
125
+ root_uri = model.canonical_uri
126
+
127
+ graph.each_statement do |statement|
128
+ original = statement.dup
129
+ if ::RDF::URI === statement.subject and statement.subject.relative?
130
+ statement.subject = normalize_resource(root_uri.join(statement.subject))
131
+ end
132
+
133
+ if ::RDF::URI === statement.object and statement.object.relative?
134
+ statement.object = normalize_resource(root_uri.join(statement.object))
135
+ end
136
+
137
+ if statement != original
138
+ graph.delete(original)
139
+ graph.insert(statement)
140
+ end
141
+ end
142
+ graph
143
+ end
144
+
145
+ def child_for_model(model, data)
146
+ model.add_graph_child(data)
147
+ model.processed
148
+ end
149
+ end
150
+
151
+ #application/ld+json
152
+ class JSONLD < RDFHandler
153
+ def local_to_network(rdf)
154
+ JSON::LD::Writer.buffer do |writer|
155
+ rdf.each_statement do |statement|
156
+ writer << statement
157
+ end
158
+ end
159
+ end
160
+
161
+ def network_to_local(source)
162
+ graph = ::RDF::Graph.new
163
+ reader = JSON::LD::Reader.new(source.to_s)
164
+ reader.each_statement do |statement|
165
+ graph.insert(statement)
166
+ end
167
+ graph
168
+ end
169
+ end
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,47 @@
1
+ require 'addressable/uri'
2
+ require 'excon'
3
+ require 'roadforest/http/message'
4
+ module RoadForest
5
+ module HTTP
6
+ class ExconAdapter
7
+ def initialize(url)
8
+ @root_url = Addressable::URI.parse(url)
9
+ @connections = Hash.new{|h,k| h[k] = build_site_connection(k)}
10
+ @connection_defaults = {}
11
+ end
12
+ attr_reader :connection_defaults
13
+
14
+ def build_site_connection(site)
15
+ Excon.new(site.to_s, @connection_defaults)
16
+ end
17
+
18
+ def site_connection(uri)
19
+ uri = Addressable::URI.parse(uri)
20
+ @connections[uri.normalized_site]
21
+ end
22
+
23
+ def do_request(request)
24
+ uri = @root_url.join(request.url)
25
+
26
+ connection = site_connection(uri)
27
+ excon_response = connection.request(
28
+ :method => request.method,
29
+ :path => uri.path,
30
+ :headers => request.headers,
31
+ :body => request.body
32
+ )
33
+
34
+ response = HTTP::Response.new
35
+ if excon_response.body.is_a? String
36
+ response.body_string = excon_response.body
37
+ else
38
+ response.body = excon_response.body
39
+ end
40
+ response.headers = excon_response.headers
41
+ response.status = excon_response.status
42
+
43
+ return response
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,20 @@
1
+ module RoadForest
2
+ module HTTP
3
+ class GraphResponse
4
+ attr_accessor :graph
5
+ attr_reader :request, :response
6
+
7
+ def initialize(request, response, graph)
8
+ @request, @response, @graph = request, response, graph
9
+ end
10
+
11
+ def url
12
+ request.url
13
+ end
14
+
15
+ def status
16
+ response.status
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,112 @@
1
+ require 'roadforest/http/message'
2
+ require 'roadforest/http/graph-response'
3
+ require 'roadforest/content-handling/engine'
4
+
5
+ module RoadForest
6
+ module HTTP
7
+ class GraphTransfer
8
+ attr_accessor :http_client
9
+ attr_writer :type_handling
10
+
11
+ def initialize
12
+ @type_preferences = Hash.new{|h,k| k.nil? ? "*/*" : h[nil]}
13
+ end
14
+
15
+ def type_handling
16
+ @type_handling ||= ContentHandling::Engine.default
17
+ end
18
+
19
+ def put(url, graph)
20
+ make_request("PUT", url, graph)
21
+ end
22
+
23
+ def get(url)
24
+ make_request("GET", url)
25
+ end
26
+
27
+ def post(url, graph)
28
+ make_request("POST", url, graph)
29
+ end
30
+
31
+ def make_request(method, url, graph=nil)
32
+ method = method.to_s.upcase
33
+
34
+ validate(method, url, graph)
35
+
36
+ request = setup_request(method, url)
37
+
38
+ response = send_request(request, graph)
39
+
40
+ return build_response(request, response)
41
+ end
42
+
43
+ def validate(method, url, graph)
44
+ case method
45
+ when "GET", "HEAD", "DELETE"
46
+ raise "Method #{method} requires an empty body" unless graph.nil?
47
+ when "POST", "PATCH", "PUT"
48
+ raise "Method #{method} requires a body" if graph.nil?
49
+ #when "OPTION", "TRACE" #Need to put verbs where they go
50
+ else
51
+ raise "Unrecognized method: #{method}"
52
+ end
53
+ end
54
+
55
+ def best_type_for(url)
56
+ return @type_preferences[url]
57
+ end
58
+
59
+ def setup_request(method, url)
60
+ request = Request.new(method, url)
61
+ request.headers["Accept"] = type_handling.parsers.types.accept_header
62
+ request
63
+ end
64
+
65
+ def select_renderer(url)
66
+ end
67
+
68
+ def record_accept_header(url, types)
69
+ return if types.nil? or types.empty?
70
+ @type_preferences[nil] = types
71
+ @type_preferences[url] = types
72
+ end
73
+
74
+ def render_graph(graph, request)
75
+ return unless request.needs_body?
76
+
77
+ content_type = best_type_for(request.url)
78
+ renderer = type_handling.choose_renderer(content_type)
79
+ request.headers["Content-Type"] = renderer.content_type_header
80
+ request.body_string = renderer.from_graph(graph)
81
+ end
82
+
83
+ class Retryable < StandardError; end
84
+
85
+ def send_request(request, graph)
86
+ retry_limit ||= 5
87
+ render_graph(graph, request)
88
+
89
+ response = http_client.do_request(request)
90
+ case response.status
91
+ when 415 #Type not accepted
92
+ record_accept_header(request.url, response.headers["Accept"])
93
+ raise Retryable
94
+ end
95
+ return response
96
+ rescue Retryable
97
+ raise unless (retry_limit -= 1) > 0
98
+ retry
99
+ end
100
+
101
+ def parse_response(response)
102
+ parser = type_handling.choose_parser(response.headers["Content-Type"])
103
+ parser.to_graph(response.body_string)
104
+ end
105
+
106
+ def build_response(request, response)
107
+ graph = parse_response(response)
108
+ return GraphResponse.new(request, response, graph)
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,91 @@
1
+
2
+ require 'stringio'
3
+
4
+ module RoadForest
5
+ module HTTP
6
+ class Message
7
+ attr_accessor :headers
8
+
9
+ def initialize
10
+ @body_string = ""
11
+ @headers = {}
12
+ end
13
+
14
+ def body_string=(value)
15
+ @body_string = value
16
+ @body = nil
17
+ end
18
+
19
+ def body=(value)
20
+ @body = value
21
+ @body_string = nil
22
+ end
23
+
24
+ def body
25
+ return @body ||= StringIO.new(body_string||"")
26
+ end
27
+
28
+ def body_string
29
+ @body_string ||=
30
+ begin
31
+ case @body
32
+ when nil
33
+ nil
34
+ when StringIO
35
+ @body.string
36
+ when IO
37
+ @body.rewind
38
+ @body.read
39
+ else
40
+ raise "Unknown class for body: #{@body.class.name}"
41
+ end
42
+ end
43
+ end
44
+
45
+ def inspect
46
+ "#<#{self.class.name}:#{'0x%0xd'%object_id}\n #{inspection_payload.join("\n ")}\n>"
47
+ end
48
+
49
+ def inspection_payload
50
+ old_pos = body.pos
51
+ body.rewind
52
+ [headers.inspect, body.read]
53
+ ensure
54
+ body.pos = old_pos
55
+ end
56
+
57
+ def empty?
58
+ if @body.nil?
59
+ @body_string.nil? || @body_string.empty?
60
+ else
61
+ @body.respond_to?(:size) && @body.size <= 0
62
+ end
63
+ end
64
+ end
65
+
66
+ class Request < Message
67
+ attr_accessor :method, :url
68
+
69
+ def initialize(method, url)
70
+ super()
71
+ @method, @url = method, url
72
+ end
73
+
74
+ def needs_body?
75
+ %w{POST PATCH PUT}.include?(@method)
76
+ end
77
+
78
+ def inspection_payload
79
+ ["#{method} #{url}"] + super
80
+ end
81
+ end
82
+
83
+ class Response < Message
84
+ attr_accessor :status
85
+
86
+ def inspection_payload
87
+ [status] + super
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,151 @@
1
+ require 'roadforest/rdf/graph-store'
2
+ module RoadForest
3
+ class ProcessingSequenceError < StandardError
4
+ end
5
+
6
+ class Model
7
+ def initialize(route_name, params, services)
8
+ @route_name = route_name
9
+ @params = params
10
+ @services = services
11
+ @data = nil
12
+ @response_values = {}
13
+ end
14
+ attr_reader :route_name, :params, :services, :data
15
+
16
+ def path_for(route_name = nil, params = nil)
17
+ services.router.path_for(route_name, (params || self.params).to_hash)
18
+ end
19
+
20
+ def canonical_host
21
+ services.canonical_host
22
+ end
23
+
24
+ def canonical_uri
25
+ Addressable::URI.parse(canonical_host.to_s).join(my_path)
26
+ end
27
+
28
+ def type_handling
29
+ services.type_handling
30
+ end
31
+
32
+ def my_path
33
+ path_for(route_name, params)
34
+ end
35
+
36
+ def my_url
37
+ canonical_uri.to_s
38
+ end
39
+
40
+ def reset
41
+ end
42
+
43
+ def exists?
44
+ !data.nil?
45
+ end
46
+
47
+ def etag
48
+ nil
49
+ end
50
+
51
+ def last_modified
52
+ nil
53
+ end
54
+
55
+ def response_location
56
+ @response_values.fetch(:location) do
57
+ raise ProcessingSequenceError, "Location not available until request processed"
58
+ end
59
+ end
60
+
61
+ def response_location=(location)
62
+ @response_values[:location] = location
63
+ end
64
+
65
+ def response_data
66
+ @response_values.fetch(:data) do
67
+ raise ProcessingSequenceError, "Location not available until request processed"
68
+ end
69
+ end
70
+
71
+ def response_data=(data)
72
+ @response_values[:data] = data
73
+ end
74
+
75
+ def processed
76
+ [:location, :data].each do |key|
77
+ unless @response_values.has_key?(key)
78
+ @response_values[key] = nil
79
+ end
80
+ end
81
+ end
82
+
83
+ def expires
84
+ nil
85
+ end
86
+
87
+ def update(data)
88
+ raise NotImplementedError
89
+ end
90
+
91
+ def add_child(results)
92
+ raise NotImplementedError
93
+ end
94
+
95
+ def retrieve
96
+ raise NotImplementedError
97
+ end
98
+ alias retreive retrieve
99
+
100
+ def delete
101
+ false
102
+ end
103
+
104
+ end
105
+
106
+ class RDFModel < Model
107
+ def update(graph)
108
+ graph_update(start_focus(graph))
109
+ end
110
+
111
+ def graph_update(focus)
112
+ fill_graph(focus)
113
+ end
114
+
115
+ def add_graph_child(graph)
116
+ add_child(start_focus(graph))
117
+ new_graph
118
+ end
119
+
120
+ def add_child(focus)
121
+ new_graph
122
+ end
123
+
124
+ def retrieve
125
+ new_graph
126
+ end
127
+
128
+ def fill_graph(graph)
129
+ end
130
+
131
+ def start_focus(graph, resource_url=nil)
132
+ focus = RDF::GraphFocus.new
133
+ focus.graph = graph
134
+ focus.subject = resource_url || my_url
135
+
136
+ yield focus if block_given?
137
+ return focus
138
+ end
139
+
140
+ def start_graph(resource_url=nil, &block)
141
+ graph = ::RDF::Graph.new
142
+ start_focus(graph, resource_url, &block)
143
+ end
144
+
145
+ def new_graph
146
+ focus = start_graph(my_url)
147
+ fill_graph(focus)
148
+ self.response_data = focus.graph
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,2 @@
1
+ require 'roadforest/model'
2
+ require 'roadforest/blob-model'
@@ -0,0 +1,25 @@
1
+ require 'rdf'
2
+
3
+ module RoadForest::RDF
4
+ class ContextFascade
5
+ include ::RDF::Countable
6
+ include ::RDF::Enumerable
7
+ include ::RDF::Queryable
8
+
9
+ def initialize(store, resource, rigor)
10
+ @store, @resource, @rigor = store, resource, rigor
11
+ end
12
+
13
+ def query_execute(query, &block)
14
+ ResourceQuery.from(query, @resource, @rigor).execute(@store, &block)
15
+ end
16
+
17
+ def query_pattern(pattern, &block)
18
+ ResourcePattern.from(pattern, {:context_roles => {:subject => @resource}, :source_rigor => @rigor}).execute(@store, &block)
19
+ end
20
+
21
+ def each(&block)
22
+ @store.each(&block)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,23 @@
1
+ require 'stringio'
2
+ require 'roadforest'
3
+
4
+ module RoadForest::RDF
5
+ #Wrapper for text to be parsed into RDF
6
+ class Document
7
+ attr_accessor :content_type, :code, :source, :root_url, :body, :body_string
8
+ def initialize
9
+ @content_type = "text/html"
10
+ @code = 200
11
+ @body_string = ""
12
+ end
13
+
14
+ def body_string=(value)
15
+ @body_string = value
16
+ @body = nil
17
+ end
18
+
19
+ def body
20
+ return @body ||= StringIO.new(body_string)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,19 @@
1
+ require 'roadforest/rdf/focus-wrapping'
2
+ require 'roadforest/rdf/normalization'
3
+ module RoadForest::RDF
4
+ class FocusList < ::RDF::List
5
+ include Normalization
6
+
7
+ attr_accessor :root_url, :base_node, :source_rigor
8
+
9
+ def first
10
+ base_node.unwrap_value(super)
11
+ end
12
+
13
+ def each
14
+ super do |value|
15
+ yield base_node.unwrap_value(value)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,30 @@
1
+ require 'rdf/model/node'
2
+
3
+ module RoadForest::RDF
4
+ module FocusWrapping
5
+ def new_focus
6
+ dup
7
+ end
8
+
9
+ def wrap_node(value)
10
+ next_step = new_focus
11
+ if ::RDF::Node === value
12
+ next_step.root_url = self.root_url
13
+ else
14
+ next_step.root_url = normalize_context(value)
15
+ end
16
+ next_step.subject = value
17
+ next_step.graph = graph
18
+ next_step.source_rigor = source_rigor
19
+ next_step
20
+ end
21
+
22
+ def unwrap_value(value)
23
+ if value.respond_to? :object
24
+ value.object
25
+ else
26
+ wrap_node(value)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,16 @@
1
+ require 'rdf'
2
+ require 'roadforest/rdf/graph-focus'
3
+
4
+ module RoadForest
5
+ module RDF
6
+ class GraphCopier < GraphFocus
7
+ attr_accessor :source_graph, :subject
8
+
9
+ alias target_graph graph
10
+
11
+ def initialize
12
+ @graph = ::RDF::Graph.new
13
+ end
14
+ end
15
+ end
16
+ end