Assignment 4: Models, Controllers, and Views

November 14th, 2009 Leave a comment Go to comments

Errata

  1. (14-Nov-2009) The AddTags migration is missing one tag and had the wrong name for another. Without this tweak, clicking on a couple of publications will raise an exception. So, in AddTags, change the lines that add the tags to this:
    %W[ java ruby ruby1.9 rails projects crud models rest activerecord ].each do |tag_name|
    Tag.create!(:name => tag_name, :user => john)
     end
    
  2. (9-Nov-2009) At the request of Ron, the download version 1.0.004 wraps up a number of small changes from these errata, namely: (a) the use of :uniq on Tag.index_publications; (b) a similar addition on Publication.index_tags; (c) removal of .uniq in views/publications/_publication.html.erb; (d) fix for doubled occurrence of the word “for” in views/index_entries/new.html.erb; (e) a check that there’s a current_user before displaying the link to add an index entry in views/publications/show.html.erb, and, in this same template, another check for a current_user before displaying annotations, as well as the use of the “for_user” association extension on Publication.annotations; (f) from Keith, functional tests of the PublicationsController and SessionsController. That should be it.
  3. (8-Nov-2009) Thanks to a mistake on my part, I forgot to constrain the indexed publications for a tag so that the results are unique. Ron found the bug and suggested the fix, which works. In app/models/tag.rb, add “:uniq => true” to the indexed_publications association:
    has_many :indexed_publications,
      :source => :publication,
      :through => :index_entries,
      :uniq => true
    

    “uniq” is described on p. 371 (print), p. 375 (PDF).

  4. (5-Nov-2009) For item 1, the CreateIndexEntries migration: The self.down method is already done for you — disregard the comment in the code that says you need to do something. I accidentally left in the drop_table. :-)
  5. (4-Nov-2009) As I was revising some slides on Rails filtering, a stream of invective emerged from my mouth as I discovered that I had not “turned on” the filtering completely in the code download for assignment 4. This tweak does not affect your ability to get the assignment done, but I recommend it nonetheless. For all controllers except ApplicationController, RequiresAuthenticationController, and SessionController, make sure that the controllers subclass RequiresAuthenticationController. Example:
    class TagsController < RequiresAuthenticationController
    

    This tweak is reflected in e168-assignments4and5-jgn-1.0.003.zip on the downloads page. Again, this goof doesn’t really affect your ability to complete the assignment. The sympton is that if you log out and then go to a page such as /tags/new, you won’t properly be kicked over to authentication.

  6. In app/models/publication.rb, there may be a “for_tag” association extension on on the wrong association. The has_many associations are as follows. This is the only difference between e168-assignments4and5-jgn-1.0.000.zip, and e168-assignments4and5-jgn-1.0.002.zip (001 was missing the update). You can either download e168-assignments4and5-jgn-1.0.002.zip from the downloads page, or put this code below in place of the associations in the Publication model. The reason I wanted you to have this is because I’ll be discussion association extensions Thursday (at Keith’s request) and these additional methods should make it easier to avoid putting “find” code into your views.
      belongs_to :user
      has_many :annotations, :dependent => :destroy do
        # Students: This is an "association extension":
        # See AWDR, p. 372 (print), p. 376 (PDF)
        def for_user(user)
          find(:first, :conditions => ['annotations.user_id = ?', user.id])
        end
      end
      has_many :publication_tags, :dependent => :destroy
      has_many :tags, :through => :publication_tags
      has_many :index_entries, :dependent => :destroy do
        # Another "association extension"; see references above.
        def for_tag(tag)
          find(:all, :conditions => { :tag_id => tag.id })
        end
      end
      has_many :index_tags,
        :source => :tag,
        :through => :index_entries
    

    This is fixed in the freshest download: e168-assignments4and5-jgn-1.0.002.zip on the downloads page.


Both assignments 4 and 5 are due 23-Nov.; automatic extension to 25-Nov. If you complete assignment 4 early, submit it, and we’ll see if we can get comments back to you early.

In this assignment you will implement parts of a full-blown web application: migrations, models, views, and controllers. These components are the foundation of any Rails application. If you can do this well, it should be clear sailing to the end of the course, because most of the other aspects of Rails go deeper or decorate the application with plugin or gem functionality.

You can find a package for the assignment on the downloads page.

