How can I link up my routes so that the spec will pass? I have read here http://www.engineyard.com/blog/2010/the-lowdown-on-routes-in-rails-3/
The spec
require 'spec_helper'
describe ConversationMembersController do
describe "routing" do
it "recognizes and generates #index" do
{ :get => "/conversations/123/members" }.should route_to(:controller => "conversation_members", :action => "index", :conversation_id=>"123")
end
end
end
Failing spec
ConversationMembersController routing recognizes and generates #index
Failure/Error: { :get => "/conversations/123/members" }.should route_to(:controller => "conversation_members", :action => "index", :conversation_id=>"123")
The recognized options <{"action"=>"123", "id"=>"members", "controller"=>"conversations"}> did not match <{"conversation_id"=>"123",
"action"=>"index",
"controller"=>"conversation_members"}>, difference: <{"conversation_id"=>"123",
"action"=>"index",
"id"=>"members",
"controller"=>"conversation_members"}>
The routes
resources :conversations, :except => [:edit] do
resources :conversation_members, :as => "members", :except => [:show, :edit, :update, :destroy] do
collection do
delete :leave
end
end
The output of rake routes | grep conversation_members
52: leave_conversation_members DELETE /conversations/:conversation_id/conversation_members/leave(.:format) {:action=>"leave", :controller=>"conversation_members"}
53: conversation_members GET /conversations/:conversation_id/conversation_members(.:format) {:action=>"index", :controller=>"conversation_members"}
54: POST /conversations/:conversation_id/conversation_members(.:format) {:action=>"create", :controller=>"conversation_members"}
55: new_conversation_member GET /conversations/:conversation_id/conversation_members/new(.:format) {:action=>"new", :controller=>"conversation_members"}
try this?
resources :conversations, :except => [:edit] do
resources :members, :controller => "conversation_members", :except => [:show, :edit, :update, :destroy] do
collection do
delete :leave
end
end
end
From the documentation it appears that the :as option just changes the named helpers...so the actual url is still
/conversations/xxx/conversation_members
but you can refer to the route as
conversation_members_path
check out this great guide to routing
Related
I'm working with the following route:
resources :groups, :only => [:show, :create, :update, :destroy] do
get 'viewmembers' => 'groups#view_members"
end
But when I'm trying to test in RSpec:
describe "GET #viewmembers" do
context "the group members are retrieved" do
before do
#group = FactoryGirl.create :group
..
get :viewmembers, group_id: #group.id
end
it { should respond_with :ok}
# others tests...
end
I get the following error:
Failure/Error: get 'viewmembers', group_id: #group.id
ActionController::UrlGenerationError:
No route matches {:action=>"viewmembers", :controller=>"api/v1/groups", :group_id=>"1"}
Is there a different way I should be formulating the GET request in my Rspec test?
Here is what's listed in rake routes:
GET /api/v1/groups/:group_id/viewmembers(.:format) api/v1/group#viewmembers{:format=>:json}`
If i take the get 'viewmembers'... route OUT of the nested do-end block for the group resource like so:
resources :groups, :only => [:show, :create, :update, :destroy]
get '/groups/viewmembers' => 'groups#view_members'
then the rspec test runs fine, so it has to be a problem with the way I'm formulating the request...
I am using Ruby on Rails 4.2 and I would like to route "nested" paths by using namespace or scope :module / scope :path within the block of a resource.
That is, I have the following route:
resources :users, :only => [:show]
that matches
user_path GET /users/:id(.:format) users#show
I would like to match the following paths
users_sessions_path POST /users/sessions users/sessions#create
user_session_path GET /users/:id/session users/sessions#show
delete_user_session_path GET /users/:id/session/delete users/sessions#delete
user_session_path DELETE /users/:id/session users/sessions#destroy
I read the official documentation and I tried to state something like
resources :users, :only => [:show] do
scope :module => :users do
scope :module => :sessions do
# scope :path => :sessions do
# namespace :sessions do
...
end
end
end
but no attempt has been successful. How should I state routes?
Update after the #dgilperez answer
I tried the following code
resources :users, :only => [:show] do
scope :module => :users do
resource :session, :only => [:show, :new, :create, :destroy] do
get :delete, :on => :collection, :to => 'sessions#delete'
end
end
end
that matches
delete_user_session_path GET /users/:user_id/session/delete(.:format) users/sessions#delete
new_user_session_path GET /users/:user_id/session/new(.:format) users/sessions#new
user_session_path POST /users/:user_id/session(.:format) users/sessions#create
user_session_path GET /users/:user_id/session(.:format) users/sessions#show
DELETE /users/:user_id/session(.:format) users/sessions#destroy
but I still need to map new and create actions without needing to pass the :user_id parameter. That is, I would like to map something like
new_user_session_path GET /users/session/new(.:format) users/sessions#new
user_session_path POST /users/session(.:format) users/sessions#create
I think you are overcomplicating it: you don't need to use scope or path to render nested resources. You just nest them:
resources :users, :only => [:show] do
resources :sessions
end
will render the following routes:
user_sessions GET /users/:user_id/sessions(.:format) sessions#index
POST /users/:user_id/sessions(.:format) sessions#create
new_user_session GET /users/:user_id/sessions/new(.:format) sessions#new
edit_user_session GET /users/:user_id/sessions/:id/edit(.:format) sessions#edit
user_session GET /users/:user_id/sessions/:id(.:format) sessions#show
PATCH /users/:user_id/sessions/:id(.:format) sessions#update
PUT /users/:user_id/sessions/:id(.:format) sessions#update
DELETE /users/:user_id/sessions/:id(.:format) sessions#destroy
user GET /users/:id(.:format) users#show
Those are not the routes you mention you need, but I'd like you to reconsider if you really need them like that, with namespaced controllers and custom names such as delete_user_ or you prefer to go more standard. If you really need those exact routes, let me know.
UPDATE after OP's update
To get the two routes missing you need to get them out the rested resource. I'd just write them directly like this:
resources :users, :only => [:show] do
scope :module => :users do
resource :session, :only => [:show, :destroy] do
get :delete, :on => :collection, :to => 'sessions#delete'
end
end
end
get 'users/session/new', to: 'users/sessions#new', as: :new_user_session
post 'users/session', to: 'users/sessions#create'
So I keep getting the error:
No route matches {:action=>"create", :controller=>"xaaron/api_keys"}
Which is thrown in the test:
it "should not create an api key for those not logged in" do
post :create
expect(response).to redirect_to xaaron.login_path
end
when I go to spec/dummy and run the rake routes command I see:
api_keys GET /api_keys(.:format) xaaron/api_keys#index
POST /api_keys(.:format) xaaron/api_keys#create
new_api_key GET /api_keys/new(.:format) xaaron/api_keys#new
edit_api_key GET /api_keys/:id/edit(.:format) xaaron/api_keys#edit
api_key GET /api_keys/:id(.:format) xaaron/api_keys#show
PATCH /api_keys/:id(.:format) xaaron/api_keys#update
PUT /api_keys/:id(.:format) xaaron/api_keys#update
DELETE /api_keys/:id(.:format) xaaron/api_keys#destroy
Which shows that yes this route does exist. My routes file for this engine looks like:
Xaaron::Engine.routes.draw do
get 'login' => 'sessions#new', :as => 'login'
get 'logout' => 'sessions#destroy', :as => 'logout'
get 'signup' => 'users#new', :as => 'signup'
get 'permission_denied' => 'error#denied', :as => 'permission_denied'
get 'record_not_found' => 'error#error', :as => 'record_not_found'
get 'password_reset' => 'password_resets#edit', :as => 'rest_user_password'
resource :error, controller: 'error'
resources :users
resources :api_keys
resources :sessions
resources :roles
resources :password_resets
end
What am I missing?
update
For those of you curious how I am getting these routes, its because the dummy app's routes file is set up (by default) as such:
Rails.application.routes.draw do
mount Xaaron::Engine => "/xaaron"
end
Update II
I have been reading this api docs on how routing is done in engines and I believe the way I have done this is correct, how ever the controller is defined as such:
module Xaaron
class ApiKeysController < ActionController::Base
before_action :authenticate_user!
def index
#api_key = Xaaron::ApiKey.where(:user_id => current_user.id)
end
def create
#api_key = Xaaron::ApiKey.new(:user_id => current_user.id, :api_key => SecureRandom.hex(16))
create_api_key(#api_key)
end
def destroy
Xaaron::ApiKey.find(params[:id]).destroy
flash[:notice] = 'Api Key has been deleted.'
redirect_to xarron.api_keys_path
end
end
end
You need to tell your spec you are using the engine routes:
describe ApiKeysController do
routes { Xaaron::Engine.routes }
it "should not create an api key for those not logged in" do
post :create
expect(response).to redirect_to xaaron.login_path
end
end
I am trying to implement internationalization as seen in railscasts, and every time I scope my routes file I get the error
No route matches [GET] "/"
or the error
missing :controller
config/routes.rb:6:in `block (2 levels) in <top (required)>'
config/routes.rb:5:in `block in <top (required)>'
config/routes.rb:1:in `<top (required)>'
Here is my routes.rb file
Jensenlocksmithing::Application.routes.draw do
get "log_out" => "sessions#destroy", as: "log_out"
get "log_in" => "sessions#new", as: "log_in"
scope ":locale" do
get "site/home"
get "site/about_us"
get "site/faq"
get "site/discounts"
get "site/services"
get "site/contact_us"
get "site/admin"
get "site/posts"
root :to => 'site#home'
end
#match '*path', to: redirect("/#{I18n.default_locale}/%{path}")
#match '', to: redirect("/#{I18n.default_locale}")
match "/savesort" => 'site#savesort'
resources :users
resources :abouts
resources :sessions
resources :coupons
resources :monthly_posts
resources :reviews
resources :categories do
collection { post :sort }
resources :children, :controller => :categories, :only => [:index, :new, :create, :new_subcategory]
end
resources :products do
member do
put :move_up
put :move_down
end
end
resources :faqs do
collection { post :sort }
end
end
So, why whenever I add the scope ":locale" do end line do I get these errors? It all works fine without. Let me know if you need to see any more code. Thanks guys
Edit
In my application controller I have the following:
private
def default_url_options(options = {})
{locale: I18n.locale}
end
Does this do the same thing as the passing the hash in the routes?
Edit 2
I changed my route to the following as seen in this gist.
https://gist.github.com/2322844
So why is the :id part being added to the get route? like this one
about_us_site GET /sites/:id/about_us(.:format)
shouldn't it be something like this
about_us_site GET /sites/about_us(.:format)
Also added my entire routes.rb file and the routes it generates for more information.
https://gist.github.com/2322861
Answer for anyone interested:
I changed
get "site/home"
get "site/about_us"
get "site/faq"
get "site/discounts"
get "site/services"
get "site/contact_us"
get "site/admin"
get "site/posts"
root :to => 'site#home'
to
resources :sites, except: [:new, :edit, :index, :show, :update, :destroy, :create] do
collection do
get :home
get :about_us
get :faq
get :discounts
get :services
get :contact_us
get :admin
get :posts
end
end
Passing in a hash should fix your routes:
scope "(:locale)", :defaults => { :locale => "en" } do
resources :sites
end
Also, you may want to consider creating a SitesController and giving it members:
resources :sites do
member do
get :about_us # Points to /sites/about_us
end
end
Rails Guides on Defining Defaults In Routes
I was able to successfully override url_after_create, but my url_after_destroy is being ignored.
What am I messing up?
routes:
map.resource :session,
:controller => 'sessions',
:only => [:new, :create, :destroy]
my Sessions controller:
class SessionsController < Clearance::SessionsController
private
def url_after_create
puts "************after create****************" #called on sign in
end
def url_after_destroy
puts "************after destroy****************" #never called
end
end
The docs say:
You may also need to add code such as
the following to your routes.rb:
map.sign_out 'sign_out',
:controller => 'sessions',
:action => 'destroy',
:method => :delete
That's what I'm missing. Perhaps 'may' is not the best choice of verb for documentation.