Rails Routing question - ruby-on-rails

I'm not very familiar with routing, but here is my dilemma:
I have a photos controller with the usual show, edit, etc. views. I am trying to build a view to moderate photos. I have a moderate.html.erb view under my photos views. I have also defined moderate method in my photos_controller. If I try to access this view like /photos/moderate I get Couldn't find Photo with ID=moderate.
Am I building this the correct way, or does moderate need to have its own separate controller and view? Seems silly to me for that to be the case. Is this just something I need to configure in my routes?
UPDATE:
I've added this to my routes:
resources :photos do
resources :comments
collection do
get 'moderate'
end
end
Still getting the same Couldn't find Photo with ID=moderate message when I go to /photos/moderate...
UPDATE:
Crap! Just figured out the problem. I had a before_filter running that needed to ignore the moderate action... It's now working fine. Sorry for the trouble.

You can add a further restful action. Take a look at the docs

So you're trying to split out the concern of being able to edit and view photos?
If you wanted to keep your actions RESTful, you could create a "moderate" namespace and put all the actions that require authentication for photos in that namespace.
For instance,
#routes.rb
namespace :moderate do
resources :photos # will create paths like '/moderate/photos/', '/moderate/photos/1'
end
#controllers/moderate/photos_controller.rb
class Moderate::PhotosController < ActionController
before_filter :authorize_moderator!
#your standard restful actions like 'index', 'show', 'edit, 'update' would go in here.
end

Related

Best practice for adding a non standard view and controller action?

