How do I make a route to a custom controller action in Rails 3? - ruby-on-rails-3

I'm new to Rails, and a bit confused about routes:
I have a Devices controller:
#devices_controllers.rb
class DevicesController < ApplicationController
def index
#devices = Device.all
end
def show
#device = Device.find(params[:id])
end
def new
#device = Device.new
end
def create
#device = Device.new(params[:device])
if #device.save
flash[:notice] = "Successfully created device."
redirect_to #device
else
render :action => 'new'
end
end
def edit
#device = Device.find(params[:id])
end
def update
#device = Device.find(params[:id])
if #device.update_attributes(params[:device])
flash[:notice] = "Successfully updated device."
redirect_to #device
else
render :action => 'edit'
end
end
def destroy
#device = Device.find(params[:id])
#device.destroy
flash[:notice] = "Successfully destroyed device."
redirect_to devices_url
end
def custom_action
"Success"
end
I'd like to access the "custom_action" action via a url like this:
http://foo.bar/devices/custom_action
I've added this line to my routes.rb file:
match 'devices/custom_action' => 'devices#custom_action'
However, when I try the URL in the browser, I get this error:
ActiveRecord::RecordNotFound in DevicesController#show
Couldn't find Device with ID=custom_action
It seems to be going to #show action instead of #custom_action. If a user id is not supplied, and I go to http://foo.bar/devices/custom_action, I'd like it to go #custom_action.
I've read Rails Routing from the Outside, but still can't still seem to figure out the problem.

I think the problem may be because of the order in which you have defined your routes.
I suspect you have resources :devices in your routes.rb. In addition, I suspect you have defined your custom route after this. If you type rake routes into your console/terminal, you will see that there is already a route defined for the following pattern:
GET /devices/:id
This route is a product of resources :devices, which is taking precedence over your custom route. Referring back to the Edge Guides, specifically in 1.1. Connecting URLs to Code, it states that the request will be dispatched to the first matching route. So a simple fix would be to define your custom route before resources :devices.

Related

uninitialized constant ProfilesController

