• Stars
    star
    463
  • Rank 94,661 (Top 2 %)
  • Language
    Ruby
  • License
    MIT License
  • Created over 16 years ago
  • Updated 9 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

routing-filter wraps around the complex beast that the Rails routing system is, allowing for unseen flexibility and power in Rails URL recognition and generation.

Routing Filter

Build Status

Routing filters wrap around the complex beast that the Rails routing system is to allow for unseen flexibility and power in Rails URL recognition and generation.

As powerful and awesome the Rails' routes are, when you need to design your URLs in a manner that only slightly leaves the paved cowpaths of Rails conventions, you're usually unable to use all the goodness of helpers and convenience that Rails ships with.

This library comes with four more or less reusable filters and it is easy to implement custom ones. Maybe the most popular one is the Locale routing filter:

  • Locale - prepends the page's :locale (e.g. /de/products)
  • Pagination - appends page/:num (e.g. /products/page/2)
  • Uuid - prepends a uuid for authentication or other purposes (e.g. /d00fbbd1-82b6-4c1a-a57d-098d529d6854/products/1)
  • Extension - appends an extension (e.g. /products.html)

Requirements

Latest routing-filter (~> 0.7.0) only works with Rails >= 6.1. It should not be all too hard to get it working with plain Rack::Mount but I haven't had that usecase, yet.

For older Rails versions you have to use respective older releases.

Installation

Just install the Gem:

$ gem install routing-filter

The Gem should work out of the box with Rails 6.1 after specifying it in your application's Gemfile.

# Gemfile
gem 'routing-filter'

Usage

Once the Gem has loaded you can setup the filters in your routes file like this:

# in config/routes.rb
Rails.application.routes.draw do
  filter :pagination, :uuid
end

Filters can also accept options:

Rails.application.routes.draw do
  filter :extension, :exclude => %r(^admin/)
  filter :locale,    :exclude => /^\/admin/
end

The locale filter may be configured to not include the default locale:

# in config/initializers/routing_filter.rb
# Do not include default locale in generated URLs
RoutingFilter::Locale.include_default_locale = false

# Then if the default locale is :de
# products_path(:locale => 'de') => /products
# products_path(:locale => 'en') => /en/products

Testing

RoutingFilter should not be enabled in functional tests by default since the Rails router gets bypassed for most testcases. Having RoutingFilter enabled in this setup can cause missing parameters in the test environment. Routing tests can/should re-enable RoutingFilter since the whole routing stack gets executed for these testcases.

To disable RoutingFilter in your test suite add the following to your test_helper.rb / spec_helper.rb:

RoutingFilter.active = false

Running the tests

$ bundle install
$ bundle exec rake test

Filter order

You can picture the way routing-filter wraps filters around your application as a russian puppet pattern. Your application sits in the center and is wrapped by a number of filters. An incoming request's path will be passed through these layers of filters from the outside in until it is passed to the regular application routes set. When you generate URLs on the other hand then the filters will be run from the inside out.

Filter order might be confusing at first. The reason for that is that the way rack/mount (which is used by Rails as a core routing engine) is confusing in this respect and Rails tries to make the best of it.

Suppose you have a filter :custom in your application routes.rb file and an engine that adds a :common filter. Then Rails makes it so that your application's routes file will be loaded first (basically route.rb files are loaded in reverse engine load order).

Thus routing-filter will make your :custom filter the inner-most filter, wrapping the application first. The :common filter from your engine will be wrapped around that onion and will be made the outer-most filter.

This way common base filters (such as the locale filter) can run first and do not need to know about the specifics of other (more specialized, custom) filters. Custom filters on the other hand can easily take into account that common filters might already have run and adjust accordingly.

Implementing your own filters

For example implementations have a look at the existing filters in lib/routing_filter/filters

The following would be a skeleton of an empty filter:

module RoutingFilter
  class Awesomeness < Filter
    def around_recognize(path, env, &block)
      # Alter the path here before it gets recognized.
      # Make sure to yield (calls the next around filter if present and
      # eventually `recognize_path` on the routeset):
      yield.tap do |params|
        # You can additionally modify the params here before they get passed
        # to the controller.
      end
    end

    def around_generate(params, &block)
      # Alter arguments here before they are passed to `url_for`.
      # Make sure to yield (calls the next around filter if present and
      # eventually `url_for` on the controller):
      yield.tap do |result|
        # You can change the generated url_or_path here by calling `result.update(url)`.
        # The current url is available via `result.url`
      end
    end
  end
end

You can specify the filter explicitly in your routes.rb:

