Rails: half-restful routes - ruby-on-rails

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.

Related

Rails Polymorphic Redirect Nested Resources

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.

Rails cache sweeper fails on create

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

rails devise using current_user and matching routes :id

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

Rails 3 - Helping with a Commenting Module

class CommentsController < ApplicationController
def create
#commentable= context_object()
#comment = #commentable.comments.build(params[:comment].merge(:user_id => current_user.id))
if #comment.save
respond_to do |format|
format.js
end
else
render :action => 'new'
end
end
private
def context_object
params[:constraint][:context_type].singularize.classify.constantize.find( context_id )
end
def context_id
params["#{ params[:constraint][:context_type].singularize }_id"]
end
end
This commenting module has served me well but I ran into a hitch this morning, possibly because of my use of nested resources. Essentially, I now have a URL like:
/projects/3/albums/6/attachments/84
When I comment on that page, I get the error:
ActiveRecord::RecordNotFound (Couldn't find Project without an ID):
app/controllers/comments_controller.rb:102:in `context_object'
app/controllers/comments_controller.rb:14:in `create'
My routes file looks like:
resources :projects do
resources : albums do
resources :attachments
end
end
resources :attachments do
resources :comments, :only => [:create, :update,:destroy],
:constraint => {:context_type => "conversations"}
end
Any ideas on how I can get the commenting module to play nicely with commenting on project>Album>Attachment ?
Thanks for the input,
Posting this as an answer in order not to clutter the comments to the original question.
Since you don't have the requirement to keep attachments available via /attachments - making the second resources block useless, do something like this:
resources :projects do
resources :albums do
resources :attachments do
resources :comments, :only => [:create, :update,:destroy],
:constraint => {:context_type => "conversations"}
end
end
end
That's going to change your routes helpers (_path and _url), go through your controller(s) and view(s) and change them to reflect your new helpers.
Specifically, attachment_comments_path becomes project_album_attachment_comments_path.
The full list of routes for those models can be viewed by running rake routes in a console. I'd also recommend you take a closer look to the Rails routing guide.

before_filter except override not working

Good afternoon all,
I've got a controller running a before filter with an except override. It works fine for all the methods in the controller, save one. I've copied the code for the method in question from another part of my app, just making the changes I need to get it working for this particular area.
Here is an example of the controller code:
class Admin::DealsController < ApplicationController
before_filter :populate, :except => [:index, :create, :new, :search]
# GET /deals
def index
#deals = Deal.paginate(:page => params[:page], :per_page => PAGESIZE, :order => 'start_time DESC')
#page_subtitle = "Deals"
end
def search
#searchString = params[:search_string]
#deals = Deal.paginate(:page => params[:page], :per_page => PAGESIZE, :conditions => "subject LIKE '%#{#searchString}%'")
#page_subtitle = "Deal Search Results"
render 'index'
end
protected
def populate
#deal = Deal.find(params[:id])
end
end
And here is the route in my routes.rb file for the method in question:
admin.search_deal 'deals/search', :controller => 'deals', :action => 'search', :method => 'get'
Now, when I attempt to visit this URL:
/admin/deals/search?search_string=test&commit=Search
It generates the following error message:
Couldn't find Deal with ID=search
This code works on several other controllers, and I've been beating my head against the wall all day trying to see what I'm doing wrong. I would appreciate any help, thank you.
It would appear that your routes are not in the correct order. If you look more closely at the log file you may be able to verify this, but the action being called is probably "show" and not "search".
Any custom routes should be listed above the default ones, or better, you should remove the defaults entirely.
Look for a line like this in your log/development.log:
Processing Admin::DealsController#show
If you list your routes with rake routes make sure that your custom search_deal route is listed before any generic routes.
You might want to use resourceful routes, too:
map.namespace :admin do |admin|
admin.resources :deals, :collection => { :search => :get }
end

Resources