roadforest 0.5 → 0.7

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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/examples/file-management.rb +70 -58
  3. data/lib/roadforest/application.rb +9 -17
  4. data/lib/roadforest/application/dispatcher.rb +76 -9
  5. data/lib/roadforest/application/parameters.rb +9 -1
  6. data/lib/roadforest/application/path-provider.rb +30 -3
  7. data/lib/roadforest/application/route-adapter.rb +96 -14
  8. data/lib/roadforest/application/services-host.rb +21 -3
  9. data/lib/roadforest/augment/affordance.rb +82 -11
  10. data/lib/roadforest/augment/augmentation.rb +24 -6
  11. data/lib/roadforest/augment/augmenter.rb +12 -3
  12. data/lib/roadforest/authorization.rb +7 -229
  13. data/lib/roadforest/authorization/auth-entity.rb +26 -0
  14. data/lib/roadforest/authorization/authentication-chain.rb +79 -0
  15. data/lib/roadforest/authorization/default-authentication-store.rb +33 -0
  16. data/lib/roadforest/authorization/grant-builder.rb +23 -0
  17. data/lib/roadforest/authorization/grants-holder.rb +58 -0
  18. data/lib/roadforest/authorization/manager.rb +85 -0
  19. data/lib/roadforest/authorization/policy.rb +19 -0
  20. data/lib/roadforest/graph/access-manager.rb +25 -2
  21. data/lib/roadforest/graph/focus-list.rb +4 -0
  22. data/lib/roadforest/graph/graph-focus.rb +30 -13
  23. data/lib/roadforest/graph/nav-affordance-builder.rb +62 -0
  24. data/lib/roadforest/graph/normalization.rb +3 -3
  25. data/lib/roadforest/graph/path-vocabulary.rb +64 -0
  26. data/lib/roadforest/graph/post-focus.rb +5 -0
  27. data/lib/roadforest/graph/vocabulary.rb +4 -1
  28. data/lib/roadforest/http/adapters/excon.rb +4 -0
  29. data/lib/roadforest/http/graph-transfer.rb +17 -1
  30. data/lib/roadforest/http/keychain.rb +121 -33
  31. data/lib/roadforest/http/user-agent.rb +5 -3
  32. data/lib/roadforest/interface/application.rb +25 -8
  33. data/lib/roadforest/interface/rdf.rb +114 -15
  34. data/lib/roadforest/interface/utility.rb +3 -0
  35. data/lib/roadforest/interface/utility/backfill.rb +63 -0
  36. data/lib/roadforest/interface/utility/grant-list.rb +45 -0
  37. data/lib/roadforest/interface/utility/grant.rb +22 -0
  38. data/lib/roadforest/interfaces.rb +1 -0
  39. data/lib/roadforest/path-matcher.rb +471 -0
  40. data/lib/roadforest/remote-host.rb +159 -35
  41. data/lib/roadforest/resource/read-only.rb +23 -4
  42. data/lib/roadforest/server.rb +32 -3
  43. data/lib/roadforest/source-rigor/graph-store.rb +0 -2
  44. data/lib/roadforest/source-rigor/rigorous-access.rb +138 -21
  45. data/lib/roadforest/templates/affordance-property-values.haml +3 -0
  46. data/lib/roadforest/templates/rdfpost-curie.haml +1 -1
  47. data/lib/roadforest/test-support/matchers.rb +41 -12
  48. data/lib/roadforest/test-support/remote-host.rb +3 -3
  49. data/lib/roadforest/type-handlers/rdfa-writer/environment-decorator.rb +1 -1
  50. data/lib/roadforest/type-handlers/rdfa-writer/render-engine.rb +40 -27
  51. data/lib/roadforest/type-handlers/rdfa.rb +10 -3
  52. data/lib/roadforest/utility/class-registry.rb +44 -4
  53. data/spec/affordance-augmenter.rb +46 -19
  54. data/spec/affordances-flow.rb +46 -30
  55. data/spec/authorization.rb +16 -4
  56. data/spec/client.rb +22 -4
  57. data/spec/focus-list.rb +24 -0
  58. data/spec/full-integration.rb +8 -3
  59. data/spec/graph-store.rb +8 -0
  60. data/spec/keychain.rb +18 -14
  61. data/spec/rdf-normalization.rb +32 -6
  62. data/spec/update-focus.rb +36 -39
  63. metadata +19 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c6aad7c24680d035ff3f8a2a72d660888d400be6