The application is essentially an online bibliography/index of publications. (We actually wanted to use the word “resource,” but that has a technical meaning in Rails, so we’re going with something a bit more ordinary.)  Here’s the home page of the reference implementation (which you can try out at http://publications.plugh.org):

assn4-home-small

For the most part, the application just gives a list of books and links to web sites. Each publication may have tags and annotations. There are also links to Amazon and Safari when appropriate.

[NOTE regard "Safari" links: For the "reference implementation" and your own app, you can log in as john@7fff.com to see the Safari links; or, in your own implementation, you can set user.safari = true [and save that user] to turn on the Safari links — I decided to leave user management out of this assignment, because it gets complicated fast.]

If a user logs in, there are buttons on the page that allow the user to see the detail for a publication, edit a publication (if the user added it in the first place), and add an annotation (if the user has never added an annotation for the publication). Additionally, in the “detail” view, there are some additional buttons for adding index entries. You will need to play around with the application to get a feel for what’s there. Here’s what the home page looks after a user logs in:

assn4-home-logged-in-small

Here’s the schema (click to enlarge):

assn4-schema-small

The central table is publications. As you can see, a Publication has many annotations, publication tags, and index entries. A publication has tags through both index entries and through publication tags. For example, the book Agile Web Development with Rails might be tagged as “rails.” That means there would be a row in the publication_tags table with publication_id set to the id of AWDR in the publications table, and tag_id set to the id for the “rails” tag. There might also be an index entry for the tag “models.” This would be represented by a row in index_entries. The publication_id would be set to the id for AWDR, and the tag_id would be set to the id for the “models” tag.

The annotations table has brief comments on publications. A publication has many of these. Note that a user is only allowed one annotation per publication.

Finally, notice that all of the tables except users have a foreign key user_id pointing back to the users table. By this means, we know who created every row in the application. In general, we only want the user who created a row to be able to edit or delete it. I believe this is implemented completely in the reference implementation, but I might have forgotten a case!

Here’s what you need to do. We have left a number of parts of the application unimplemented. You need to implement them so that they behave in the same manner as the reference implemention (link above). Here’s the list:

  1. You must implement the CreateIndexEntries migration. The full set of migrations won’t complete without it. The down method is already done (disregard the code comment that says there’s something for you to do — there isn’t).
  2. You must complete the Annotation model.
    • You must validate the uniqueness of the publication_id for a particular value of the user_id. In other words, the combination of publication_id and user_id must be unique. Hint: Study the validations for PublicationTag.
    • The presence of the ‘brief’ attribute is required.
    • Add a before validation callback that converts all whitespace in the ‘brief’ and ‘full’ attributes to one space.
  3. On the Publication model, add a custom validation that ensures that there is a value for either the ‘title’ or ‘url’ attributes, or both.
  4. Add helpers link_to_amazon and link_to_safari in ApplicationHelper that format links to Amazon and Safari. For the formats, hover over the links in the reference implementation. Hint: Implement these by calling the link_to helper with the appropriate values. See the “Note regarding Safari links” above, regarding how to see them in the application.
  5. In AnnotationController, implement the new, edit, create, update, and destroy actions. It is critical that it be impossible for a user to edit or delete another user’s annotation. For example, the edit URL (in the reference implemenation) is http://localhost:3000/annotations/5/edit. If the current user didn’t create annotation 5, the action should fail (it doesn’t have to fail gracefully; it just has to not work). Hint: See the other controllers. It would also help to compare how the other controllers are implemented, vs. what script/generate scaffold gives you.
  6. In PublicationController, change create and update so that they can handle the keywords. This is by far the hardest part of the assignment because of the management of the checkboxes. To get some ideas for handling the free-form tag box, take a look at IndexEntriesController. The “Child-Care Coop” application also provides some guidance for checkbox handling.
  7. Implement views/annotations/_annotation.html.erb
  8. Implement views/shared/_indexed_publication.html.erb
  9. Implement the missing parts of views/publications/show.html.erb

That’s it!

  1. November 23rd, 2009 at 19:55 | #1

    @Qilong Xu

    Qilong: I have sent you the username and password via e-mail. It is the same as it has always been.

    Have you checked your e-mail? Have you checked your junk folder? Perhaps the e-mail is in there.

  2. Qilong Xu
    November 23rd, 2009 at 19:59 | #2

    @john
    Thank you. the username and password works. Very sorry, my bad, I did not know it needs course username and password.

  3. DavidL
    November 23rd, 2009 at 22:13 | #3

    Can I assume that the behaviour of the reference implementation is always good enough?

    For example, when implementing #8 the reference implement seems to show index entries in the order they were added rather than page number order. Am I safe assuming this is good enough.

  4. November 23rd, 2009 at 22:19 | #4

    @DavidL

    Yes. If you see something in the reference implementation and you're wondering if it's significant, post a note here. In general, though, the comments in the "missing" parts should describe the operation fairly completely.

  5. Ron Newman
    November 23rd, 2009 at 22:36 | #5

    One useful and easy functionality change I made while doing assignment #5: I edited app/views/publications/_publication.html.erb so that it always displays a "details" button link for each publication, whether or not you are logged in.

  6. Ben Dawson
    November 25th, 2009 at 07:16 | #6

    What does a message like:

    XML Parsing Error: no element found
    Location: http://localhost:3000/annotations/13
    Line Number 1, Column 2:

    generally mean. I'm getting in when performing an annotation update, and it's on a plain page. IE it doesn't have the standard formatting of most of the rails errors handling.

  7. November 25th, 2009 at 07:50 | #7

    It may mean that you are using an incorrect path helper, and you're sending your user to the wrong controller. The annotations controller doesn't have a view templates in HTML for the "show" action, for instance.

  8. Ben Dawson
    November 25th, 2009 at 08:27 | #8

    @john
    Thanks, it was a pretty straight forward issue.

  9. Lateral Punk
    November 27th, 2009 at 14:55 | #9

    Hi,
    John, asked me to post this after 4 & 5 were submitted. It's a link on how I used Ruby meta-programming to make constant look-up tables.
    http://www.lateralpunks.com/lateralpunks.com/cfrlt.html

Comment pages
1 ... 3 4 5 493
  1. No trackbacks yet.