Rails routing with a letter as separator - ruby-on-rails-3

I am looking to create a route which does image resizing, and I want it to be of the following format:
/image/1024x768/hello.jpg
and I need to collect the width and height out dynamically, as such, I need routing to be something like:
/image/:widthx:height/:name.jpg
obviously this won't work because :width would be mixed as :widthx
I have tried searching for solution to this, but I can't seem to find one - surely rails routing isn't so rigid that it doesn't faciliate this?
Also, I don't want to set "x" as a "separator" as that would mean I can't use it as part of the image filename.
Any ideas of how I can set up this routing?

You can enclose the named parameters in brackets:
match 'resize/(:width)x(:height)/:image',
:width => /\d+/, :height => /\d+/,
:to => 'image#resize'
An URL like /resize/100x400/hello.jpg will end up as the following parameters:
{
"controller"=>"image",
"action"=>"resize",
"width"=>"100", "height"=>"400",
"image"=>"hello", "format"=>"jpg"
}

Related

Where did my filename extension go in the parameter list

I am following this answer while trying to figure out how to display images located in my app directory Rails 3, displaying jpg images from the local file system above RAILS_ROOT
I've created a controller as shown there, added the match line to routes.rb, and in a test webpage I want to load up an image called test.jpg using the code
<img src="/serve_image/test.jpg" />
But I get an error saying the file <...>/public/images/test was not found.
Then I went and renamed my image so that the .jpg was gone, and then the script found my image and loaded it up as wanted.
Any ideas where the extension went? I am not sure how to debug this issue.
By default, Rails doesn't match dots in a dynamic segment. So for this route:
match '/serve_image/:filename' => 'images#serve'
:filename will only match up to the first dot it finds. So /serve_image/test.jpg will match test as the filename and will think you're expecting a JPG as a response. What you need to do is tell the router that you want to include the dot in the filename, something like this:
match '/serve_image/:filename' => 'images#serve', :constraints => {:filename => /[^\/]+/}
That will match anything except a forward slash, giving you your complete filename in params[:filename]
See the Rails Routing guide section on Dynamic Segments for more info.

How do I do a redirection in routes.rb passing on the query string

I had a functioning redirect in my routes.rb like so;
match "/invoices" => redirect("/dashboard")
I now want to add a query string to this so that, e.g.,
/invoices?show=overdue
will be redirected to
/dashboard?show=overdue
I've tried several things. The closest I have got is;
match "/invoices?:string" => redirect("/dashboard?%{string}")
which gives me the correct output but with the original URL still displayed in the browser.
I'm sure I'm missing something pretty simple, but I can't see what.
You can use request object in this case:
match "/invoices" => redirect{ |p, request| "/dashboard?#{request.query_string}" }
The simplest way to do this (at least in Rails 4) is do use the options mode for the redirect call..
get '/invoices' => redirect(path: '/dashboard')
This will ONLY change the path component and leave the query parameters alone.
While the accepted answer works perfectly, it is not quite suitable for keeping things DRY — there is a lot of duplicate code once you need to redirect more than one route.
In this case, a custom redirector is an elegant approach:
class QueryRedirector
def call(params, request)
uri = URI.parse(request.original_url)
if uri.query
"#{#destination}?#{uri.query}"
else
#destination
end
end
def initialize(destination)
#destination = destination
end
end
Now you can provide the redirect method with a new instance of this class:
get "/invoices", to: redirect(QueryRedirector.new("/dashboard"))
I have a written an article with a more detailed explanation.

Rails 3 route matching with tokens

I'm trying to setup a redirect that keeps the format of the request. However, in some cases the format is not specified and I need to account for that too.
I thought format was part of the path so at first I set it up like this...
match 'v:api/*path', :to => redirect("/%{path}")
However, format is not part of the request. So then changing it to this will work when a format is specified.
match 'v:api/*path', :to => redirect("/%{path}.%{format}")
The downside is in some cases the format isn't specified and this results in an error because it can't match on format.
So my question is... What is the correct way to create this catchall?
The intent is that if a version number is specified to which a version of that route doesn't exist it should go to the root route, without a version specified.
Okay I think I've figured it out. Be forewarned, it's ugly though...
match '/v:api/*path', to: redirect { |params, request|
if params[:format]
"/#{params[:path]}.#{params[:format]}"
else
"/#{params[:path]}"
end
}

Rails route to catch everything except assets

