Rails 3.1 respond_to & render_403 problem - rendering

I'm not sure what it is. I've just upgraded to Rails 3.1 from 3.0.9 and here's what I get in my specs:
PeopleController edit action should require owner
Failure/Error: response.should render_template("/public/403.html")
expecting <"/public/403.html"> but rendering with <"search/_search_menu_item">
This is all over my specs in various controllers. I also have this code in my AppController:
def render_403
respond_to do |format|
format.html { render :file => "#{Rails.root}/public/403.html", :status => 403, :layout => false }
format.json { render :json => { :error => true, :message => "Error 403, you don't have permissions for this operation." } }
end
end
And this in PeopleController:
def edit
render_403 unless #person.account.id == current_account.id
end
I'm certain that format.html block gets executed (checked it). However the spec expectation fails. Wonder what is going on here.
(search/_search_menu_item is a partial that gets included onto every page, which basically means that the app layout gets rendered here instead.)
Update: I've replaced render_403 in #edit with render(:file => "#{Rails.root}/public/403.html", :status => 403, :layout => false) to see what happens - got the same result.

Ok, figured it out. Possibly not a Rails problem. At least the problem appears only when running the specs.
I've been checking if 403 pages are rendered with this:
response.should render_template("public/403.html")
Doesn't work no more. Replacing it with
response.status.should == 403
fixed the issue.

Related

Rails action caching refresh after expire

