Rails routes match by regexp - ruby-on-rails

I have routes rule:
match '*path' => redirect("/some_scope/%{path}")
But I need to apply this only if the current path doesn't start from /some_scope/

http://guides.rubyonrails.org/routing.html#dynamic-segments
How do I use regular expressions in a Rails route to make a redirection?
Those two should be able to help you solve your problem. It would be nice if you gave us real code, so I can determine exactly what you're trying to do, because it seems like you might not even need a regex in this case.
If you're trying to do what I think you're trying to do... that is apply a scope to a path if it doesn't already contain that scope, you would be better off doing before_filters in your ApplicationController. That is:
before_filter :check_locale
protected
def check_locale
redirect_to "/some_scope#{request.path_info}" if not request.path_info =~ /^\/some_scope\//
end

Here is my solution:
DEFAULT_PATH_SCOPE = "/my_scope"
default_scope_constraint = ->(request) do
!request.path.starts_with?(DEFAULT_PATH_SCOPE)
end
constraints(default_scope_constraint) do
match '*path' => redirect("#{DEFAULT_PATH_SCOPE}/%{path}")
end

Related

Rails 4.1.2 - to_param escapes slashes (and breaks app)

I use in my app to_param to create custom URL (this custom path contains slashes):
class Machine < ActiveRecord::Base
def to_param
MachinePrettyPath.show_path(self, cut_model_text: true)
end
end
The thing is, that since Rails 4.1.2 behaviour changed and Rails doesn't allow to use slashes in the URL (when use custom URL), so it escapes slashes.
I had such routes:
Rails.application.routes.draw do
scope "(:locale)", locale: /#{I18n.available_locales.join("|")}/ do
resources :machines, except: :destroy do
collection do
get :search
get 'search/:ad_type(/:machine_type(/:machine_subtype(/:brand)))', action: 'search', as: :pretty_search
get ':subcategory/:brand(/:model)/:id', action: 'show', as: :pretty
patch ':subcategory/:brand(/:model)/:id', action: 'update' # To be able to update machines with new rich paths.
end
end
end
end
I tried by recommendation in the thread to use glob param just for show method to make sure it works:
resources :machines, except: :destroy do
#...
end
scope format: false do
get '/machines/*id', to: "machines#show"
end
But it absolutely doesn't work. I still have such broken links:
http://localhost:3000/machines/tractor%2Fminitractor%2Fmodel1%2F405
Of course, if I replace escaped slashes on myself:
http://localhost:3000/machines/tractor/minitractor/model1/405
And try to visit path, then page'll be opened.
Any ideas how can I fix that?
I've been having the same problem when using the auto-generated url helpers. I used a debugger to trace the new behavior to its source (somewhere around ActionDispatch::Journey::Visitors::Formatter), but didn't find any promising solutions. It looks like the parameterized model is now strictly treated as a single slash-delimited segment of the path and escaped accordingly, with no options to tell the formatter otherwise.
As far as I can tell, the only way to get the url helper to produce the old result is to use your original routes file and pass each segment separately, something like:
pretty_machine_path(machine.subcategory, machine.brand, machine.model, machine.id)
This is ugly as hell and obviously not something you'll want to do over and over. You could add a method to MachinePrettyPath to generate the segments as an array and explode the result for the helper (say, pretty_machine_path(*MachinePrettyPath.show_path_segments(machine))) but that's still pretty verbose.
Between the above headaches and the "You're Doing it Wrong" attitude from the devs in that Rails ticket you linked to, the simplest option for me was to bite the bullet and write a custom URL helper instead of using to_param. I've yet to find a good example of the "right" way to do that, but something like this bare-bones example should serve the purpose:
#app/helpers/urls_helper.rb
module UrlsHelper
def machine_path(machine, options = {})
pretty_machine_path(*MachinePrettyPath.show_path_segments(machine), options)
end
end
#app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
helper :urls #for the views
include UrlsHelper #for controllers
#...
end
If you're sure the returned url is safe you should add .html_safe to the returned string:
MachinePrettyPath.show_path(self, cut_model_text: true).html_safe
(didn't see anywhere else where it can be escaped but check all the flow in your app, maybe manually testing method by by method)
How about defining the url yourself?
def to_param
"#{ subcategory.title.parameterize }/#{ brand.name.parameterize }/#{ model.name.parameterize) }/#{ id }"
end
And then in your routes something like this:
get 'machines/*id', to: "machines#show"
You also have to split your params[:id] when you do a find on your model.
Machine.find( params[:id].split("/").last )

How to override routes path helper in rails?

My route is defined like this
match '/user/:id' => 'user#show', :as => :user
If for some reason a nil ID is passed I want the route helper to return only '#' and if ID is not nil i want it to return normal path like '/user/123'.
or is there any better way to do it.
this route helper has been used in lot of places in my code so I dont want to change that. Instead I am looking for one place which will effect all instances of user_path.
Thanks
I did this in app/helpers/url_helper.rb
module UrlHelper
def resource_path(resource, parameters = {})
# my implementation
"/#{resource.category}/#{resource.name}#{ "?#{parameters.to_query}" if parameters.present? }"
end
end
I know there are ways to define such a nested route but I have a codebase using that route in several parts, as the question states.
I tried to alias old method, but was not recognised:
alias_method :old_resource_path, :resource_path
module CustomUrlHelper
def user_path(user, options = {})
if user.nil?
"#"
else
super
end
end
end
# Works at Rails 4.2.6, for older versions try http://stackoverflow.com/a/31957323/474597
Rails.application.routes.named_routes.url_helpers_module.send(:include, CustomUrlHelper)

What will be my route?

I'm working on Rails 3.
My URL is: http://localhost:3000/terms_and_conditions?val=pp
My method is below:
class GeneralController < ApplicationController
def terms_and_conditions
if !params[:val].nil?
#val=params[:val]
else
#val='tc'
end
end
end
What will be my route? Please help me to create the route.
I suggest you first read the guides titled Rails Routing from the Outside In.
To setup a simple GET accessible route add the following to your routes.rb file
get "/terms_and_conditions" => "general#terms_and_conditions"
If you need more than just GET, you can use match instead. In your app root you can perform rake routes to see all the routes of your app as well. With regards to your choice of exposing /terms_and_conditions — it would be better if you used a shorter path such as /terms and/or consider doing /terms-and-conditions instead.
Try:
[YourAppNameHere]::Application.routes.draw do
match '/terms_and_conditions', to: 'general#terms_and_conditions'
end

Add constraint to route to exclude certain keyword

I am using Rails and I want to use contraint in route to exclude that route if keyword "incident" is anywhere in the url.
I am using rails3.
Here is my existing routes.
match ':arg', :to => "devices#show", :constraints => {:arg => /???/}
I need to put something in constraints so that it does not match if word "incident" is there.
Thanks
(?!.*?incident).*
might be what you want.
This is basically the same question as How to negate specific word in regex?. Go there for a more detailed answer.
Instead of bending regular expressions a way it is not intended to, I suggest this approach instead:
class RouteConstraint
def matches?(request)
not request.params[:arg].include?('incident')
end
end
Foo::Application.routes.draw do
match ':arg', :to => "devices#show", :constraints => RouteConstraint.new
...
Its a lot more verbose, but in the end more elegant I think.
Adding onto #Johannes answer for rails 4.2.5:
config/routes.rb (at the VERY end)
constraints(RouteConstraint) do
get "*anythingelse", to: "rewrites#page_rewrite_lookup"
end
config/initializers/route_constraint.rb
class RouteConstraint
def self.matches?(request)
not ["???", "Other", "Engine", "routes"].any? do |check|
request.env["REQUEST_PATH"].include?(check)
end
end
end

How to implement "short" nested vanity urls in rails?

I understand how to create a vanity URL in Rails in order to translate
http://mysite.com/forum/1 into http://mysite.com/some-forum-name
But I'd like to take it a step further and get the following working (if it is possible at all):
Instead of:
http://mysite.com/forum/1/board/99/thread/321
I'd like in the first step to get to something like this: http://mysite.com/1/99/321
and ultimately have it like http://mysite.com/some-forum-name/some-board-name/this-is-the-thread-subject.
Is this possible?
To have this work "nicely" with the Rails URL helpers you have to override to_param in your model:
def to_param
permalink
end
Where permalink is generated by perhaps a before_save
before_save :set_permalink
def set_permalink
self.permalink = title.parameterize
end
The reason you create a permalink is because, eventually, maybe, potentially, you'll have a title that is not URL friendly. That is where parameterize comes in.
Now, as for finding those posts based on what permalink is you can either go the easy route or the hard route.
Easy route
Define to_param slightly differently:
def to_param
id.to_s + permalink
end
Continue using Forum.find(params[:id]) where params[:id] would be something such as 1-my-awesome-forum. Why does this still work? Well, Rails will call to_i on the argument passed to find, and calling to_i on that string will return simply 1.
Hard route
Leave to_param the same. Resort to using find_by_permalink in your controllers, using params[:id] which is passed in form the routes:
Model.find_by_permalink(params[:id])
Now for the fun part
Now you want to take the resource out of the URL. Well, it's a Sisyphean approach. Sure you could stop using the routing helpers Ruby on Rails provides such as map.resources and define them using map.connect but is it really worth that much gain? What "special super powers" does it grant you? None, I'm afraid.
But still if you wanted to do that, here's a great place to start from:
get ':forum_id/:board_id/:topic_id', :to => "topics#show", :as => "forum_board_topic"
Take a look at the Rails Routing from the Outside In guide.
maybe try something like
map.my_thread ':forum_id/:board_od/:thread_id.:format', :controller => 'threads', :action => 'show'
And then in your controller have
#forum = Forum.find(params[:forum_id])
#board = #forum.find(params[:board_id])
#thread = #board.find(params[:thread_id])
Notice that you can have that model_id be anything (the name in this case)
In your view, you can use
<%= link_to my_thread_path(#forum, #board, #thread) %>
I hope this helps

Resources