described_routes 0.5.1 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +5 -0
- data/LICENSE +1 -1
- data/Manifest.txt +5 -0
- data/README.rdoc +19 -3
- data/lib/described_routes/helpers/described_routes_helper.rb +131 -0
- data/lib/described_routes/rails_controller.rb +12 -11
- data/lib/described_routes/rails_routes.rb +37 -9
- data/lib/described_routes.rb +1 -1
- data/lib/resource_template.rb +41 -10
- data/test/test_resource_template.rb +29 -0
- data/test_rails_app/app/controllers/application_controller.rb +8 -1
- data/test_rails_app/app/controllers/users_controller.rb +9 -0
- data/test_rails_app/app/controllers/welcome_controller.rb +9 -0
- data/test_rails_app/app/views/layouts/default.html.erb +9 -0
- data/test_rails_app/test/integration/headers_test.rb +38 -0
- metadata +10 -7
data/History.txt
CHANGED
data/LICENSE
CHANGED
data/Manifest.txt
CHANGED
@@ -5,6 +5,7 @@ PostInstall.txt
|
|
5
5
|
README.rdoc
|
6
6
|
Rakefile
|
7
7
|
lib/described_routes.rb
|
8
|
+
lib/described_routes/helpers/described_routes_helper.rb
|
8
9
|
lib/described_routes/rails_controller.rb
|
9
10
|
lib/described_routes/rails_routes.rb
|
10
11
|
lib/described_routes/rake_task_methods.rb
|
@@ -19,7 +20,10 @@ test/test_helper.rb
|
|
19
20
|
test/test_resource_template.rb
|
20
21
|
test_rails_app/Rakefile
|
21
22
|
test_rails_app/app/controllers/application_controller.rb
|
23
|
+
test_rails_app/app/controllers/users_controller.rb
|
24
|
+
test_rails_app/app/controllers/welcome_controller.rb
|
22
25
|
test_rails_app/app/helpers/application_helper.rb
|
26
|
+
test_rails_app/app/views/layouts/default.html.erb
|
23
27
|
test_rails_app/config/boot.rb
|
24
28
|
test_rails_app/config/environment.rb
|
25
29
|
test_rails_app/config/environments/development.rb
|
@@ -43,5 +47,6 @@ test_rails_app/test/fixtures/run_time/described_routes.text
|
|
43
47
|
test_rails_app/test/fixtures/run_time/described_routes.xml
|
44
48
|
test_rails_app/test/fixtures/run_time/described_routes.yaml
|
45
49
|
test_rails_app/test/integration/described_routes_run_time_test.rb
|
50
|
+
test_rails_app/test/integration/headers_test.rb
|
46
51
|
test_rails_app/test/integration/rake_tasks_test.rb
|
47
52
|
test_rails_app/test/test_helper.rb
|
data/README.rdoc
CHANGED
@@ -2,9 +2,7 @@
|
|
2
2
|
|
3
3
|
== DESCRIPTION
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
Also the home (for now at least) of the ResourceTemplate class.
|
5
|
+
Dynamic, framework-neutral metadata describing path/URI structures, with natural translations to/from JSON, YAML and XML. Bonus features: easy Rails integration, link element / link header generation and a plain text report format!
|
8
6
|
|
9
7
|
See roadmap for described_routes and path-to[http://github.com/asplake/path-to/tree] at http://positiveincline.com/?p=213.
|
10
8
|
|
@@ -164,6 +162,24 @@ JSON example (after pretty printing):
|
|
164
162
|
}
|
165
163
|
]
|
166
164
|
}
|
165
|
+
|
166
|
+
=== Link Generation
|
167
|
+
|
168
|
+
TODO: document how to integrate described_routes_helper; meanwhile see the test_rails_app for guidance.
|
169
|
+
|
170
|
+
Generate HTML link elements by generated by adding
|
171
|
+
|
172
|
+
<%= link_elements %>
|
173
|
+
|
174
|
+
to the <head> part of you layout.
|
175
|
+
|
176
|
+
Generate link headers (see the draft spec http://tools.ietf.org/id/draft-nottingham-http-link-header-05.txt) by including
|
177
|
+
|
178
|
+
before_filter :set_link_header
|
179
|
+
|
180
|
+
in your controllers. You can do this once in ApplicationController to get this behaviour across all controllers.
|
181
|
+
|
182
|
+
See DescribedRoutes::DescribedRoutesHelper#link_data for configuration options.
|
167
183
|
|
168
184
|
== DATA STRUCTURES and FORMATS
|
169
185
|
|
@@ -0,0 +1,131 @@
|
|
1
|
+
module DescribedRoutes
|
2
|
+
module DescribedRoutesHelper
|
3
|
+
# Map rels to standard relation types, used by #link_data
|
4
|
+
REGISTERED_RELS = {
|
5
|
+
'edit' => 'edit'
|
6
|
+
}
|
7
|
+
|
8
|
+
# The default options parameter to #link_elements; controls which links appear in html link elements
|
9
|
+
LINK_ELEMENT_OPTIONS = {
|
10
|
+
:self => true, :describedby => true, :describedby => true, :describedbywithparams => true, :up => true, :related => true
|
11
|
+
}
|
12
|
+
|
13
|
+
# The default options parameter to #link_headers; controls which links appear in html link elements
|
14
|
+
LINK_HEADER_OPTIONS = {
|
15
|
+
:self => true, :describedby => true, :describedby => true, :describedbywithparams => true, :up => true, :related => true
|
16
|
+
}
|
17
|
+
|
18
|
+
# get the resource template structure, initialised (once) from Rails routes
|
19
|
+
def resource_templates
|
20
|
+
@@resource_templates ||= begin
|
21
|
+
base_url = root_url rescue nil
|
22
|
+
RailsRoutes.get_resource_templates(base_url)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# get the resource template for the current request
|
27
|
+
def resource_template
|
28
|
+
r = resource_templates.routing[[controller_name, action_name]]
|
29
|
+
r[0] if r
|
30
|
+
end
|
31
|
+
|
32
|
+
# combined path and query parameters (not POST parameters), with the id param renamed to foo_id (say)
|
33
|
+
def resource_parameters
|
34
|
+
@resource_parameters ||= begin
|
35
|
+
p = request.path_parameters.merge(request.query_parameters)
|
36
|
+
r = resource_templates.routing[[controller_name, action_name]]
|
37
|
+
if r && r[1] && p[:id]
|
38
|
+
p[r[1]] = p.delete(:id)
|
39
|
+
end
|
40
|
+
p.except("action", "controller")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Render link_data as <link <url> rel=<rel> ... type=<type>> elements. Add to the <head> part of your layout with:
|
45
|
+
# <%= link_elements %>
|
46
|
+
def link_elements(options=LINK_ELEMENT_OPTIONS)
|
47
|
+
link_data(options).map{|url, rels, type|
|
48
|
+
%Q(<link href="#{url}" #{rels.map{|r| %Q(rel="#{r}")}.join(" ")} type="#{type}">)
|
49
|
+
}.join("\n")
|
50
|
+
end
|
51
|
+
|
52
|
+
# Render link_data as a link header. Usage:
|
53
|
+
# response.headers["Link"] = link_header(options)
|
54
|
+
# or use #set_link_header
|
55
|
+
def link_header(options=LINK_HEADER_OPTIONS)
|
56
|
+
link_data(options).map{|url, rels, type|
|
57
|
+
%Q(<#{url}>; #{rels.map{|r| %Q(rel="#{r}")}.join("; ")}; type="#{type}")
|
58
|
+
}.join(', ')
|
59
|
+
end
|
60
|
+
|
61
|
+
# Sets a link header in the response
|
62
|
+
# before_filter :set_link_header
|
63
|
+
def set_link_header(options=LINK_HEADER_OPTIONS)
|
64
|
+
response.headers["Link"] = link_header(options)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Returns an array of link information, each element containing
|
68
|
+
# 0) a URL
|
69
|
+
# 1) a *list* of link relation types (there can be more than one) - see explanation below
|
70
|
+
# 2) a "type" - actually a route name or (literally) "ResourceTemplate"
|
71
|
+
# The list of link relations types will contain a standard type ('self', 'up', 'describedby') &/or an extention type
|
72
|
+
# in the form "described_route_url(name)#rel", using the name and rel of the resource template.
|
73
|
+
#
|
74
|
+
# Example output for a request to a "user" route:
|
75
|
+
#
|
76
|
+
# [
|
77
|
+
# ['http://example.com/users/dojo', ['self'], 'user'],
|
78
|
+
# ['http://example.com/users', ['up'], 'users'],
|
79
|
+
# ['http://example.com/users/described_routes/user?user_id=dojo', ['describedby'], 'ResourceTemplate'],
|
80
|
+
# ['http://example.com/users/dojo/edit', ['edit', 'http://example.com/user/described_routes/user#edit'], 'edit_user'],
|
81
|
+
# ['http://example.com/users/dojo/edit', ['http://example.com/user/described_routes/user#profile'], 'user_profile']
|
82
|
+
# ]
|
83
|
+
#
|
84
|
+
# The output is filtered by the options hash, with members :self, :describedby, :describedby, :describedbywithparams, :up, :related.
|
85
|
+
# Rel values will include a short value (e.g. 'edit') if the template's rel has a mapping in REGISTERED_RELS.
|
86
|
+
#
|
87
|
+
def link_data(options)
|
88
|
+
result = []
|
89
|
+
rt = resource_template
|
90
|
+
if rt
|
91
|
+
if rt.name == 'root'
|
92
|
+
described_by = described_routes_url
|
93
|
+
related = resource_templates
|
94
|
+
else
|
95
|
+
described_by = described_route_url(rt.name)
|
96
|
+
related = rt.resource_templates
|
97
|
+
end
|
98
|
+
|
99
|
+
if resource_parameters.empty?
|
100
|
+
described_by_with_params = described_by
|
101
|
+
else
|
102
|
+
described_by_with_params = described_by + '?' + resource_parameters.to_query
|
103
|
+
end
|
104
|
+
|
105
|
+
type_prefix = described_routes_url + '#'
|
106
|
+
|
107
|
+
result << [request.url, ['self'], type_prefix + rt.name] if options[:self]
|
108
|
+
result << [described_by, ['describedby'], type_prefix + 'ResourceTemplate'] if options[:describedby]
|
109
|
+
if options[:describedbywithparams]
|
110
|
+
result << [described_by_with_params, ['describedby'], type_prefix + 'ResourceTemplate'] if described_by_with_params != described_by || !options[:describedby]
|
111
|
+
end
|
112
|
+
if options[:up]
|
113
|
+
if rt.parent
|
114
|
+
result << [rt.parent.uri_for(resource_parameters), ['up'], type_prefix + rt.parent.name]
|
115
|
+
elsif rt.name != 'root'
|
116
|
+
result << [root_url, ['up'], type_prefix + "root"]
|
117
|
+
end
|
118
|
+
end
|
119
|
+
if options[:related]
|
120
|
+
related.expand_links(resource_parameters).map do |l|
|
121
|
+
if l.name != rt.name
|
122
|
+
rel = l.rel || l.name
|
123
|
+
result << [l.uri, REGISTERED_RELS[rel].to_a + [described_by + '#' + rel], type_prefix + l.name]
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
result
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -1,30 +1,31 @@
|
|
1
1
|
require 'described_routes/rails_routes'
|
2
|
+
require 'described_routes/helpers/described_routes_helper'
|
2
3
|
require 'active_support'
|
3
4
|
|
4
5
|
module DescribedRoutes
|
5
6
|
class RailsController < ActionController::Base
|
7
|
+
include DescribedRoutes::DescribedRoutesHelper
|
8
|
+
|
6
9
|
def index
|
7
|
-
|
8
|
-
resource_templates = RailsRoutes.get_resource_templates(base_url).partial_expand(request.query_parameters)
|
10
|
+
expanded_templates = resource_templates.partial_expand(request.query_parameters)
|
9
11
|
|
10
12
|
respond_to do |format|
|
11
|
-
format.html # index.html.erb
|
12
|
-
format.json { render :json =>
|
13
|
-
format.text { render :text =>
|
14
|
-
format.yaml { render :text =>
|
15
|
-
format.xml { render :xml =>
|
13
|
+
format.html { render rescue redirect_to :format => :text } # render index.html.erb or fall back to text
|
14
|
+
format.json { render :json => expanded_templates.to_json }
|
15
|
+
format.text { render :text => expanded_templates.to_text }
|
16
|
+
format.yaml { render :text => expanded_templates.to_yaml }
|
17
|
+
format.xml { render :xml => expanded_templates.to_xml(Builder::XmlMarkup.new(:indent => 2)).target! }
|
16
18
|
end
|
17
19
|
end
|
18
|
-
|
20
|
+
|
19
21
|
def show
|
20
|
-
|
21
|
-
resource_templates = RailsRoutes.get_resource_templates(base_url).partial_expand(request.query_parameters)
|
22
|
+
expanded_templates = resource_templates.partial_expand(request.query_parameters)
|
22
23
|
resource_template = resource_templates.all_by_name[params[:id]]
|
23
24
|
# TODO 404 if nil
|
24
25
|
resource_template = resource_template.partial_expand(request.query_parameters)
|
25
26
|
|
26
27
|
respond_to do |format|
|
27
|
-
format.html # show.html.erb
|
28
|
+
format.html { render rescue redirect_to :format => :text } # render show.html.erb or fall back to text
|
28
29
|
format.json { render :json => resource_template.to_json }
|
29
30
|
format.text { render :text => resource_template.to_text }
|
30
31
|
format.yaml { render :text => resource_template.to_yaml }
|
@@ -2,6 +2,33 @@ require 'resource_template'
|
|
2
2
|
|
3
3
|
module DescribedRoutes
|
4
4
|
module RailsRoutes
|
5
|
+
# This is just glue really. It captures some extra data in the top level ResourceTemplates container to help address a
|
6
|
+
# couple of issues:
|
7
|
+
# 1) Inconsistent id parameter naming - id become {foo}_id when foo becomes the parent of another type of resources
|
8
|
+
# 2) Rails doesn't pass in the route object (not even its name) when routing a request to a controller so we have
|
9
|
+
# to keep enough information here for us to be able to guess it. Controller and action will be enough 99% of the time.
|
10
|
+
class RailsResourceTemplates < ResourceTemplate::ResourceTemplates
|
11
|
+
# Maps [controller, action] to [resource_template, id_name]
|
12
|
+
attr_reader :routing
|
13
|
+
|
14
|
+
def initialize(parsed)
|
15
|
+
super
|
16
|
+
|
17
|
+
@routing = {}
|
18
|
+
save_routing(parsed)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def save_routing(parsed)
|
24
|
+
if parsed
|
25
|
+
parsed.each do |p|
|
26
|
+
@routing[[p["controller"], p["action"]]] = [all_by_name[p["name"]], p["id_name"]]
|
27
|
+
save_routing(p["resource_templates"])
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
5
32
|
|
6
33
|
#
|
7
34
|
# Hook to customise the "parsed" (Array/Hash) data. For example, to remove certain sensitive routes:
|
@@ -11,12 +38,12 @@ module DescribedRoutes
|
|
11
38
|
mattr_accessor :parsed_hook
|
12
39
|
|
13
40
|
#
|
14
|
-
# Process Rails routes and return an array of ResourceTemplate objects
|
41
|
+
# Process Rails routes and return an array of ResourceTemplate objects.
|
15
42
|
#
|
16
|
-
def self.get_resource_templates(base_url =
|
43
|
+
def self.get_resource_templates(base_url=nil, routing=nil)
|
17
44
|
parsed = get_parsed_rails_resources(base_url)
|
18
45
|
parsed = parsed_hook.call(parsed) if parsed_hook
|
19
|
-
|
46
|
+
RailsResourceTemplates.new(parsed)
|
20
47
|
end
|
21
48
|
|
22
49
|
#
|
@@ -34,13 +61,11 @@ module DescribedRoutes
|
|
34
61
|
|
35
62
|
# prefix :id parameters consistently
|
36
63
|
# TODO - probably a better way to do this, just need a pattern that matches :id and not :id[a-zA-Z0-9_]+
|
64
|
+
id_name = nil
|
37
65
|
segs.gsub!(/:[a-zA-Z0-9_]+/) do |match|
|
38
66
|
if match == ":id" && controller
|
39
|
-
|
40
|
-
|
41
|
-
else
|
42
|
-
":#{controller.singularize.sub(/.*\//, "")}_id"
|
43
|
-
end
|
67
|
+
id_name = (controller == "described_routes/rails") ? "route_name" : "#{controller.singularize.sub(/.*\//, "")}_id"
|
68
|
+
':' + id_name
|
44
69
|
else
|
45
70
|
match
|
46
71
|
end
|
@@ -81,12 +106,15 @@ module DescribedRoutes
|
|
81
106
|
# options #=> ["GET"]
|
82
107
|
# name #=> "edit_user"
|
83
108
|
# controller #=> "rails"
|
109
|
+
# id_name #=> "user_id"
|
84
110
|
|
85
111
|
# create a new route hash
|
86
112
|
resource = {
|
87
113
|
"path_template" => template,
|
88
114
|
"options" => options,
|
89
|
-
"controller" => controller
|
115
|
+
"controller" => controller,
|
116
|
+
"action" => action,
|
117
|
+
"id_name" => id_name
|
90
118
|
}
|
91
119
|
resource["params"] = params unless params.empty?
|
92
120
|
resource["optional_params"] = optional_params unless optional_params.empty?
|
data/lib/described_routes.rb
CHANGED
data/lib/resource_template.rb
CHANGED
@@ -27,6 +27,9 @@ class ResourceTemplate
|
|
27
27
|
# Nested resource templates, a Resources object
|
28
28
|
attr_reader :resource_templates
|
29
29
|
|
30
|
+
# Inverse of resource_templates
|
31
|
+
attr_reader :parent
|
32
|
+
|
30
33
|
#
|
31
34
|
# Initialize a ResourceTemplate from a hash. For example:
|
32
35
|
#
|
@@ -46,10 +49,11 @@ class ResourceTemplate
|
|
46
49
|
# user_articles = ResourceTemplate.new(JSON.parse(json))
|
47
50
|
# user_articles = ResourceTemplate.new(YAML.load(yaml))
|
48
51
|
#
|
49
|
-
def initialize(hash={})
|
52
|
+
def initialize(hash={}, parent=nil)
|
50
53
|
@name, @rel, @uri_template, @path_template = %w(name rel uri_template path_template).map{|attr| hash[attr]}
|
51
54
|
@params, @optional_params, @options = %w(params optional_params options).map{|attr| hash[attr] || []}
|
52
|
-
@resource_templates = ResourceTemplates.new(hash["resource_templates"])
|
55
|
+
@resource_templates = ResourceTemplates.new(hash["resource_templates"], self)
|
56
|
+
@parent = parent
|
53
57
|
end
|
54
58
|
|
55
59
|
# Convert to a hash (equivalent to its JSON or YAML representation)
|
@@ -156,6 +160,11 @@ class ResourceTemplate
|
|
156
160
|
Addressable::Template.new(t).expand(params_hash).to_s
|
157
161
|
end
|
158
162
|
|
163
|
+
# Returns a URI (assuming the template needs to parameters!)
|
164
|
+
def uri
|
165
|
+
uri_for({}, nil)
|
166
|
+
end
|
167
|
+
|
159
168
|
# Returns an expanded path template with template variables filled from the given params hash.
|
160
169
|
# Raises ArgumentError if params doesn't contain all mandatory params, and a RuntimeError if there is no path_template.
|
161
170
|
def path_for(params_hash)
|
@@ -165,7 +174,13 @@ class ResourceTemplate
|
|
165
174
|
Addressable::Template.new(path_template).expand(params_hash).to_s
|
166
175
|
end
|
167
176
|
|
168
|
-
#
|
177
|
+
# Returns a path (assuming the template needs to parameters!)
|
178
|
+
def path
|
179
|
+
path_for({})
|
180
|
+
end
|
181
|
+
|
182
|
+
# Return a new resource template with the path_template or uri_template partially expanded with the given params; does the same
|
183
|
+
# recursively descending through child resource templates.
|
169
184
|
def partial_expand(actual_params)
|
170
185
|
self.class.new(
|
171
186
|
"name" => name,
|
@@ -183,14 +198,14 @@ class ResourceTemplate
|
|
183
198
|
template && Addressable::Template.new(template).partial_expand(params).pattern
|
184
199
|
end
|
185
200
|
|
186
|
-
# Find member ResourceTemplate objects
|
187
|
-
def find_by_rel(
|
188
|
-
resource_templates.select{|resource_template| resource_template.rel
|
201
|
+
# Find member ResourceTemplate objects matching the given rel
|
202
|
+
def find_by_rel(matching_rel)
|
203
|
+
resource_templates.select{|resource_template| matching_rel === resource_template.rel}
|
189
204
|
end
|
190
205
|
|
191
206
|
class ResourceTemplates < Array
|
192
207
|
# Initialize Resources (i.e. a new collection of ResourceTemplate objects) from given collection of ResourceTemplates or hashes
|
193
|
-
def initialize(collection=[])
|
208
|
+
def initialize(collection=[], parent=nil)
|
194
209
|
if collection
|
195
210
|
raise ArgumentError.new("#{collection.inspect} is not a collection") unless collection.kind_of?(Enumerable)
|
196
211
|
|
@@ -198,7 +213,7 @@ class ResourceTemplate
|
|
198
213
|
if r.kind_of?(ResourceTemplate)
|
199
214
|
push(r)
|
200
215
|
elsif r.kind_of?(Hash)
|
201
|
-
push(ResourceTemplate.new(r))
|
216
|
+
push(ResourceTemplate.new(r, parent))
|
202
217
|
else
|
203
218
|
raise ArgumentError.new("#{r.inspect} is neither a ResourceTemplate nor a Hash")
|
204
219
|
end
|
@@ -267,7 +282,6 @@ class ResourceTemplate
|
|
267
282
|
]
|
268
283
|
resource_template.resource_templates.to_table(resource_template, t, indent + ' ')
|
269
284
|
end
|
270
|
-
t
|
271
285
|
end
|
272
286
|
|
273
287
|
# text report
|
@@ -287,7 +301,24 @@ class ResourceTemplate
|
|
287
301
|
# Partially expand the path_template or uri_template of the given resource templates with the given params,
|
288
302
|
# returning new resource templates
|
289
303
|
def partial_expand(actual_params)
|
290
|
-
|
304
|
+
ResourceTemplates.new(map{|resource_template| resource_template.partial_expand(actual_params)})
|
305
|
+
end
|
306
|
+
|
307
|
+
# Return a new resource template with the path_template or uri_template expanded; expands also any child resource templates
|
308
|
+
# that don't require additional parameters.
|
309
|
+
def expand_links(actual_params)
|
310
|
+
ResourceTemplates.new(
|
311
|
+
select{|rt| (rt.positional_params(rt.parent) - rt.optional_params).empty?}.map {|rt|
|
312
|
+
{
|
313
|
+
"name" => rt.name,
|
314
|
+
"rel" => rt.rel,
|
315
|
+
"uri_template" => rt.partial_expand_uri_template(rt.uri_template, actual_params),
|
316
|
+
"path_template" => rt.partial_expand_uri_template(rt.path_template, actual_params),
|
317
|
+
"params" => rt.params - actual_params.keys,
|
318
|
+
"optional_params" => rt.optional_params - actual_params.keys,
|
319
|
+
"options" => rt.options
|
320
|
+
}
|
321
|
+
})
|
291
322
|
end
|
292
323
|
end
|
293
324
|
end
|
@@ -99,4 +99,33 @@ class TestResourceTemplate < Test::Unit::TestCase
|
|
99
99
|
ResourceTemplate.new.path_for({}) # no path_template
|
100
100
|
end
|
101
101
|
end
|
102
|
+
|
103
|
+
def test_parent
|
104
|
+
assert_equal("user", user_articles.parent.name)
|
105
|
+
assert_equal("users", user_articles.parent.parent.name)
|
106
|
+
assert_nil(user_articles.parent.parent.parent)
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_expand_links
|
110
|
+
assert_equal(
|
111
|
+
[
|
112
|
+
{
|
113
|
+
"name" => "new_user_article",
|
114
|
+
"options" => ["GET"],
|
115
|
+
"path_template" => "/users/dojo/articles/new{-prefix|.|format}",
|
116
|
+
"uri_template" => "http://localhost:3000/users/dojo/articles/new{-prefix|.|format}",
|
117
|
+
"rel" => "new_user_article",
|
118
|
+
"optional_params" => ["format"]
|
119
|
+
},
|
120
|
+
{
|
121
|
+
"name" => "recent_user_articles",
|
122
|
+
"options" => ["GET"],
|
123
|
+
"path_template" => "/users/dojo/articles/recent{-prefix|.|format}",
|
124
|
+
"uri_template" => "http://localhost:3000/users/dojo/articles/recent{-prefix|.|format}",
|
125
|
+
"rel" => "recent",
|
126
|
+
"optional_params" => ["format"]
|
127
|
+
}
|
128
|
+
],
|
129
|
+
user_articles.resource_templates.expand_links({'user_id' => 'dojo'}).to_parsed)
|
130
|
+
end
|
102
131
|
end
|
@@ -1,8 +1,15 @@
|
|
1
|
+
require 'described_routes/helpers/described_routes_helper'
|
2
|
+
|
1
3
|
# Filters added to this controller apply to all controllers in the application.
|
2
4
|
# Likewise, all the methods added will be available for all controllers.
|
3
|
-
|
4
5
|
class ApplicationController < ActionController::Base
|
5
6
|
helper :all # include all helpers, all the time
|
7
|
+
include DescribedRoutes::DescribedRoutesHelper
|
8
|
+
helper DescribedRoutes::DescribedRoutesHelper
|
9
|
+
|
10
|
+
before_filter :set_link_header
|
11
|
+
layout "default"
|
12
|
+
|
6
13
|
protect_from_forgery # See ActionController::RequestForgeryProtection for details
|
7
14
|
|
8
15
|
# Scrub sensitive parameters from your log
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'test/test_helper'
|
2
|
+
|
3
|
+
class DescribedRoutesRunTimeTest < ActionController::IntegrationTest
|
4
|
+
def test_root_headers
|
5
|
+
get "/"
|
6
|
+
assert_equal(
|
7
|
+
'<http://www.example.com/>; rel="self"; type="http://www.example.com/described_routes#root", ' +
|
8
|
+
'<http://www.example.com/described_routes>; rel="describedby"; type="http://www.example.com/described_routes#ResourceTemplate", ' +
|
9
|
+
'<http://www.example.com/admin/products>; rel="http://www.example.com/described_routes#admin_products"; type="http://www.example.com/described_routes#admin_products", ' +
|
10
|
+
'<http://www.example.com/described_routes>; rel="http://www.example.com/described_routes#described_routes"; type="http://www.example.com/described_routes#described_routes", ' +
|
11
|
+
'<http://www.example.com/pages>; rel="http://www.example.com/described_routes#pages"; type="http://www.example.com/described_routes#pages", ' +
|
12
|
+
'<http://www.example.com/users>; rel="http://www.example.com/described_routes#users"; type="http://www.example.com/described_routes#users"',
|
13
|
+
headers["Link"])
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_users_headers
|
17
|
+
get "/users"
|
18
|
+
assert_equal(
|
19
|
+
'<http://www.example.com/users>; rel="self"; type="http://www.example.com/described_routes#users", ' +
|
20
|
+
'<http://www.example.com/described_routes/users>; rel="describedby"; type="http://www.example.com/described_routes#ResourceTemplate", ' +
|
21
|
+
'<http://www.example.com/>; rel="up"; type="http://www.example.com/described_routes#root", ' +
|
22
|
+
'<http://www.example.com/users/new>; rel="http://www.example.com/described_routes/users#new_user"; type="http://www.example.com/described_routes#new_user"',
|
23
|
+
headers["Link"])
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_user_headers
|
27
|
+
get "/users/dojo"
|
28
|
+
assert_equal(
|
29
|
+
'<http://www.example.com/users/dojo>; rel="self"; type="http://www.example.com/described_routes#user", ' +
|
30
|
+
'<http://www.example.com/described_routes/user>; rel="describedby"; type="http://www.example.com/described_routes#ResourceTemplate", ' +
|
31
|
+
'<http://www.example.com/described_routes/user?user_id=dojo>; rel="describedby"; type="http://www.example.com/described_routes#ResourceTemplate", ' +
|
32
|
+
'<http://www.example.com/users>; rel="up"; type="http://www.example.com/described_routes#users", ' +
|
33
|
+
'<http://www.example.com/users/dojo/edit>; rel="edit"; rel="http://www.example.com/described_routes/user#edit"; type="http://www.example.com/described_routes#edit_user", ' +
|
34
|
+
'<http://www.example.com/users/dojo/articles>; rel="http://www.example.com/described_routes/user#articles"; type="http://www.example.com/described_routes#user_articles", ' +
|
35
|
+
'<http://www.example.com/users/dojo/profile>; rel="http://www.example.com/described_routes/user#profile"; type="http://www.example.com/described_routes#user_profile"',
|
36
|
+
headers["Link"])
|
37
|
+
end
|
38
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: described_routes
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Burrows
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-06-28 00:00:00 +01:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -43,9 +43,7 @@ dependencies:
|
|
43
43
|
version: 1.8.0
|
44
44
|
version:
|
45
45
|
description: |-
|
46
|
-
|
47
|
-
|
48
|
-
Also the home (for now at least) of the ResourceTemplate class.
|
46
|
+
Dynamic, framework-neutral metadata describing path/URI structures, with natural translations to/from JSON, YAML and XML. Bonus features: easy Rails integration, link element / link header generation and a plain text report format!
|
49
47
|
|
50
48
|
See roadmap for described_routes and path-to[http://github.com/asplake/path-to/tree] at http://positiveincline.com/?p=213.
|
51
49
|
email:
|
@@ -67,6 +65,7 @@ files:
|
|
67
65
|
- README.rdoc
|
68
66
|
- Rakefile
|
69
67
|
- lib/described_routes.rb
|
68
|
+
- lib/described_routes/helpers/described_routes_helper.rb
|
70
69
|
- lib/described_routes/rails_controller.rb
|
71
70
|
- lib/described_routes/rails_routes.rb
|
72
71
|
- lib/described_routes/rake_task_methods.rb
|
@@ -81,7 +80,10 @@ files:
|
|
81
80
|
- test/test_resource_template.rb
|
82
81
|
- test_rails_app/Rakefile
|
83
82
|
- test_rails_app/app/controllers/application_controller.rb
|
83
|
+
- test_rails_app/app/controllers/users_controller.rb
|
84
|
+
- test_rails_app/app/controllers/welcome_controller.rb
|
84
85
|
- test_rails_app/app/helpers/application_helper.rb
|
86
|
+
- test_rails_app/app/views/layouts/default.html.erb
|
85
87
|
- test_rails_app/config/boot.rb
|
86
88
|
- test_rails_app/config/environment.rb
|
87
89
|
- test_rails_app/config/environments/development.rb
|
@@ -105,6 +107,7 @@ files:
|
|
105
107
|
- test_rails_app/test/fixtures/run_time/described_routes.xml
|
106
108
|
- test_rails_app/test/fixtures/run_time/described_routes.yaml
|
107
109
|
- test_rails_app/test/integration/described_routes_run_time_test.rb
|
110
|
+
- test_rails_app/test/integration/headers_test.rb
|
108
111
|
- test_rails_app/test/integration/rake_tasks_test.rb
|
109
112
|
- test_rails_app/test/test_helper.rb
|
110
113
|
has_rdoc: true
|
@@ -132,10 +135,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
132
135
|
requirements: []
|
133
136
|
|
134
137
|
rubyforge_project: describedroutes
|
135
|
-
rubygems_version: 1.3.
|
138
|
+
rubygems_version: 1.3.4
|
136
139
|
signing_key:
|
137
140
|
specification_version: 3
|
138
|
-
summary:
|
141
|
+
summary: Dynamic, framework-neutral metadata describing path/URI structures, with natural translations to/from JSON, YAML and XML
|
139
142
|
test_files:
|
140
143
|
- test/test_described_routes.rb
|
141
144
|
- test/test_helper.rb
|