RoR - add content in relation to other model - ruby-on-rails

so i have got two models with each one controller:
Model Project has_many Themes
Model Theme belongs_to Project
in my routes file i added resources :projects and also for themes. Now i can add a project with localhost/projects/new which works fine and i can add Themes with localhost/themes/new. But thats not the way i want.
I only want to add Themes related to a project. Whats the best way to do this? I tried something like this: match "projects/:project_id/themes/new" => 'themes#new', :as => 'themes' which seems to work, but after submitting my new form nothing happens. the new form gets rendered again without a error message or something like that. my form tag in html gets rendered as the following:
<form accept-charset="UTF-8" action="/projects/3/themes/new" class="new_theme" id="new_theme" method="post">
do you have any ideas what went wrong? is there a best practice for something like that, because i think its a often wanted model.

You want to use nested resources
resources :projects do
resources :themes
end

This situation is called nested resources.
In your routes define:
resources :projects do
resources :themes
end
It will create exactly the url you described, as well as a bunch of helper methods. See the rails guides for a complete list, but here's an example:
To reach a single theme you would use projects_theme_path(#project, #theme), or to view all the themes for a project you would use projects_themes_path(#project). Again, see the rails guides for the full explanation and all the helpers.
Also, at any time you can run rake routes to see the EXACT helper methods as they are set up for your project.

You should also check out https://github.com/josevalim/inherited_resources which makes modeling and implementing these routes stupid simple.

Related

Rails index on show page

What is the standard practice for a rails app when a standard 'show.html.erb' for one model essentially just lists its has_many of another model? There is no index for the 'child' model as without the context of its 'parent' model, a list of them would be useless.
Creating /parents/1/children/1 feels like the right thing to do (especially in terms of the API) and yet on the /parents/1 page there would be nothing but a bit button saying "carry on" ... again, useless.
Do I creat separate routes, like the one above, purely for the API or am I missing a cleaner more 'Railsy' way.
EDIT
I'm not sure the above is totally clear. If I have an Exam that was completely made up of Questions, would the Rails way to be to link to /exam/1/questions or purely to /exam/1 with a list of questions. Obviously the API would go to /exam/1/questions in most cases but what about the standard HTML page? It would seem like it wants to be /exam/1 but with nested resources the create route (by default) goes to the /exam/1/questions route. If there is a validation error on the /exam/1/questions/new route the form is rendered again on the create (/exam/1/questions) route; if the page is then refreshed you then get a 404 error as there is no index for the children.
All this leads me to believe that Rails expects be an index action for the majority of models; but in the above example this feels odd.
If I understand you right you dont whant the route to /parents/1 to be on your path. For this reason you should specify it in your routes.rb
resource :parents, except: :show do
resource :children
end

link_to 'new' action in a different controller?

I want to have a link at the bottom of my show.html.erb that links to the new action in a different controller.
class Sample < ActiveRecord::Base
belongs_to :song
end
class Song < ActiveRecord::Base
has_many :samples
end
So, at the bottom of the show action for songs, I want to link to new for samples. This seems pretty easy, but I'm struggling to figure this out. I would also like to pass the id from the song to the form as :song_id
Fiddy, because you're new, let me explain how this works...
Routes
Your problem is that you don't understand the Rails routing structure - I'll hopefully explain it for you.
Rails, since it's an MVC framework, builds a series of "routes" for you. These "routes" are stored in the file available at config/routes.rb.
Routes, as described by the Rails documentation are as follows:
The Rails router recognizes URLs and dispatches them to a controller's
action. It can also generate paths and URLs, avoiding the need to
hardcode strings in your views.
The most important thing you should consider here is the way the routes generate paths for you. These paths are simply Rails "helper" methods, which you can call from your views. The reason these exist is two-fold -
They provide you with a DRY (don't repeat yourself) way of accessing / manipulating data
They are constructed around objects, helping maintain the object-orientated nature of Rails
These will likely mean nothing to you. However, what you need to realize that if set up your routes correctly, it seriously helps your app's infrastructure immensely.
--
Rails
This leads us quite nicely onto appreciating the way in which Rails works
Rails is an MVC (model view controller) framework. This might seem somewhat trivial, but in reality, it's one of the most important aspects to learn about Rails development, and here's why:
The Rails software system works by taking "requests" (user input) and then routing them to specific controller#actions. Those controllers then build model data from the database, and will translate that into either variables or objects, which you can use in your view.
The reason I mention this is that this type of development takes a lot of getting used-to, in that your program's flow is not about logic / functionality, but the accessibility of data. Therefore, when you ask about the routes or other parts of your app, you need to firstly remember what data you wish to show, and also how you want that data to be shown - this will give you the ability to construct & use the routes / controller actions which will get it to work properly
--
Fix
In terms of what you're saying, the way you'd go about achieving the result you want will be to use a nested route:
#config/routes.rb
resources :songs do
resources :samples #-> domain.com/songs/:song_id/samples/new
end
This will create a new route for you (which you can check by firing rake routes in your rails c (console). This will give you a path to use for your samples#new action:
#app/views/songs/show.html.erb
<%= link_to #song.name, new_song_sample_path(#song) %>
The above link will take you to the samples#show action, which you'll be able to populate with as much data as you require from the samples controller. The important thing to note is this action will have params[:song_id] available for you to either build an object from, or otherwise
<%= link_to "New Sample", new_sample_path(:song_id => #song_id) %>
Where #song_id is the variable that has that id in it.
Set paths in link_to tag which you can get by running rake_routes in terminal.
Ex
link_to "New song", new_sample_path(#song)
In the example given above #song is the instance variable of your current page.
You can also get some idea from here:
link_to Base URL Randomly Changed
Song Model:
accepts_nested_attributes_for :sample, allow_destroy: true
Route:
resources :songs do
resources :samples
end
Song's Show file:
<%= link_to "New Sample", new_song_sample_path(#song) %>
in url it will be:
/songs/:song_id/sample/new
Try this and let me know it works or not... I hope this helps you

Rails nested resource creation on separate pages

resources :books do
resources :chapters
end
Let's assume I have the above properly nested resources. I want to create a page where I create parent book resources and another page to create the chapters resources. When creating chapters, I want users to be able to select parent books they created.
Right now I have...
protected
def find_book
#book = Book.find(params[:book_id])
end
...in the chapter controller but I believe this only works when there is already a book id present in the URL. So to create a new chapter I would have to visit "rootpath/book/book_id/chapter/new" when I want to be able to create chapters on a separate page.
Although I'm really not sure how to approach the problem, right now my plan is to put an association(?) form on the chapter creation page that links the nested resources.
The problem is, I'm really new to web development and I'm not sure if I'm approaching this right at all. How would I put a form that sends :book_id to the chapter controller? Would this method work at all? Are there more efficient ways to go at it?
I realize my questions might be a little vague but Any help would be greatly appreciated!
The dull answer is: your proposal does not make sense with only the nested route.
The nested route implies that upon accessing the chapters#new action, you already know exactly which book that should contain the chapter.
But on the bright side: you can use both nested and non-nested routes at the same time.
If you want to keep the nested route, but also provide a new and create actions that lets the user choose the desired Book for the chapter, you can add a non-nested route for Chapter creation.
For example:
resources :books do
resources :chapters
end
resources :chapters
Note that your controllers may need to be rewritten a bit to accomodate the dual routes.
If you want, you could create both resources in the same page. Look up accepts_nested_attributes_for to do that. It's really easy, once you get the hang of it.

Best practice for further actions in rails controllers

i'm just writing my first app in rails and i wonder, if there is a best practice to do the following:
i have a customer model created by a scaffold and pumping it up. a customer has to be displayed in a google map, so if go to /customers/23, the customer information are displayed. additionally i have a link within this page to show the user in a map (with a query ui dialog that comes up via ajax).
The question for me is, how does this fits in the normal crud structure of the model. Should i do like creating an action, called "show_map" and give it an extra route additionally to the resources routes? How do you handle this kind of things?
Lets do it like
resources :customers do
resource :map, :only => [:index]
end
it will generate routes like this
{:action=>"show", :controller=>"maps"} customer_map GET /customers/:customer_id/map(.:format)

Can someone please explain to me in clear, layman's terms what the deal is with mapped resources and named routes in Ruby on Rails?

I've been using Ruby for the first time on a project at my work, so I am still somewhat learning the ropes (and loving every minute of it).
While I understand the point of the map.connect functions in the route.rb file, I don't understand the "resources" and "named route" features of Rails. I have my Rails book here and read it over several times, but I still don't get it. The named routes I kinda get - I think that they are either rules, either explicitly defined, or calculated by a code block, but the resources are a complete mystery to me; the only thing I've gleamed rom them is that you just NEED them if you want some of the cool stuff to work, such as being able to call 'resource_path' (and its awesome related family of methods).
My current project has:
map.resources :application_forms
map.resources :sections
map.resources :questions
map.resources :seed_answers
map.resources :question_types
map.resources :form_questions
map.resources :rules
map.resources :form_rules
..but my Rails book has this awesome kinda "has_many" and "only" type hashes and parameters hanging off them and I can't work out exactly when I am supposed to use them, nor what the benefit is.
Can anyone set me straight?
Named routes are just that; a route with a name attached, so that you can easily refer to it when you want to generate a URL. Among other things, it can eliminate ambiguity.
A resource is basically a 'thing' that you want to have routes to manipulate. When you define that 'sections' is a resource, what you're doing is saying "I want a route to get all the sections. I want a route to add a new section. I want a route to edit an existing section. I want a route to delete a section." That sort of thing. These routes point to standardized method names like index, new, edit, and so on. Each of these routes will have a name assigned based on what it is; so there is now a route named 'edit_section'.
The :has_many parameter lets you say that a certain kind of thing has sub-things. For example, you can say map.resources :sections, :has_many => [:questions]. This means that a question belongs to a section, and this will be reflected in the url and the route. You'd get urls like '/sections/27/questions/12' and named routes like 'section_questions'.
The :only parameter says "only make routes for these actions"; you could use it if you only want to allow listing, viewing, and adding items, not editing or deleting.
Honestly the Rails Routing Guide will give you a good explanation in about as plain wording as you can get. Just know that a resource route == RESTful route and you're good to go.
We all struggled with understanding resources and REST when DHH introduced it to the Rails community at the first RailsConf in 2006, so it is not wonder you have trouble grasping the concept.
I admit there is much better and more up-to-date explanations of the concepts today, but back then, right after David's keynote, I wrote a blog post in which I, from discussion with other conference attendees, tried to understand and explain it. It might help you, as it doesn't take for granted that you know everything about REST as more recent articles do.

Resources