4
- data.tar.gz: 4ed267799005efaba995beab6a5adfff4e171021
3
+ metadata.gz: 6db5d72add13365fd11e006d8942448265f13f61
4
+ data.tar.gz: 85a7105fe1995e4a340c6540e4f02d4f84c5d900
5
5
  SHA512:
6
- metadata.gz: c1a97a99c84940a429ebf824b919704d336d21d28a7113c0663a35dbc37ee451be3af7a6913b45e915a75d0d1f5fbf69ef2cc6a684df1d3f5a389f65526f1d62
7
- data.tar.gz: 986679ca8ed11258a435a0f7b14d9f79b91487e7a2ec16dc2712b944fac032c5bd037a511725f62bc350f3c770401558b0b54ae76955710ade5f613fa4338911
6
+ metadata.gz: 6eaa87181e7ee19771dd8e3e561afac00885dee1b9f49b27978423e1d6850ca3bd9768bd21861f528b0327fb8732b5d109bdb03a852812ce3d97cd8a8ef2648b
7
+ data.tar.gz: 441c94a9a790b7e6e94978a8c4ca5407a343562ad106c2a45d86ae6f7b09d6e90afb888501e1b401c03c7517de3000f5aa437802bd70e5cd6b0399cff6490b05
@@ -3,7 +3,9 @@ require 'rdf/vocab/skos'
3
3
 
4
4
  module FileManagementExample
5
5
  module Vocabulary
6
- class LC < ::RDF::Vocabulary("http://lrdesign.com/vocabularies/logical-construct#"); end
6
+ class LC < ::RDF::Vocabulary("http://lrdesign.com/vocabularies/logical-construct#")
7
+ property :name
8
+ end
7
9
  end
8
10
 
9
11
  class ServicesHost < ::RoadForest::Application::ServicesHost
@@ -11,13 +13,7 @@ module FileManagementExample
11
13
 
12
14
  def initialize
13
15
  @file_records = []
14
- end
15
- end
16
-
17
- FileRecord = Struct.new(:name, :resolved)
18
16
 
19
- class Application < RoadForest::Application
20
- def setup
21
17
  router.add :root, [], :read_only, Interfaces::Navigation
22
18
  router.add :unresolved_needs, ["unresolved_needs"], :parent, Interfaces::UnresolvedNeedsList
23
19
  router.add_traced :need, ["needs",'*'], :leaf, Interfaces::Need
@@ -25,77 +21,93 @@ module FileManagementExample
25
21
  route.content_engine = RoadForest::ContentHandling.plaintext_engine
26
22
  end
27
23
  end
24
+ end
28
25
 
29
- module Interfaces
30
- class Navigation < RoadForest::Interface::RDF
31
- def exists?
32
- true
33
- end
26
+ FileRecord = Struct.new(:name, :resolved)
34
27
 
35
- def update(graph)
36
- return false
37
- end
28
+ module Interfaces
29
+ class Navigation < RoadForest::Interface::RDF
30
+ def exists?
31
+ true
32
+ end
38
33
 
39
- def nav_entry(graph, name, path)
40
- graph.add_node([:skos, :hasTopConcept], "#" + name) do |entry|
41
- entry[:rdf, :type] = [:skos, "Concept"]
42
- entry[:skos, :prefLabel] = name
43
- entry[:foaf, "page"] = path
44
- end
45
- end
34
+ def update(graph)
35
+ return false
36
+ end
46
37
 
47
- def fill_graph(graph)
48
- graph[:rdf, "type"] = [:skos, "ConceptScheme"]
49
- nav_entry(graph, "Unresolved", path_for(:unresolved_needs))
38
+ def nav_entry(graph, name, path)
39
+ graph.add_node([:skos, :hasTopConcept], "#" + name) do |entry|
40
+ entry[:rdf, :type] = [:skos, "Concept"]
41
+ entry[:skos, :prefLabel] = name
42
+ entry[:foaf, "page"] = path
50
43
  end
51
44
  end
