roadforest 0.5 → 0.7

Sign up to get free protection for your applications and to get access to all the features.
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