In this project, we'll practice tailoring the experience for users by allowing them to sign in so that we know who they are. We'll use the Devise gem to make authentication a snap.
We'll be building a simple clone of Instagram.
Here is your target for the required parts of the assignment (associations, authentication).
Later, optionally, you will add file uploading and social networking.
Suppose we design the following domain model for our application:
+---------------------+
| |
|Comment |
/|======= |\
+---------------|- body:text |---------------+
| \|- photo_id:integer |/ |
| |- user_id:integer | |
| | | |
| +---------------------+ |
| |
| |
- -
| |
+---------------------+ +---------------------+
| | |User |
|Photo | |==== |
|===== |\ |- username:string |
|- caption:string |-----------------------------|-|- Devise columns |
|- image:string |/ |(email, password, |
|- user_id:integer | |etc) |
| | | |
+---------------------+ +---------------------+
| |
- -
| |
| |
| +---------------------+ |
| | | |
| |Like | |
| /|==== |\ |
+---------------|- photo_id:integer |---------------+
\|- user_id:integer |/
| |
| |
+---------------------+
-
Associations
-
Users have many photos, a photo belongs to a user
-
Photos have many comments, a comment belongs to a photo
-
Users have many comments, a comment belongs to a user
-
Users have many likes, a like belongs to a user
-
Photos have many likes, a like belongs to a photo
-
Users have many
liked_photos
through likes. Since this breaks naming conventions (the method name,.liked_photos
, does not exactly match the class name,Photo
), we'll have to write out the full form of the has_many/through:has_many :liked_photos, :through => :likes, :source => :photo
-
Similarly, Photos have many fans through likes (source: user):
has_many :fans, :through => :likes, :source => :user
-
-
Validations
- User
- username: presence, uniqueness
- Photo
- user_id: presence
- Like
- user_id: presence, uniqueness in combination with photo
- photo_id: presence
- Comment
- user_id: presence
- photo_id: presence
- body: presence
- User
Right now, this is a brand new application with nothing at all in it. Your job is to make it function and look like the target.
Below I suggest a plan of attack. Try to imagine, as you go through it, how each step would apply to your own app idea.
-
Fork and clone as usual.
-
I've already added starter_generators and Devise to the Gemfile. You might want to pull up those cheatsheets and have them handy.
-
bundle install
-
rails grade
as you go along to see how you are doing. -
Generate the User table with Devise:
rails generate devise:install rails generate devise user username:string
Devise will automatically add email, password, and all the other columns that it needs to secure accounts. You just specify any columns you want besides those (in this case, we want usernames).
-
Generate the rest of your CRUD resources with starter_generators:
rails generate starter:resource photo caption:text image:string user_id:integer rails generate starter:resource like user_id:integer photo_id:integer rails generate starter:resource comment photo_id:integer body:text user_id:integer
-
Now that you have generated your model files, add all of the associations and validations listed above immediately.
-
Set the root URL to the photos index page:
# In config/routes.rb root "photos#index"
-
You can finally
rails server
and navigate to http://localhost:3000 to see your work so far. If you haven'trails db:migrate
d yet, it will ask you to now. -
Generate a better application layout, including Bootstrap:
rails generate starter:style paper
-
I've included some random starter data for you to use while developing:
rails db:seed
Now click around the app and see what we've got. (If you're curious, I used the faker gem to create the silly random seed comments. It's very useful for quickly generating random names, addresses, etc.)
-
Let's require that someone be signed in before they can do anything else. In
application_controller.rb
, add the linebefore_action :authenticate_user!
Now try and navigate around the app. It should demand that you sign in before allowing you to visit any page.
-
Sign in with one of the seeded users; you can use
alice@example.com
,bob@example.com
, orcarol@example.com
. All of the passwords are12341234
. -
Fix the dummy edit profile and sign-out links in the navbar.
- If there is currently a signed-in user,
- The link to edit profile should display the signed-in user's username instead.
- The link to sign out should work as is, but remove the word "dummy".
- If not, display links to sign-in (
/users/sign_in
) and sign-up (/users/sign_up
) instead.
- If there is currently a signed-in user,
-
On the new photo form, the user should not have to provide their ID number. Fix it using Devise's
current_user
helper method to prepopulate that input. -
Create an RCAV: When I visit http://localhost:3000/users, I should see an index of all users. This RCAV does not exist right now, since we used Devise to generate the User resource rather than starter_generators. Devise only builds the RCAVs required for sign-up/sign-in/sign-out/etc; it doesn't build the standard Golden Seven. But that's okay, because we can easily add the ones that we want ourselves (if any). Once done, add a link to the navbar.
-
Create an RCAV: When I visit http://localhost:3000/users/1, I should see the details of user #1 along with all of his or her photos. Once done, add a link to the navbar that leads to the current user's show page. (This may lead to a problem when no one is signed in -- how can you fix it? Also, be careful where you add this route in
routes.rb
-- it needs to be below the linedevise_for :users
, otherwise it will conflict with/users/sign_in
and/users/sign_up
.) -
Create an RCAV: When I visit http://localhost:3000/my_likes, I should see only the photos that I have liked. Once done, add a link to the navbar.
-
On the photo show page, I should only see the "Edit" and "Delete" buttons if it is my own photo.
-
Make the photos look like the target:
- Useful Bootstrap things: panel with heading, media list, img-responsive, text-muted, heading subtext
- Useful Rails methods: time_ago_in_words, .pluck, .to_sentence
View Source on the target if you need to.
-
Make the form to quick-add a comment directly below a photo work.
-
Make the heart to quick-add/delete a like directly below a photo work.
-
Customize the generated sign-in/sign-out/edit profile forms to a) include a field for username, b) make them look nicer. Here are some Bootstrapped Devise forms that you can build off of, but you need to add a field for username, and then let that param through additional security before the form will work.
-
Optional: Use the Carrierwave cheatsheet to enable image uploads (rather than pasting in existing URLs). You'll find an accompanying video under Pages in Canvas.
-
Optional: Follow the Tweeter example project to enable followers/timeline. You'll find an accompanying video titled "Social Network" under Pages in Canvas.
Here is a target for the optional parts of the assignment (file uploads, social network).
Submit as usual by syncing to your fork and rails grade
.