52
45
 
53
- class UnresolvedNeedsList < RoadForest::Interface::RDF
54
- def exists?
55
- true
56
- end
46
+ def fill_graph(graph)
47
+ graph[:rdf, "type"] = [:skos, "ConceptScheme"]
48
+ nav_entry(graph, "Unresolved", url_for(:unresolved_needs))
49
+ end
50
+ end
57
51
 
58
- def update(graph)
59
- end
52
+ class UnresolvedNeedsList < RoadForest::Interface::RDF
53
+ def exists?
54
+ true
55
+ end
60
56
 
61
- def add_child(graph)
62
- services.logger.debug(graph.access_manager.source_graph.dump(:nquads))
63
- new_file = FileRecord.new(graph.first(:lc, "name"), false)
64
- services.file_records << new_file
65
- end
57
+ def update(graph)
58
+ end
66
59
 
67
- def fill_graph(graph)
60
+ def add_child(graph)
61
+ services.logger.debug(graph.access_manager.source_graph.dump(:nquads))
62
+ new_file = FileRecord.new(graph.first(:lc, "name"), false)
63
+ services.file_records << new_file
64
+ end
65
+
66
+ def fill_graph(graph)
67
+ unresolved = services.file_records.select do |record|
68
+ !record.resolved
69
+ end
70
+ if unresolved.empty?
71
+ graph.add_list(:lc, "needs")
72
+ else
68
73
  graph.add_list(:lc, "needs") do |list|
69
- services.file_records.each do |record|
70
- if !record.resolved
71
- need = copy_interface(graph, :need, '*' => [record.name])
72
- need[:lc, :name]
73
- need[:lc, :digest]
74
-
75
- list.append(need.subject)
76
- end
74
+ unresolved.each do |record|
75
+ need = copy_interface(graph, :need, '*' => [record.name])
76
+ need[:lc, :name]
77
+ need[:lc, :digest]
78
+
79
+ list.append(need.subject)
77
80
  end
78
81
  end
79
82
  end
80
83
  end
84
+ end
81
85
 
82
- class Need < RoadForest::Interface::RDF
83
- def data
84
- @data = services.file_records.find do |record|
85
- record.name == params.remainder
86
- end
86
+ class Need < RoadForest::Interface::RDF
87
+ def data
88
+ @data ||= services.file_records.find do |record|
89
+ record.name == params.remainder
87
90
  end
91
+ end
88
92
 
89
- def graph_update(graph)
90
- data.resolved = graph[:lc, "resolved"]
91
- new_graph
93
+ def update_payload
94
+ payload_focus do |payload|
95
+ payload.add_node([:path, "forward"]) do |resolved|
96
+ resolved[[:path, "predicate"]] = [:lc, "resolved"]
97
+ resolved[[:path, "type"]] = [:xsd, "boolean"]
98
+ end
92
99
  end
100
+ end
93
101
 
94
- def fill_graph(graph)
95
- graph[[:lc, "resolved"]] = data.resolved
96
- graph[[:lc, "name"]] = data.name
97
- graph[[:lc, "contents"]] = path_for(:file_content)
98
- end
102
+ def graph_update(graph)
103
+ data.resolved = graph[:lc, "resolved"]
104
+ new_graph
105
+ end
106
+
107
+ def fill_graph(graph)
108
+ graph[[:lc, "resolved"]] = data.resolved
109
+ graph[[:lc, "name"]] = data.name
110
+ graph[[:lc, "contents"]] = url_for(:file_content)
99
111
  end
100
112
  end
101
113
  end
@@ -13,33 +13,25 @@ require 'roadforest/authorization'
13
13
 
14
14
  module RoadForest
15
15
  class Application
16
- include Graph::Normalization
17
-
18
- def initialize(canonical_host, services = nil, configuration = nil, dispatcher = nil)
19
- @canonical_host = normalize_resource(canonical_host)
16
+ def initialize(services, configuration = nil)
17
+ @services = services
20
18
  configuration ||= Webmachine::Configuration.default
21
- dispatcher ||= Dispatcher.new(self)
22
19
  super(configuration, dispatcher)
23
- self.services = services unless services.nil?
24
-
25
- setup
26
20
  end
27
21
 
