Rails action caching refresh after expire - ruby-on-rails-3

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

Related

How to construct form_tag URL that PUTs to an external API

I'm using the Shopify API http://api.shopify.com/
And the Shopify Gem: https://github.com/Shopify/shopify_api that does most of the heavy lifting- just can't quite figure out how to make it work.
To update an #variant object I need to PUT here: PUT /admin/variants/#{id}.json
In config/routes.rb I made default resource routes with resources :variants and now I'm trying to make a form that updates a variant resource but can't configure the form to have the proper action.
Basically I'm constructing form_tag with a text field input that takes an integer and updates variant.inventory_quantity
Rake Routes give me this:
rake routes:
variants GET /variants(.:format) variants#index
POST /variants(.:format) variants#create
new_variant GET /variants/new(.:format) variants#new
edit_variant GET /variants/:id/edit(.:format) variants#edit
variant GET /variants/:id(.:format) variants#show
PUT /variants/:id(.:format) variants#update
DELETE /variants/:id(.:format) variants#destroy
You need to declare variants resource under admin namespace like this:
config/routes.rb
namespace :admin do
resources :variants
end
EDIT:
You don't have to do anything special for Rails to accept JSON. Rails will convert the JSON you passed in PUT into params and make it available to update method.
Here is the standard implementation of 'update' method:
app/controllers/admin/variants_controller.rb
def update
#variant = Variant.find(params[:id])
respond_to do |format|
if #variant.update_attributes(params[:variant])
format.html { redirect_to(#variant,
:notice => 'Variant was successfully updated.') }
format.json { head :no_content }
else
format.html { render :action => "edit" }
format.json { render :json => #variant.errors,
:status => :unprocessable_entity }
end
end
end
Refer to Rails guide and layout and rendering for details.

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

Rails Devise 2.0 own Log-out action

I use Devise and I want to do my logout action.
What I want to do is, that when I log out, I want to create a own JSON object to return. At this time, after I logt out, I get all my root articles.
How can I write my own destory action like I have found the create action?
class SessionsController < Devise::SessionsController
def create
resource = warden.authenticate!(:scope => resource_name, :recall => :failure)
return sign_in_and_redirect(resource_name, resource)
end
def sign_in_and_redirect(resource_or_scope, resource=nil)
scope = Devise::Mapping.find_scope!(resource_or_scope)
resource ||= resource_or_scope
sign_in(scope, resource) unless warden.user(scope) == resource
return render :json => {:success => true, :redirect => stored_location_for(scope) || after_sign_in_path_for(resource)}
end
def failure
return render:json => {:success => false, :errors => ["Login failed."]}
end
end
And my Routes in routes.rb
devise_for :users, :controllers => {:session => "sessions"} do
get "/users/sing_out" => "devise/sessions#destroy"
end
this is the destroy method of the sessions-controller.
you should be able to customize it to your needs. i think that it would be wiser to add another action and implementing your custom behavior there, as this will be less likely to cause unexpected errors with upgrading devise in the future.
# DELETE /resource/sign_out
def destroy
redirect_path = after_sign_out_path_for(resource_name)
signed_out = (Devise.sign_out_all_scopes ? sign_out : sign_out(resource_name))
set_flash_message :notice, :signed_out if signed_out
# We actually need to hardcode this as Rails default responder doesn't
# support returning empty response on GET request
respond_to do |format|
format.any(*navigational_formats) { redirect_to redirect_path }
format.all do
method = "to_#{request_format}"
text = {}.respond_to?(method) ? {}.send(method) : ""
render :text => text, :status => :ok
end
end
end

Rails 3 Correctly routing the destroy action for a session

I am refactoring my access_controller into a sessions_controller and can't seem to get my destroy action working properly.
Logging in seems to work fine, but I am unable to log out of a session. Here is the link I have for logging out:
<%= link_to("Logout", :controller => "sessions", :action => 'destroy') %>
routes.rb
resources :sessions
sessions_controller.rb
class SessionsController < ApplicationController
def new
end
def create
...
end
def destroy
session[:user_id] = nil
flash[:notice] = "You are now logged out"
redirect_to root_url
end
end
When I click "Logout" I get redirected to "/sessions/destroy" with a message of "The action 'show' could not be found for SessionsController". The destroy actions seems to want an id, but I don't need to pass in an id, I just want to run the action.
Ah, I found the answer here: http://railscasts.com/episodes/250-authentication-from-scratch
I need to set up my routes as follows:
get "log_out" => "sessions#destroy", :as => "log_out"
get "log_in" => "sessions#new", :as => "log_in"
resources :sessions

Rails 3.1 respond_to & render_403 problem

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.