Skip to content
Permalink
Browse files
Merge pull request #9001 from schneems/schneems/routes-path-js
In Browser Path Matching with Javascript
  • Loading branch information
rafaelfranca committed Jan 21, 2013
2 parents 8b815b9 + 8b72d68 commit 68a6fb695346f1b6f84dcba21bb48114cde297a5
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 12 deletions.
@@ -1,5 +1,10 @@
## Rails 4.0.0 (unreleased) ##

* Add javascript based routing path matcher to `/rails/info/routes`.
Routes can now be filtered by whether or not they match a path.

*Richard Schneeman*

* Given

params.permit(:name)
@@ -7,7 +7,7 @@
<td data-route-verb='<%= route[:verb] %>'>
<%= route[:verb] %>
</td>
<td data-route-path='<%= route[:path] %>'>
<td data-route-path='<%= route[:path] %>' data-regexp='<%= route[:regexp] %>'>
<%= route[:path] %>
</td>
<td data-route-reqs='<%= route[:reqs] %>'>
@@ -1,22 +1,58 @@
<% content_for :style do %>
#route_table td { padding: 0 30px; }
#route_table { margin: 0 auto 0; }
#route_table {
margin: 0 auto 0;
border-collapse: collapse;
}

#route_table td {
padding: 0 30px;
}

#route_table tr.bottom th {
padding-bottom: 10px;
line-height: 15px;
}

#route_table .matched_paths {
background-color: LightGoldenRodYellow;
}

#route_table .matched_paths {
border-bottom: solid 3px SlateGrey;
}

#path_search {
width: 80%;
font-size: inherit;
}
<% end %>

<table id='route_table' class='route_table'>
<thead>
<tr>
<th>Helper<br />
<th>Helper</th>
<th>HTTP Verb</th>
<th>Path</th>
<th>Controller#Action</th>
</tr>
<tr class='bottom'>
<th><%# Helper %>
<%= link_to "Path", "#", 'data-route-helper' => '_path',
title: "Returns a relative path (without the http or domain)" %> /
<%= link_to "Url", "#", 'data-route-helper' => '_url',
title: "Returns an absolute url (with the http and domain)" %>
title: "Returns an absolute url (with the http and domain)" %>
</th>
<th><%# HTTP Verb %>
</th>
<th><%# Path %>
<%= search_field(:path, nil, id: 'path_search', placeholder: "Path Match") %>
</th>
<th><%# Controller#action %>
</th>
<th>HTTP Verb</th>
<th>Path</th>
<th>Controller#Action</th>
</tr>
</thead>
<tbody class='matched_paths' id='matched_paths'>
</tbody>
<tbody>
<%= yield %>
</tbody>
@@ -25,7 +61,7 @@
<script type='text/javascript'>
function each(elems, func) {
if (!elems instanceof Array) { elems = [elems]; }
for (var i = elems.length; i--; ) {
for (var i = 0, len = elems.length; i < len; i++) {
func(elems[i]);
}
}
@@ -46,11 +82,63 @@
function setupRouteToggleHelperLinks() {
var toggleLinks = document.querySelectorAll('#route_table [data-route-helper]');
onClick(toggleLinks, function(){
var helperTxt = this.getAttribute("data-route-helper");
var helperElems = document.querySelectorAll('[data-route-name] span.helper');
var helperTxt = this.getAttribute("data-route-helper"),
helperElems = document.querySelectorAll('[data-route-name] span.helper');
setValOn(helperElems, helperTxt);
});
}

// takes an array of elements with a data-regexp attribute and
// passes their their parent <tr> into the callback function
// if the regexp matchs a given path
function eachElemsForPath(elems, path, func) {
each(elems, function(e){
var reg = e.getAttribute("data-regexp");
if (path.match(RegExp(reg))) {
func(e.parentNode.cloneNode(true));
}
})
}

// Ensure path always starts with a slash "/" and remove params or fragments
function sanitizePath(path) {
var path = path.charAt(0) == '/' ? path : "/" + path;
return path.replace(/\#.*|\?.*/, '');
}

// Enables path search functionality
function setupMatchPaths() {
var regexpElems = document.querySelectorAll('#route_table [data-regexp]'),
pathElem = document.querySelector('#path_search'),
selectedSection = document.querySelector('#matched_paths'),
noMatchText = '<tr><th colspan="4">None</th></tr>';


// Remove matches if no path is present
pathElem.onblur = function(e) {
if (pathElem.value === "") selectedSection.innerHTML = "";
}

// On key press perform a search for matching paths
pathElem.onkeyup = function(e){
var path = sanitizePath(pathElem.value),
defaultText = '<tr><th colspan="4">Paths Matching (' + path + '):</th></tr>';

// Clear out results section
selectedSection.innerHTML= defaultText;

// Display matches if they exist
eachElemsForPath(regexpElems, path, function(e){
selectedSection.appendChild(e);
});

// If no match present, tell the user
if (selectedSection.innerHTML === defaultText) {
selectedSection.innerHTML = selectedSection.innerHTML + noMatchText;
}
}
}

setupMatchPaths();
setupRouteToggleHelperLinks();
</script>
@@ -34,6 +34,23 @@ def name
super.to_s
end

def regexp
__getobj__.path.to_regexp
end

def json_regexp
str = regexp.inspect.
sub('\\A' , '^').
sub('\\Z' , '$').
sub('\\z' , '$').
sub(/^\// , '').
sub(/\/[a-z]*$/ , '').
gsub(/\(\?#.+\)/ , '').
gsub(/\(\?-\w+:/ , '(').
gsub(/\s/ , '')
Regexp.new(str).source
end

def reqs
@reqs ||= begin
reqs = endpoint
@@ -101,7 +118,11 @@ def collect_routes(routes)
end.collect do |route|
collect_engine_routes(route)

{ name: route.name, verb: route.verb, path: route.path, reqs: route.reqs }
{ name: route.name,
verb: route.verb,
path: route.path,
reqs: route.reqs,
regexp: route.json_regexp }
end
end

@@ -21,6 +21,14 @@ def draw(options = {}, &block)
inspector.format(ActionDispatch::Routing::ConsoleFormatter.new, options[:filter]).split("\n")
end

def test_json_regexp_converter
@set.draw do
get '/cart', :to => 'cart#show'
end
route = ActionDispatch::Routing::RouteWrapper.new(@set.routes.first)
assert_equal "^\\/cart(?:\\.([^\\/.?]+))?$", route.json_regexp
end

def test_displaying_routes_for_engines
engine = Class.new(Rails::Engine) do
def self.inspect

0 comments on commit 68a6fb6

Please sign in to comment.