28
- def setup
29
- end
30
-
31
- attr_accessor :services, :canonical_host, :default_content_engine
22
+ attr_reader :services
32
23
 
24
+ def dispatcher
25
+ services.dispatcher
26
+ end
33
27
  alias router dispatcher
34
28
 
35
- #XXX Is this the right place for this?
36
- def services=(service_host)
37
- @services = service_host
38
- service_host.application = self
29
+ def canonical_host
30
+ services.canonical_host
39
31
  end
40
32
 
41
33
  def default_content_engine
42
- @default_content_engine || ContentHandling.rdf_engine
34
+ services.default_content_engine
43
35
  end
44
36
  end
45
37
  end
@@ -1,31 +1,57 @@
1
1
  require 'webmachine'
2
2
  require 'roadforest/application/route-adapter'
3
+ require 'roadforest/application/path-provider'
3
4
  require 'roadforest/resource'
4
5
 
5
6
  module RoadForest
6
7
  class Dispatcher < Webmachine::Dispatcher
7
- def initialize(application)
8
+ def initialize(services)
8
9
  super(method(:create_resource))
9
- @application = application
10
+ @services = services
10
11
  @route_names = {}
12
+ @route_mappings = []
11
13
  @trace_by_default = false
12
14
  end
13
- attr_accessor :application, :trace_by_default
15
+ attr_accessor :services, :trace_by_default
14
16
 
15
17
  def route_for_name(name)
16
18
  @route_names.fetch(name)
17
19
  end
18
20
 
19
- def default_content_engine
20
- @application.default_content_engine
21
+ def mapped_route_for_name(from, name, params)
22
+ mapping = @route_mappings.find do |mapping|
23
+ mapping.matches?(from, name, params)
24
+ end
25
+
26
+ unless mapping.nil?
27
+ name = mapping.to_name
28
+ end
29
+
30
+ return route_for_name(name)
21
31
  end
22
32
 
23
- def path_provider
24
- @path_provider ||= PathProvider.new(self)
33
+ def find_route(*args)
34
+ if block_given?
35
+ @routes.find{|route| yield(route)}
36
+ else
37
+ super
38
+ end
39
+ end
40
+
41
+ def each_route(&block)
42
+ @routes.each(&block)
43
+ end
44
+
45
+ def each_name_and_route(&block)
46
+ @route_names.each_pair(&block)
47
+ end
48
+
49
+ def default_content_engine
50
+ @services.default_content_engine
25
51
  end
26
52
 
27
- def services
28
- @application.services
53
+ def path_provider(route_name)
54
+ PathProvider.new(route_name, self)
29
55
  end
30
56
 
31
57
  # Add a named route to the dispatcher - the 90% case is handled by passing
@@ -68,5 +94,46 @@ module RoadForest
68
94
  end
69
95
  end
70
96
  alias add_traced add_traced_route
97
+
98
+ def add_route_map(route_map)
99
+ @route_mappings << route_map
100
+ end
101
+
102
+ class RouteMap
103
+ class Configurator
104
+ def initialize(name, router)
105
+ @router = router
106
+ @map = RouteMap.new
107
+ @map.in_name = name
108
+ end
109
+
110
+ def from(name, params=nil)
111
+ @map.from_name = name
112
+ @map.from_params = [*params]
113
+ self
114
+ end
115
+
116
+ def to(name)
117
+ @map.to_name = name
118
+ @router.add_route_map(@map)
119
+ nil
120
+ end
121
+ end
122
+
123
+ def initialize
124
+ end
125
+ attr_accessor :in_name, :from_name, :from_params, :to_name
126
+
127
+ def matches?(in_name, name, params)
128
+ return false unless in_name == @in_name
129
+ return false unless name == @from_name
130
+ return false unless @from_params.all?{|name| params.has_key?(name)}
131
+ return true
132
+ end
133
+ end
134
+
135
+ def map_in(route_name)
136
+ RouteMap::Configurator.new(route_name, self)
137
+ end
71
138
  end
72
139
  end
@@ -19,7 +19,15 @@ module RoadForest
19
19
 
20
20
  def fetch(field_name)
21
21
  return path_tokens if field_name == '*'