I have an error "uninitialized constant ProfilesController" on my Profiles controller. This is the profiles_controller.rb:
class ProfilesController < ApplicationController
def new
#profile = Profile.new
end
def create
#profile = Profile.new(params[:profile])
if #profile.save
redirect_to profile_path, notice: I18n.t('.profile.created')
else
render action: "new"
end
end
end
This is the routes.rb:
resources :profiles, only: [:new, :create]
And this the output of rake routes:
profiles POST /profiles(.:format) profiles#create
new_profile GET /profiles/new(.:format) profiles#new
When I click a link for "new_profile_path" I get the error, but to me everything seems OK? The controller name is plural,the routes are OK?
You most likely spelled your controller file wrong. Confirm that file is truly: `/app/controllers/profiles_controller.rb'
Really strange, I created a Books controller with a generator, renamed everything to Profiles and then it works as normal. As far as I can see the routes are identical. Strange....
I had the same problem when i checked the name of the controller was 'profile_controller.rb'(i had created it manually though). but inside the definition it was "ProfilesController".
class ProfilesController < ApplicationController
def index
end
def new
#profile = Profile.new
end
def create
end
end
So if your controller name is correct and you have added the route("resources :profiles") then it will work as expected

In Rails 3 how could I create a db only contact form not using scaffold?

New to rails 3
I would like to create a contact form that people fill out, its saved to the db and then a thank you page comes up.
I would like to do this without scaffold so I can learn better, and I figure that by doing it this way it would be easer to setup so that people cannot try and look at other people's entries by modifying the url.
ideally it would keep their state in the session or cookie so that they would end up on the thanks page if they came back.
Have been trying to do this for about 3 days and reading/googling tons, but between the new routes redirect_to controller stuff in rails3 havn't managed to figure it out.
Routes.rb
Contact::Application.routes.draw do
resources :contactees, :only => [:new, :create]
# to make sure crud doesn't have the routest I don't want
get 'contactees/submitted'
root :to => 'contactees#new'
contactees_controller.rb
ContacteesController < ApplicationControler
def share
end
def new
#contactee = Contactee.new
end
def create
#contactee = Contactee.new(params[:contactee])
if #contactee.save
redirect_to submitted_contactee
else
render action: "new"
end
end
end
Views
contactees
_form.html.erb
new.html.erb
submitted.html.erb
Get rid of the submitted route, you don't need it. Perhaps something like this?
def new
render :submitted if session[:contacted]
end
def create
#contactee = Contactee.new(params[:contactee])
if #contactee.save
session[:contacted] = true
render :submitted
else
render action: "new"
end
end

respond_with is redirecting to specified location even on validation errors, in rails3

When using location in respond with, it is ignoring validation errors and redirecting to the specified location. Is this expected behavior?
I checked in the responder module that it checking if there are any errors on the model. I inspected the model and it contains validation errors in the #solution object. What am I missing here?
controller:
def create
#problem = Problem.find(params[:problem_id])
#solution = #problem.solutions.build params[:solution]
#solution.save
respond_with(#solution, :location => detail_problem_solution_path(#problem, #solution)
end
model:
validates :body, :presence => true, :unless => :reference
reference is true or false false.
I encountered this problem today, and come upon this Rails issue over at github. The exception seems to be thrown since the route url helper can't generate a valid for unsaved (invalid) records.
There's discussion on the github issue about allowing procs as an argument to the location parameter, but it doesn't look like it'll be added anytime soon.
For now I'll stick with using the following solution:
def create
#post = Post.new(params[:post])
if #post.save
respond_with(#post, location: edit_post_path(#post))
else
respond_with #post
end
end
The only way I was able to solve is this:
def create
#problem = Problem.find(params[:problem_id])
#solution = #problem.solutions.build solution_params
success = #solution.save
respond_with(#solution) do |format|
format.html {redirect_to detail_problem_solution_path(#problem, #solution) } if success
end
end

How do you pass validation errors across controllers?

I'm making a conventional forum in Rails to practice. I have a Topic model and a nested Post model. Topics can have many Posts.
Topics#Show has a list of #topic.posts and then a new Post form.
# Topics#Show
def show
#topic = Topic.find(params[:id])
#post = #topic.posts.new
end
Submitting a new post sends it to Posts#Create
# Posts#Create
def create
#topic = Topic.find(params[:topic_id])
#post = #topic.posts.new(params[:post])
#post.user = current_user
if #post.save
redirect_to #topic, :notice => "Successfully created post."
else
render :action => 'new' # <-- Unsure what to do here
end
end
If the Post fails to save, I want it to render Topics#Show and display the validation errors there.
From what I understand, params don't persist through a redirect_to because a 302 redirect starts a new request.
You should render the topics/show view. So instead of
render :action => 'new' # <-- Unsure what to do here
Do:
render :template => 'topics/show'
Use render :template => "topics/show" and be sure to set up the #topic variable identically to how you do it in the TopicsController#show action. You will not be able to call this show method from the PostsController though.

Rails3 and Respond_with problem

I have an application, on which I have two user interfaces.
The first one is for normal users and the second one is for iphone users.
Everything was working fine until i refactored my code within controller to use the respond_with declarative instead of respond_to.
The application is still working for the html interface(:format => :html) but not on the iphone interface(:format => :iphone).
On the iphone, when I do the following action (:index, :new, :edit, :show) it works.
But when i do (:create, :update, :destroy), I get errors saying the template is not found(create.iphone.haml for example).
On my controller I have
respond_to :html, :iphone
And then for example, the edit and the update action
def edit
#refund = Refund.find(params[:id])
respond_with(#refund)
end
def update
#refund = Refund.find(params[:id])
if #refund.update_attributes(params[:refund])
flash[:notice] = 'Refund was successfully updated.'
end
respond_with(#refund, :location => project_refunds_path(#project))
end
In fact, I would like the :iphone format is handle as :html is ... and not by calling the to_format method as it is specified into the doc.
Solved it by myself.
Just need to add this to an initializer file :
ActionController::Responder.class_eval do
alias :to_iphone :to_html
end
What if you do:
respond_with(#refund, :location => project_refunds_path(#project)) do |format|
format.iphone { whatever you had here before refactoring }
end