actionpack/lib/action_dispatch/middleware/templates/routes/_table.html.erb

Summary

Maintainability
Test Coverage
<% content_for :style do %>
  h2, p {
    padding-left: 30px;
  }

  #route_table {
    margin: 0;
    border-collapse: collapse;
    word-wrap:break-word;
    table-layout: fixed;
    width:100%;
  }

  #route_table thead tr {
    border-bottom: 2px solid #ddd;
  }

  #route_table th {
    padding-left: 30px;
    text-align: left;
  }

  #route_table thead tr.bottom {
    border-bottom: none;
  }

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

  #route_table #search_container {
    padding: 7px 30px;
  }

  #route_table thead tr th input#search {
    -webkit-appearance: textfield;
    width:100%;
  }

  #route_table thead th.http-verb {
    width: 10%;
  }

  #route_table tbody tr {
    border-bottom: 1px solid #ddd;
  }

  #route_table tbody tr:nth-child(odd) {
    background: #f2f2f2;
  }

  #route_table tbody.exact_matches,
  #route_table tbody.fuzzy_matches {
    background-color: LightGoldenRodYellow;
    border-bottom: solid 2px SlateGrey;
  }

  #route_table tbody.exact_matches tr,
  #route_table tbody.fuzzy_matches tr {
    background: none;
    border-bottom: none;
  }

  #route_table td {
    padding: 4px 30px;
  }

  @media (prefers-color-scheme: dark) {
    #route_table tbody tr:nth-child(odd) {
      background: #282828;
    }

    #route_table tbody.exact_matches tr,
    #route_table tbody.fuzzy_matches tr {
      background: DarkSlateGrey;
    }
  }
<% end %>

<table id='route_table'>
  <thead>
    <tr>
      <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)"   %>)
      </th>
      <th class="http-verb">HTTP Verb</th>
      <th>Path</th>
      <th>Controller#Action</th>
      <th>Source Location</th>
    </tr>
    <tr>
      <th colspan="5" id="search_container"><%= search_field(:query, nil, id: 'search', placeholder: "Search") %></th>
    </tr>
  </thead>
  <tbody class='exact_matches' id='exact_matches'>
  </tbody>
  <tbody class='fuzzy_matches' id='fuzzy_matches'>
  </tbody>
  <tbody>
    <%= yield %>
  </tbody>
</table>

<script>
  // support forEach iterator on NodeList
  NodeList.prototype.forEach = Array.prototype.forEach;

  // Enables query search functionality
  function setupMatchingRoutes() {
    // Check if there are any matched results in a section
    function checkNoMatch(section, trElement) {
      if (section.children.length <= 1) {
        section.appendChild(trElement);
      }
    }

    // get JSON from URL and invoke callback with result
    function getJSON(url, success) {
      var xhr = new XMLHttpRequest();
      xhr.open('GET', url);
      xhr.onload = function() {
        if (this.status == 200)
          success(JSON.parse(this.response));
      };
      xhr.send();
    }

    function delayedKeyup(input, callback) {
      var timeout;
      input.onkeyup = function(){
        if (timeout) clearTimeout(timeout);
        timeout = setTimeout(callback, 300);
      }
    }

    // remove params or fragments
    function sanitizeQuery(query) {
      return query.replace(/[#?].*/, '');
    }

    var pathElements = document.querySelectorAll('#route_table [data-route-path]'),
        searchElem   = document.querySelector('#search'),
        exactSection = document.querySelector('#exact_matches'),
        fuzzySection = document.querySelector('#fuzzy_matches');

    // Remove matches when no search value is present
    searchElem.onblur = function(e) {
      if (searchElem.value === "") {
        exactSection.innerHTML = "";
        fuzzySection.innerHTML = "";
      }
    }

    function buildTr(string) {
      var tr = document.createElement('tr');
      var th = document.createElement('th');
      th.setAttribute('colspan', 4);
      tr.appendChild(th);
      th.innerText = string;
      return tr;
    }

    // On key press perform a search for matching paths
    delayedKeyup(searchElem, function() {
      var query = sanitizeQuery(searchElem.value),
          defaultExactMatch = buildTr("Routes matching '" + query + "':"),
          defaultFuzzyMatch = buildTr("Routes containing '" + query + "':"),
          noExactMatch      = buildTr('No exact matches found'),
          noFuzzyMatch      = buildTr('No fuzzy matches found');

      if (!query)
        return searchElem.onblur();

      getJSON('/rails/info/routes?query=' + query, function(matches){
        // Clear out results section
        exactSection.replaceChildren(defaultExactMatch);
        fuzzySection.replaceChildren(defaultFuzzyMatch);

        // Display exact matches and fuzzy matches
        pathElements.forEach(function(elem) {
          var elemPath = elem.getAttribute('data-route-path');
          if (matches['exact'].indexOf(elemPath) != -1)
            exactSection.appendChild(elem.parentNode.cloneNode(true));

          if (matches['fuzzy'].indexOf(elemPath) != -1)
            fuzzySection.appendChild(elem.parentNode.cloneNode(true));
        })

        // Display 'No Matches' message when no matches are found
        checkNoMatch(exactSection, noExactMatch);
        checkNoMatch(fuzzySection, noFuzzyMatch);
      })
    })
  }

  // Enables functionality to toggle between `_path` and `_url` helper suffixes
  function setupRouteToggleHelperLinks() {

    // Sets content for each element
    function setValOn(elems, val) {
      elems.forEach(function(elem) {
        elem.innerHTML = val;
      });
    }

    // Sets onClick event for each element
    function onClick(elems, func) {
      elems.forEach(function(elem) {
        elem.onclick = func;
      });
    }

    var toggleLinks = document.querySelectorAll('#route_table [data-route-helper]');

    onClick(toggleLinks, function(){
      var helperTxt   = this.getAttribute("data-route-helper"),
          helperElems = document.querySelectorAll('[data-route-name] span.helper');

      setValOn(helperElems, helperTxt);
    });
  }

  setupMatchingRoutes();
  setupRouteToggleHelperLinks();

  // Focus the search input after page has loaded
  document.getElementById('search').focus();
</script>