22
- @path_info.fetch(field_name){ @query_params.fetch(field_name) }
22
+ @path_info.fetch(field_name) do
23
+ if @query_params.respond_to?(:fetch)
24
+ @query_params.fetch(field_name) do
25
+ @query_params.fetch(field_name.to_s)
26
+ end
27
+ else
28
+ raise KeyError, "No parameter: #{field_name}"
29
+ end
30
+ end
23
31
  end
24
32
 
25
33
  def slice(*fields)
@@ -1,9 +1,18 @@
1
1
  module RoadForest
2
2
  class PathProvider
3
- def initialize(dispatcher)
3
+ def initialize(route_name, dispatcher)
4
+ @route_name = route_name
4
5
  @dispatcher = dispatcher
5
6
  end
6
7
 
8
+ def services
9
+ @dispatcher.services
10
+ end
11
+
12
+ def route_for_name(name, params=nil)
13
+ @dispatcher.mapped_route_for_name(@route_name, name, params)
14
+ end
15
+
7
16
  # Get the URL to the given resource, with optional variables to be used
8
17
  # for bindings in the path spec.
9
18
  # @param [Webmachine::Resource] resource the resource to link to
@@ -12,10 +21,28 @@ module RoadForest
12
21
  # @return [String] the URL
13
22
  def path_for(name, vars = nil)
14
23
  vars ||= {}
15
- route = @dispatcher.route_for_name(name)
24
+ route = route_for_name(name)
16
25
  ::RDF::URI.parse(route.build_path(vars))
17
26
  end
18
27
 
28
+ def pattern_for(name, vals = nil, extra = nil)
29
+ vars ||= {}
30
+ route = route_for_name(name)
31
+ Addressable::URI.parse(services.canonical_host.to_s).join(route.build_pattern(vals, extra))
32
+ end
33
+
34
+ def find_route(&block)
35
+ @dispatcher.find_route(&block)
36
+ end
37
+
38
+ def each_name_and_route(&block)
39
+ @dispatcher.each_name_and_route(&block)
40
+ end
41
+
42
+ def url_for(route_name, params = nil)
43
+ ::RDF::URI.new(Addressable::URI.parse(services.canonical_host.to_s).join(path_for(route_name, params)))
44
+ end
45
+
19
46
  def request_for(name, vars = nil)
20
47
  url = path_for(name, vars) #full url?
21
48
 
@@ -23,7 +50,7 @@ module RoadForest
23
50
  end
24
51
 
25
52
  def interface_for(name, vars = nil)
26
- route = @dispatcher.route_for_name(name)
53
+ route = route_for_name(name)
27
54
  params = route.build_params(vars)
28
55
  route.resource.build_interface(params)
29
56
  end
@@ -1,5 +1,5 @@
1
1
  module RoadForest
2
- class Application
2
+ class Application #XXX Lotta classes in this one file
3
3
  #Embedded in WebMachine's Routes to compose the object structure at need
4
4
  class ResourceAdapter
5
5
  attr_accessor :resource_builder, :interface_builder, :route_name, :router, :services, :content_engine, :trace, :router
@@ -21,7 +21,15 @@ module RoadForest
21
21
  end
22
22
 
23
23
  def build_interface(params)
24
- interface_builder.call(route_name, params, router, router.services)
24
+ interface_builder.call(route_name, params, router.path_provider(route_name), router.services)
25
+ end
26
+
27
+ def interface_class
28
+ if interface_builder.respond_to? :interface_class
29
+ interface_builder.interface_class
30
+ else
31
+ nil
32
+ end
25
33
  end
26
34
 
27
35
  def trace?
@@ -36,20 +44,82 @@ module RoadForest
36
44
  #Extension of Webmachine's Routes that allows for rendering url paths and
37
45
  #parameter lists.
38
46
  class Route < Webmachine::Dispatcher::Route
47
+ attr_accessor :name
39
48
  # Create a complete URL for this route, doing any necessary variable
40
49
  # substitution.
41
50
  # @param [Hash] vars values for the path variables
42
51
  # @return [String] the valid URL for the route
43
52
  def build_path(vars = nil)
44
53
  vars ||= {}