Rails.application.routes.draw do
  filter :awesomeness
end

(I am not sure if it makes sense to provide more technical information than this because the usage of this plugin definitely requires some advanced knowledge about Rails internals and especially its routing system. So, I figure, anyone who could use this should also be able to read the code and figure out what it's doing much better then from any lengthy documentation.

If I'm mistaken on this please drop me an email with your suggestions.)

Rationale: Two example use cases

Conditionally prepending the locale

An early use-case from which this originated was the need to define a locale at the beginning of a URL in a way so that

  • the locale can be omitted when it is the default locale
  • all the url_helpers that are generated by named routes as well as url_for continue to work in a concise manner (i.e. without specifying all parameters again and again)
  • ideally also plays nicely with default route helpers in tests/specs

You can read about this struggle and two possible, yet unsatisfying solutions here. The conclusion so far is that Rails itself does not provide the tools to solve this problem in a clean and dry way.

Expanding /sections/:id to nested tree segments

Another usecase that eventually spawned the implementation of this plugin was the need to map an arbitrary count of path segments to a certain model instance. In an application that I've been working on recently I needed to map URL paths to a nested tree of models like so:

root
+ docs
  + api
  + wiki

E.g. the docs section should map to the path /docs, the api section to the path /docs/api and so on. Furthermore, after these paths there need to be more things to be specified. E.g. the wiki needs to define a whole Rails resource with URLs like /docs/wiki/pages/1/edit.

The only way to solve this problem with Rails' routing toolkit is to map a big, bold /*everything catch-all ("globbing") route and process the whole path in a custom dispatcher.

This, of course, is a really unsatisfying solution because one has to reimplement everything that Rails routes are here to help with: regarding both URL recognition (like parameter mappings, resources, ...) and generation (url_helpers).

Solution

This plugin offers a solution that takes exactly the opposite route.

Instead of trying to change things between the URL recognition and generation stages to achieve the desired result it wraps around the whole routing system and allows to pre- and post-filter both what goes into it (URL recognition) and what comes out of it (URL generation).

This way we can leave everything else completely untouched.

  • We can tinker with the URLs that we receive from the server and feed URLs to Rails that perfectly match the best breed of Rails' conventions.
  • Inside of the application we can use all the nice helper goodness and conveniences that rely on these conventions being followed.
  • Finally we can accept URLs that have been generated by the url_helpers and, again, mutate them in the way that matches our requirements.

So, even though the plugin itself is a blatant monkey-patch to one of the most complex area of Rails internals, this solution seems to be effectively less intrusive and pricey than others are.

Etc

Authors: Sven Fuchs License: MIT

More Repositories

1

rails-i18n

Repository for collecting Locale data for Ruby on Rails I18n as well as other interesting, Rails related I18n stuff
Ruby
3,979
star
2

gem-release

Release your ruby gems with ease.
Ruby
512
star
3

adva_cms

cutting edge cms, blog, wiki, forum ...
JavaScript
489
star
4

i18n-active_record

I18n ActiveRecord backend
Ruby
281
star
5

adva-cms2

Cutting edge Rails 3 CMS framework
Ruby
115
star
6

hashr

Simple Hash extension to make working with nested hashes (e.g. for configuration) easier and less error-prone.
Ruby
109
star
7

simple_states

A super-slim statemachine-like support library
Ruby
95
star
8

steam

Headless integration testing w/ HtmlUnit: enables testing JavaScript-driven web sites
JavaScript
51
star
9

ripper2ruby

Similar to ruby2ruby this library allows to parse Ruby code, modify and recompile it back to Ruby.
Ruby
41
star
10

simple_opts.sh

Simple Bash option parser
Shell
31
star
11

i18n-missing_translations

Find missing translations in your code more easily.
Ruby
31
star
12

scriptaculous-sortabletree

Implements a sortable tree for scriptacolous
JavaScript
29
star
13

minimal

Minimal templating engine inspired by Markaby & Erector but much smaller and targeting Rails 3
Ruby
26
star
14

rdom

experimental browser implementation in ruby using nokogiri and johnson
Ruby
25
star
15

simple_nested_set

Ruby
22
star
16

space

multi-repository monitoring and shell helper tool to ease development across multiple dependent repositories
Ruby
17
star
17

activesupport-slices

Lazy loaded vertical code slices based on ActiveSupport Dependencies
Ruby
14
star
18

capture_stdout

Adds Kernel.capture_stdout(&block). Useful e.g. for testing command line tools
Ruby
14
star
19

data_migrations

Ruby
14
star
20

vim-tree

vim filesystem tree plugin in ruby
Ruby
13
star
21

rack-cache-purge

Support for purging rack-cache
Ruby
13
star
22

cl

Object-oriented OptionParser based CLI support for rapid CLI development
Ruby
12
star
23

i18n-tools

Tools for working with ruby/rails i18n
Ruby
10
star
24

vim-todo

Provides a simple todo list similar to Textmate’s todo.bundle
Ruby
10
star
25

reference_tracking

Ruby
10
star
26

locator

Generic html element locators for testing tools
Ruby
9
star
27

taskmate

Simplistic TextMate bundle for getting more done with your favorite missing text editor.
Ruby
9
star
28

scrumtious

toying with a remote scrum tool pulling from lighthouse for
Ruby
7
star
29

mephisto_paged_article_list

Mephisto doesn't page article lists out of the box. This plugin adds that.
Ruby
6
star
30

stubby

lightweight and fast stubbing framework
Ruby
6
star
31

with

highly experimental, lightweight and flexible contexts for test/unit
Ruby
6
star
32

resque-heartbeat

Ruby
6
star
33

activemodel-error

I18n support for validation error messages in ActiveModel
Ruby
6
star
34

vim-layout

Ruby
5
star
35

simple_slugs

Ruby
5
star
36

micro_migrations

Minimal ActiveRecord standalone migrations
Ruby
5
star
37

inherited_resources_helpers

Ruby
5
star
38

google_analytics

This plugin is primarily targeted at being used with Mephisto but should be useful with other Rails based CMS or blogging plattforms, too.
Ruby
5
star
39

test_server

Playing around with a test server for Rails that keeps the environment loaded (just like spec_server)
Ruby
5
star
40

will_paginate_liquidized

This plugin allows you to use will_paginate with Liquid templates. That's it.
Ruby
4
star
41

ruby-i18n.tmbundle

Ruby
4
star
42

middleman-toc

Ruby
4
star
43

rjb-require

Adds the ability to import and map Java packages to nested Ruby modules/classes to RJB.
Ruby
4
star
44

globalize-rails.org

Ruby
3
star
45

resque-tagged_queues

Ruby
3
star
46

sh_vars

Shell variable parser
Ruby
3
star
47

em-stdout

Ruby
3
star
48

rack-cache-tags

Support for tagging rack-cache entries
Ruby
3
star
49

rbac

Ruby
3
star
50

with-sugar

Test macros for being used with With
Ruby
3
star
51

treetop_css

Treetop CSS grammar/parser
Ruby
3
star
52

i18n-message

Object-oriented abstraction for looking up translations from I18n.translate
Ruby
3
star
53

adva-cms.org

Ruby
3
star
54

mephisto_tag_cloud

Most complete, sophisticated, standard-conform and allover-awesome implementation of a tag cloud plugin for Mephisto :)
Ruby
3
star
55

