This week, we have a lab session to continue working on movie_app getting us into Authentication while setting us up for Association. For the movie_app, we apply the lessons from shirts_app including: basic CRUD actions (create, read, update, destroy); creating partials for forms and movie lists. This continues to re-iterate the prominence of the MVC framework in all Rails projects.
Next, we moved into authentication. Initially, I thought we would develop our own authentication system, but we ended up using the popular Devise gem. Authentication adds another layer of dynamism and complexity to, up to this point, a fairly straightforward app.
Quick Tangent into RailsCast
The full details of this process was heavily informed by Ryan Bates‘ Railscast. [Note: This particular RailsCast, creating authentication from scratch, is meant to supplement our learning in addition to using Devise].
Bates’ command line actions are:
$rails g controller users new $rails g model user email:string password_hash:string password_salt:string $rake db:migrate $rails g controller sessions new #handles login forms
Then he adds code / makes edits to:
- users_controller.rb (define user and create action in the UsersController class)
- new.html.erb (sign-up form <%= form_for %> ; he recommends making a helper method – not quite sure how that’s different from a partial; I recall this was a partial in our shirts_app)
- routes.rb (sign-up route and resources: users)
- user.rb (user model; validates_confirmation_of, validates_presenece_of: password and also def encrypt_password; method for self.authenticate etc)
- Gemfile (add gem “bcrypt-ruby”, :require => “bcrypt” ; followed by $bundle install)
- new.html.erb (for new sessions)
- routes.rb (set for “sessions#new”)
- sessions_controller.rb (method definitions for new, create, destroy actions)
- application.html.erb (adding flash messages to application layout)
There are much more subtle details that can only be picked up be watching the actual Railscast.
$rails generate devise User
Not only did this allow us to create sign-in and sign-up (as well as login/logout), but more importantly, it allows us to model ‘users’ as separate from ‘movies’. This is evident in db/schema and $rake routes – you see a completely new set of routes once the user model has been added to the database.
Prior to adding user authentication, the $rake routes command yielded routes associated with the movies model consisting of basic CRUD controller#actions (i.e., create, edit, new, show, update, destroy etc; also search). However, after adding user authentication from the Devise gem, 15 additional routes appeared as follows:
Prefix Verb URI Pattern Controller#Action new_user_session GET /users/sign_in(.:format) devise/sessions#new user_session POST /users/sign_in(.:format) devise/sessions#create destroy_user_session DELETE /users/sign_out(.:format) devise/sessions#destroy user_password POST /users/password(.:format) devise/passwords#create new_user_password GET /users/password/new(.:format) devise/passwords#new edit_user_password GET /users/password/edit(.:format) devise/passwords#edit PATCH /users/password(.:format) devise/passwords#update PUT /users/password(.:format) devise/passwords#update cancel_user_registration GET /users/cancel(.:format) devise/registrations#cancel user_registration POST /users(.:format) devise/registrations#create new_user_registration GET /users/sign_up(.:format) devise/registrations#new edit_user_registration GET /users/edit(.:format) devise/registrations#edit PATCH /users(.:format) devise/registrations#update PUT /users(.:format) devise/registrations#update DELETE /users(.:format) devise/registrations#destroy
With an understanding of models, views, and controllers as foundational, we now have to figure out how to get the ‘movie’ model and ‘user’ model to sync up. Said differently, now that we allow different users to sign-up, sign-in, login and logout, the question becomes how to we create ‘associations’ between users and models. We’re now moving into interacting with the database territory, where the major concerns go beyond how the different directories ‘talk’ to each other to how best to model the database to achieve our goals.
After all, it would be pointless to require user authentication if users could edit/delete each other’s movies. What we’re now tasked with establishing is that each movie can only belong to one user, but that each user can create (edit/destroy) many movies.
Fortunately, there’s a lesson from One Month Rails that can be applied to address this very problem. Moreover, I’m excited about a chance to revisit the One Month Rails tutorial with a deeper understanding of models, views, and controller.
Having established that a separate model is required for Users and Movies, we have to generate an additional migration:
$rails generate migration AddUserIdToMovies user_id:integer:index #this 'magically' adds user_id to the Movies table; integer suggests the user_id is a number and index means we'll be able to search by user_id instead of just movie id $rake db:migrate
To actually see the changes made to the database, you’d fire up
$ rails console $ @movie = Movie.last #you'll be able to see user_id: nil in the output where there was none before
But, still, the most important question hasn’t been answered.
How can we set movies to have ‘one’ user, while users can have ‘many’ movies?
This relationship (association) is set in our models (app/model/user.rb & app/model/movie.rb):
class User has_many :movies end class Movie belongs_to :user end
The six different types of associations can be found in here.
For additional details and nuanced explanations, I highly recommend One Month Rails.
What happened to Ruby?
After some reflection, I think I’ve located a source of confusion for me personally on the relationship between Rails and Ruby. The disconnect, for me, can be linked to terminology. While learning Ruby, we got familiar with object-oriented language (remember everything’s an object? Classes, Objects, Modules,); inheritance (instances of classes; global vs. instance variables etc); and method definitions and parameters. Well now that we’re in Rails, suddenly ‘method definitions’ in the controller folders are called “actions”, so instead of method definition for create, edit, new, update, destroy, it’s called ‘create-action’, ‘edit-action’, ‘new-action’, ‘update-action’.
And instead of referring to UsersController or MoviesController as “MoviesController class”, it’s simply “we’re setting the ‘create-action’ in the MoviesController”. The particular jargon and convention in Rails makes it easy to forget that we were dealing with Ruby a mere few weeks ago.
The next post will examine Ritly, our Bitly clone.