My situation I have a "Parent" model and controller. I want to know the best practice for adding independent pages such as a dashboard for users. My thought is that I can create a view dashboard.html.erb and inside the parent controller create a method of:
Parent controller
def dashboard
end
Routes.rb
get 'parents/dashboard'
I've done this once and it worked fine, but is was for a 'child' model.
When I run this same situation in the parent model I get the error
ActiveRecord::RecordNotFound in ParentsController#show
Couldn't find Parent with 'id'=dashboard
1.) All I've done is add a view, added the dashboard model to the controller, and placed get 'parents/dashboard' into the routes.rb and it tries to reference the show method??? Why?
2.) And is this the wrong way to add pages/actions to a rails application?
Do this:
#config/routes.rb
resources :parents do
get :dashboard, on: :collection #-> url.com/parents/dashboard
end
And is this the wrong way to add pages/actions to a rails application?
It's not "wrong", it's just ineffective, as demonstrated by your problem.
The problem you have is you've included your custom route below the resources :parents route. Because resources creates a /:id url which captures any requests sent to parents/:id, your "dashboard" request is being sent to the show action of the parents controller:
There are two remedies to your issue:
Put get 'parents/dashboard' above the resources :parents directive
Include an additional route to resources :parents (above)
You must remember that Rails matches your request with a route. That means the first route to match your request is processed.
So if you have...
#config/routes.rb
resources :parents
get "parents/dashboard"
... Rails will assume the dashboard is the :id in url.com/parents/:id, thus sending the request to show.
Apart from the very top code (the recommended answer), you could have the following:
#config/routes.rb
get "parents/dashboard", to: :dashboard
resources :parents
If you want to add an additional route with an :id then the syntax is different.
get 'parent_dashboard/:id', to: 'parents#dashboard'
Notice that the person string after get. This is used as the address of the website, when the user hits this, it would go to localhost:3000/parent_dashboard/1 if it is the first dashboard. You can exclude :id if you'd like. Of course, this would be different from the use case.
The second part of the route syntax is the :to, this method tells your app which controller and method to look at.
Hope this helps!
If you want to add a new view to the parents folder. Just do this:
parents_controller.rb
def dashboard
#parent = Parent.find(params[:id])
end
routes.rb
get '/parents/:id/dashboard', to: 'parents#dashboard', as: :parents_dashboards
resources :parents
Then in your parents/dashboard.html.erb view you can do everything that you can do in the parents/show view.
The link to your dashboard view would be parents_dashboards_path and you might have to use parents_dashboards_path(#parent) or parents_dashboards_path(parent) in certain circumstances.
This is an example of a custom path that works without using nested resources to accomplish access to the parent's dashboard.
I am using this approach in a project so I would like to hear any critique or comments on this approach. PEACE I'M OUTTA HERE!

Including attributes in custom Rails routes

I hope the title is not to misleading, as I don't know a better title for the problem I'm working on:
I have a doctor which belongs to location and specialty. I'd like to route to show action of the doc controller like this:
/dentist/berlin/7
I defined my routes like this:
get ':specialty/:location/:id', to: 'docs#show'
And in my views create the following url to link to the show action of the doc controller:
<%= link_to doc.name, "#{doc.specialty.name}/#{doc.location.name}/#{doc.id}" %>
Is this a good solution to the problem? If not, is there a cleaner way to construct urls like this possibly using resources? What the heck is the name for a this problem?
Thank your very much for your help in advance.
For references, you should have a look at this page (especially the end of section 2.6)
If it is only for a single route, it's okay as you did. But then if you want to have more than one route (like /dentist/berlin/7, /dentist/berlin/7/make_appointment, etc.) you might want to structure a bit more your routes so as to take advantage of rails resources.
For example, instead of
get ':specialty/:location/:id', to: 'doctors#show'
get ':specialty/:location/:id/appointment', to: 'doctors#new_appointment'
post ':specialty/:location/:id/appointment', to: 'doctors#post_appointment'
You could have something like this (the code is almost equivalent, see explanation below)
resources :doctors, path: '/:specialty/:location', only: [:show] do
member do
get 'new_appointment'
post 'create_appointment'
end
end
Explanation
resources will generate the RESTful routes (index, show, edit, new, create, destroy) for the specified controller (doctors_controller I assume)
The 'only' means you don't want to add all the RESTful routes, just the ones specified
Then you want to add member actions, ie. actions that can be executed on a particular item of the collection. You can chose different syntaxes
resources :doctors do
member do
# Everything here will have the prefix /:id so the action applies to a particular item
end
end
# OR
resources :doctors do
get 'new_appointement', on: :member
end
By default, the controller action is the same as the path name you give, but you can also override it
member do
get 'appointment', action: 'new_appointment'
post 'appointment', action: 'post_appointment'
end
Rails has some wonderful helpers when it comes to routing !
The correct approach is to give your route a name, like this:
get ':specialty/:location/:id', to: 'docs#show', as: 'docs_show'
Then you can use it like this:
<%= link_to doc.name, docs_show_path(doc.specialty.name, doc.location.name, doc.id) %>
Note 1:
Rails appends _path at the end of the route names you define.
Note 2:
You can see all the available named routes by executing rake routes.

Add a 'destroyAll' action and route to my RESTful rails app

I want to add a destroyAll action that destroys all posts in a blog app that I am writing. I have my routes.rb like this :
Rails.application.routes.draw do
resources :writings
root "writings#index"
end
And inside my controller I want to add the following action :
def destroyAll
#writing = Writing.all
#writing.each.destroy
end
please guide me through how to add a route to the destroyAll action and how to use it inside my view to make a link to use that action.
Thanks so much in advance
It should be defined as collection route of writings:
resources :writings do
collection do
delete :destroy_all
end
end
Having it, you can link to delete_all action like this:
link_to 'delete all', [:destroy_all, :writings], method: :delete
You should notice that I wrote delete_all instead of deleteAll, because the former sticks to Ruby naming convention.
Very late to the party, but wanted to add that perhaps another way is to consider all of the writings as a kind of resource itself, so you can keep the controllers adhering to the basic RESTful actions Rails typically uses.
resource :writings_collection, only: :destroy
# DELETE /writings_collection --> WritingsCollectionController#destroy
The advantage is the route helpers will not conflict with the writings ones.
You could even name the collection resource something meaningful that represents the collection concept.
I think Marek Lipka's answer is perfectly adequate, but I'd like to offer an alternative, which IMO is a tad bit more RESTful. I'd model the collection of all writings as a separate resource. Since this resource does not have an id and there can only be one instance of this resource, it must be a singular resource:
resource :writings, only: :destroy
The problem with this is, that Rails will map this action to WritingsController#destroy, since Rails uses plural controllers for singular resources (any our singular resource has a plural name anyway), so this will conflict with the destroy action for individual writings. In that case you could either create a custom controller for this and implement a destroy action there:
resource :writings, only: :destroy, controller: 'collections/writings'
or you could still use the old controller, but specify another action:
resource :writings, only: :destroy, action: :destroy_all
In that case, you could use the following in your views:
link_to 'delete all', writings_path, method: :delete

Rails controller and routes configuration for website

I have an application in RAILS, it is composed of a set of API, a basic website, and an admin dashboard.
For the API routing I have no problems, as they belong to a model and a controller and are compliant with the RAILS RESTful pattern (a controller for each model, and a method for each HTTP method).
What I'm not comfortable with is writing routes and controllers for website.
The main website is at / so the default route is root :to => "home#index"and I have
Routes for the website pages which look like
get "home/index"
get "map/index"
get "api/index"
get "cgu/index"
get "legal/index"
Which I think it is not good, as I have a controller per view and I need to define a get for each views.
Now for the dashboard I tried a different approach.
It is at /dashboard, the default route is match "dashboard" => "dashboard#index" and here is few pages as an examples
get "dashboard/index"
get "dashboard/users"
get "dashboard/users_stats"
get "dashboard/routes"
get "dashboard/routes_stats"
get "dashboard/charts"
get "dashboard/financial"
So for the dashboard I have a massive dashboard_controller, which contains a def method for each dashboard pages. IE:
#dashboard/users
def users
#users = User.all
respond_to do |format|
format.html {render :layout => 'dashboard'}
end
end
the controller of the dashboard is at /controller but for views and assets I have put it in /folder/dashboard/
Here is 2 questions:
What is the best way to build the home website and dashboard ? Should I have a controller per page or a global controller where I have a method per pages ? (Which I find very convenient, less code).
How should I organize my routes to avoid to set a get "something/something" for each page ?
Or it is normal with RAILS that I have a route defined for each of my page ? I'm fairly new.
EDIT:
To clarify, the dashboard is built around an existing application with API that follow RESTFul Rails pattern:
resources :users
resources :routes
But the dashboard is not tied to any existing resources, it only do stats about those resources.
If you have custom controller action names, then yes, you'll need to define every route. If you use Restful routes, then you can define them easily as
resources :users
which will automatically create routes for actions: index, show, edit, update, create and destroy.
This might be helpful: http://guides.rubyonrails.org/routing.html
For your dashboard, which probably is bringing together a lot of resources, so they'll probably be custom methods. I'd suggest focusing on building your app by individual resource. Then, once you've defined them all, build your dashboard.
I agree with everything that other people have said here. You should definitely try to be more RESTful and create more routes like this:
resources :users
However, there is usually a controller that is not RESTful (usually called pages or static) that serves pages such as Privacy, About Us, etc, etc. For those routes, I usually do this:
['api', 'privacy', 'us'].each do |p|
get p, :controller => 'pages', :action => p
end
check this guide out if you haven't;
I think you haven't embraced the MVC concept of Rails.
For example, say you have "users". You should have users_controller.rb, users.rb (model), /views/users (view) under the app directory.
users_controller contains the index, show, create, etc default actions and your custom actions like stats
user.rb contains instance/static/helper methods
/views/users/ contains templates that corresponds to actions in the controller.

How to call a custom method in a Rails controller from a View

I have set up a site that is correctly using basic CRUD functionality succesfully. However, when I try to add a custom method to my controller I cannot seem to hook it up to a link_to call. I keep getting a method not found error.
The Controller method looks like this:
def complete
return render :text => "Complete"
end
and my call in the View looks like this:
<%= link_to 'Complete', complete_list_task_path(#list,#task) %>
This same call works for my Edit method, so I'm not sure what I'm doing wrong. Do I need to do anything special when the method is not a basic CRUD call?
The only relevant part of my route file looks like this (List and Task are nested resources. List has many tasks, and task belongs to a list):
resources :lists do
resources :tasks
end
I have also tried adding post "complete" => "lists/:id/tasks/:id#complete", :as => "complete" to my route to see if it would help to implicitly try to call it, but I still got a "method not found error".
Any help in figuring out how to make this call would be greatly appreciated. Thank you!
See Adding More RESTful Actions in the Rails Routing guide for details; the nutshell is that if you want routing to recognize anything other than the standard methods, you need to add it.
You need to declare the method in the router, 'resources' refers to the 7 crud actions (index, new, create, edit, update, delete, show).
Off the top of my head, I think you'd need:
resources :lists do
resources :tasks do
member do
post :complete
end
end
end
The nesting makes me less confident, but that's the general thing you need to do.

Resources