I have a controller where:
caches_action :show
cache_sweeper :the_model_sweeper, :only => [:update, :destroy]
and sweeper:
observe TheModel
def after_save(the_model)
expire_cache(the_model)
end
def after_destroy(the_model)
expire_cache(the_model)
end
def expire_cache(the_model)
expire_action :controller => '/the_model', :action => 'show'
end
and am getting:
ActionController::RoutingError (No route matches {:controller=>"/the_model", :action=>"show"}):
The problem I'm guessing is becuase the sweeper is called after_save, when on a new record there will be nothing to destroy, even though I have specifically said for it only to sweep on update or delete.
(I have obviously renamed the model to "The Model" for example purposes)
The problem was due to using ActiveAdmin, and forgetting (doh...) to add :only => [:update, :destroy] to the active admin config for that model
Related
Bumping into a redirect problem with nested resources and polymorphic associations. I think I can find a way forward however I'd like to find out what can be considered as best practice.
I have the following nested resources:
namespace :navigate do
resources :boks, :only => [:show] do
resources :groups, :only => [:show]
resources :categories, :only => [:show]
resources :tools, :only => [:show, :index]
resources :artifact_types, :only => [:show]
resources :artifacts, :only => [:show, :index]
resources :processus do
resources :notes, module: :processus
end
end
end
Notes being a polymorphic association (that I will also later use with the Tool model).
Most of my code is inspired but the excellent gorails.com episode: https://gorails.com/episodes/comments-with-polymorphic-associations
The controller that I use to manage notes is:
class Navigate::NotesController < Navigate::NavigateController
before_action :authenticate_user!
def create
#note = #noteable.notes.new note_params
#note.user = current_user
#note.save
redirect_to [:navigate, #bok, #noteable], notice: "Your note was succesfully created."
end
def update
#note = #noteable.notes.where(user: current_user).first
if #note.update(note_params)
redirect_to polymorphic_url([:navigate,*** HOW TO REFERENCE BOK ***, #noteable]), notice: "Your note has been updated."
else
flash.now[:alert] = "Unable to update your note."
render :edit
end
end
private
def note_params
params.require(:note).permit(:content, :public)
end
end
Note the * HOW TO REFERENCE BOK * part. This is where my problem is. Once I update the "Note" I want to redirect to #noteable related controller (here Processus) but in order to construct the URL I need to have an #bok object which I don't have in this instance because I actually don't need it.
I can also retrieve the correct #bok model but I wonder if there is another way to deal with this redirect?
My URL for redirection should be http://localhost:3000/navigate/boks/1/processus/2 but in order to construct it I would need a Bok object which I haven't go in my controller above (as I don't need it).
Any ideas? Thanks!
You can't reference a route for a nested object without the id of the object it is nested into.
Say I have a module name Server that was created with a scaffold. I want the url 'www.example.com/server/' to be redirected to the first Server object that exists. So for example to be redirected to 'www.example.com/server/2'.
How could this be done with routes.rb (or any other way)?
route.rb:
Rails.application.routes.draw do
resources :servers
end
Server controller:
class ServersController < ApplicationController
before_action :set_server, only: [:show, :edit, :update, :destroy]
# GET /servers
# GET /servers.json
def index
#servers = Server.all
end
....
your can put
redirect_to server_path(Server.first) and return
inside your index method it'll redirect you when ever index action is called.
and just to extent #richfisher's answer (which might be a more appropriate way to do it.)
resources :servers, except: [:index] # this won't generate redundant routes
get '/servers/' => 'servers#first' #note this is now accessible via "server_path" instead of "servers_path" helper.
For what it's worth, I'd do this:
#config/routes.rb
resources :servers, except: :index do
get "", action: :show, id: Server.first.id, on: :collection
end
This will allow you to use the show action in place of index in a super efficient setup:
#app/controllers/servers_controller.rb
class ServersController < ApplicationController
def show
#server = Server.find params[:id]
end
end
I have a controller whose routes are defined as:
resources :items, except: [:new, :edit]
and the new and edit actions are not defined in the controller.
When I browse to /items/new I get an error from the database saying that the item was not found.
And the parameters contain {"id"=>"new"} from which I understand that the new part of the path is interpreted as the id.
How could I get the /items/new to fail routing?
You can use constraints on the :id segment of your routes.
If you know your id will always be a number for instance, try using:
resources :items, except: [:new, :edit], constraints: { id: /\d+/ }
This will prevent anything that does not match the /\d+/ regex (ie. one or several digits) to be considered as an id value, thus preventing the route to be matched for /items/new
You can simply do it in your controller to rescue it from failing by rendering a view
class ItemsController < ApplicationController
rescue_from ActiveRecord::RecordNotFound, :with => :record_not_found
def record_not_found
render 'record_not_found'
#do the other stuff
true
end
end
I've got these three bottom routes below which are very error-prone because of other routes declared normally:
# normal routes
resources :documents, :except => [:show, :edit, :update]
resources :photos, :except => [:show, :index]
...
# error-prone routes
get ":client_code" => "share#index", :as => :shares, :format => false
get ":client_code/:id" => "share#show", :as => :share, :format => false
get ":client_code/:document_id/more/:component_id" => "share#more", :as => :more, :format => false
I've got a few methods in the ShareController to deal with the requests like so:
def show
get_user_by_parameter
if get_document_by_user_or_issue and #document.is_showable? and #parameter_user == #document.user
...
end
private
def get_user_by_parameter
#parameter_user = User.where(:client_code => params[:client_code]).first
end
def get_document_by_user_or_issue
if params[:id].match(/\D/)
#document = Document.where(:user_id => #user.id, :issue => params[:id]).first
else
#document = Document.find(params[:id])
end
end
I need the routes to be that minimal, but not only is this ugly and un-RESTful but it's very error prone.
The :client_code will always be the owner of the #document being viewed. It's kinda like a safety check/ownership kinda function. But, because of all the reasons listed above: is there a better way to write this? There's gotta be a better way than that.
Thanks.
Controller Based Check:
before_filter :find_document
def find_document
Document.find(params[:id])
end
def is_owner?(document)
redirect_to root_path if current_user.id != document.owner_id
end
Isn't a check like this much easier? I'm not sure why you have a share controller, so I don't want to be presumptuous here.
Which will allow you to do:
resources :shares, only: [:index, :show]
Also:
User.where(:client_code => params[:client_code]).first
Can be refactored to:
User.find_by(client_code: params[:client_code])
Assuming you are on latest rails version, else:
User.find_by_client_code(params[:client_code])
Let me know what the shares are for, I'm not sure I provided the full solution for you.
Cheers.
EDIT
if you are using shares to provide a different view, i suggest doing this:
Within the controller,
def index
if params[:shares]
render 'shares'
end
end
Unless you really wish to have a different route for it. This allows you to not have a shares controller for essentially what is the document model.
I am having issues with devise current_user confusing my model's :id as the users :id.
routes:
match "/causes/:id/:slug" => "causes#show", :as => :cause, :via => 'get'
match "/causes/:id/:slug/edit" => "causes#edit", :as => :edit_cause, :via => 'get'
match "/causes/:id/:slug" => "causes#update", :via => 'put'
resources :causes, :only => [:index, :new, :create]
in my :causes controller:
before_filter :check_privileges, only: [:new, :create, :edit, :new, :update]
def check_privileges
#when I use this code everyone can access edit, etc.
redirect_to root_path unless current_user
end
and in my :causes model
belongs_to :user
For some reason, when I use current_user at all, in this controller, it always thinks that current_user is equal to the id in /causes/:id/:slug/
I have tried putting the check privileges code in the application controller,
I have even tried assigning code like this:
def check_privileges
#when I use this code no one can access edit, etc
#user = User.find_by_id(params[:id])
redirect_to root_path unless #user
end
I need help, anyone have suggestions? All I want it to do is verify the user is the current user so not everyone can edit the cause.
Your post is a bit confusing. IIRC devise stores the current user id in the session and does not ever get it from the url.
Seeing that this is a problem associated with privileges and rolling out your own solution. I would highly recommend an alternative.
https://github.com/ryanb/cancan
This works great with devise and should solve your problems