This is what I emailed my students for my Web Application Development course this morning...
Hi All,
I wanted to share some commits with you that illustrate a few common Rails concerns that have been cropping up from a number of you. This is based on the app we started in class, and can be found in the myapp branch of the 531 repo, https://github.com/karlstolley/531/tree/myapp
Two examples of this. The first seeds the Page model with a Welcome and About page. Because Page is a standalone model (without relationships to any other model), the seeding is pretty straightforward: https://github.com/karlstolley/531/commit/aebb27da948148e3f5698a265c1562da37e5b713
As with most of the files I've committed, there are comments throughout explaining the how & why; for a better view of the source code for any of those commits, choose the View file @ button at the top right of the diff on GitHub, e.g., https://github.com/karlstolley/531/blob/aebb27da948148e3f5698a265c1562da37e5b713/db/seeds.rb
The second example seeds data for Quotes and Comments, the basic functionality of this little app (quotes can be uploaded, and then users can comment on them). As you can see from this commit, a Comment belongs to a Quote, and a Quote has many Comments:
https://github.com/karlstolley/531/commit/4fbc96dc40ca3c9bbecb6b684994673db242f3be
To seed relatioinship tables is not too difficult. I've done a longer-form method of seeding using two local variables, quote and comments; note that the comments are actually created in reference to the quote object. That is how Rails tracks foreign keys for you. You don't have to explicitly seed, for example, a quote_id value for each comment.
https://github.com/karlstolley/531/commit/42be0a23d25308387f7a5a04491397f02e858065 Highlighted file: https://github.com/karlstolley/531/blob/42be0a23d25308387f7a5a04491397f02e858065/db/seeds.rb
A number of you have asked about created a special home page, accessed at http://localhost:3000/ or whatever your root URL is.
First, rather than delete the public/index.html file that's served by default at the root URL, I prefer to move it using $ git mv (see the note I posted on this page for the command line magic): https://github.com/karlstolley/531/commit/a7bdee3ca66dec6c97a17c47697d309a1d657f35
Second, and this is a multi-file commit, we set up a custom welcome action on the Pages Controller that finds the page of ID 1 in the database (this is not the best way to do this, but it'll work for now), which gets fed out to a corresponding view that you'd create yourself, in this case, app/views/pages/welcome.html.erb And third, we set up root :to => in config/routes.rb to point at the welcome action on the pages controller, i.e., pages#welcome https://github.com/karlstolley/531/commit/7ec7e80ea1c68b88f271fb86991d1b86efc5f8e5
Now to some more meaty stuff. Many of your are struggling with how to build views & wrangle controllers for handling data from multiple tables. One method is to create what are called Nested Resources; there is a good discussion of this in the Rails Recipes book & in the Rails Guides (in the latter case, I've linked to relevant documentation within the comments of this source code.
The first step to setting up nested resources is to say which resource is nested in side of which. In the case of this app, Comments are nested inside of Quotes, because in the language of the model, a Comment belongs_to :quote https://github.com/karlstolley/531/commit/6ec502ba942c600cf17bd44ce3dba7416a55984b
As you can see in that commit, I elected to only have comments route to particular actions: index, to list all of the comments associated with a particular Quote; create, to allow a user to post a new comment, and destroy, to allow comments to be deleted.
As you can also see in the note I posted to that commit, nested resources give us new URLs and new named routes when we run $ rake routes
. There is no longer a named route called comments (e.g., comments_path). Instead, there's a named route called quote_comments (e.g., quote_comments_path). That routes to a URL structured like:
/quotes/:quote_id/comments(.:format)
Which just means that the comments will be shown in the context of a Quote of a specific ID. Ducky, but we need to dive into the controller to handle this new nested context.
Because the nested resource route for comments only specifies index, create, and destroy, those are the only actions that need to be edited on the controller: https://github.com/karlstolley/531/commit/dd38ca380e542720dff5657335e5d4a36585957a
The important thing to note in the modified Comments controller is that each action calls up this line:
@quote = Quote.find(params[:quote_id]) # Grab the Quote based on the URL
That means we're relying on the URL pattern /quotes/:quote_id/comments to find the current quote.
The other important thing to note are the changed redirect_to paths, which reflect the new named routes created by nesting comments inside of resources.
To simplify things, I pretty much wiped out the content of the view files generated by the Quote and Comment scaffolds, with the exception of the form partial, which is still useful: https://github.com/karlstolley/531/commit/8c3002e0e0eb182bbcb97d687508ad4e08d2e41b
The best reason to do that kind of wiping is that the scaffolds will reference a bunch of named routes that no longer exist, and so will throw errors all over the place.
I then modified the Comments index view and the Quote show view to be basically the same: https://github.com/karlstolley/531/commit/4755f5eadab1f617ce0d68b809b1c58c519b11fe
The only real difference is that the Quote show view reports the number of comments on a given quote, and then used a named route to point to the comments index view, which actually outputs the comments and provides a form for entering a new comment. Yes, that happens on the Comments index view, but that's OK. Rails is smart enough to still point the form to the create action on the Comments controller.
Finally, and this is essential, I modified the form partial to handle both an instance of @quote and an instance of @comment: https://github.com/karlstolley/531/commit/5332098bcc67e0416ce679250ff02c75ae3bc878
That is how Rails handles foreign key associations in forms. There's no need to manually hack in a hidden field for IDs, etc. And then that frees your attention to rework your HTML to make use of, for example, Twitter Bootstrap: https://github.com/karlstolley/531/commit/1171f71660ea4628c7ef51abc711682c2bacd767
Here's a little bonus. Want to process Markdown from your input fields? No problem. Just add RDiscount (or another Markdown-processing Gem) to your Gemfile, and run $ bundle install
-- then, create (or just copy from this code) two little application-helper methods for processing Markdown:
https://github.com/karlstolley/531/commit/df0d48f81edaf263bffc69cc1c1e1ed685c84789
Highlighted application_helper.rb file: https://github.com/karlstolley/531/blob/df0d48f81edaf263bffc69cc1c1e1ed685c84789/app/helpers/application_helper.rb
Then, you can refer to those little helper methods from within any view in your application. For example, to process any markdown in the content of a quote, just do this: md @quote.content
https://github.com/karlstolley/531/commit/4c4a57980b154deede66c016565a4a3d509401b5
Here are some commits posted more or less for your edification; they demonstrate how to import Twitter Bootstrap into your SCSS, and start reworking/reusing the variables and mixins that the sass-twitter-bootstrap gem gives you: https://github.com/karlstolley/531/commit/e678cabbd8da1c89d5f861212cbecbd58caea451 https://github.com/karlstolley/531/commit/f4d444c6838f4fc4cfbf3e076a1455f48fdee2f8
If you have any questions on anything, I invite you to log into your GitHub account and comment right on the commits themselves.
See you tomorrow night, Karl
-- Dr. Karl Stolley http://karlstolley.com Associate Professor of Digital Writing and Rhetoric & Co-Director of Graduate Studies, Dept. of Humanities Illinois Institute of Technology