Ruby on Rails: link_to not having appropriate ID - ruby-on-rails

So, I got this error
Couldn't find MenuBar with ID=add_page_to_menu
But my code for the link that creates that error is as follows:
<%= link_to "add",
:controller => "admin/menu_bars",
:action => "add_page_to_menu",
:page => page.id,
:menu => #menu_bar.id %>
The URL that I get the error on is
http://example.com/admin/menu_bars/add_page_to_menu?menu=1&page=1
it should look something like example.com/admin/menu_bars/add_page_to_menu/1?menu=1&page=1
(I think, I could be wrong, seeing as how its not working =(
the corresponding action in the controller:
def add_page_to_menu
#menu_bar = MenuBar.find(params[:menu])
#page = LinkPage.find(params[:page])
#menu_bar.link_pages << #page
if #menu_bar.save
format.html { render :action => "edit" }
else
format.html { render :action => "edit" }
format.xml { render :xml => #menu_bar.errors, :status => :unprocessable_entity }
end
end
Routes:
map.namespace "admin" do |admin|
admin.root :controller => :site_prefs, :action => :index
admin.resources :site_prefs
admin.resources :link_pages
admin.resources :menu_bars
end

Your route is going to eval to
http://example.com/admin/menu_bars/:id?menu=1&page=1
so Rails is looking for a MenuBar with an ID of add_page_to_menu. You need to add a member method to the routes for your custom action. The routes should look like this:
map.namespace "admin" do |admin|
admin.root :controller => :site_prefs, :action => :index
admin.resources :site_prefs
admin.resources :link_pages
admin.resources :menu_bars, :member => { :add_page_to_menu => :get }
end
and the link_to should look something like this:
link_to("add", menu_bar_add_page_to_menu_path(#menu_bar, :page => #page.id)
and the url produced should look something like this:
http://example.com/admin/menu_bars/1/add_page_to_menu?page=1
There's still some optimization to be done in that, but I think it will get you beyond this issue at least.

Related

RSS link in Rails

I need to add a link to a rss page but i don't know how
def index
#boxes = Box.paginate :page => params[:page], :order => "boxes.id desc", :per_page => 5,
:include => [:suppliers, :manufacturer]
#page_title = 'Catálogo'
respond_to do |format|
format.html
format.xml { render :xml => #boxes }
format.rss { render :layout => false }
end
end
the url to access is http://localhost:3000/catalog.rss but i don't know how to make it like this <%= link_to 'Canal RSS', :action => 'index' %>
In your routes.rb, you add the route first:
get 'catelog' => '<YOUR_CONTROLLER_NAME>#index', :constraints => {:format => :rss}
Then you can use it like this:
<%= link_to 'Canal RSS', catelog_url %>
catelog is the route prefix. You can run rake routes to see it.

Rails: Mapping Conflicting Routes by Detecting Param

Hey! I am trying to set up routes in a Rails application so that, depending on the type of parameter passed, Rails sends the request to a different action.
I have courses which have an attribute state which is a string with a two letter state abbreviation. When a user visits /courses/1, I want Rails to display the show action in the courses controller (and pass the parameter as :id). When a user visits /courses/CO though, I want Rails to display the index action and pass the parameter as :state.
So /courses/1 would be equivalent to
:controller => 'courses', :action => 'show', :id => '1'
And /courses/CO would be equivalent to
:controller => 'courses', :action => 'index', :state => 'CO'
I have tried this:
map.resources :courses, :except => { :index, :show }
map.connect 'courses/:state', :controller => 'courses', :action => 'index', :state => /[A-Z]{2}/
map.connect 'courses/:id', :controller => 'courses', :action => 'show', :id => /[0-9]+/
But it breaks (the rails server wont even start). I don't usually do things like this with routes, so I am outside of my know-how. Thanks!
Edit: Fixed a typo, thanks JC.
Current solution looks like this:
map.resources :courses, :except => [ :index, :show ]
map.courses '/courses', :controller => 'courses', :action => 'index', :state => 'AL', :method => :get
map.courses '/courses/:state', :controller => 'courses', :action => 'index', :requirements => { :state => /[A-Z]{2}/ }, :method => :get
map.course '/courses/:id', :controller => 'courses', :action => 'show', :requirements => { :id => /[0-9]+/ }, :method => :get
This works, but you will need to go edit all your links to the index to say things like courses_path('AA') and you won't be able to use some of the nice helpers, like form_for, which assume you are following the convention that #create is simply #index with a POST request. (Get comfortable with form_tag)
ActionController::Routing::Routes.draw do |map|
map.resources :courses, :except => [ :index, :show ]
map.courses '/courses/:state', :controller => 'courses', :action => 'index', :requirements => { :state => /[A-Z]{2}/ } , :method => :get
map.course '/courses/:id', :controller => 'courses', :action => 'show', :requirements => { :id => /[0-9]+/ } , :method => :get
end
It will keep your routes named the same, though.
(by the way, your /co does not match your regex, which requires upper case chars)
Fun aside: Do we really need the abstraction of a router? http://blog.peepcode.com/tutorials/2010/rethinking-rails-3-routes
I'm afraid this won't work since the structure that maps paths to controllers and actions is setup on start of the rails application, parameter handling happens at request time.
What you could do is to match the :id-parameter in the show-action of the CoursesController against a list of valid states and then either redirect or render a different action.
Hope this helps.

Rails Route that Updates or Redirects

Im not sure if I'm doing this right. I have an action that I would like to either copy, create, and save a new object if a user is logged in, or redirect if they are not logged in. Im not using a form here because I am using a stylized button with an image that looks like this:
<a href="/lists/add/<%= #list.id %>" class="button">
<span class="add_list">Learn these words</span>
</a>
and the action looks like this:
def add
if is_logged_in?
list = logged_in_user.copy_list(params[:id])
if list.save
flash[:notice] = "This list is now in your stash."
redirect_to stash_zoom_nav_quiz_path(list, "zoomout", "new", "quizoff")
else
flash[:notice] = "There was a problem adding this list."
redirect_to :back
end
else
redirect_to :controller => "users", :action => "signup_and_login", :list_id => params[:id]
end
end
map.resources :lists, :collection => {:share => :get, :share_callback => :get, :add => :put}
I have added this action as a :put in my routes and I'm not sure if this is right or if the other stuff is the right way to even do it for that matter. Any help is appreciated.
The specific answer to your question is
map.resources :lists, :collection => { :share => :get, :share_callback => :get }, :member => { :add => :put }
add action works on a member, not on a collection.
But there are other problems in your code. First, you should always use Rails helpers to generate the URLs. In fact, the path /lists/add/<%= #list.id %> is wrong. It should be /lists/<%= #list.id %>/add
Change
<a href="/lists/add/<%= #list.id %>" class="button">
<span class="add_list">Learn these words</span>
</a>
to
<% link_to add_list_path(#list), :class => "button" do %>
<span class="add_list">Learn these words</span>
<% end %>
The controller can be simplified. Move the is_logged_in? check in a before filter.
class MyController < ActionController::Base
before_filter :require_logged_user, :only => %w( add )
def add
list = logged_in_user.copy_list(params[:id])
if list.save
flash[:notice] = "This list is now in your stash."
redirect_to stash_zoom_nav_quiz_path(list, "zoomout", "new", "quizoff")
else
flash[:notice] = "There was a problem adding this list."
redirect_to :back
end
end
protected
def require_logged_user
if !is_logged_in?
redirect_to :controller => "users", :action => "signup_and_login", :list_id => params[:id]
end
end
end
Try this in your routes.rb:
map.resources :lists, :member => {:add => :put}, :collection => {:share => :get, :share_callback => :get}
:member - Same as :collection, but for actions that operate on a specific member.

Rendering Actions with Custom Routes

I am trying to maintain a pretty URL when having a user register with failed validation
I have a routes file that looks like the following:
map.resources :users
map.signup '/signup', :controller => "users", :action => "new"
This works well enough, except that if a user enters invalid information during registration then the create method does the following:
def create
#user = User.new(params[:user])
if #user.save
flash[:notice] = "Successfully Registered."
redirect_to root_url
else
render :action => 'new'
end
end
This works, but if the information is correct it switches the URL to domain.com/users. If I switch it to redirect_to '/signup' it works, but all the previous information that was entered is lost, and I would ideally like to maintain that.
Is there any way to keep my nice urls during a failed validation?
You'll need to add conditions to your routes:
# Routes files
map.resources :users
map.signup "/signup", :controller => "users", :action => "new", :conditions => { :method => :get }
map.signup "/signup", :controller => "users", :action => "create", :conditions => { :method => :post }
Then, you'll need to make sure your controller and view handle them correctly:
# Controller
def new
#user = User.new
end
def create
#user = User.new(params[:user])
if #user.save
flash[:notice] = "Successfully registered."
redirect_to root_url
else
render "new"
end
end
# new.html.erb
<% form_for #user, :url => signup_path do |form| %>
....
<% end %>
Try adding to routes:
map.signup_post '/signup', :controller => "users", :action => "create", :method => :post
And in your form:
<%= form_for #user, :url => signup_post_path do |f| %>

Rails routing for namespaced resource deletion

I have a page model and a pages_controller within an admin namespace. My routes file looks like this:
map.resources :pages, :only => [:index,:show]
map.resources :admin, :only => [:index]
map.namespace :admin do |admin|
admin.resources :pages
end
I am not able to figure out the correct method to create a link for deleting a page (In the same way the scaffold generator generates a delete link on the index page).
Any ideas on the correct parameters for the link_to function?
TIA,
Adam
rake routes is your friend here. It'll spit out the list of your generated routes - particularly useful if you have a bunch of nested or custom routes.
the paths will be
admin_pages_path #(with GET) routes to :controller => 'admin/pages', :action => 'index'
admin_pages_path #(with POST) routes to :controller => 'admin/pages', :action => 'create'
new_admin_page_path #(with GET) routes to :controller => 'admin/pages', :action => 'new'
edit_admin_page_path(:id) #(with GET) routes to :controller => 'admin/pages', :action => 'edit'
admin_page_path(:id) #(with GET) routes to :controller => 'admin/pages', :action => 'show'
admin_page_path(:id) #(with PUT) routes to :controller => 'admin/pages', :action => 'update'
admin_page_path(:id) #(with DELETE) routes to :controller => 'admin/pages', :action => 'delete'
Your link_to for delete should therefore be:
<%= link_to("delete page", admin_page_path(#page), :confirm => "sure you want to delete this page?", :method => :delete) %>
Note that Rails will work its magic calling to_param on #page, so that you don't have to specify #page.id - useful for an example like this as you often want to use permalinks for 'pages'.

Resources