My application is using a namespace for administrative purposes. I recently tried to start using action caching however I ran into some problems trying to expire the cache using expire_action. Basically I have a index action in my default namespace newsposts controller that is cached using action caching like this:
class NewspostsController < ApplicationController
caches_action :index, :layout => false
def index
#posts = Newspost.includes(:author).order("created_at DESC").limit(5)
end
end
This caches the view under views/host/newsposts.
The default namespace has no actions for modifying data, they are all in my admin namespace. In my Admin::NewspostsController I am trying to expire this cache in the create action like this:
expire_action(:controller => 'newsposts', :action => 'index')
however this will expire a cache file located under views/host/admin/newsposts. Obviously it can not work since im in the admin namespace and rails is (rightfully) looking to expire cache for this namespace. Sadly I can not pass a namespace parameter to the axpire_action method, so how can i expire the action cache in another namespace?
after some more digging I finally found the solution. It's a bit hinted in the url_for method:
In particular, a leading slash ensures no namespace is assumed. Thus, while url_for :controller => 'users' may resolve to Admin::UsersController if the current controller lives under that module, url_for :controller => '/users' ensures you link to ::UsersController no matter what.
So basically,
expire_action(:controller => '/newsposts', :action => 'index')
Will expire in the default namespace, and
expire_action(:controller => 'admin/newsposts', :action => 'index')
in the admin namespace (when in default).
RailsCast
One additional note I learned, if you want to expire a specific format, such as XML, JSON, etc., just
expire_action(:controller => '/newsposts', :action => 'index', :format => 'xml')
or whatever format you want. It look me a while to figure out.
Related
I'm making an app in Ruby on Rails 3.1.3. I have different types of users (i.e. admin, operator, advertiser, etc...), and each has a different main (or home) page. I want to make a route helper that will give me the respective route for the home page of the current logged in user by using something like home_path. This is mainly for redirecting after certain actions (I want to redirect back to the respective home pages depending on the type of user).
I already have some methods available such as current_user (returns the current logged in user), current_user.admin? (returns true if the current logged in user is admin), current_user.operator?, etc.
Right now I'm using a helper method to do this, but it doesn't seem like a very Rails way to do it. The code follows anyway:
def home_path(params = {})
user = current_user
case user
when user.admin?
params = {:controller => 'administrators', :action => 'index'}.merge(params)
when user.advertiser?
params = {:controller => 'advertisers', :action => 'show', :id => user.advertiser_id}.merge(params)
when user.operator?
params = {:controller => 'callcenter', :action => 'index'}.merge(params)
else
params = {:controller => 'posts', :action => 'home'}.merge(params)
end
url_for(params)
end
I figure this should be done with constrained routes, but I still don't get how it could be done to depend on the .admin?, .operator?, etc. methods. Any help on this would be greatly appreciated.
Using a helper method is fine for this. It should probably end up in your controller, rather than a view helper, though, which gives it access to the current_user. With some cleanup, you can arrive at something that ain't half bad with the same idea you have now.
module DefaultHomeHelper
DEFAULT_PARAMS = { controller: :posts, action: :home }.freeze
ROLE_SPECIFIC_PARAMS = {
admin: { controller: :administrators, action: :index },
advertiser: { controller: :advertisers, action: :show },
operator: { controller: :callcenter, :action: :index }
}.freeze
def home_path(params = {})
url_for params.reverse_merge(ROLE_SPECIFIC_PARAMS[current_user.role] || DEFAULT_PARAMS)
end
end
I've made the assumption you can be more direct and ask your User object to just tell you its role instead of guessing one after the other. You will almost certainly need to tweak the code to accomodate whatever you're calling this on your user. I've also used the newer hash syntax, but if you're running or accommodating Ruby < 1.9 you will need to update. I've used symbols for the actions and controller names, too, because I like referring to objects and methods with symbols instead of strings (and controllers and actions are objects and methods).
You could do a simple include DefaultHomeHelper in your ApplicationController to use this. You can also make it available to your views with helper_method :home_path.
I'm attempting to use a sweeper to clear the home page index action when a new article is published.
The home page cache is working fine in development environment and expires after 1 minute. However when an article is saved, the sweeper action is not triggered.
class HomeController < ApplicationController
caches_action :index, :expires_in => 1.minute
cache_sweeper :article_sweeper
def index
#articles = Article.published.limit(5)
end
end
class ArticleSweeper < ActionController::Caching::Sweeper
observe Article
def after_update(article)
expire_action(:controller => 'home', :action => 'index')
end
end
Either I've gone wrong somewhere or a different approach is needed to expire the home page cache.
My app uses ActiveAdmin to update articles, and Dalli for Memcache (as I'll be using Heroku).
Two steps to the solution:
The controller performing the changes on the model needs to have the sweeper reference, not the destination controller as shown above. In this case it is active_admin, so I added this to my admin/articles.rb file (source) instead of the home controller.
controller do
cache_sweeper :article_sweeper
end
And the controller name needs a slash
expire_action(:controller => '/home', :action => 'index')
I have an application that will have an API, with a /api/v1/ namespace:
namespace :api do
namespace :v1 do
resources :gateways do
resources :mappings do
# maybe more stuff
end
end
end
end
my application uses devise and cancan.
My mappings controller down in app/controllers/api/v1/mappings_controller.rb works correctly from rspec test cases if I leave out :format=>:yaml (asking for HTML, and getting a 406).
If I ask for :yaml, devise seems to think that my test user is not allowed.
My test case is stupid simple:
describe "Agent access to mappings" do
it "gets a list of mappings that includes test_user mapping" do
#test_agent = users(:firewallagent)
sign_in(#test_agent)
get :show, {:gateway_id => 1, :id => 2} #, :format => :yaml
assert_response 200
end
end
I can't see anything in devise/warden which would be format specific, but maybe I've missed it.
The fault was that :format=>:yaml needs to go into the first hash, rather than into the second hash for get. So:
get :show, {:gateway_id => 1, :id => 2, :format => :yaml}
This function is defined in the application_help.rb:
def gravatar_url_for(email, options = {})
url_for(
{
:protocol => 'http://',
:host => 'www.gravatar.com',
:controller => 'avatar',
# :controller => 'avatar.php',
:gravatar_id => Digest::MD5.hexdigest(email),
:only_path => false
}.merge(options)
)
end
It's used in views:
<%= image_tag(gravatar_url_for user.email, {:d => 'identicon', :s => 32, :r => 'g'}) %>
Occasionally, its usage will result in a routing error:
No route matches {:controller=>"avatar", :d=>"identicon", :r=>"g", :gravatar_id=>"486575e581db04b7c8ca218af8488657", :s=>32}
A valid email is being supplied when the error occurs.
If I replace the url_for() with this logic, it works as expected:
url_for("http://www.gravatar.com/avatar/" + Digest::MD5.hexdigest(email) + "?d=identicon&s=40&r=g")
** edit **
I had removed the following line from the routes.rb file:
# This is a legacy wild controller route that's not recommended for RESTful applications.
# Note: This route will make all actions in every controller accessible via GET requests.
match ':controller(/:action(/:id(.:format)))'
Is there a way to get the url_for to work without the 'legacy wild controller route'?
You might want to take a look at the Gravtastic plugin for Rails which supports Gravatar images in both Ruby and JavaScript.
I am porting a Merb app to Rails 3. In Merb we could put an Identify block around a route to define how an :id route parameter was to be supplied, e.g.,
# this is a Merb route that I want to port to Rails 3 routing; I get everything except
# how to replicate the behavior of Merb's Identify block which doesn't require one to
# futz with overriding to_param on user; a user instance gets passed to the url builder
# ala url(:edit_password_reset, user) and this tells the router to use the
# reset_password_token method on user to supply the :id value for this one route
Identify User => :reset_password_token do
match("/reset-password/:id", :method => :get).to(:controller => "password_resets", :action => "edit").name(:edit_password_reset)
end
# and then later define more routes that use the user's id without a problem
# since to_param was not overridden on user; here I have already translated to
# Rails 3 and this works fine
controller :users do
get "/register", :action => "new", :as => "new_user"
get "/users", :action => "index", :as => "users"
get "/users/:id", :action => "show", :as => "show_user"
get "/users/:id/edit", :action => "edit", :as => "edit_user"
put "/users/:id", :action => "update", :as => "update_user"
post "/users", :action => "create", :as => "create_user"
end
In Rails, as in Merb, you can override to_param to provide an alternative id value for routes, but for a case where one time you want to use an id and another time you want to use a different method on the same object (as above), Identify is convenient. What is the Rails 3 equivalent? I looked through the Rails 3 source and tests and didn't see anything equivalent to Identify. Did I miss it?
I can refactor things and maybe should to not need it in this case, but still I would like to know if I missed something.
Thanks.
I came across the same problem; turns out the best way is to skip to_param entirely when calling a url or path. For instance:
# This will set params[:id] to #user.to_param
edit_password_reset_url(#user)
# This will set params[:id] to #user.reset_password_token
edit_password_reset_url(#user.reset_password_token)
In other words, to_param is only called when passing a record to the url helpers; if you pass it a string instead, it will just parse the string.