CS312 - Spring 2012 - Class 9

  • administrative
       - take-home quiz next week (will be available Monday)
       - homework 2 grades sent out
       - last assignment out soon
       - A word of warning: after the break, you're going to need to put regular time into this course

  • friends table
          sqlite> .schema
          CREATE TABLE friends (
           friend_id INTEGER PRIMARY KEY AUTOINCREMENT,
           last_name TEXT NOT NULL,
           first_name TEXT NOT NULL,
           email TEXT NOT NULL DEFAULT '',
           prof_id INTEGER /* foreign key */
          );
          CREATE TABLE professions (
           prof_id INTEGER PRIMARY KEY AUTOINCREMENT,
           name TEXT NOT NULL default ''
          );


  • select command: allows us to query the database

  • inserting into a table
       - so far, all we've done is query existing data in the table
       - we can both insert, update and delete data into the tables
       - INSERT: two basic forms:
          - specify all of the values
          INSERT INTO table_name VALUES (value1, value2, value3,...)

          - specify particular column values (assuming a default is allowed for that column)
          INSERT INTO table_name (column1, column2, column3,...) VALUES (value1, value2, value3,...)

          sqlite> insert into friends (last_name, first_name, email, prof_id) values("Z", "Bobby", "bobbyz@phoenix.edu", 5);
          sqlite> select * from friends;
          friend_id|last_name|first_name|email|prof_id
          1|Smith|Joe|jsmith@woohoo.com|1
          2|Lee|Phoebe|superchef@aol.com|4
          3|Speaker|Tony|ts@xyz.edu|5
          4|Johnson|Jennifer|jenniferj@medny.net|2
          5|Z|Bobby|bobbyz@phoenix.edu|5

       - NULL
          - NULL represents no value in sql (like a null pointer)
          - our friends table has 5 values
          - the first is an friends_id
             - we could specify this ourselves, but better to let sqlite just take care of it for us
             - we can say we don't have a value by specifying null

             sqlite> insert into friends values(NULL, "Z", "Bob", "bob@aol.com", 2);
             sqlite> select * from friends;
             friend_id|last_name|first_name|email|prof_id
             1|Smith|Joe|jsmith@woohoo.com|1
             2|Lee|Phoebe|superchef@aol.com|4
             3|Speaker|Tony|ts@xyz.edu|5
             4|Johnson|Jennifer|jenniferj@medny.net|2
             5|Z|Bobby|bobbyz@phoenix.edu|5
             6|Z|Bob|bob@aol.com|2

             - if we didn't include the NULL we'd get an error, since we need to specify 5 things

  • update
       - sometimes we don't want to insert a new entry, but just want to update some of the values of an entry
       - the update command allows us to do this
          UPDATE table_name
          SET column1=value, column2=value2,...
          WHERE some_column=some_value
          
          sqlite> select * from friends where last_name="Z";
          friend_id|last_name|first_name|email|prof_id
          5|Z|Bobby|bobbyz@phoenix.edu|5
          6|Z|Bob|bob@aol.com|2

          sqlite> update friends set email="bobby@aol.com", prof_id=1 where last_name='Z' AND first_name='Bob';
          sqlite> select * from friends where last_name="Z";
          friend_id|last_name|first_name|email|prof_id
          5|Z|Bobby|bobbyz@phoenix.edu|5
          6|Z|Bob|bobby@aol.com|1

       - be VERY careful with update!
          - you can update multiple entries if you're not careful
          - if you don't specify a WHERE clause, you'll update everything!
          - often a good idea to do a select to check your WHERE and then run the update

  • deleting from a table
       - DELETE allows you to remove entries from the table
       - it has similar syntax to select

          DELETE FROM table_name WHERE some_column=some_value

       - again, be VERY careful with delete!
          - you will delete any entry that matches the where
          - if you don't include a where, you will delete everything!
          - often a good idea to do a select to check your WHERE and then run the update

  • creating tables
       - as we've seen a database can contain multiple tables
       - before you can perform an insert (or a select, etc) you first need to create the table
       - in SQLite when we type .schema it actually shows us the statement that was used to create the table
          
          sqlite> .schema professions

          CREATE TABLE professions (
           prof_id INTEGER PRIMARY KEY AUTOINCREMENT,
           name TEXT NOT NULL default ''
          );

       - syntax for creating tables:
          
          CREATE TABLE <table_name>(
             <column_name1> <data_type> [constraints],
             <column_name2> <data_type> [constraints],
             <column_name3> <data_type> [constraints],
             <column_name4> <data_type> [constraints]
          )


       - the table name should describe the contents of the tables and often is made plural (by convention)
       - column names
          - should also be descriptive
          - if they contain multiple words we separate by underscores (by convention)
       - we've seen the different data types that you can have in SQLite
          - integer
          - text
          - numeric
          - real
          - none
       - there are a number of constraints (http://www.w3schools.com/sql/sql_constraints.asp), the ones we'll use most frequently:
          - NOT NULL
          - UNIQUE
          - PRIMARY KEY

       - specify all attributes that are applicable
          - it will help keep your data in good shape
          - it can help the performance of your queries
       - Even if you don't anticipate needing it, a table should almost always have a primary key (i.e. a unique id for each entry)
       - Many other things to think about when designing a database
          - there's an art to designing understandable and efficient database schemas
          - there are ways of improving performance by adding indexes, but that's beyond our scope

  • other structure changes to a database/table
       - you can remove a table from the database using the DROP commands
          - be careful this removes the table along with all the contents of the table

          DROP TABLE <table_name>

       - You can add or remove columns from a table using the ALTER command
          - you should try avoid this (particularly adding since it can be expensive and sometimes requires populating new data)

          ALTER TABLE <table_name> ADD <column_name> <data_type> [constraings]

          ALTER TABLE <table_name> DROP <column_name>

          - there are also other ways you can alter the table (look online)

  • running commands from a file
       - so far, we've run all of our commands/statements from the shell
       - you can also put your statements in a file and then run them all at once:

          sqlite3 my_database.db < my_commands.sql

       - one common place where this happens is when you get a "dump" of the database
          - a database dump are all the contents of the database in statements such that if they were issues, the database would be recreated
          - in sqlite3 the .dump command gives you this

          sqlite> .dump
          BEGIN TRANSACTION;
          DELETE FROM sqlite_sequence;
          INSERT INTO "sqlite_sequence" VALUES('professions',5);
          INSERT INTO "sqlite_sequence" VALUES('friends',4);
          CREATE TABLE professions (
           prof_id INTEGER PRIMARY KEY AUTOINCREMENT,
           name TEXT NOT NULL default ''
          );
          INSERT INTO "professions" VALUES(1,'Software Developer');
          INSERT INTO "professions" VALUES(2,'Medical Doctor');
          INSERT INTO "professions" VALUES(3,'Financial Analyst');
          INSERT INTO "professions" VALUES(4,'Chef');
          INSERT INTO "professions" VALUES(5,'Professor');
          CREATE TABLE friends (
           friend_id INTEGER PRIMARY KEY AUTOINCREMENT,
           last_name TEXT NOT NULL,
           first_name TEXT NOT NULL,
           email TEXT NOT NULL DEFAULT '',
           prof_id INTEGER /* foreign key */
          );
          INSERT INTO "friends" VALUES(1,'Smith','Joe','jsmith@woohoo.com',1);
          INSERT INTO "friends" VALUES(2,'Lee','Phoebe','superchef@aol.com',4);
          INSERT INTO "friends" VALUES(3,'Speaker','Tony','ts@xyz.edu',5);
          INSERT INTO "friends" VALUES(4,'Johnson','Jennifer','jenniferj@medny.net',2);
          COMMIT;

  • web servers
       - when you go to a url how do you get the html?
          - to contact another computer, you need to know its IP address (think of it like a phone number)
          - the url gets translated into an IP address
             - there are DNS servers (domain name servers) with known IP address that do this translation for you
          - once you have the IP address you issue an HTTP GET request from that computer
          - servers have ports
             - think of a port like a phone extension. the ip address is the main line into the computer and then the port tells you what program within the computer you should be talking through
             - there are some standard ports and some open
             http://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers
          - a web server can run on any port 80 by default (though sometimes on 8080)
          - in response to your request, the server then issues the page
             - note that sometimes this is just a "static" pages like we've looked at in class so far
             - often, though, a "page" is dynamic and involves some computation/generation on the backend
                - this concept of dynamic page content has revolutionized web pages

  • ruby on rails
       "Ruby on a Rails an open-source web framework that's optimized for programmer happiness and sustainable productivity. It lets you write beautiful code by favoring convention over configuration"

       - created in 2003 (version 1.0 released in 2005)
       - built on top of Ruby
          - Ruby syntax and Ruby commands play an important role in Ruby on Rails
       - allows for quick development of web applications, i.e. dynamically generated web content
       - general framework tends to promote
          - separation between data, implementation and visualization
          - quick development cycles
          - incremental development (try something out, see the results and try again)

  • MVC setup
       - one of the key concepts behind Ruby is the Model View Controller framework
       - see Figure 4 on pg. 30 of the book
       - Model
          - behind most web applications is some data, often stored in a database
          - the model is responsible for
             - interfacing between the other components and the database
                - abstracts away the details of the underlying database, query language
             - providing the only way of accessing, editing and updating the data
             - allows for integrity checking, etc. all in one place
       - View
          - in charge of how the data is displayed and visualized
          - should not be doing much computation or calculation
          - in our case:
             - html
             - css
             - some Ruby interspersed
       - Controller
          - glues everything together
          - receive events and information from the outside world
          - handles all of the logic and queries

  • our first rails application
       - to setup a new project type:
          $ rails new demo

          - in response to this a lot of files will be created
          - if you're curious about the options for creating new projects
       
             $ rails new -h

       - if we look now, we have a directory called "demo" created (i.e. the name of the project)
       - inside the project directory are a bunch of directories and files
          - files:
             Gemfile
             README
             Gemfile.lock
             Rakefile
             config.ru

          - directories:
             app
             doc
             log
             script
             tmp
             config
             db
             lib
             public
             test
             vendor
          
          - pg. 257 in the book has a description of what is in each
          - don't be overwhelmed by all of these directories
             - the names are fairly intuitive
             - we'll introduce them slowly
       - a project defines a new application at the base of a web server
          - eventually, we would deploy this application to public facing web server
          - for now, though, we can run our own local web server
       - ruby comes with its own web server (or you can specify your own)
          - inside your project directory run

          $ rail server

          - this starts the web server with this project at its base
          - logging information, etc. will be printed out
          - to kill it, type Ctrl+C
       - we can now see what our current application is by going to:
          http://localhost:3000/

          - 'localhost' means this current computer
          - ':3000' says port 3000
          - note that this is not a publicly available web server, but will suffice for testing
       - the page that is displayed is the default starter page for a ruby application
       - the "public" directory contains the static web pages and is by default at the base of the application
          - if we look in the public directory we see a number of html files
       
             404.html 422.html 500.html favicon.ico index.html robots.txt
          - rails has generated all of this default information for us
          - if we look in index.html we see the html/css for the default web page we saw
       - let's change this:
          - rename the index.html page:
             $ mv index.html index.old.html
          - and now let's put in our own index.html file
             <! DOCTYPE html>
             <html>
             <head>
             <title>My own starter page</title>
             </head>
             <body>
             <h1>This is where it all starts...</h1>
             <body>
             </html>
          
          - now if we hit refresh on our web browser we see the updated content
       - inside the public directory, you can put any static content/pages you'd like and build up the static structures

  • dynamic content (controllers and views)
       - the power of ruby on rails, though, is dynamically generated content
       - to do this, though, we need to generate a new controller and view
          $ rails generate controller Name forward backwards
       
          - this tells rails to generate a controller (and associated files) called "Name" and it should have actions forward and backwards
       - each controller is by default routed as a subdirectory and each action it's own subdirectory from there
          http://localhost:3000/name/forward/
          http://localhost:3000/name/backwards/

       - the views
          - the view shows how the content is presented
          - in app/views/name/ we have views for these different actions/pages
          - all of the files are of type .html.erb
             - the .erb stands for "embedded ruby" and tells rails to process ruby commands nested in the html file
          - if we look in forward.html.erb we see
             <h1>Name#forward</h1>
             <p>Find me in app/views/name/forward.html.erb</p>

          - we can change this html and see the change in the page
             <h1>This is the name forward</h1>
             <p>Dave</p>
             <p>It has 4 characters in it</p>
          
             - notice that when we refresh the page, we see updated commands
  • erb: nesting ruby
       - we can nest ruby commands inside a .erb file in two ways
          - we can wrap code in <% ... %>
             - in this case the statement is run but nothing it output to the html file
          - we can wrap code in <%= ... %>
             - in this case, the return value of the statement is inserted into the html file
       - for example, if we change forward.html.erb to:

          <h1>This is the name forward</h1>
          <% name = "Dave" %>
          <p><%= name %></p>
          <p>It has <%= name.length %> characters in it (calculated automatically)</p>

          - the first embedded statement just creates the variable name
          - which we use in the next two embedded statements
       - notice that when we refresh, we see the updated content
          - if we look in the html all of these ruby commands have been replaced by text

  • the controller
       - let's update the backward page to show the name backwards
       - we could edit backwards.html.erb to be something like:
          h1>This is the name backward</h1>
          <% name = "Dave" %>
          <p><%= name.reverse %></p>

          - but if we ever wanted to change the name, we'd have to change it in both files
       - ruby also created a controller for us in app/controllers
       - if we look at name_controller.rb
          - a class that inherits from ApplicationController
          - we have three methods that are associated with our two actions
             - each method is called before the corresponding view is generated
       - adding a centralized name instance variable
          - to start with, let's add a name instance variable here that is shared between the views
          - lets add an initialize method that defines an instance variable @name
             def initialize
              super
               @name = "Dave"
             end

             - don't forget the call to super() (i.e. the parent class's initialize) otherwise things won't work correctly
          - now, we can go back to our view files and access this variable
             - the views associated with a controller have access to all of the instance variables as if they were inside the class
             <h1>This is the name forward</h1>
             <p><%= @name %></p>
             <p>It has <%= @name.length %> characters in it (calculated automatically)</p>
          - we can also change backwards to work appropriately with this variable
             <h1>This is the name backwards</h1>
             <p><%= @name.reverse %>
       - avoid work in the views
          - in general, the views should just be for displaying content
             - even though calling reverse is not a major computation, it hides implementation details in the view
          - we should move this computation to the controller
             - setup the controller to do all the reversing, etc

             def initialize
              super
              @name = "Dave"
              @reversed = false
              end

              def forward
              if @reversed
              @name = @name.reverse
              end
              end

              def backwards
              if not @reversed
              @name = @name.reverse
              end
              end


             - and then edit backwards to just display the name
             <h1>This is the name backwards</h1>
             <p><%= @name %>

  • 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
       - 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 %>