Implementing a great final project

Posted by john in Assignments, Announcements

Last revised: 22-Dec-2007, 2 PM: You are to implement the e-mail plus TWO (2) of the other features.

Original: 22-Dec-2007, 11 AM

NOTE: This page is the “official” version of the requirements for the final project. Please add comments here, not in the e-mail list. If we add information here, we will announce it via the e-mail list.

In this post we want to break down the 7 categories for “additional features.” The final project is your version of CCC from assignment 5, with additional features (the other option was to propose a new project with similar complexity to CCC, implement that, and then add the additional features).

One additional feature is required; you get to chose the other two.

A very important note: There is no “right” way to implement the following. What you are reading are requirements. We are not going to spell out the implementation, except to point you to certain places in the code. Part of your grade will be based on your ability to move from requirements to implementation.

Tips

  • “What is the simplest thing that could possibly work?” (Kent Beck)
  • “Don’t Repeat Yourself” (DRY - Dave Thomas and others)
  • get it working first
  • use good taste (if you get your code working, and it looks like a hairball, see if you can make it more understandable)
  • Don’t forget it’s Ruby; write like a Rubyist

Writeup

Please provide a writeup of what you have accomplished in the final project. Anything new should be described in terms of what the user experiences, and then in a separate section, you should explain how you did it. In other words, the writeup should have two sections:

  • Use Cases
  • Implementation

The easiest way to write is to think about your intended reader. For the use cases, the reader is someone who needs to know what is going to happen. The use case is essentially the “story” of the user experience, using the vocabulary of the application (Users, Playdates, Invitations, etc.), just as we did for the assignment overview. That reader might be someone in QA, a VP of marketing, or someone who is evaluating your product. For the implementation section, the intended reader is another developer. Explain to that reader what they need to know to understand, maintain, and modify your code.

It is hard for me to imagine that a writeup would be fewer than 4 pages (about 1000 words). Your writeup is key. Oftentimes your grader cannot see what you’ve done unless you tell the grader. So “crow” about the compelling nature of your features, the elegance of your UI, and the power of your implementation.

[Amy says: if you have a hard time figuring out how to write something of use to another developer, imagine telling someone in section how to do what you did, or complaining to them about all the things you had to try before you hit upon the thing that worked. Especially where your code is a little hairy or obscure, you want to explain why you did it that way instead of some other way that the other developer thinks, on first glance, would have been better. If the actual reason you did something was “I didn’t have time to do this any other way,” that’s still valuable information to provide, as it allows the future developer to distinguish between necessary and unnecessary complexity in your code. If you do not point out unnecessary complexity in your code, we will assume that you did not notice it, and from a grading perspective it is worse to do something nasty and not notice it than it is to do it and admit it. (Of course, it may just make sense to point out nasty bits in inline comments; not everything deserves a mention in your writeup itself. The point is not to leave ugly or ’smelly’ code in your project and hope we won’t notice it.) Think of your writeup as writing the first part of a good story about your project. You want the reader (QA, potential users, future developers, etc.) to be engaged, informed, sympathetic, and eager to be involved in the work of adding on to the story.

You DON’T want to write a sentence, run a word count, write another sentence, run another word count, and so on until you reach John’s 1000 word floor. If you tend to save the writeup until the very end and then sit staring at a blank screen wondering what to say, try keeping a log while actually at work on the project: jot down problems you’re facing, decisions you come to and why, expressions of frustration, notes about how long something took you to do, resources you found helpful, etc. Then when you get to the writeup you will have a wealth of raw material to use.]

Required

First, you must implement the distribution of invitations by e-mail. Here’s what that entails:

In InvitationsController#create, you probably have a line something like this:

Invitation.create!(:invitee_id => id, :playdate_id => params[:invitation][:playdate_id], :viewed => false, :accepted => false)

In addition to this, you must send that person an invitation by e-mail. That mean sending a link. What kind of link? It might be something like the following:

http://localhost:3000/invitations/invite/341

where 341 is the id for the invitation. Let us say that invitation 341 is for john@7fff.com.

The recipient of the e-mail clicks on the link. Since there is no action for invite in InvitationsController, you will need to write one. I click on it. If I am not already logged in, I should be given an opportunity to log in. If I *am* logged in, the software should check to see if invitation 341 is really for me. If it isn’t, I should get an appropriate error message.

When an invitation is viewed, the “viewed” attribute should be changed to “true” in the database.

Then the template for the invite action should show me the details of the invitation, and I should be able to submit a form that means I am accepting the invitation. Note that you might choose not to blend into this form adding/removing children from the playdate (there is an existing UI for that).

The best implementations will do the following: They will allow the invitee to add/remove children when they accept the invitation. It should be possible to click the invite link twice, and something reasonable should happen the 2nd time (perhaps just a message saying that you’ve already accepted the invitation, if that is the case). It should be possible for a new user (someone who only has an e-mail defined in the users table, but not first name, last name, password, etc.) to provide more registration information. If you will recall, in the original model, we require new users to pay, so this would be when we would do that. [DO NOT TRY to implement e-commerce! If you like, you can pretend that it’s implemented with a checkbox that says that the new user agrees to pay.]