mephisto_full_archives

simple plugin that adds a full archives view to Mephisto's own archives
Ruby
2
star
56

bash_opts

SImple Bash options parser
Shell
2
star
57

test_declarative

Simply adds a declarative test method syntax to test/unit
Ruby
2
star
58

simple_taggable

Ruby
2
star
59

dotfiles

Vim Script
2
star
60

mephisto_inverse_captcha

Mephisto anti-comment-spam plugin that adds an "outer floodgate" to the existing (Akismet) spam-protection
Ruby
2
star
61

trsh

Experimental Travis CI Shell using API v3 in Go
Go
2
star
62

uki_reader

Experimental Google Reader UI based on Uki
JavaScript
2
star
63

travis

2
star
64

travis-lxc

Ruby
2
star
65

vim-deliminator

Balancing brackets and quotes
Ruby
1
star
66

database_recorder

Ruby
1
star
67

identity

Ruby
1
star
68

gmail_filters

Ruby
1
star
69

ruby-i18n.org

ruby-i18n.org - static html export, see the source branch for sources
Ruby
1
star
70

command

Ruby
1
star
71

registry

Ruby Class Registry
Ruby
1
star
72

travis-worker

Ruby
1
star
73

pathname_local

Ruby
1
star
74

dom-test

Ruby export of Level 1 and 2 W3C DOM tests
Ruby
1
star
75

led-go

A line editor in Go. Inspired by linenoise, but written with extensibility and separation of concerns in mind.
Go
1
star
76

svenfuchs.github.com

jo
HTML
1
star
77

rails-i18n-chart

Ruby
1
star
78

box

C++
1
star
79

statics

Ruby
1
star
80

silence_log_tailer

Silence rails/server log tailing to console
Ruby
1
star