Rails3 and Respond_with problem - ruby-on-rails-3

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

Related

How to create a thank you page?

I'm trying to create a thank you page, my route for this works fine since I test it the url and works just fine, however when I try to redirect in the create action I get:
Routing Error
No route matches {:action=>"thank_you", :locale=>:en, :controller=>"appointments"}
Controller
def create
#appointment = Appointment.new(params[:appointment])
if #appointment.save
#send email
AppointmentMailer.appointment_confirmation(#appointment).deliver
AppointmentMailer.new_appointment(#appointment).deliver
redirect_to :action => "thank_you"
else
render :action => 'new', :alert => #appointment.errors.full_messages.split(', ')
end
end
def thank_you
#appointment = Appointment.find(params[:id])
end
Route
resources :appointments, :except => :new do
member do
get :thank_you
end
end
You need to add it as a RESTful action (or assume a default matching route).
Nutshell:
resources :appointments do
member do
get 'thank_you'
end
end
Or:
resources :appointments do
get 'thank_you', :on => :member
end
For getting a new page, you have to do more than just editing the controller.
Edit config/routes.rb and include
match "/appointments/thank_you" => "appointments#thank_you"
You might want to insert render command in the controller, that brings you to a thank you view you have to create...

Rails 3 routing - :delete method on :collection

I want to create a route to allow deleting all shares. RESTful way would be to use verb DELETE. How can I create a routing that points to:
DELETE /shares
I tried in the routes:
resources :shares do
delete :on => :collection
end
But this yielded an error that rails can't turn nil into a symbol.
For now I have:
resources :shares do
delete 'delete_all', :on => :collection
end
EDIT: I had a typo in controller action name and this latter way works, but produces URL /shares/delete_all which is not very RESTful.
How can I drop the _delete_all_ part?
For Rails 3 you can do it this way and have nice resourceful GET/DELETE collection actions pointing to index and delete_all respectively:
resources :shares do
delete :index, on: :collection, action: :delete_all
end
If you're using Rails 4 you can make use of concerns to DRY this up and apply it to many resources:
concern :deleteallable do
delete :index, on: :collection, action: :delete_all
end
resources :shares, concerns: :deleteallable
resources :widgets, concerns: :deleteallable
What am I missing?
match 'shares', :to => 'shares#delete_all', :via => :delete
more info: http://www.engineyard.com/blog/2010/the-lowdown-on-routes-in-rails-3/
<subjective opinion>
This is generally a bad idea and a code/design smell. The need to be deleting all records via a RESTful interface should really be behind a protected (authenticated) action and/or the action should be scoped to the user somehow.
The right way for your case, in routes:
resources :shares, except: :destroy
resource :share, only: :destroy
Please, pay attention, that I wrote word resource for destroy action
Then redefine "destroy" in shares_controller:
def destroy
respond_to do |format|
if Share.destroy_all
format.html { redirect_to root_path, notice: 'Share collection successfully deleted' }
else
format.html { redirect_to root_path, notice: 'Share collection cannot be deleted.' }
end
end
end
Here is the non REST way of doing it:
resources :shares do
collection do
delete :destroy_all
end
end
Then in your controller you will need something like this:
def destroy_all
Share.delete_all
end
Then this is what you want to do:
resources :shares do
collection do
delete :index
end
end
Then in your controller you will need something like this:
def index
if request.method == delete #delete might need to be a string here, I don't know
Share.delete_all
else
#shares = Share.all
end
end
There's a slightly simpler syntax for this that works in Rails 4.2 at least:
resources :shares do
delete on: :collection, action: :destroy_all
end
resources :shares do
collection do
delete '/', :to => :delete_all
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.

problem with RESTful forms in Rails 3

okay, so basically, I have a normal form for my model:
= form_for #operator do |f|
blah blah blah
In my operators controller, i have this:
def new
#operator = Operator.new
#operator.build_user
respond_to do |format|
format.html {}
end
end
def create
#user = User.create(params[:operator].delete(:user))
#user.update_attributes(:login => #user.email)
#operator = Operator.new(params[:operator].merge(:user => #user))
respond_to do |format|
if #operator.save
format.html {redirect_to new_operator_aircraft_path(#operator)}
else
format.html { render :action => "new", :error => #operator.errors }
end
end
end
very basic stuff. I have some validates_presence_of stuff in my model so naturally when I submit my form, it should show me that I have errors(and keep the fields I have filled up)
Right so far? yeah. The problem is, it seems I am posting to /operators and that's what renders. I seem to have forgotten about what happens in Rails2.3+ but shouldn't I be redirected to /operators/new again? or was that the intended behavior all along?
Here's what I think you are asking:
After I submit a form with errors, why does the URL
read "/operators" rather than
"/operators/new".
Thanks to resourceful routing, when submitting a form via POST to "/operators" the create action is called on the OperatorsController. If you encounter errors when saving your operator, you've instructed the controller to render the new action within the same request.
render :action => "new", :error => #operator.errors
This means a redirect is not occurring and therefore the URL remains "/operators".
If a redirect were to occur, you would lose all the state information of the #operator object in the current request, including the errors you encountered as well as the form values you just submitted.
In other words, working as intended.

Rspec2: response.should render_template("new") after invalid params fails

I am testing a controller in RSpec2 and for both my create and update actions, when passed invalid params, the controller should render either the "new" or "edit" templates respectively. It is doing that, but my test never passes.
describe "with invalid params" do
before(:each) do
User.stub(:new) { mock_user(:valid? => false, :save => false) }
end
it "re-renders the 'new' template" do
post :create, :company_id => mock_company.id
response.should render_template("new")
end
end
Results in this:
re-renders the 'new' template
expecting <"new"> but rendering with <"">
Here is the controller action:
respond_to do |format|
if #user.save
format.html {
flash[:notice] = "#{#user.full_name} was added to #{#company.name}."
redirect_to company_users_url(#company)
}
else
logger.debug #user.errors
format.html{
render :new
}
end
end
This problem also seems to be isolated to this controller. I have almost identical code running another controller and it is fine. I am not sure where the problem could be.
Update:
Here are the two mock methods
def mock_user(stubs={})
#mock_user ||= mock_model(User, stubs).as_null_object
end
def mock_company(stubs={})
(#mock_company ||= mock_model(Company).as_null_object).tap do |company|
company.stub(stubs) unless stubs.empty?
end
end
Turned out it was a problem with stubbing and CanCan. CanCan was loading the resources and uses some different methods than what I thought.