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 CHANGED
@@ -1,3 +1,8 @@
1
+ == 0.6.0 2009-06-28
2
+
3
+ * Add automatic link header/element generation
4
+ * LICENSE, README tweaks
5
+
1
6
  == 0.5.1 2009-05-22
2
7
 
3
8
  * Add ResourceTemplate::ResourceTemplate.find_by_rel
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- path-to, Copyright (c) 2009 Mike Burrows
1
+ described_routes, Copyright (c) 2009 Mike Burrows
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
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
- Framework-neutral metadata, describing (among other things) your Rails routes in JSON, YAML, XML and plain text formats.
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 &lt;head&gt; 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
- base_url = root_url rescue nil
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 => resource_templates.to_json }
13
- format.text { render :text => resource_templates.to_text }
14
- format.yaml { render :text => resource_templates.to_yaml }
15
- format.xml { render :xml => resource_templates.to_xml(Builder::XmlMarkup.new(:indent => 2)).target! }
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
- base_url = root_url rescue nil
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 = nil)
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
- ResourceTemplate::ResourceTemplates.new(parsed)
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
- if controller == "described_routes/rails"
40
- ":route_name"
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?
@@ -2,5 +2,5 @@ require 'resource_template'
2
2
 
3
3
  module DescribedRoutes
4
4
  # rubygem version
5
- VERSION = "0.5.1"
5
+ VERSION = "0.6.0"
6
6
  end
@@ -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
- # Return a new resource template with the path_template or uri_template partially expanded with the given params
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 with the given rel
187
- def find_by_rel(rel)
188
- resource_templates.select{|resource_template| resource_template.rel == 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
- self.class.new(map{|resource_template| resource_template.partial_expand(actual_params)})
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,9 @@
1
+ class UsersController < ApplicationController
2
+ def index
3
+ render :text => "<h1>index</h1>", :layout => true
4
+ end
5
+
6
+ def show
7
+ render :text => "<h1>show #{params[:id]}</h1>", :layout => true
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ class WelcomeController < ApplicationController
2
+ def index
3
+ render :text => "<h1>index</h1>", :layout => true
4
+ end
5
+
6
+ def show
7
+ render :text => "<h1>show #{params[:id]}</h1>", :layout => true
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ <html>
2
+ <head>
3
+ <title>A test page with links</title>
4
+ <%= link_elements %>
5
+ </head>
6
+ <body>
7
+ <%= yield %>
8
+ </body>
9
+ </html>
@@ -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.5.1
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-05-25 00:00:00 +01:00
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
- Framework-neutral metadata, describing (among other things) your Rails routes in JSON, YAML, XML and plain text formats.
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.3
138
+ rubygems_version: 1.3.4
136
139
  signing_key:
137
140
  specification_version: 3
138
- summary: Framework-neutral metadata, describing (among other things) your Rails routes in JSON, YAML, XML and plain text formats
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