Associating two objects without nested resource - ruby-on-rails

Right now I have this:
resources :sources do
resources :pages
end
And when I want to create a page for a particular resource, I use the following route helper:
new_source_page_path(source_id)
So in the controller I know for which source I need to create the page for.
My routes are getting really messy with deep levels of nesting, and I have decided to avoid doing the nesting. However my doubt is, if I have this:
resources :sources
resources :pages
In the new action of my PagesController, how do I pass or tell that this new object needs to be associated with that particular Source?

You can pass the source_id as a GET param. Create your page#new links like this:
<%= link_to "new page", "/pages/new?source_id=#{source_id}" %>
Then in the page#new action assign that source to the page when you create the new page:
#page = Page.new
#page.source = Source.find params[:source_id]

Related

Creating link_to for sequential nested resources

I am creating a rails app whereby from a record's "show" page, the user is able to cycle through the record's nested resources (similar to a slideshow). For example, from the "show" page, the user will be able to link to the "show" for the first nested resource. From there, the user will be able to link to the "show" page of the next nested resource and so on. As the ids of each nested resource should be ordered smallest to largest, how can I create a link_to that looks for the next highest id (assuming nested resources are being created for multiple records simultaneously they may not necessarily be sequential) of a nested resource within a given record.
Because of Rails magic, you can pass the resource directly to the route helper, and it will use the correct id for that resource. For example:
<% #foo.bars.each do |bar| %>
<%= link_to bar.name, foo_bar_path(#foo, bar) %>
<% end %>
The above assumes that your route file looks something like:
resources :foos do
resources :bars
end
I highly recommend Rails Routing from the Outside In; it's been a very helpful resource for me!
To set the order of the child resource, you could use a scope, like this:
class Bar < ActiveRecord::Base
scope :ordered, -> { order(id: :asc) }
end
And then in your view, call foo.bars.ordered.each do |bar| etc.... Your nested resource will be returned from lowest to highest ID, skipping over any that have been deleted.
I hope this helps with what you were asking.
EDIT
I misread the question. To dynamically generate the "next" id, you could create method, next, on your child class. This answer seems to be something like what you want. Then in your view, you can just call:
<%= link_to "Next", bar_path(current_bar.next) %>

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.

Ruby on Rails multiple methods per page in routes.rb?

Stupid question... I have two forms with two different functions on one page, my views/projects/new.html.erb file. So far I've only implemented one, with the option to "Create" a new project. I want to add another function to sort the records displayed on the same page, something like:
<%= link_to "Category", { :controller => "projects", :action => "sortTable", :filter => "Category" }, :remote => true %>
--
My routes.rb file:
Docside::Application.routes.draw do
resources :projects
resources :categories
#get "home/index"
root :to => "projects#new"
match 'project/new',:controller=>"projects",:action=>"create"
end
But I'm getting the error "No route matches {:action=>"sortTable", :controller=>"projects"}". When I tried adding " match 'project/new',:controller=>"projects",:action=>"sortTable" " my other function didn't work, and the create function got screwed up. What should I have instead?
Try that:
resources :projects do
collection do
post :sortTable
end
end
And look at this guide
You can only have one route for a given path and method combination. You're trying to define multiple routes on the same path, so only one of these will work (the first one). You should be ok if you use distinct paths for each of these actions (instead of project/new for all of them. Beware of collisions with your existing routes)
You'll also make you life easier if you stick to rails' conventions (and the code will be easier to read if someone else starts working on it). For example resources :projects already creates a route for the create action. Additional actions can be added like so
resources :projects do
collection do
get :sort_table
end
end
Sets up a collection route (ie one that isn't about a specific project) for the sort_table action and sets up a URL helper for you (sort_table_projects_path). There are alternative syntaxes you can use - I encourage you to have a look at the routing guide

Router for nested resources in a "not usual" Ruby on Rails way

I am using Ruby on Rails 3.0.7 and I am trying to set nested resource routing to make it to work in a "not regular" RoR way.
In my routes.rb file I have
resources :articles do
resources :categories, :only => [:index], :controller => 'articles/categories' # The related controller is Articles::CategoriesController
end
so that I can browse following URLs:
<my_site>/articles/1/categories
<my_site>/articles/2/categories
...
What I would to do is to access new, edit and show controller actions for categories by using the same articles/categories controller used for the nested resource stated above (that is, Articles::CategoriesController) and by accessing these URLs:
<my_site>/articles/categories/new
<my_site>/articles/categories/edit
<my_site>/articles/categories/1
<my_site>/articles/categories/2
...
How can I do that? How I must code the router?
Maybe I can do something by using the router collection method like this
resources :articles do
collection do
# match something here for the Articles::CategoriesController...
end
resources :categories, :only => [:index], :controller => 'articles/categories'
end
but I don't know how to do that.
I'm not real sure what you're trying to do with those routes, so I'm not quite sure how to answer your questions. If your intent is to be able to add a new category for a particular article, or edit all the categories for a particular article, you have to pass an ID for the article. If you're trying to create a new article and a new category all at once, you don't need category in the route, just the article and you can do something like form_for([#article,#category]) in your form and use the build method in your controller. If you can clarify, I might be able to help you further (in other words, it's not hard to construct those routes -- but it depends on what you want to do with them.

Resources