Add constraint to route to exclude certain keyword - ruby-on-rails

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

Related

Rails routes match by regexp

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

Basic rails 3 routes and unknown routes (including wildcards)

I've been reading about routing in Rails 3 and have been unsuccessful in achieving what I need. Still fairly new to routes in Rails 3 so I may simply be overlooking things or overcomplicating it.
This is what I'm looking to achieve:
website/foo routes to the foo controller, index action
website/foo/index routes to the foo controller, index action
website/foo/bar routes to the foo controller, bar action
website/foo/random routes to the foo controller, index action
website/foo/bar/rondom routes to the foo controller, bar action
where "random" can be any text, numbers, paths (/new/x/w/y/23) or whatever.
I tried using both match, and resources with collection and while it handled the base case, it did not handle "random".
I'm also looking for the respective named path, should that be specified or will it be generated?
You are looking for route globbing.
foo/bar/*additional => "foo#bar"
Examples:
website/foo/bar/random # params[:additional] = "random"
website/foo/bar/random/2 # params[:additional] = "random/2"
website/foo/bar/random/and/more/1/random/stuff/ # params[:additional] = "random/and/more/1/random/stuff/"
http://guides.rubyonrails.org/routing.html contains heaps of really useful information, especially the section on route globbing.
To match exactly what you defined above you could:
# config/routes.rb
namespace :website do
match 'foo' => 'foo#index'
match 'foo/index' => 'foo#index'
match 'foo/bar' => 'foo#bar'
match 'foo/*random' => 'foo#index' # params[:random] will contain "hello/world" if the URL is /website/foo/hello/world
match 'foo/bar/*random' => 'foo#bar'
end
You can use the :as option to specify a named route, e.g.
match 'foo' => 'foo#index', as: 'foo' # helper would be website_foo_path

Rails 3 Routing - specifying an exact controller from within a namespace

I'm having a bit of a problem with route namespaces that I've not encountered before. This is actually a part of some gem development I'm doing - but i've reworked the problem to fit with a more generic rails situation.
Basically, I have a namespaced route, but I want it to direct to a generic (top-level) controller.
My controller is PublishController, which handles publishing of many different types of Models - which all conform to the same interface, but can be under different namespaces. My routes look like this:
# config/routes.rb
namespace :manage do
resources :projects do
get 'publish' => 'publish#create'
get 'unpublish' => 'publish#destroy'
end
end
The problem is that this creates the following routes:
manage_project_publish GET /manage/projects/:project_id/publish(.:format) {:controller=>"manage/publish", :action=>"create"}
manage_project_unpublish GET /manage/projects/:project_id/unpublish(.:format) {:controller=>"manage/publish", :action=>"destroy"}
Which is the routes I want, just not mapping to the correct controller. I've tried everything I can think of try and allow for the controller not to carry the namespace, but I'm stumped.
I know that I could do the following:
get 'manage/features/:feature_id/publish' => "publish#create", :as => "manage_project_publish"
which produces:
manage_project_publish GET /manage/projects/:project_id/publish(.:format) {:controller=>"publish", :action=>"create"}
but ideally, I'd prefer to use the nested declaration (for readability) - if it's even possible; which I'm starting to think it isn't.
resource takes an optional hash where you can specify the controller so
resource :projects do
would be written as
resource :projects, :controller=>:publish do
Use scope rather than namespace when you want a scoped route but not a controller within a module of the same name.
If I understand you correct, you want this:
scope :manage do
resources :projects, :only => [] do
get 'publish' => 'publish#create'
get 'unpublish' => 'publish#destroy'
end
end
to poduce these routes:
project_publish GET /projects/:project_id/publish(.:format) {:action=>"create", :controller=>"publish"}
project_unpublish GET /projects/:project_id/unpublish(.:format) {:action=>"destroy", :controller=>"publish"}
Am I understanding your need correctly? If so, this is what Ryan is explaining.
I think what you want is this:
namespace :manage, module: nil do
resources :projects do
get 'publish' => 'publish#create'
get 'unpublish' => 'publish#destroy'
end
end
This does create the named routes as you wish(manage_projects...) but still call the controller ::Publish
It's a bit late but for anyone still struggling with this, you can add a leading slash to the controller name to pull it out of any existing namespace.
concern :lockable do
resource :locks, only: [] do
post "lock" => "/locks#create"
post "unlock" => "/locks#destroy"
end
end
Now if you include that concern anywhere (either namespaced or not) you will always hit the LocksController.

Specifying Entire Path As Optional Rails 3.0.0

I want to create a Rails 3 route with entirely optional parameters. The example broken route is:
match '(/name/:name)(/height/:height)(/weight/:weight)' => 'people#index'
Which results in 'rake:routes' yielding:
/(/name/:name)(/height/:height)(/weight/:weight)
And thus adding an initial slash to all links:
...
The route works if I specify it as:
match '/people(/name/:name)(/height/:height)(/weight/:weight)' => 'people#index'
But I want to have this as the root URL (as with the first example, which does not work). Thanks.
I don't know if this can be done with Rails' routing engine, but you can add a custom middleware to your stack and it should work just fine.
Put this in lib/custom_router.rb
class CustomRouter
def initialize(app)
#app = app
end
def call(env)
if env['REQUEST_PATH'].match(/^\/(name|height|weight)/)
%w(REQUEST_PATH PATH_INFO REQUEST_URI).each{|var| env[var] = "/people#{env[var]}" }
end
#app.call(env)
end
end
and add
config.middleware.use "CustomRouter"
to your config/application.rb.
You can then set the route like
match '/people(/name/:name)(/height/:height)(/weight/:weight)' => 'people#index'
and it will work.
Does it work if you use a separate root mapping?
root :to => 'people#index'
match '(/name/:name)(/height/:height)(/weight/:weight)' => 'people#index'
It does seem like a pretty major oversight in the new routing system.
There could be a way to hack it through a rack middleware (maybe overriding Rack::URLMap), but that's a little out of my league.

Rails RESTful Routes: override params[:id] or params[:model_id] defaults

I'm trying to understand how to change this rule directly on the map.resources:
supposing I have a route:
map.resource :user, :as => ':user', :shallow => true do |user|
user.resources :docs, :shallow => true do |file|
file.resources :specs
end
end
so I would have RESTful routes like this:
/:user/docs
/docs/:id
/docs/:doc_id/specs
So I see that is difficult to track the params[:doc_id] on this case because sometimes its params[:id] and sometimes its params[:doc_id] and in this case I would like to always call for one specific name so I won't have to create two different declarations for my filters.
Well, I did a little bit of research and I found this patch:
http://dev.rubyonrails.org/ticket/6814
and basically what this does is give you the ability to add a :key parameter on you map.resources so you can defined how you would like to reference it later so we could have something like:
map.resources :docs, :key => :doc ...
so I always would call the param with params[:doc] instead.
But actually this patch is a little bit old (3 years now)
so I was wondering if we don't have anything newer and already built-in for rails to do this task?
P.S I'm not sure about that to_param method defined inside the model, apparently this didn't change anything on my requests, and on the logs I still getting:
Parameters: {"doc_id"=>"6"} or Parameters: {"id"=>"6"} all the time.
One method of making the parameters a little more friendly without writing fully custom routes is
# docs_controller.rb
def show
#doc = Doc.find(params[:doc].to_i)
end
# doc.rb
def to_param
[ id, permalink ].join("-")
# assumes you're storing a permalink formatted version of the name of your doc
end
# routes.rb
map.resources :docs
This will give you URLs that look something like example.com/docs/234-the-name-of-your-doc

Resources