I'm looking to expire and then refresh the cache for a controller action using a publicly accessible endpoint.
In my app currently, /all returns cached json, and /update expires the cache.
You can see the existing relevant code below.
What I'd like to do is not only expire the cache but force a refresh.
So, my question is:
Is there is a way to initiate the refresh of an action cache after expiring it, without hitting the action?
If the answer to that is no (as I'm beginning to suspect), then what would be the best way to do this? I require the update action to return an HTTP 200 status, not a 301 redirect so just redirecting to /all isn't an option.
VendorsController
caches_action :all, :expires_in=>2.weeks
....
def all
respond_to do |format|
format.json { render :json => Vendor.all }
format.html { render :json => Vendor.all }
end
end
....
def update
render :nothing => true
expire_action :action => :all
end
You should use write_fragment
def update
render :nothing => true
expire_action :action => :all
cache_path = ActionCachePath.new(self, {:action => :all}, false).path
write_fragment(cache_path, render_to_string(:json => Vendor.all))
end
Source that may help:
ActionCacheFilter
expire_action

Rails: nested resources and routing errors

I've got a Template model, and a Doc model. They're nested resources, with the Templates being the parent, thus:
resources :templates do
get "/documents/lock/:id" => "docs#lock", :as => :lock_doc
get "/documents/unlock/:id" => "docs#unlock", :as => :unlock_doc
get "/documents/pdf/:id" => "docs#pdf", :as => :pdf_doc
resources :docs, :path => :documents
end
That part, I think, all works fine. When I try to submit the form for creating a doc the record exists but I get routing errors, thus:
ActionController::RoutingError (No route matches {:action=>"edit", :controller=>"docs", :template_id=>nil, :id=>#<Doc id: 2, user_id: "admin", cover: "1209hpnl", message: "The world economic outlook is improving, albeit slo...", created_at: "2013-01-07 03:54:05", updated_at: "2013-01-07 03:54:05", issue_code: "1209hpnl", title: "January 2013", locked: nil, retired: "active", template: nil>}):
app/controllers/docs_controller.rb:134:in `block (2 levels) in create'
app/controllers/docs_controller.rb:132:in `create'
The lines correspond to the create method:
def create
#doc = Doc.new(params[:doc])
respond_to do |format|
if #doc.save
format.html { redirect_to share_url(#doc), notice: "Saved. You may from here #{view_context.link_to('edit', edit_template_doc_url(#doc))} it further, #{view_context.link_to('finalise', template_lock_doc_url(#doc))} it, or return #{view_context.link_to('home', root_url)}.".html_safe }
format.json { render json: #doc, status: :created, location: #doc }
else
format.html { render action: "new" }
format.json { render json: #doc.errors, status: :unprocessable_entity }
end
end
end
I think the problem lies somewhere in here, but I can't for the life of me figure it out.
Cheers for any help!
EDIT: with rake routes
template_lock_doc GET /templates/:template_id/documents/lock/:id(.:format) docs#lock
template_unlock_doc GET /templates/:template_id/documents/unlock/:id(.:format) docs#unlock
template_pdf_doc GET /templates/:template_id/documents/pdf/:id(.:format) docs#pdf
template_docs GET /templates/:template_id/documents(.:format) docs#index
POST /templates/:template_id/documents(.:format) docs#create
new_template_doc GET /templates/:template_id/documents/new(.:format) docs#new
edit_template_doc GET /templates/:template_id/documents/:id/edit(.:format) docs#edit
template_doc GET /templates/:template_id/documents/:id(.:format) docs#show
PUT /templates/:template_id/documents/:id(.:format) docs#update
DELETE /templates/:template_id/documents/:id(.:format) docs#destroy
templates GET /templates(.:format) templates#index
POST /templates(.:format) templates#create
new_template GET /templates/new(.:format) templates#new
edit_template GET /templates/:id/edit(.:format) templates#edit
template GET /templates/:id(.:format) templates#show
PUT /templates/:id(.:format) templates#update
DELETE /templates/:id(.:format) templates#destroy
The problem is in your call to edit_template_doc_url(#doc) inside the notice string. You need to supply the template as well, like this:
edit_template_doc_url(params[:template_id], #doc)

respond_with location ignored on GET request

At each step in my checkout process, an order is updated via a PUT request. However, one of the states has a form that submits to a third party which redirects back to my site, calling the update method with GET (no control over this).
Why does my respond_with code appear to be totally ignored and I get a Missing Template checkout/update error? It should be hitting #edit.
CheckoutController.rb
before_filter :load_order
def update
if #order.update_attributes(params[:order])
#order.next
end
respond_with(#order, :location => checkout_state_url(#order.state))
end
routes.rb
match '/checkout/update/:state' => 'checkout#update', :as => :update_checkout
match '/checkout/:state' => 'checkout#edit', :as => :checkout_state
match '/checkout' => 'checkout#edit', :state => 'client_details', :as => :checkout
It looks like respond_with does different things depending upon the HTTP verb and whether the resource has errors. See here and here.
The following code worked for me:
def update
if #order.update_attributes(params[:order]) && #order.next
respond_with(#order) { |format| format.html { redirect_to checkout_state_url(#order.state) } }
else
respond_with(#order) { |format| format.html { render :edit } }
end
end

How can I use rspec to test routes that are being rescued and redirected?

I was trying to write some quick routing tests for a simple api so I wrote:
it "delete" do
delete("/api/notifications/:id").should_not be_routable
end
But I received:
Failure/Error: delete("/api/notifications/:id").should_not be_routable
expected {:delete=>"/api/notifications/:id"} not to be routable, but it routes to {:controller=>"application", :action=>"rescue404", :a=>"api/notifications/:id"}
I quickly realized I was rescuing from 404, so pretty much everything is routable.
unless Rails.env.development?
rescue_from NotFound, :with => :rescue404
rescue_from ActiveRecord::RecordNotFound, :with => :rescue404
rescue_from ActionController::RoutingError, :with => :rescue404
end
def rescue404
respond_to do |format|
format.json { render :text => 'Something went wrong. Record not found or url is incorrect.\n' }
format.xml { render :text => 'Something went wrong. Record not found or url is incorrect.\n' }
format.html { redirect_to root_url, :alert => 'That page does not exist, sorry bro!' }
end
end
That leaves me with this for a test:
it "delete" do
delete("/api/notifications/:id").should route_to("application#rescue404", :a => "api/notifications/:id")
end
Writing this way is error prone to me as I'm constantly getting the ':a =>' wrong. Is there any way I can test if an exception is being rescued?
This works:
it "delete" do
delete("/api/notifications/:id").should raise_error()
end
...but what error should I be checking for? Or should I just leave it at that?
You could change your rescues. Change:
unless Rails.env.development?
to:
if Rails.env.production?
That should leave the test env out of your rescue404 behavior.

Ruby on Rails 3 - 404 routing

I am trying to setup my 404s in rails...
I have followed these instructions
and its seems to work if do do something like:
www.myapp.com/no_controller/
but if I do:
www.myapp.com/existing_controller/no_action
I get the Active Record, record not found...etc...
I would like that to also route to the 404 page...is this possible?
When you go to
www.myapp.com/existing_controller/no_action
you actually call show action of existing_controller with no_action as id. In the development mode you get RecordNotFound error. In the production you will get a 404 page.
If you want to customize this behavior in the development mode and root to the 404 page (BTW I don't suggest it! because it was done intentionally to help you debug), you can rescue_from this error:
class ApplicationController < ActionController::Base
rescue_from ActiveRecord::RecordNotFound do
render_404
end
def render_404
respond_to do |type|
type.html { render :template => "shared/error_404/message", :layout => "application", :status => "404 Not Found" }
type.all { render :nothing => true, :status => "404 Not Found" }
end
end
end
Out of the scope. One technique in this example can be useful when designing 404 pages: unlike standard public/404.html you can use application layout with this approach.