I'm trying to allow admin to create pages on the root path. So far i have:
get ':path' => "pages#show" ,:as =>:page, :path => /[^\.]+/
Basically i'm trying to ignore all paths with a dot in them (like .png). This does not seem to work as everything is rejected (i only want things in the public directory to be rejected, like fonts, icons, images..)
Thanks
As I explained in my comment above, "everything in public is directly rendered by the webserver" is NOT true if the desired asset does not exist. This will result in your catch-all route catching this undesired side-effect. This could cause a number of problems, as I explained. So, A specific catch-all route is needed to compensate for this:
get ':path' => "pages#show", :as => :page, :constraints => lambda{|req| req.path !~ /\.(png|jpg|js|css)$/ }
you can manipulate the regex how ever you see fit as my goal was just to get you on the right track by showing you that you can pass a block to the :constraints option. Also, I didn't just test req.format because that would exclude requests with header information for js format and would result in the catch all not working for these types of requests (not a usual case for a catch-all, but that's irrelevant). By using req.path instead, the header info is left intact/working and the path dictates whether or not this request is caught by this route.
I hope this helps you.
TESTING:
To test to see if your catch-all is actually catching what you want and not additional public resources, follow these steps. First put a debugger in your catch-all action, in your PagesController. Then make a request to a public file png/js/css file that DOES exist, like localhost:3000/images/example_image.png, and it should not hit your catch-all, as usual. Now, change the path to an image that doesn't exist, localhost:3000/images/no_image.png . If the request does not hit your debugger, your catch-all is not catching the image file request, and your ALL SET. If the request does hit your debugger, that means your catch-all is catching the image file request which means you need to revise your constraints in your catch-all.
By default dynamic segments don’t accept dots – this is because the
dot is used as a separator for formatted routes. If you need to use a
dot within a dynamic segment add a constraint which overrides this –
for example :id => /[^/]+/ allows anything except a slash.
http://guides.rubyonrails.org/routing.html#bound-parameters
So just removing the condition works. There might be another better solution to this problem though.

Can you remove the _snowman in Rails 3?

I'm building an app on Rails 3 RC. I understand the point behind the _snowman param (http://railssnowman.info/)...however, I have a search form which makes a GET request to the index. Therefore, submitting the form is creating the following query string:
?_snowman=☃&search=Box
I don't know that supporting UTF encoding is as important as a clean query string for this particular form. (Perhaps I'm just too much of a perfectionist...hehe) Is there some way to remove the _snowman param for just this form? I'd rather not convert the form to a POST request to hide the snowman, but I'd also prefer it not be in my query string. Any thoughts?
You can avoid the snowman (now a checkmark) in Rails 3 by.... not using Rails for the search form. Instead of using form_tag, write your own as outlined in:
Rails 3 UTF-8 query string showing up in URL?
Rails helpers are great unless they're not helping. Do-it-yourself is good as long as you understand the consequences, and are willing to maintain it in the future.
I believe the snowman has to be sent over the wire to ensure your data is being encoded properly, which means you can't really remove the snowman input from forms. Since, it's being sent in your GET request, it will have to be appended to the URL.
I suppose you could write some javascript to clean up the URL once the search page loads, or you could setup a redirect to the equivalent URL minus the snowman. Both options don't really feel right to me.
Also, it doesn't seem there is any way to configure Rails to not output it. If you really wanted to get rid of it, you could comment out those lines in Rails' source (the committed patches at the bottom of railssnowman.info should lead you to the files and line numbers). This adds some maintenance chores for you when you upgrade Rails. Perhaps you can submit a patch to be able to turn this off?
EDIT: Looks like they just switched it to what looks like a checkmark instead of a snowman.
EDIT: Oops, back to a snowman.
In Rails 4.1 you can use the option :enforce_utf8 => false to disable utf8 input tag.
However I want to use this in Rails 3, so I monkey-patched my Rails. I put the following in the config/initializers directory.
# allow removing utf8 using enforce_utf8, remove after Rails 4.1
module ActionView
module Helpers
module FormTagHelper
def extra_tags_for_form(html_options)
authenticity_token = html_options.delete("authenticity_token")
method = html_options.delete("method").to_s
method_tag = case method
when /^get$/i # must be case-insensitive, but can't use downcase as might be nil
html_options["method"] = "get"
''
when /^post$/i, "", nil
html_options["method"] = "post"
token_tag(authenticity_token)
else
html_options["method"] = "post"
tag(:input, :type => "hidden", :name => "_method", :value => method) + token_tag(authenticity_token)
end
enforce_utf8 = html_options.delete("enforce_utf8") { true }
tags = (enforce_utf8 ? utf8_enforcer_tag : ''.html_safe) << method_tag
content_tag(:div, tags, :style => 'margin:0;padding:0;display:inline')
end
end
end
end