CS312 - Spring 2012 - Class 10
administrative
- take-home quiz is available on the course web page
- follow the instructions
- you may NOT use the Ruby to run your programs
- everything we've covered through the semester
- won't have much on rails
- project discussion for next time
- hand-in a 1-2 sentence blurb of a project that you've discussed with at least one person in the class
- can hand-in one idea for up to 3-4 people
- anyone interested in doing some web design on the side?
quick recap on rails last time
- MVC framework
- model handles the data
- the interface with the database
- validation
- get, set, search, select-like queries, etc.
- view handles the output/display to the user
- consists of:
- html files
- css files
- .erb (embedded ruby) files
- controller
- glues everything together
- receive events and information from the outside world
- handles all of the logic and queries
- created a new rails project
- rails new myproject
- started the web server with rails server
- this needs to stay running while you're debugging
- by default the project is hosted on
http://localhost:3000/
- the public directory houses the static content
- created a controller and the associated views
rails generate controller Name forwards backwards
- rails to generates a controller (and associated files) called "Name" with actions forward and backwards
- this ends up in app/controllers/name_controller.rb
- rails also generates views name#forward and name#backward
- this ends up in app/view/name/
- rails adds lines to the route.rb file:
- get "name/forwards"
- get "name/backwards"
- now if we go to:
http://localhost:3000/name/forward/
http://localhost:3000/name/backwards/
we see the views for the two actions
- dynamic content generation
- .erb files allow us to nest ruby inside html before the html is generated
- <%= %> inserts whatever is the resulting value in the html
- <% %> is used just for control statements, etc.
- the controller is just a class that inherits from ApplicationController
- each method in there generally maps to a route (i.e. a web page path)
- this is where the key processing should happen
- you should avoid work in the views
- the views are for displaying and formatting data, not processing
linking between views
- let's say we'd like to add links between forwards and backwards
- we could put in a <a href=..., but it's very likely that this address wouldn't remain constant (we might move things, deploy somewhere else, etc.)
- rails has a link_to function that allows us to link_to a page within the application
- it also precomputes some paths for us to use in our program, in particular for ours:
- name_forward_path contains the path to forward
- name_backwards_path contains the path to backwards
- in general, when you create a controller you will end up with: <controller_name>_<action_name>_path
- using this, we could add the following to the bottom of forward to add a navigation bar:
<p><%= link_to "Forward", name_forward_path %> <%= link_to "Backward", name_backwards_path %>
html templates
- if we wanted this to be a nice navigation bar, we'd also need to add it to backward.html.erb
- however, if we wanted to change this, we'd have to edit it in two places
- if we look at the html pages that are being generated by our application, we see that there is a lot of html besides just what we're entering being generated
- if we look in the app/views/layouts there is a file called application.html.erb
- this file is the default html for any page in this application
- inside it is a called to "yield" which generates the html page from the individual .erb calls
- if we can to have a global template change, we can make the change here
- add the code above to the bottom, i.e.
<p><%= link_to "Forward", name_forward_path %> <%= link_to "Backward", name_backwards_path %>
- and now we have that information on all of the pages
symbols
- rails makes extensive use of what are called "symbols"
- we'll see them a lot, so get used to them
- they're part of the Ruby programming language
- a symbol is any string of characters that is preceded by a colon, e.g.
:student
:banana
:class
- symbols are just like any other values
- think of them like a constant (but they don't have a value)
- Ruby guarantees that if you use a symbol anywhere during a programs execution, it will have the same value and the value will be unique
- it's a nice way to reference the same thing
grading applications
- quick layout of the basic application on paper first
- we'd like to develop a grade storing application
- we will keep track of the grades for each student
- a student may have multiple assignments
- each assignment will have a score associated with it
- we'd like to have multiple ways of getting at/editing the data
- in one view, we can edit an individual student's scores and add assignments/scores
- in another view, we can see all the students with all of their grades
student class roster application
- create a new project
- generate "scaffolding" for our students project
- model
- statements to generate our database
- a class for interfacing with the database (inherits from ActiveRecord)
- controllers to handle basic functionality
- views to display default information
$ rails generate scaffold Student name:string
- created a db/migrate file (for setting up the database)
- created a model class for us
- setup a controller for us (students_contoller.rb)
- setup a view for us (students)
- the view has multiple files in it
- setup routes for us in the route.rb file
- setup some base .css files for us
- first thing, let's setup our database
- cd into the db (database) directory
- if we look in here it's created an empty database for us:
development.db
- if you look in it it doesn't have any tables yet
- if we go into the "migrate" directory there is a file with a timestamp and "_create_students.rb"
- if we look inside this we see:
class CreateStudents < ActiveRecord::Migration
def change
create_table :students do |t|
t.string :name
t.timestamps
end
end
end
- rails has generated some ruby code (in fact a class) that will create a database table for us
- rake is a command-line tool that comes with rails that does things for us.
- in this case, we're going to tell it to go ahead and migrate the database
$ rake db:migrate
(in /Users/dkauchak/classes/cs312/lectures/lecture9-rails/working/grades)
== CreateStudents: migrating =================================================
-- create_table(:students)
-> 0.0009s
== CreateStudents: migrated (0.0010s) ========================================
- it tells us that it created the table
- one of the advantages of this approach (vs. trying to make the table ourselves) is that we can undo/rollback migrations
- rake
- rake stands for "Ruby make"
- "Make is a tool which controls the generation of executables and other non-source files of a program from the program's source files."
- very common tool for c/c++
- can read more about rake files if you're interested
- now if we look in the development.db we see our table there
sqlite> .tables
schema_migrations students
- if we now got the our web application in the browser we'll see that the default controllers and views for students
http://localhost:3000/students/
- even without doing much of anything, we can add new students, etc.
- controller
- if we look in app/controllers we see there is a file called student_controllers.rb
- just like other controllers, it is a class that inherits from ApplicationController
- the methods inside this class are called depending on the url as a subdirectory of "students/"
- index is called if no name is specified
- show is called if the url looks like "students/1" or "students/15"
- params[:id] is the number following students
- new is normal (i.e. is called when "students/new" is visited
- and many more...
-
http://guides.rubyonrails.org/routing.html
- if we look at these functions we can learn a little bit about what's going on
basic chain of events when a page is visited
- the routes.rb file is checked to figure out where to route the command (i.e. which controller)
- a new instance of that controller is instantiated
- the particular method for that controller is called
- a view is then instantiated
- the view gets data from the controller
- the html file is generated from the view (and associated files)
- the html is served up to the user
adding in the assignments
- now we'd like to add the assignment
- we don't need another controller or view, just another database
rails generate model Assignment assignment_number:integer score:decimal student:references
- the student:references tells Ruby that we want an entry the references our student database
- if we look at the migration file we see:
class CreateAssignments < ActiveRecord::Migration
def change
create_table :assignments do |t|
t.integer :assignment_number
t.decimal :score
t.references :student
t.timestamps
end
add_index :assignments, :student_id
end
end
- as always, after generating a new model we need to actually create the database rake db:migrate
- if we look at the tables in the database now:
CREATE TABLE "assignments" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "assignment_number" integer, "score" decimal, "student_id" integer, "created_at" datetime, "updated_at" datetime);
CREATE TABLE "students" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar(255), "created_at" datetime, "updated_at" datetime);
- we have now have an assignments table with the cross-references student_id
now we need to state the relationship between assignments and students
- the model enforces constraints
- if we look in app/models/assignment.rb we see it has:
belongs_to :student
- if added this because of the student:references
- we'd also like to tell rails that we can have more than one assignment per student
- in app/models/student.rb we can add:
has_many :assignments to student.rb
- the main benefit of this is that it will allow us to retrieve all of the assignments associated with a given student using just . notation (it does the join syntax, etc. for us)
updating the route.rb file
- we now need to tell the route.rb file that assignments is a nested resoure
resources :students do
resources :assignments
end
- this nests the assignment pages on top of the student page, e.g. notice that:
http://localhost:3000/students/1/assignments/create
routes to the AssignmentsController
Now let's generate the controller for the assignment
rails generate controller Assignments
- which generates controllers and viewers for us