Extra credit for the required feature

For a couple of points extra: It is problematic that the id’s for the invitation links are in a known sequence. This means that someone could very easily guess what other invitation links are, and could create spurious “view” or “accept” values. Add a column that represents a “globally-unique identifier” (you should be able to Google for this to get some ideas; a synonym is UUID), and put that id on the invitation links. For example, here is a link we use at Duffy’s Cliff to confirm a user:

http://www.duffyscliff.com/account/verify_user/OEeaw-64kD4p5yqcfjn_-F1HPPg

By this means, it should be virtually impossible to fiddle with someone else’s link.

And Implement Two of the Following

After each feature, I will provide a bit in brackets that “scores” the feature in terms of difficulty, potential to learn, and value for the application (10 means high value, 1 means low value. E.g., “Difficulty: 3, Learning: 1, App: 10″ would mean not too difficult to implement, you wouldn’t learn a lot, but it would be a great feature for the user).

1. User management: Adapt the login / registration code to use the acts_as_authenticated plugin (http://agilewebdevelopment.com/plugins/acts_as_authenticated). You should provide a means for the user to fully register (adding first_name, last_name, etc.). You may use either the RESTful version or the non-RESTful (it is claimed that since Rails is going RESTful, using the RESTful version is more in keeping with Rails best practices, but it seems to me that the dust has not settled on this yet; so pick one and don’t worry excessively about your choice). The best implementations will build on acts_as_authenticated so that the user can register during the acceptance of an invitation. The best implementations will study the migration provided by acts_as_authenticated, and will figure out a way to save the existing user data as it is migrated to the new scheme. This may be hard.

[Difficulty: 5; Learning: 5; App: 7]

2. Ajax: Ajaxify as much as seems appropriate. The very best implementations would implement a number of the following:

  • Updating sections of the page as appropriate
  • Replacement of a form with the results of that form after posting
  • In-page asynchronous notification of events (e.g., “Naomi has accepted your invitation for the playdate on 26-Dec-2007″)

A possibly neat use of Ajax would be to modify the creation of an invitation so that when the user invites to a playdate (/invitations/new) the page would detect when the playdate drop-down is modified, and then indicate dynamically (through an update of a <div>) who is already invited, and/or who has already accepted.

Yet another cool effect is to accept the form for the addition of a playdate, and then update the table showing all of the playdates right in place, with the newly-added playdate. One neat thing people do with this kind of update is to use the “yellow fade” effect to highlight the playdate that has just been added (Google for yellow fade effect to learn more about this).

By all means, write e-mail to the course list if you have other ideas. Amy and I are not the biggest Ajax people, so we would likely learn from your ideas in this department.

The best implementations will also ensure that your app degrades gracefully for those users who have disabled javascript or may be accessing the app through a non-standard browser. Be sure to mention anything you do in this regard in your writeup. If you don’t ensure graceful degradation, your writeup should explain why you don’t think it’s important to do so in a way that your ‘product people’ will understand and agree with.

[Difficulty: 5; Learning: 5; App: 2]

3. REST. Provide a REST interface for at least one of the models. Location would be a good place to start. More ambitious would be to get the “schedule” listing, in some form. The best implementations will implement nested resources, so that one can say /users/5/playdates or /users/5/playdate/10 and so forth. The very, very best implementations will use the UUID idea (see above under the required e-mail feature) so that the user id is not given. E.g., /users/OEeaw-64kC1p5XBcfjn_-F1HPPg/playdates/12 — this way you have a certain measure of “security by obscurity”).

[Difficulty: 7; Learning: 7; App: 4]

4. Implement the answer you gave for the question in Assignment 4.

[Difficulty: 3; Learning: 2; App: ?]

5. Add the ability to upload photos of the users and children. Photos of children should only be viewable by other users whose children are participating in a playdate with the that child. You may use the strategy outlined in AWDR (pp. 501ff), or use the attachment_fu plugin. See this tutorial: http://clarkware.com/cgi/blosxom/2007/02/24#FileUploadFu

Tip: For attachment_fu, on Windows, use RMagick.

If you plan to do this feature, you should probably do it first, because it may turn out that the install of ImageMagick is problematic.

[Difficulty: 3; Learning: 2; App: 4]

6. Implement ratings

