I tried to add constraints to a group of scoped routes like so:
constraints locale: 'de' do
scope 'magazin' do
get '', to: 'magazine#index', as: 'magazine'
# more routes
end
end
It doesn't make use of the restriction.
Whereas putting the restriction to a single route works as expected.
get '', to: 'magazine#index', as: 'magazine', constraints: { locale: 'de' }
I tried to use the constraints block in different positions, inside and outside the scope block. Without any change in the result.
The Rails Guide for Routing has this example which I pretty much copied:
namespace :admin do
constraints subdomain: 'admin' do
resources :photos
end
end
Any ideas what's wrong with the code?
Without having the whole routes.rb file it is hard to say why it doesn't work as expected.
Is it possible you have some kind of scope defined for locale??
Imagine sth like
scope '/:locale', locale: /de|en/ do
# lots of routes so you are not aware of the scope
constraints locale: "de" do
scope 'magazin' do
get '', to: 'magazine#index', as: 'magazine'
end
end
end
With this your are actually setting a constraint to locale to be either de or en. The constraint from the scope has precedence over the constraints block.
While this is not clear from the rails guide I found a merge request that proves my argumentation.
Related
I hoped route constraints would allow me to have admin.example.com/widgets/ and example.com/admin/widgets/ be effectively the same, with the URL helper admin_widgets_path pointing to the correct one based on the current subdomain. However, the route helper seems to only point to one or the other regardless of constraints.
Am I doing something wrong? Is this a bug? Is there a better way to solve this problem?
A rails app with an example of the problem has been published here, but the relevant details are below.
My config/routes.rb
class Subdomains
# any domain that starts with 'admin.'
def self.admin?
-> (request) { request.host =~ /^admin\./ }
end
# any other domain
def self.primary?
-> (request) { !admin?.(request) }
end
end
Rails.application.routes.draw do
constraints(Subdomains.primary?) do
namespace :admin do
resources :widgets
end
end
constraints(Subdomains.admin?) do
scope module: "admin", as: :admin do
resources :widgets
end
end
root to: "admin/widgets#index"
end
My app/views/admin/widgets/index.html.erb
<%= link_to "Widgets", admin_widgets_url %>
In this configuration, admin_widgets_url always returns /admin/widgets/ which isn't a valid route on admin.example.com so clicking the link results in a routing error on that subdomain. If the admin subdomain constraint block is put first in the routes, then the URL helper always returns /widgets/, breaking the link on a non-admin domain.
The routes otherwise work, but not sure how to get the URL helpers to point to the correct one.
Your expectations are wrong - Rails reads the entire routes definition as part of the setup phase. This is also when the route helpers are created. The constraint is not actually evaluated until later when the request is matched.
Rails splits this into distinct phases to allow forking.
Instead you may need to override the route helpers.
Currently I am using a custom constraint that is linked to a single route like:
get '/hello', to: 'account#index',
constraints: AccountConstraint.new
Basically my custom constraint is looking up the request.host, and if it is found in the database the matches? method will return true and then the account#index action will get called.
What I want to do is, if the constraint matches, then based on the path it will go do a different action.
So my constraint would be like:
class AccountConstraint
def matches?(request)
# lookup the database, return true if record found
end
end
Then I want my route.rb file to do something like this (pseudo code below_:
if AccountConstraint matches
get '/', to: "account#index"
get '/hello', to: "account#hello"
end
Is something like this possible? If so, how?
I'm not sure if I'm understanding the question, but it sounds like what you want is a scope:
scope constraints: AccountConstraint.new do
get '/', to: "account#index"
get '/hello', to: "account#hello"
end
The routes inside the scope will only be accessible if AccountConstraint matches.
My application was written in English and it was all good. Yesterday I starts to play with the Rails.I18n internationalization support. It is all good. When I browse http://localhost:3000/jp/discounts it is in Japanese, and 'http://localhost:3000/discounts' gives me the default English locale (when locale is not specified).
Here is my route.rb and as you can see, the admin namespace is not localized:
scope '(:locale)' do
resources :discounts do
resource :map, only: :show
collection do
get :featured_city
end
end
end
namespace :admin do
resources :users do
collection do
get :members
get :search
end
end
end
However my RSpec starts to fail.
Failure/Error: it { should route_to('admin/users#edit', id: '1') }
The recognized options <{"action"=>"edit", "controller"=>"users", "locale"=>"admin", "id"=>"1"}>
did not match <{"id"=>"1", "controller"=>"admin/users", "action"=>"edit"}>,
difference: <{"controller"=>"admin/users", "locale"=>"admin"}>.
<{"id"=>"1", "controller"=>"admin/users", "action"=>"edit"}> expected but was
<{"action"=>"edit", "controller"=>"users", "locale"=>"admin", "id"=>"1"}>
The tests related to admin all have this kind of problem. How can I resolve this? It works fine in development.
Here are other locale-related code:
application_controller.rb
def default_url_options
{ locale: I18n.locale }
end
config/initializers/i18n.rb
#encoding: utf-8
I18n.default_locale = :en
LANGUAGES = [
['English', 'en'],
["Japanese", 'jp']
]
When Rails attempts to match a given URL to a route, it starts at the top of the config/routes.rb file and stops at the first route that it considers to be a match. Since, in your original question, you had the scope block first, Rails thought your /admin URLs indicated a route with :locale => 'admin'.
You need Rails to match paths beginning in /admin to your admin namespace. By placing that first in your routes file, you cause Rails to "stop looking" once it finds that match.
This is a gross oversimplification, but I hope it's helpful.
Also check out the Rails routing guide if you haven't already.
Is there a way to remove routes specified in a gem in Rails 3? The exception logger gem specifies routes which I don't want. I need to specify constraints on the routes like so:
scope :constraints => {:subdomain => 'secure', :protocol => 'https'} do
collection do
post :query
post :destroy_all
get :feed
end
end
Based on the Rails Engine docs, I thought I could create a monkey patch and add a routes file with no routes specified to the paths["config/routes"].paths Array but the file doesn't get added to ExceptionLogger::Engine.paths["config/routes"].paths
File: config/initializers/exception_logger_hacks.rb
ExceptionLogger::Engine.paths["config/routes"].paths.unshift(File.expand_path(File.join(File.dirname(__FILE__), "exception_logger_routes.rb")))
Am I way off base here? Maybe there is a better way of doing this?
It is possible to prevent Rails from loading the routes of a specific gem, this way none of the gem routes are added, so you will have to add the ones you want manually:
Add an initializer in application.rb like this:
class Application < Rails::Application
...
initializer "myinitializer", :after => "add_routing_paths" do |app|
app.routes_reloader.paths.delete_if{ |path| path.include?("NAME_OF_GEM_GOES_HERE") }
end
Here's one way that's worked for me.
It doesn't "remove" routes but lets you take control of where they match. You probably want every route requested to match something, even if it is a catch all 404 at the bottom.
Your application routes (MyApp/config/routes.rb) will be loaded first (unless you've modified the default load process). And routes matched first will take precedence.
So you could redefine the routes you want to block explicitely, or block them with a catch all route at the bottom of YourApp/config/routes.rb file.
Named routes, unfortunately, seem to follow ruby's "last definition wins" rule. So if the routes are named and your app or the engine uses those names, you need to define the routes both first (so yours match first), and last (so named routes point as you intended, not as the engine defines.)
To redefine the engine's routes after the engine adds them, create a file called something like
# config/named_routes_overrides.rb
Rails.application.routes.draw do
# put your named routes here, which you also included in config/routes.rb
end
# config/application.rb
class Application < Rails::Application
# ...
initializer 'add named route overrides' do |app|
app.routes_reloader.paths << File.expand_path('../named_routes_overrides.rb',__FILE__)
# this seems to cause these extra routes to be loaded last, so they will define named routes last.
end
end
You can test this routing sandwich in the console:
> Rails.application.routes.url_helpers.my_named_route_path
=> # before your fix, this will be the engine's named route, since it was defined last.
> Rails.application.routes.recognize_path("/route/you/want/to/stop/gem/from/controlling")
=> # before your fix, this will route to the controller and method you defined, rather than what the engine defined, because your route comes first.
After your fix, these calls should match each other.
(I posted this originally on the refinery gem google group here: https://groups.google.com/forum/?fromgroups#!topic/refinery-cms/N5F-Insm9co)
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