Rails 3- Understanding the Controller/Routes relationship - ruby-on-rails

which allows me to load URLs like
/photos
/photos/2
/photos/2/edit
Edit allows the user to change the image, but I want a different kind of edit for permission type stuff specific to the app I'm building, which would look like:
/photos/2/updatesettings
So in the photos controller I added "def updatesettings ...."
And in the routes I added:
resources :photos do
collection do
get 'updatesettings'
end
end
But I'm getting an error: "Routing Error No route matches"
Suggestions? thanks

What you have in your routes file will match the url '/photos/updatesettings'
The only way I know how to do what you want to do is:
match "photos/:id/updatesettings" => "photos#updatesettings"
In the second part of that line, photos is telling it to look in the photos controller, and #updatesettings is telling it the method to call.
You would put this outside of resources :photos, so your code would be
resources :photos
match "photos/:id/updatesettings" => "photos#updatesettings"

There is a high chance you're using a form to update these settings, am I right?*
In which case you want to do post 'updatesettings' in your routes file, not get. This will define a route that responds to POST requests, vs one that only responds to GET requests. If you want both then use a get and a post line in your routes file.
* Most of the time, yes I am.

Related

Rails uncountable model name, no route matches get name_index

I have a model with uncountable name - class Equipment and in this article (https://markembling.info/2011/06/uncountable-nouns-rails-3-resource-routing) I found that in such cases we get into problems while trying to get model's index path. So article provides tips how to use inflection rules. However, I believe word 'Equipment', just like 'person' is already understood by Rails and I dont even need to define inflection rule, since I still get this path:
equipment_index GET /equipment(.:format) equipment#index
But, for some reason, after I navigate to localhost:3000/equipment_index, I get
No route matches [GET] "/equipment_index"
All other paths works (like localhost:3000/equipment).
Any ideas whats going on..?
p.s. please do not write how to add a custom path. I hope to solve this in the Rails way - convention over configuration. Thanks.
routes:
equipment_index GET /equipment(.:format) equipment#index
POST /equipment(.:format) equipment#create
new_equipment GET /equipment/new(.:format) equipment#new
edit_equipment GET /equipment/:id/edit(.:format) equipment#edit
equipment GET /equipment/:id(.:format) equipment#show
PATCH /equipment/:id(.:format) equipment#update
PUT /equipment/:id(.:format) equipment#update
DELETE /equipment/:id(.:format) equipment#destroy
routes.rb:
resources :users do
member do
get 'generate_raport'
end
end
resources :client_users
resources :clients
devise_for :users, skip: [:registrations]
resources :equipment
root to: 'static#homepage'
equipment_index is a named route, not a url string. The url string that corresponds to this named route is in this part:
GET /equipment(.:format)
When you say:
equipment_index GET /equipment(.:format) equipment#index
you are really saying that equipment_index is a named route (an alias so to say) for the actual url route localhost:3000/equipment. The last part that says:
equipment#index
just says that your request will be routed through the equipment controller and the corresponding index action.
Solution
You can simply navigate to localhost:3000/equipment to get to the index page for your equipment controller.
For example, you would link to this page using a rails link_to helper and the named route discussed above like this:
link_to "My index path", equipment_index_path
Follow up on comments
change add the following line to your routes.rb file directly after the line that contains resources :equipment. It would now look like:
resources :equipment
get 'equipment', to: 'equipment#index', as: 'equipment'
This is convention over configuration!
You're simply reading the output of rake routes wrong or have the wrong expectations about how its supposed to work. The first column is just the name of the route which is primarily used for creating path helpers. The actual paths are in the third column*.
equipment_index_path() # /equipment
equipment_path(1) # /equipment/1
equipment_path() # error due to missing id param
Since equipment is an uncountable noun Rails cleverly avoids an issue where the generated path helpers would be ambiguous - equipment_path could potentially lead to either the index action or the show action. Regular countable nouns don't have this issue so the _index postfix is not usually needed.
# no ambiguity
cats_path() # /cats
cat_path(1) # /cats/1
While you could argue that rails in that case should use the presence of the id param to differentiate that is not how its built and could mask bugs where you pass nil instead of a record.

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!

Nested routes in rails

I am someone who has always liked sinatra better than rails, and has never had to do a large enough scale project that rails was required (all the sources I have read say that rails is better for larger scale projects) and now I do have to do a large scale project. I have gotten very confused with the url structure of rails. What I am trying to do is the rails equivalent of this:
get "/" do
erb :index
end
get "/home" do
erb :dashboard
end
get "/home/profile" do
erb :profile
end
get "/home/friends" do
erb :friends
end
In the first one I understand that I should put in app/routes.rb I should put root home#index and in the home controller I should put def index end.
In the second one, I understand that I should do the same except replacing index with home.
But for the third and forth ones I have no idea what to do.
Also, is the a RESTful way to do the first two?
You probably want something like this
root 'home#index'
get 'home' => 'home#dashboard'
get 'home/profile' => 'home#profile'
get 'home/friends' => 'home#friends'
remember to use the command rake routes to see all your routes, where they lead and what their names are (if they have any)
I never understood what RESTful means, so someone else will have to answer that part of your question.
K M Rakibul Islam has shown you what can be called a "resourceful" way to do routes (because it uses the keyword resources) but it looks like you're just doing the static pages at this stage, so it's not necessary.
The simplest way to do routes is with this formula:
method url => controller::action, as: route_name
where method can be get, post, patch or delete so you can have different actions linked to the same URL depending on the method the request uses.
Putting a name on the route is optional, but it gives you a clean way to use the route in your views (route_name_path)
When you start making models then you'll find that using the resources keyword comes in handy. Read about it.
You can have this:
resources :home do
collection do
get :profile
end
collection do
get :friends
end
end
end
This will give you routes like this:
profile_home_index GET /home/profile(.:format) home#profile
friends_home_index GET /home/friends(.:format) home#friends
The standard way of declaring the root path:
root 'home#index'
And for the 2nd one, you have to do:
get 'home' => 'home#dashboard'
which will give you this route:
GET /home(.:format) home#dashboard
One route can be defined in many ways that works. But, Rails has conventions that should be followed while defining routes in your Rails app.
I would highly recommend you to take a look at the Rails Routing Guide

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.

Custom route yields dot instead of '?id='

I am adding a custon new action for my rails app by adding the following to my routes.rb:
resources :adventures do
member do
match :upvote, via: [:post, :delete]
match :downvote, via: [:post, :delete]
end
get 'seed', on: :new
end
(you can ignore the voting piece, just wanted to show you the whole block)
upvote_adventure POST|DELETE /adventures/:id/upvote(.:format) adventures#upvote
downvote_adventure POST|DELETE /adventures/:id/downvote(.:format) adventures#downvote
seed_new_adventure GET /adventures/new/seed(.:format) adventures#seed
adventures GET /adventures(.:format) adventures#index
POST /adventures(.:format) adventures#create
new_adventure GET /adventures/new(.:format) adventures#new
edit_adventure GET /adventures/:id/edit(.:format) adventures#edit
adventure GET /adventures/:id(.:format) adventures#show
PATCH /adventures/:id(.:format) adventures#update
PUT /adventures/:id(.:format) adventures#update
DELETE /adventures/:id(.:format) adventures#destroy
but this:
seed_new_adventure_path(#adventure_collection.id)
generates this:
http://localhost:3000/adventures/new/seed.6
instead of this:
http://localhost:3000/adventures/new/seed?id=6
I read a lot of posts with people getting dots instead of slashes, but none with adding a an additional new action. Am I doing something wrong, or do I need to add something more?
EDIT: I did make a mistake and did not mean to plurailze the adventure path (Is how I had it originally). The real problem is that all I needed to do was pass the id as a parameter.
Here is the path I was looking for:
redirect_to seed_new_adventure_path(:id => #adventure_collection.id)
It's because you are using the wrong pluralization.
In your example, you are using:
seed_new_adventures_path(#adventure_collection.id)
But the route is properly described as:
seed_new_adventure_path(#adventure_collection.id)
And will probably work fine and be more readable as:
seed_new_adventure_path(#adventure_collection)
Routes
Although Brad Werth is correct (your route pluralization is incorrect), the big problem you have is what you're trying to achieve.
You have specified the following link:
domain.com/adventure/new/seed
This is a get request with no other parameters present. I don't understand why you're passing an object to this route? This is why you're receiving the .6 problem (because Rails cannot build the routes), instead of getting /6
After thinking about what you're trying to do, and I believe you can fix it as follows:
#config/routes.rb
resources :adventures do
...
get "seed(/:id)", on: :new #-> domain.com/adventures/new/seed/6
end
OK, so in order to get this:
http://localhost:3000/adventures/new/seed?id=7
I need to pass a parameter to the link like this:
seed_new_adventure_path(:id => #adventure_collection.id)
I just forgot how to pass parameters!

Resources