2

I have created a route plus controller for doing dynamic css in ruby on rails as per the instructions here:

http://www.misuse.org/science/2006/09/26/dynamic-css-in-ruby-on-rails/

It took some changing to account for a newer version of ruby on rails, but the problem comes in with the routes.rb entry. The original entry was this:

  # dynamic CSS (stylesheets)
  map.connect 'rcss/:rcssfile',
    :controller => 'rcss',
    :action => 'rcss'

This did not work with a newer version of RoR, and I found this solution to work:

  # dynamic CSS (stylesheets)
  map.connect 'rcss/:rcssfile.css',
    :controller => 'rcss',
    :action => 'rcss'

However, now I was bummed that I couldn't get a catch-all filetype extension handler. The request had to have the .css extension. Playing around with it further I came up with this:

  # dynamic CSS (stylesheets)
  map.connect 'rcss/:rcssfile.:format',
    :controller => 'rcss',
    :action => 'rcss'

So this is much better. Now I could potentially request a file that ended in .foobar or whatever and match it with a handler. Not that I would necessarily, but it's more about understanding everything.

So then I tried creating a file that looked something like "foo.net.rcss" . Now it would seem that the first dot messes everything up. "no routes match rcss/foo.net.css". My questions are as follows:

  1. How can I match any filename and any extension regardless of how many dots are in the filename?

  2. Why does the first example not work in later RoR versions?

  3. Why do multiple dots screw up the match?

Thanks in advance for any help.

------- update -------

I am using Rails 3.0.5 . As per some more research I can shorten the syntax to:

match 'rcss/:rcssfile', :to => 'rcss#rcss'

This is the equivalent of the first example that did not seem to work, however using this syntax it works just as expected.

match 'rcss/:rcssfile:.:format', :to => 'rcss#rcss'

This also works just like my previous example #3, however it still has the problem of not matching a file with multiple periods.

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
Corey O.
  • 466
  • 9
  • 19

3 Answers3

5

It would seem that labeling a standard ":paramater" takes special consideration for the period character. ":parameter" will match a path with up to one period, ":parameter.:extension" will match a path with up to two periods, but the :extension will be only what's between the two periods, etc.

A way around this is to use what is called "Route Globbing", which uses an asterisk instead of a colon:

match 'rcss/*rcssfile', :to => 'rcss#rcss'

The only caveat is that this will match ANYTHING after the asterisk, including subdirectories. As such, you want to make sure that this does not accidentally expose any secure files or accidentally render things unintentionally.

Corey O.
  • 466
  • 9
  • 19
  • 3
    I had tried using this approach, but found that `params[:rcssfile]` doesn't contain the file extension. So browsing to `rcss/main.css` ends up sending `"main"` as the parameter, stripping off the `".css"` extension. – CodingWithSpike Mar 02 '16 at 16:12
2

I used this for a general case when you don't know the extension:

get '/uploads/:basename.:extension', to: 'controller#action', basename: /.*(?=\.[\w\d]+$)/
Grant Birchmeier
  • 17,809
  • 11
  • 63
  • 98
ArashM
  • 1,379
  • 13
  • 18
  • This seems better than the accepted answer in that it doesn't have the caveat of matching `ANYTHING` as they describe.... assuming the basename regex is changed to `/[^\/]*(?=\.[\w\d]+$)/` – YWCA Hello Jun 30 '20 at 21:45
1

Use a regex to match the filename?

map.connect 'rcss/:rcssfile',
  :controller => 'rcss',
  :action => 'rcss',
  :requirements => {:rcssfile => /.+\.rcss/ }

This would match (anything).rcss - you could adjust the regex for various suffixes.

stef
  • 14,172
  • 2
  • 48
  • 70
  • Hi Stef, thanks for the reply. I'm looking for something that will match /rcss/(any_filename).(any_extension) and not just a single extension. The caveat is that (any_filename) may have multiple periods/dots and (any_extension) will be anything after the last dot (if it exists). – Corey O. Mar 21 '11 at 22:38
  • The :requirements is the bit you need - just change the regex to whatever you want - eg. `/*` would match anything – stef Mar 23 '11 at 22:48
  • Hi Stef, the :requirements is the missing piece that I needed about how to add a regex match to the route. After you pointed it out, I was able to discover a lot more about advanced routing. However, take a look at my third working example on the post. You can see that it gives the filename and the file extension separate usable parameters (:rcssfile and :format) that can be accessed in the controller (as long as there is only 1 period). Is there a way to use the :requirements to help accomplish this and solve the multiple period problem? This is the last part that I cannot figure out. – Corey O. Mar 24 '11 at 22:49
  • My apologies Stef, using rails 3.0.5, your solution actually does not work. It still will not match a file with multiple periods. The URL request does not seem to get past the initial "match 'rcss/:rcssfile'" declaration at all. As such, I don't think that the regex even gets tested. – Corey O. Mar 24 '11 at 23:13
  • In that case, the best I can suggest is using Rails Metal and adding a small Rack application that does what you need to do that isn't Rails. So you mount a small Sinatra application with one route, and put your logic in that. – stef Mar 25 '11 at 19:06