described_routes 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ path-to, Copyright (c) 2009 Mike Burrows
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Manifest.txt CHANGED
@@ -1,12 +1,16 @@
1
1
  History.txt
2
+ LICENSE
2
3
  Manifest.txt
3
4
  PostInstall.txt
4
5
  README.rdoc
5
6
  Rakefile
6
7
  lib/described_routes.rb
8
+ lib/described_routes/rails_routes.rb
9
+ lib/described_routes/resource_template.rb
7
10
  lib/tasks/described_routes.rb
8
11
  script/console
9
12
  script/destroy
10
13
  script/generate
11
14
  test/test_described_routes.rb
12
15
  test/test_helper.rb
16
+ test/test_resource_template.rb
@@ -0,0 +1,153 @@
1
+ require 'described_routes'
2
+
3
+ module DescribedRoutes
4
+ module RailsRoutes
5
+ #
6
+ # Based on the implementation of "rake routes". Returns a hash of Rails path specifications (slightly normalized)
7
+ # mapped to hashes of the attributes we need.
8
+ #
9
+ def self.get_rails_resources
10
+ ActionController::Routing::Routes.routes.inject({}) do |resources, route|
11
+ name = ActionController::Routing::Routes.named_routes.routes.index(route).to_s
12
+ controller = route.parameter_shell[:controller]
13
+ action = route.parameter_shell[:action]
14
+ options = [route.conditions[:method]].flatten.map{|option| option.to_s.upcase}
15
+ segs = route.segments.inject("") {|str,s| str << s.to_s }
16
+ segs.chop! if segs.length > 1
17
+
18
+ # prefix :id parameters consistently
19
+ # TODO - probably a better way to do this, just need a pattern that matches :id and not :id[a-zA-Z0-9_]+
20
+ segs.gsub!(/:[a-zA-Z0-9_]+/) do |match|
21
+ if match == ":id" && controller
22
+ ":#{controller.singularize.sub(/.*\//, "")}_id"
23
+ else
24
+ match
25
+ end
26
+ end
27
+
28
+ # ignore optional format parameter when comparing paths
29
+ key = segs.sub("(.:format)", "")
30
+ if resources[key]
31
+ # we've seen the (normalised) path before; add to options
32
+ resources[key]["options"] += options
33
+ else
34
+ template = segs
35
+
36
+ # collect & format mandatory parameters
37
+ params = []
38
+ template.gsub!(/:[a-zA-Z0-9_]+/) do |match|
39
+ param = match[1..-1]
40
+ param = controller.singularize.sub(/.*\//, "") + "_id" if param == "id" && controller
41
+ params << param
42
+ "{#{param}}"
43
+ end
44
+
45
+ # collect & format optional format parameter
46
+ optional_params = []
47
+ template.sub!("(.{format})") do |match|
48
+ optional_params << "format"
49
+ "{-prefix|.|format}"
50
+ end
51
+ params -= optional_params
52
+
53
+ # so now we have (for example):
54
+ # segs #=> "/users/:user_id/edit(.:format)" (was "/users/:id")
55
+ # key #=> "/users/:user_id/edit"
56
+ # template #=> "/users/{user_id}/edit"
57
+ # params #=> ["user_id"]
58
+ # optional_params #=> ["format"]
59
+ # action #=> "edit"
60
+ # options #=> ["GET"]
61
+ # name #=> "edit_user"
62
+
63
+ # create a new route hash
64
+ resource = {
65
+ "path_template" => template,
66
+ "options" => options,
67
+ }
68
+ resource["params"] = params unless params.empty?
69
+ resource["optional_params"] = optional_params unless optional_params.empty?
70
+
71
+ resources[key] = resource
72
+ end
73
+
74
+ # this may be the first time we've seen a good name for this key
75
+ resources[key]["name"] ||= name unless name.blank? or name =~ /^formatted/
76
+
77
+ resources
78
+ end
79
+ end
80
+
81
+ #
82
+ # Takes the routes from Rails and produces the required tree structure.
83
+ #
84
+ def self.get_resources
85
+ resources = get_rails_resources
86
+ resources.delete_if{|k, v| v["name"].blank? or v["name"] =~ /^formatted/}
87
+
88
+ key_tree = make_key_tree(resources.keys.sort){|possible_prefix, key|
89
+ key[0...possible_prefix.length] == possible_prefix && possible_prefix != "/"
90
+ }
91
+
92
+ tree = map_key_tree(key_tree) do |key, children|
93
+ resource = resources[key]
94
+ resource["resource_templates"] = children unless children.empty?
95
+ resource.delete("options") if resource["options"] == [""]
96
+
97
+ # compare parent and child names, and populate "rel" with either
98
+ # 1) a prefix (probably an action name)
99
+ # 2) a suffix (probably a nested resource)
100
+ # If neither applies, let's hope the child is identified by parameter (i.e. the parent is a collection)
101
+ # TODO rewrite this so that it's done when the child is created
102
+ name = resource["name"]
103
+ prefix = /^(.*)_#{name}$/
104
+ suffix = /^#{name}_(.*)$/
105
+ children.each do |child|
106
+ child_name = child["name"]
107
+ if child_name =~ prefix
108
+ child["rel"] = $1
109
+ elsif child_name =~ suffix
110
+ child["rel"] = $1
111
+ end
112
+ end
113
+
114
+ resource
115
+ end
116
+ end
117
+
118
+ #
119
+ # Depth-first tree traversal
120
+ #
121
+ # tree = [["/", []], ["/a", [["/a/b", [["/a/b/c", []]]], ["/a/d", []]]], ["/b", []]]
122
+ # map_key_tree(tree){|key, processed_children| {key => processed_children}}
123
+ # # => [{"/"=>[]}, {"/a"=>[{"/a/b"=>[{"/a/b/c"=>[]}]}, {"/a/d"=>[]}]}, {"/b"=>[]}]
124
+ #
125
+ def self.map_key_tree(tree, &blk) #:nodoc:
126
+ tree.map do |pair|
127
+ key, children = pair
128
+ blk.call(key, map_key_tree(children, &blk))
129
+ end
130
+ end
131
+
132
+ #
133
+ # Turns a sorted array of strings into a tree structure as follows:
134
+ #
135
+ # make_key_tree(["/", "/a", "/a/b", "/a/b/c", "/a/d", "/b"]){|possible_prefix, route|
136
+ # route[0...possible_prefix.length] == possible_prefix && possible_prefix != "/"
137
+ # }
138
+ # => [["/", []], ["/a", [["/a/b", [["/a/b/c", []]]], ["/a/d", []]]], ["/b", []]]
139
+ #
140
+ # Note that in the example (as in is actual usage in this module), we choose not to to have the root resource ("/") as
141
+ # the parent of all other resources.
142
+ #
143
+ def self.make_key_tree(sorted_keys, &is_prefix) #:nodoc:
144
+ head, *tail = sorted_keys
145
+ if head
146
+ children, siblings = tail.partition{|p| is_prefix.call(head, p)}
147
+ [[head, make_key_tree(children, &is_prefix)]] + make_key_tree(siblings, &is_prefix)
148
+ else
149
+ []
150
+ end
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,66 @@
1
+ require "json"
2
+
3
+ module DescribedRoutes
4
+ class ResourceTemplate
5
+ # The template's name. Optional. Making these unique across the application is helpful for clients
6
+ # that may wish to pick out nested templates by name.
7
+ attr_reader :name
8
+
9
+ # Optional attribute that describes a resource's relationship to its parent. For example:
10
+ # * a nested route to a resource's edit page would have rel of "edit"
11
+ # * a nested collection of articles under a "user" resource would have have a rel of "articles"
12
+ # Collection members generally don't need a rel as they are identified by their params
13
+ attr_reader :rel
14
+
15
+ # A template for generating paths relative to the application's base.
16
+ attr_reader :path_template
17
+
18
+ # The parameters required by the path template
19
+ attr_reader :params
20
+
21
+ # Optional paramaters that may be used by the path template
22
+ attr_reader :optional_params
23
+
24
+ # "options" in the sense of the HTTP option request - i.e. a list of HTTP methods. Optional.
25
+ attr_reader :options
26
+
27
+ # An optional list of nested resource templates
28
+ attr_reader :resource_templates
29
+
30
+ # Initialize a ResourceTemplate. See the attribute descriptions above for explanations of the parameters.
31
+ def initialize(name, rel, path_template, params, optional_params, options, resource_templates)
32
+ @name, @rel, @path_template = name, rel, path_template
33
+ @params = params || []
34
+ @optional_params = optional_params || []
35
+ @options = options || []
36
+ @resource_templates = resource_templates || []
37
+ end
38
+
39
+ # Create a ResourceTemplate from its Hash representation
40
+ def self.from_hash(hash)
41
+ attributes = %w(name rel path_template params optional_params options).map{|k| hash[k]}
42
+ if hash["resource_templates"]
43
+ attributes << hash["resource_templates"].map{|h| from_hash(h)} if hash["resource_templates"]
44
+ else
45
+ attributes << nil
46
+ end
47
+ self.new(*attributes)
48
+ end
49
+
50
+ # Convert to a hash (equivalent to its JSON or YAML representation)
51
+ def to_hash
52
+ hash = {}
53
+ hash["name"] = name if name && !name.empty?
54
+ hash["rel"] = rel if rel && !rel.empty?
55
+ hash["path_template"] = path_template if path_template && !path_template.empty?
56
+
57
+ hash["options"] = options if options && !options.empty?
58
+
59
+ hashes = DescribedRoutes.to_parsed(resource_templates)
60
+ hash["resource_templates"] = hashes if hashes && !hashes.empty?
61
+
62
+ hash
63
+ end
64
+ end
65
+ end
66
+
@@ -2,7 +2,7 @@ require 'described_routes/resource_template'
2
2
 
3
3
  module DescribedRoutes
4
4
  # rubygem version
5
- VERSION = "0.0.2"
5
+ VERSION = "0.0.3"
6
6
 
7
7
  # Convert an array of ResourceTemplate objects to array of hashes equivalent to their JSON or YAML representations
8
8
  def self.to_parsed(resource_templates)
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.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Burrows
@@ -46,17 +46,21 @@ extra_rdoc_files:
46
46
  - README.rdoc
47
47
  files:
48
48
  - History.txt
49
+ - LICENSE
49
50
  - Manifest.txt
50
51
  - PostInstall.txt
51
52
  - README.rdoc
52
53
  - Rakefile
53
54
  - lib/described_routes.rb
55
+ - lib/described_routes/rails_routes.rb
56
+ - lib/described_routes/resource_template.rb
54
57
  - lib/tasks/described_routes.rb
55
58
  - script/console
56
59
  - script/destroy
57
60
  - script/generate
58
61
  - test/test_described_routes.rb
59
62
  - test/test_helper.rb
63
+ - test/test_resource_template.rb
60
64
  has_rdoc: true
61
65
  homepage:
62
66
  licenses: []