I'm using rails 3.0.5, rspec2 with latest capybara.
Routes setup like:
scope "(:locale)", :locale => /de|fr|it|en/ do
resources :dossiers
end
In application_controller I have this:
def default_url_options(options={})
options[:locale] = "es"
options
end
So in my views I can use
link_to 'test', dossier_path(1)
without any problems.
But when I do the same in capybara's visit it tries to use the 1 for the locale and not for the id. It only works when I use
visit dossier_path(nil, 1)
or
visit dossier_path(:id => 1)
But both are ugly and looks like a dirty hack. So why do I need to use this dirty hack and what do I jave to do, so that I can use the path methods just like in the views (so without the dirty hack of having to add nil or explicitly pass :id => ...)? :)
I ran into a similar issue. You can set the default_url_options in a before block like this in request specs:
before :each do
app.default_url_options = { :locale => :es }
end
Unfortunately the route generation happens outside of Application Controller. And Capybara doesn't do any magic to provide default url options from it to route helpers.
But you can specify default locale inside your routes.rb
scope "(:locale)", :locale => /de|fr|it|en/, :defaults => { :locale => "es" } do
resources :dossiers
end
And now if you don't pass :locale option to a route helper it will default to "es". Actually, it isn't necessary to keep def default_url_options anymore in your controller.
I'm running rails 3.2.6 and I use a technique that I found here https://github.com/rspec/rspec-rails/issues/255 under Phoet's comment. Just put this somewhere in /spec/support and it should cover all your specs
class ActionView::TestCase::TestController
def default_url_options(options={})
{ :locale => I18n.default_locale }
end
end
class ActionDispatch::Routing::RouteSet
def default_url_options(options={})
{ :locale => I18n.default_locale }
end
end
Opposite as shown here under Using Capybara with RSpec the only way I've been able to get it working is writing
visit user_path(:id => myuser.id.to_s)
so for you it should be
visit dossier_path(:id => "1")
Does it work?
Related
Is there any way that i can make url_for to return the url based on the request.host during action dispatch routing ?
mount Collaborate::Engine => '/apps/collaborate', :constraints => {:host => 'example.com' }
mount Collaborate::Engine => '/apps/worktogether'
Example:
When the user is on example.com host
collaborate_path => /apps/collaborate
When the user is on any other host
collaborate_path => /apps/worktogether
After a lot of research, i realize that RouteSet class has named_routes which does not consider the constraints to return the url.
I've tried overriding #set in action_dispatch/routing/route_set.rb to pickup from rails application but dint work as expected
#search_set = Rails.application.routes.set.routes.select{|x| x.defaults[:host] == options[:host] }[0]
#set = #search_set unless #search_set.blank?
Remove .com in your example
mount Collaborate::Engine => '/apps/collaborate', :constraints => {:host => 'examplesite' }
mount Collaborate::Engine => '/apps/worktogether'
Should just work
If you need a more advanced constraint, make your own constraint:
class CustomConstraint
def initialize
# Things you need for initialization
end
def matches?(request)
# Do your thing here with the request object
# http://guides.rubyonrails.org/action_controller_overview.html#the-request-object
request.host == "example"
end
end
Rails.application.routes.draw do
get 'foo', to: 'bar#baz',
constraints: CustomConstraint.new
end
You can also specify constraints as a lambda:
Rails.application.routes.draw do
get 'foo', to: 'foo#bar',
constraints: lambda { |request| request.remote_ip == '127.0.0.1' }
end
source: http://guides.rubyonrails.org/routing.html#advanced-constraints
As for as my concern if you handle it at middleware level then it would be good. This is what my assumption.
Add this line in config/application.rb
config.middleware.insert_before ActionDispatch::ParamsParser, "SelectiveStack"
Add a middleware in app directory with middleware directory as a Convention
app/middleware/selective_stack.rb
class SelectiveStack
def initialize(app)
#app = app
end
def call(env)
debugger
if env["SERVER_NAME"] == "example.com"
"/apps/collaborate"
else
"/apps/worktogether"
end
end
end
Hope this will solve your issue.!!!
Alright, here's a shot in the dark; maybe you've tried it already or maybe I'm really missing something. On the surface, it really looks like you're just trying to override a path helper method for apps. So why not set up an override in the application_helper.rb? Something like:
module ApplicationHelper
def collaborate_path
if request.domain == "example.com"
"/apps/collaborate"
else
"/apps/worktogether"
end
end
end
In a classic multilingual rails 4 website I want to avoid the duplicate content problem.
I used friendly-id and globalize3 to make the website multilingual.
Here is my configuration:
classic page model:
extend FriendlyId
friendly_id :title, use: [:slugged, :history]
translates :title, :content, :slug
first routes configuration:
scope ":locale", /#{I18n.available_locales.join("|")}/ do
my_routes
end
#rails cast solution
match '*path', to: redirect("/#{I18n.default_locale}/%{path}"), constraints: lambda { |req| !req.path.starts_with? "/#{I18n.default_locale}/" }, via: :all
match '', to: redirect("/#{I18n.default_locale}"), via: :all
first application application-controller configuration:
before_action :set_locale
def default_url_options(options = {})
{locale: I18n.locale}
end
private
def set_locale
I18n.locale = params[:locale] if params[:locale].present?
end
As I want users to access the site without /the-default-locale at the end of the URL I change configuration as follow:
Routes configuration:
#Here I'm trying to avoid /en/content and /content to avoid duplication
match "/#{I18n.default_locale}/*path", to: redirect("/%{path}"), via: :all
scope "(:locale)", locale: /#{I18n.available_locales.join("|")}/ do
my_routes
end
#removed the rails cast fallback to default locale
Application controller configuration:
before_action :set_locale
def default_url_options(options = {})
{ :locale => ((I18n.locale == I18n.default_locale) ? nil : I18n.locale) }
end
private
def set_locale
I18n.locale = params[:locale] || I18n.default_locale
end
the links to switch between languages:
#here the French language is the default locale
<%= link_to_unless_current t("English"), locale: "en" %>
<%= link_to_unless_current t("French"), locale: nil %>
Questions:
1- With the friendly ids and the translated slug you can go to mywebsite.com/mon_contenu and mywebsite/en/my_content. But if you already are on mywebsite.com/mon_contenu and you click on the english switch you will be on mywebsite.com/en/mon_contenu with the english content but the url doesn't switch to the english slug.
Is this considerated as duplicate content? And if yes how can I avoid it?
2- With globalize if a content isn't translated it will display the default locale content. So mywebsite.com/mon_contenu and mywebsite.com/en/my_content can display the same content in the same language if the translation is not done.
Again is this considerated as duplication?
Options considered
Using robot.txt to disable some routes for instance to allow just the default locale to be indexed?
Using the canonical tag but I don't know how to easily setup it in the layout
How do you manage this kind of situations?
Any help/idea/comment/advice is always welcome!
As always thanks for your help.
Few months later, I'm still trying to figure out the best options.
Here is the solution I use for the question 1:
I set this in the controllers (solution from railscasts about friendly_id)
def show
if request.path != page_path(#page)
redirect_to #page, status: :moved_permanently
end
end
With this in place there are no reasons for multiple urls pointing to the same content. Instead the user will be redirected properly to the right URL. And the slug history is still useful.
I'll update this post if I figured out something for the second point! Actually I'm thinking of something to check if translation exist and if not redirect to default locale with a flash notice.
I have a Rails 4 mounted engine MyEngine
Inside a lib module I'm using the url_helper to find a route.
MyEngine::Engine.routes.url_helpers.send("test_controller_url",{:id => 1, :lang => I18n.locale})
And I have configured rspec with the default_url_options in spec/spec_helper.rb like this.
class ActionView::TestCase::TestController
def default_url_options(options={})
{ :params => { :lang => I18n.default_locale }, :host => "test.host" }.merge options
end
end
class ActionDispatch::Routing::RouteSet
def default_url_options(options={})
{ :params => { :lang => I18n.default_locale }, :host => "test.host" }.merge options
end
end
The resultant url I'm expecting from the call is:
http://test.host/controller/1/test?lang=en
But I'm getting the url:
http://test.host/?lang=en/controller/1/test?lang=en
I did some debugging into actionpack/action_dispatch/http/url.rb in actionpack-4.0.1 of rails.
The options that come to the url_for method are:
{
:params=>{:lang=>:en},
:host=>"test.host",
:use_route=>"my_engine",
:only_path=>true,
:lang => :en,
:path=>"/",
:script_name=>nil,
:user=>nil,
:password=>nil
}
and
{
:params=>{:lang=>:en},
:host=>"test.host",
:action=>"test",
:controller=>"my_engine/controller",
:use_route=>"test_controller",
:only_path=>false,
:id=>1,
:path=>"/controller/1/test",
:script_name=>"/?lang=en",
:user=>nil,
:password=>nil
}
And in the dummy rails application inside spec/dummy/config/routes.rb I've mounted the engine with:
mount MyEngine::Engine, :at => "/"
It seems to produce the malformed url only with rspec and not even with rails console
I also noticed that there are two calls being made to url_for, one for the engine and one for the actual path.
What am I doing wrong here? Is it a configuration thing or is there an actual bug?
Removing the :params and :lang from the default_url_options did the trick.
I've got a Rails app up running on a server. It's a big project so there are lots of routes involved, and two domains point to the root at the moment. I'd like to somehow design my routes.rb to interpret one domain to take it to a certain part of the app as if it was the root, and use the other for everywhere else.
Something like this (very pseudocode, hope you get the idea):
whole_app.com
whole_app.com/documents
whole_app.com/share
whole_app.com/users
partial_app.com, :points_to => 'whole_app.com/share'
Can Rails handle this? Thank-you!
You can achieve this by overriding default url_options method in application controller. This will override host url for every request.
class ApplicationController < ActionController::Base
....
def default_url_options
if some_condition
{:host => "partial_app.com"}
else
{:host => "whole_app.com"}
end
end
....
end
And for pointing a route to some specific url, you may use:
match "/my_url" => redirect("http://google.com/"), :as => :my_url_path
The better way is to do settings on server to redirect some url to a specific location.
is it going to /share based on some kind of criteria? if so you can do this:
routes.rb
root :to => 'pages#home'
pages_controller.rb
def home
if (some condition is met)
redirect_to this_path
else
render :layout => 'that'
end
end
I've seen similar questions on this, but not quite what I'm looking for...
Forgetting for a moment the wisdom of doing this, is it possible to do this?...
/object/update/123?o=section # ==> route to SectionController#update
/object/update/456?o=question # ==> route to QuestionController#update
...and if so, how would that be done?
Assuming you're using Rails 3+, you can use an "Advanced Constraint" (read more about them at http://guides.rubyonrails.org/routing.html#advanced-constraints).
Here's how to solve your example:
module SectionConstraint
extend self
def matches?(request)
request.query_parameters["o"] == "section"
end
end
module QuestionConstraint
extend self
def matches?(request)
request.query_parameters["o"] == "question"
end
end
Rails.application.routes.draw do
match "/object/update/:id" => "section#update", :constraints => SectionConstraint
match "/object/update/:id" => "question#update", :constraints => QuestionConstraint
end
More concise than #moonmaster9000's answer for routes.rb only:
match "/object/update/:id" => "section#update",
:constraints => lambda { |request| request.params[:o] == "section" }
match "/object/update/:id" => "question#update",
:constraints => lambda { |request| request.params[:o] == "question" }
Setting aside the question of whether it is wise to do so, the answer to "is this possible" is 'yes':
class QueryControllerApp
def self.call(env)
controller_name = env['QUERY_STRING'].split('=').last
controller = (controller_name.titleize.pluralize + "Controller").constantize
controller.action(:update).call(env)
rescue NameError
raise "#{controller_name} is an invalid parameter"
end
end
MyRailsApp::Application.routes.draw do
put 'posts/update/:id' => QueryControllerApp
end
Basically the route mapper can accept any Rack application as an endpoint. Our simple app parses the query string, builds the controller name and calls the ActionController method action (which is itself a Rack application). Not shown: how to deal with query strings with any format other than 'o=<controller_name>'