Writing a rails route that doesn't accept get query params - ruby-on-rails-3

I want to match a specific route but not that route with any get query params.
Lets say I have a route like this:
get '/home', to: 'home#home
This works great for /home but how to I 404 /home?foo=bar?

If ?foo=bar makes no sense in your app it will have no effect at all.
Visiting /home?foo=bar will end seeing /home. This is quite a convention. Why do you need 404?

You can do this with an advanced constraint.
http://guides.rubyonrails.org/routing.html#advanced-constraints
class NoQueryParamsConstraint
def matches?(request)
request.query_parameters.blank?
end
end
get "/home", to: 'home#home', constraints: NoQueryParamsConstraint.new

Related

Ruby on Rails undefined local variable or method

I'm having a problem with redirecting my website to a different page, if the person is younger then a certain age. Either, I need more coffee or I've lost it, I can't seem to see what I did wrong. Thanks so much.
app/models/student.rb
Im trying to sent people to a different site with redirect
private
def must_be_over_13
if birthday && birthday > 13.years.ago
redirect_to under_13_landing_page_path ``
end
end
I defined under_13_landing_page
controller/student controller
def under_13_landing_page
end
views/students
I made the redirect page
under_13_landing_page.html
config/routes.rb
I told it to redirect it to the landing page
get "games_for_kids", to: "students#under_13_landing_page", as: 'under_13_landing_page'
THE ERROR**undefined local variable or method `under_13_landing_page_path' for #**
rendering and redirection are Controller's responsibility NOT Model's. You should not be redirecting in the Model app/models/student.rb. This is the reason of the code failure as Model doesn't have access to the route helper methods such as under_13_landing_page_path in your case.
I would suggest you to return a boolean value from Student#must_be_over_13 method which you can then check in the concerned controller and redirect appropriately.

Matching same route / different HTTP verb to different controller method in Rails

I’ve got a Rails 3 app where instead of the default destruction mapping:
modelname DELETE /modelname/:id modelname#destroy
I would like a dedicated route with a GET ‘fallback’ so that users without Javascript are sent to a confirmation page:
delete_modelname DELETE /modelname/:id/delete modelname#destroy
delete_modelname GET /modelname/:id/delete modelname#confirm_destruction
I can get the above output in rake routes with the following declaration:
resources :modelname, except: [:destroy] do
member {
get 'delete', to: 'confirm_destruction'
delete 'destroy', as: 'delete'
}
end
However, one of the routes does not match, and it seems to be order-dependent, i.e. whichever is defined first then fails to match in testing. I notice that the default ‘overloaded’ routes Rails generates look a bit different in rake routes:
modelnames GET /modelname/:id/delete modelname#index
POST /modelname/:id/delete modelname#create
The route name is not repeated, and a link to create will become a link to index outside a form or a Javascript-enabled request.
It seems I’ve defined two entirely separate routes sharing the same name, rather than overloaded the path as I intended.
What am I missing? Is there any way to get the effect I’m looking for?
Things I’ve tried
Since it appeared to be the route name which was clashing, I tried this:
member {
get 'delete', to: 'confirm_destruction'
delete 'destroy', path: 'delete'
}
Changing as: to path: so that the route name would not be affected, but the paths would match. This works! The following routes are generated:
delete_modelname GET /modelname/:id/delete modelname#confirm_destruction
modelname DELETE /modelname/:id/delete modelname#destroy
This gives the effect I’m after, but unfortunately the modelname DELETE route masks the default modelname PUT route for updates.
Okay, so there is a way to do this, but it’s not quite as elegant as I’d hoped. I’m very open to a better answer if anyone has one.
resources :modelname, except: [:destroy] do
member {
get 'delete', to: 'confirm_destruction'
delete 'destroy', as: 'destroy', path: 'delete'
}
end
It works because these two new routes have unique names for using in views – delete_modelname_path and destroy_modelname_path – but if Javascript is disabled, the destroy_modelname path is still /modelnames/:id/delete, which comes in as a GET request and Rails matches it to the delete_modelname route (i.e. the confirmation page).

Generating routes with polymorphic_url in rails3

I've got some problems with generating routes with polymorphic_url
Here is a part of my route.rb file :
scope path: '/my-account', controller: 'customers/base', as: :customer do
...
resources :addresses, path: 'my-addresses'
...
end
rakes routes | grep addresses give me exactly the route i want :
customer_addresses GET /:locale/my-account/my-addresses(.:format)
Now, if i use
send('customer_addresses_path)
in a link_to, all work fine.
But if i'm not able to generate the same url with polymorphic url :
app.polymorphic_path([:customer,:addresses])
#ActionController::RoutingError: No route matches {:controller=>"addresses"}
app.polymorphic_path([Customer,:addresses])
#"/Customer/my-account/my-addresses" Not the same url :'(
app.polymorphic_path([Customer.first,:addresses])
#"/1/my-account/my-addresses" Not the same url :'(
Is there a way to use polymorphic_url to generate my url?
Asking a question is a good way to reflect on it.
Solution here:
app.polymorphic_path([:customer,:addresses], locale: :en)

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 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.