There are a lot of things worth “rating” in CCC. Users might rate playdates, locations, the organizer of the playdate . . . There are a lot of ways to do this. You might try to implement it with your own design, and if you have a good idea for it, try it. There is also a plugin: acts_as_rateable (http://agilewebdevelopment.com/plugins/acts_as_rateable). You may find that the plugin limits you. For example, if you have two children, should you get two votes? etc. If you find the plugin limiting, you may need to decide whether to continue using the plugin itself as-is, use it but extend it, or take the general idea of the plugin and implement your own version. Be sure to explain all the choices you made in the writeup. If you choose to extend the plugin, it will be especially important to point out the changes you’ve made in the writeup, because other rails developers will assume the standard plugin functionality unless told otherwise.

It might be interesting to blend this with the Ajax option, so that ratings are applied immediately when someone clicks on a list of stars or some such.

[Difficulty: 7 (to get it right); Learning: 2; App: 3]

7. Something else. You must get approval from your grader. The original deadline for this was Dec. 19, but if you discover something you really want to try, we will consider it, though our bias will be to say “no” at this late date. If you are adapting Assignment 5 for the final project, you may only use one self-defined feature.

[Difficulty: ?; Learning: ?; App: ?]

Overview of Assignments 4, 5, and 6 posted

Posted by john in Assignments, Announcements

I’ve added a page with an overview of Assignments 4, 5, and 6 (also in the sidebar under “Assignments”). Comments most welcome on the general project / product; questions for Assignment 4 and ActiveRecord specifically will be more appropriate for the page on Assignment 4, which will be appearing shortly…

Strategies for working on assignments

Posted by john in Assignments 3 Comments »

Gather ’round, I’d like to share some strategies for working on the assignments. I could have talked about some of this earlier, but I think now is really the key time, when some of you are 10%, 30%, or 70% done, but not sure how you’re going to get to the finish line.

  • The first thing I want to stress is to get some code working first. While the perfect submission for #2 will be a true Ruby one-liner with few or ideally no extra variables, statements, destruction of input data, etc., if you see a solution that has lots of lines, extra variables, go for it: then start refining. “Every block of stone has a statue inside it and it is the task of the sculptor to discover it.” — Michelangelo
  • Remember, you’re learning. It’s hard. You are literally building new neural pathways in your brain. Please, drink lots of fluids.
  • Yes, I did say that some people would finish in 4 hours. It’s a bell curve. When I took my first serious Java course back in the 1930s, I remember vividly working on it 20+ hours a week, and my code never really completely worked. But fortunately there was partial credit in that class, just like this one. And I learned a lot. I became an expert at making mistakes, and even today I say to Java developers: “Oh, yeah, I did that once.” Amy tells me that while she’s written a lot of Rails code, the concept of the one-liner was a challenge . . . “and look at me now, injecting all over the place!”
  • Be a bit self-conscious about your work strategy: If you think you’re more comfortable reading and then doing, try it. But don’t be afraid to reflect on your assumptions and switch things around if you’re not making progress: try alternating reading with doing.
  • Remember that the Pickaxe teaches in four different ways: Discursively, about Ruby in general (chapter 2); in an expository fashion for Ruby concepts (chapters 3-9); definitionally, in the chapter on the Ruby Language (chapter 22); and finally, encyclopedically — the API docs for built-in classes and modules (Chapter 27). These different modes tap different ways of understanding. (Someday there will be a Head First book on Ruby and Rails, which will truly try to educate all parts of your brain.)
  • One student who shall remain nameless turned in her assignment a few days ago and also shared her strategy for finishing. She went over the lectures, and made a list of capabilities that I presented. Then she went through the assignment, and made a list of “requirements” for each item. Then she matched them up. I think she did this informally, in her head — it was her way of putting her work into a productive order. In many cases, she used the lecture as a way to focus on sections of the Pickaxe. And, of course, she tried everything in irb.
  • Rubber ducking (http://compsci.ca/blog/rubber-ducks-help-best-with-computer-science/) can be very helpful. I happen to have plastic rubber ducks on my monitors, both at home and at work, and I discuss my code with them all the time.

    picture-7.jpg

  • Tossing code and starting over. The idea is that you probably are there conceptually, but your current implementation is messing you up. The classic popular culture example of this is a scene in the movie “A River Runs Through It,” when a boy is essentially being home-schooled by his dad. He writes an essay. The father reads it and strikes words, finally telling the boy that it is better, but that he should tear it up and do it again. What is happening is that the boy is internalizing some ideas about elegance and concision in writing. “Programming is a kind of writing.” — Gerald M. Weinberg
  • Do ask questions! If it seems useful for others, Amy or I will post the question and the answer back to the e-mail or to the course site, without attributing the question to you.

Good luck!

Clarifications for Assignment 2, Question 12

Posted by john in Assignments, Logistics, Announcements 5 Comments »

Assignment 2, Question 12 is about the growth of the population of organisms in petri dishes. There was an error in one of the generation calculations for the example scenario. I replaced that bit with a somewhat longer description of the problem, with tables that show how the dishes array is calculated at each generation. I think this should be clarifying. So if you’re working on Question 12, make sure to get the newest version of the PDF.

Also, a student asked: May I alter the values in the dishes Array? The answer is: Yes. I think it can be done without overwriting the dishes Array — you would do it by duplicating the prior generation (dishes.dup) or by creating a new Array based on the old one (Array.new(dishes)). However, this might make the solution look a lot messier.

WP Theme & Icons by N.Design Studio
Entries RSS Comments RSS Login