45
- "/" + path_spec.map do |segment|
54
+ vars = vars.to_hash
55
+ vars = vars.dup
56
+ path_spec = resolve_path_spec(vars)
57
+ if path_spec.any?{|segment| segment.is_a?(Symbol) or segment == "*"}
58
+ raise "Cannot build path - missing vars: #{path_spec.inspect}"
59
+ end
60
+ path = "/" + path_spec.join("/")
61
+ vars.delete('*')
62
+ unless vars.empty?
63
+ path += "?" + vars.map do |key,value|
64
+ [key,value].join("=")
65
+ end.join("&")
66
+ end
67
+ return path
68
+ end
69
+
70
+ def build_pattern(vals = nil, extra_vars = nil)
71
+ vals ||= {}
72
+ extra_vars ||= []
73
+ vals = vals.to_hash
74
+ vals = vals.dup
75
+ extra_vars -= path_spec.find_all{|segment| segment.is_a? Symbol}
76
+
77
+ pattern_spec = resolve_path_spec(vals)
78
+
79
+ pattern = pattern_spec.map do |segment|
80
+ case segment
81
+ when '*'
82
+ "{/rest*}"
83
+ when Symbol
84
+ "{/#{segment}}"
85
+ else
86
+ "/" + segment
87
+ end
88
+ end.join("")
89
+
90
+ vals.delete('*')
91
+ unless vals.empty?
92
+ pattern += "?" + vals.map do |key,value|
93
+ [key,value].join("=")
94
+ end.join("&")
95
+ end
96
+ unless extra_vars.empty?
97
+ pattern += "{?#{extra_vars.join(",")}}"
98
+ end
99
+ return pattern
100
+ end
101
+
102
+ def resolve_path_spec(vars)
103
+ path_spec.map do |segment|
46
104
  case segment
47
105
  when '*',Symbol
48
- vars.fetch(segment)
106
+ if (string = vars.delete(segment)).nil?
107
+ segment
108
+ else
109
+ string
110
+ end
49
111
  when String
50
112
  segment
51
113
  end
52
- end.join("/")
114
+ end
115
+ end
116
+
117
+ def interface_class
118
+ if resource.respond_to? :interface_class
119
+ resource.interface_class
120
+ else
121
+ nil
122
+ end
53
123
  end
54
124
 
55
125
  def build_params(vars = nil)
@@ -69,13 +139,24 @@ module RoadForest
69
139
  end
70
140
  end
71
141
 
142
+ class InterfaceBuilder
143
+ attr_reader :interface_class
144
+ def initialize(interface_class)
145
+ @interface_class = interface_class
146
+ end
147
+
148
+ def call(name, params, router, services)
149
+ interface_class.new(name, params, router, services)
150
+ end
151
+ end
152
+
72
153
  class RouteBinding
73
154
  def initialize(router)
74
155
  @router = router
75
156
  end
76
157
 
77
158
  attr_accessor :route_name, :path_spec, :bindings, :guard
78
- attr_accessor :resource_type, :interface_class, :services, :trace, :content_engine
159
+ attr_accessor :resource_type, :interface_builder, :interface_class, :services, :trace, :content_engine
79
160
 
80
161
  def resource_builder
81
162
  @resource_builder ||= proc do |request, response|
@@ -88,9 +169,7 @@ module RoadForest
88
169
  end
89
170
 
90
171
  def interface_builder
91
- @interface_builder ||= proc do |name, params, router, services|
92
- interface_class.new(name, params, router.path_provider, services)
93
- end
172
+ @interface_builder ||= InterfaceBuilder.new(interface_class)
94
173
  end
95
174
 
96
175
  def build_interface(&block)
@@ -100,11 +179,14 @@ module RoadForest
100
179
  def route
101
180
  @route ||=
102
181
  begin
103
- if guard.nil?
104
- Route.new(path_spec, resource_adapter, bindings || {})
105
- else
106
- Route.new(path_spec, resource_adapter, bindings || {}, &guard)
107
- end
182
+ route =
183
+ if guard.nil?
184
+ Route.new(path_spec, resource_adapter, bindings || {})
185
+ else
186
+ Route.new(path_spec, resource_adapter, bindings || {}, &guard)
187
+ end
188
+ route.name = route_name
189
+ route
108
190
  end
109
191
  end
110
192