Vous êtes sur la page 1sur 47

UC Berkeley

Intro. to Ruby & Intro. to Rails


CS 98-10/CS 198-10 Web 2.0 Programming Using Ruby on Rails
Armando Fox

Administrivia
5um signup!! Lab 0 Self-diagnostic quiz on web page Hack sessions: Th 4-5, Fri 1-2
place, staffing TBA on webpage

Books
more or less required: Agile Web Development with Rails, 1st or 2nd ed. Programming Ruby (free, online at rubycentral.org/book) or The Ruby Way by Fulton

Online resources coming soon

CS61A Keywords of the Day


Symbol
Binding

Review: MVC
Goal: separate organization of data (model) from UI & presentation (view) by introducing controller
mediates user actions requesting access to data presents data for rendering by the view

Web apps are sort of MVC by design

User actions Directives for rendering data

.rb Controller (Ruby) code

Read data Update data

.rhtml template View (or .rjs, .rxml...)


Data provided to views

SQL table + Model Ruby class

Review: CRUD in SQL


4 basic operations on a table row: Create, Read, Update attributes, Destroy
INSERT INTO students (last_name, ucb_sid, degree_expected) VALUES (Fox, 99999, 1998-1215), (Bodik, 88888, 2009-06-05) SELECT * FROM students WHERE (degree_expected < 2000-01-01) UPDATE students SET degree_expected=2008-06-05 WHERE last_name=Bodik) DELETE FROM students WHERE ucb_sid=99999

Review: C/C
If data model is called Student: model (Ruby class) is app/models/student.rb SQL table is students
table row = object instance columns = object attribute

controller methods live in app/controllers/student_controller.rb views are app/views/student/*.rhtml

QuickTime and a TIFF (Un compressed) decompres are neede d to see this picture.

Review: C/C & scaffolding


Model Student implies...
class StudentsController in students_controller.rb views in app/view/students/, .... table students, primary key id, column names match Student classs attributes

2 ways to scaffold your controller & views


metaprogramming makes it happen

While were on SQL... whats a primary key anyway?


Column whose value must be unique for every table row
Why not just use (e.g.) last name or SID#?

SQL AUTO_INCREMENT function makes it easy to specify an integer primary key If using migrations to create tables (recommended), Rails takes care of creating an autoincrement primary key field called ID
class CreateStudents<ActiveRecord::Migration CREATE TABLE students ( id INT NOT NULL AUTO_INCREMENT, last_name VARCHAR(255), first_name VARCHAR(255), ucb_sid INT(11) DEFAULT 9999 ); def self.up create_table :students do |tbl| tbl.column :last_name, :string tbl.column :first_name, :string tbl.column :ucb_sid, :integer, :null=>false, :default=>9999 end end def self.down drop_table :students end end

Using ri and irb from the shell


Ruby is interpreted (for now) irb is an interactive Ruby interpreter
follow along with the examples!

ri (Ruby info) is like man


ri Comparable ri gsub ri String#gsub

Note, need to be in a shell that has PATH and environment variables set correctly See www.ruby-doc.org for more good documents

Rubys Distinguishing Syntax Characteristics


Syntax features
Whitespace is not significant (unlike Python) Statements separated by semicolons or carriage returns Statement can span a newline* Parentheses can often be omitted*
* when unambiguous to parser; use caution!! raise "D'oh!" unless valid(arg) raise "D'oh!" unless valid(arg) raise "D'oh!" unless valid(arg)

Advice from your elders: use a good text editor

Naming conventions
ClassNames
class NewRubyProgrammer ... end

method_names and variable_names


def learn_conventions ... end

predicate_like_methods?
def is_faculty_member? ... end

dangerous_methods!
def brainwash_with_ruby! ... end

:symbols
favorite_framework = :rails

SOME_CONSTANTS or OtherConstants
result in warning if reassigned after init

Everything is an object; (almost) everything is a method call


Everything is an object
Even integers (try 57.methods) Even nil (try nil.respond_to?(:to_s))

(almost) every operator is really a method call


my_str.length => my_str.send(:length) mymethod(foo) => self.send(:mymethod, foo) 1 + 2 => 1.send(:+, 2) arr[4] => arr.send(:[], 4) arr[3] = foo => arr.send(:[]=, 3, foo) if (x == 3) => if (x.send(:==, 3))

in particular, things like implicit conversion on comparison are done by the class methods, not in the language (unlike, e.g., Perl)

Variables & Methods


Variables have no type; objects do
variables spring into existence on first assignment nil,false are Boolean false; everything else true

Everything* is passed-by-reference
can use clone method to effect pass-by-value
*except Fixnums...

Defining methods
def foo(x); [x,x+1]; end def foo(x) [x,x+1] end def foo(x) return [x,x+1] end

Arrays
x = [3, 'a', "third", :blah, :last] x[0] => 3 x[-1] => :last x[-2] => :blah x[-3..-1] => ["third", :blah, :last] y = [1,2] y += [3,4] => [1,2,3,4] y << 5 => [1,2,3,4,5] y << [6,7] => [1,2,3,4,5,[6,7]]

Note! These are nearly all instance methods of Arraynot language operators!

Hashes
h = {"key" => 1, :value => "foo" } h.has_key?("key") => true h["not a key"] => nil (not an error!) h.delete(:value) => {"key" => 1 } h.merge( {:key2 => "3", "hi" => :blah} ) => {"key"=> 1, "key2" => 3, hi => :blah}

Ruby & Rails idioms


omitting braces when a function takes a hash as its last argument omitting parens around function arguments

link_to "Edit student", :controller=>'students', :action=>'edit' link_to("Edit student", {:controller=>'students', :action=>'edit'})

Warning! if ambiguous parse...better safe than sorry!

Classes & Methods


# Methods for MyBank.com class Account @@bank_name = "MyBank.com" # constructor is always called initialize def initialize(starting_balance=0) @balance = starting_balance end # instance methods def balance @balance end def deposit(amount) @balance += amount end def withdraw(amount) @balance -= amount end # A class method def self.bank_name @@bank_name end end

Instance methods, not instance variables


Lets try a few...
my_account.@balance my_account.balance my_account.balance = 100 @@bank_name other_account = Account.new(0) other_account.bank_name

...got it?

Instance variables: shortcut


class Foo def initialize(bar,baz) @bar,@baz=bar,baz end def bar ; @bar ; end def baz ; @baz; end def bar=(newbar) ; @bar=newbar ; end def baz=(newbaz) ; @baz=newbaz; end end

Instance variables: shortcut


class Foo def initialize(bar,baz) @bar,@baz=bar,baz end

attr_accessor :bar, :baz

end

REMEMBER!
a.b means: call method b on object a a is the receiver to which you send the method call, assuming a will respond to that method does not mean: b is an instance variable of a does not mean: a is some kind of structure of which b is a member Understanding this distinction will save you from much grief and confusion

There are (almost) no Loops


Objects manage their own traversal
(1..10).each {|x| ... }

=> range traversal


my_array.each {|elt| ... }

=> array traversal


hsh.each_key {|key| ... } hsh.each_pair {|key,val| .. }

=> hash traversal 10.times {...} => iterator of arity zero


10.times do ... end {...} is a synonym for do...end

What is duck typing?


Ruby type = set of values + set of operations A ruby module defines...
a collection of behaviors that depend only on the presence of one or more specific existing behaviors

QuickTime and a TIFF (Uncompressed) decompre ssor are neede d to see this picture.

i.e.: If it looks like a duck and walks like a duck => it responds to the same methods as a duck. Note, a module a class
but module methods can get mixed in to classes

Mix-in example: Comparable


Define <=> method for your class
include Comparable methods in Comparable assume that objects of target class (into which youre includeing) respond to <=> doesnt actually matter what the class is!

Get < <= => > == between? for free


and, your class can now be sorted (by mixing in Enumerable...what do you suppose it assumes?) Enumerable also provides all?, any?, collect, find, include?, inject, map, partition, ....

Hashes and function calls


Immediate hash (any object can be a key, any object can be an attribute)
my_hsh = {:foo => 1, "x" => nil, 3 => ['a',4]}
my_hsh[:nonexistent_key] returns nil

Parens can be omitted from function calls if parsing is unambiguous x = foo(3, "no") x = foo 3, "no" Braces can be omitted from hash if parsing is unambiguous x = foo( {:a=>1,:b=>2}) x = foo(:a=>1,:b=>2)
easy way to do keyword arguments Caveat: passing immediates to a function that accepts multiple hashes as its arguments

Summary: Rubys Distinguishing Features


Object-oriented with single inheritance
everything is an object almost everything is a method call Modules play a role similar to Javas interfaces and enable duck typing

Dynamically typed
Objects have types; variables dont very few operators in the language; most are defined as instance methods on objects

Idiomatically, {} and () sometimes optional

Active Record: what is it?


A class library that provides an objectrelational model over a plain old RDBMS Deal with objects & attributes rather than rows & columns
SELECT result rows enumerable collection (later) object graph join query

More on Student Example


object attributes are just instance methods (a la attr_accessor)
so can already say stu.last_name, stu.ucb_sid, etc. what line in what file makes this happen?

ActiveRecord accessors/mutators
default attr_accessor for each table column perform type-casting as needed can be overridden, virtualized, etc.

Example: a short tour


Predicate-like method names often end with question mark
self (like Java this) not

strictly necessary here

Some useful class methods of Date

Interpolation of expressions into strings

Virtual attributes example: simple authentication


Assume we have a table customers with columns salt and hashed_password...
Defines the receiver method for password=

Why do we want to use self here?

Wheres the accessor for password?

Constructors
Method named initialize, but invoked as new (at least) 3 ways to call it...

New != Create
Call s.save to write the object to the database
s.create(args) s.new(args); s.save s.update_attributes(hash) can be used to update attributes in

place
s.new_record? is true iff no underlying database row corresponds

to s

save does right thing in SQL (INSERT or UPDATE)

Convention over configuration:


if id column present, assumes primary key if updated_at/created_at columns in table, automatically are set to update/creation timestamp

find() SQL SELECT


# To find an arbitrary single record: s = Student.find(:first) # returns a Student instance # To find all records: students = Student.find(:all) # returns enumerable! # find by 'id' primary key (Note! throws RecordNotFound) book = Book.find(1235) # Find a whole bunch of things ids_array = get_list_of_ids_from_somewhere() students = Student.find(ids_array) # To find by column values: armando = Student.find_by_last_name('Fox') # may return nil a_local_grad = Student.find_by_city_and_degree_expected('Berkeley', Date.parse('June 15,2007') # To find only a few, and sort by an attribute many_localgrads = Student.find_all_by_city_and_degree_expected('Berkeley', Date.parse('June 15,2007'),:limit=>30,:order=>:last_name)

Find by conditions
Use ? for values from parameters. Rails will sanitize the SQL and prevent any SQL injection You will want to learn some minimal SQL syntax

You can also specify ordering and use arbitrary SQL operators:
# Using SQL conditions books = Book.find(:all, :conditions => [pub_date between ? and ?, params[:start_date], params[:end_date]], :order => pub_date DESC)

Find by conditions
Use ? to substitute in condition values
not mandatory, but a good idea!

You can include other SQL functionality


# Using SQL conditions books = Book.find(:all, :conditions => [pub_date between ? and ?, params[:start_date], params[:end_date]], :order => pub_date DESC)

You can roll your own


s = Student.find_by_sql("SELECT * FROM students ...")

Advanced Find
You can also specify limits and offsets, and oh so much more
books = Book.find(:all, :conditions => [pub_date between ? and ?, params[:start_date], params[:end_date]], :limit => 10, :offset => params[:page].to_i * 10)

:lock - Holds lock on the records (default: share lock) :select - Specifies columns for SELECT (default *) :group - (used with select) to group :readonly - load as read-only (object cant be saved) :include - Prefetches joined tables (try :include first;

more about this in Section 4) Note: use SQL-specific features at your own risk....

Caveat!
The result of a find-all operation mixes in
Enumerable
Enumerable defines methods find and find_all

Not to be confused with


ActiveRecord::Base#find!

Caveats for using self. => local variable, method, methodmissing so need self. even for mutators activerecord accessors/setters are lazy & memoized dymamic finders are never memoized!

Action View
A template for rendering views of the model that allows some code embedding
commonly RHTML; also RXML, HAML, RJS note...too much code breaks MVC separation convention: views for model foo are in app/views/foo/

Helper methods for interacting with models


model valuesHTML elements (e.g. menus) HTML form inputassignment to model objects

DRY (Dont Repeat Yourself) support


Layouts capture common page content at application level, model level, etc. (app/views/layouts/) Partials capture reusable/parameterizable view patterns

Helper Methods for Input & Output


Review: we saw a simple view already...
Anatomy: <% code %> <%= output %>

But these form tags are generic...what about modelspecific form tags? In the RHTML template:
<%= text_field 'student', 'last_name' %>

In HTML delivered to browser:


<input id="student_last_name" name="student[last_name]" size="30" type="text" value="Fox" />

What happened? For that we have to look at partial.

Action Controller
Each incoming request instantiates a new Controller object with its own instance variables
Routing (Sec. 4) determines which method to call Parameter unmarshaling (from URL or form sub.) into params[] hash ...well, not really a hash...but responds to [], []=

Controller methods set up instance variables


these will be visible to the view controller has access to models class methods; idiomatically, often begins with Model.find(...)

Lets see some examples...

Then we render...
Once logic is done, render the view

exactly one render permitted from controller method (1 HTTP request 1 response)

Convention over configuration: implicit render


if no other render specified explicitly in action method looks for template matching controller method name and renders with default layouts (model, app)

What about those modelspecific form elements?


Recall:
<input type="text" id="student_last_name" name="student[last_name]"/>

Related form elements for student attributes will be named student[attr ]


marshalled into params as
params[:student][:last_name], params[:student][:degree_expected], etc.

i.e, params[:student] is a hash :last_name=>string, :degree_expected=>date, etc. and can be assigned directly to model object instance helpers for dates and other complex types...magic

What else can happen?


redirect_to allows falling through to

different action without first rendering


fallthrough action will call render instead works using HTTP 302 Found mechanism, i.e. separate browser roundtrip

example: create method


success: redirect to list action fail: render the new action (without redirect)...why?

The Session Hash


Problem: HTTP is stateless (every request totally independent). How to synthesize a session (sequence of related actions) by one user? Rails answer: session[] is a magic persistent hash available to controller
Actually, its not really a hash, but it quacks like one Managed at dispatch level using cookies You can keep full-blown objects there, or just ids (primary keys) of database records Deploy-time flag lets sessions be stored in filesystem, DB table, or distributed in-memory hash table

The Flash
Problem: Im about to redirect_to somewhere, but want to display a notice to the user yet that will be a different controller instance with all new instance variables Rails answer: flash[]
contents are passed to the next action, then cleared to this action: flash.now[:notice] visible to views as well as controller

Strictly speaking, could use session & clear it out yourself

Summary
ActiveRecord provides (somewhat-)databaseindependent object model over RDBMS ActionView supports display & input of model objects
facilitates reuse of templates via layouts & partials

ActionController dispatches user actions, manipulates models, sets up variables for views
declarative specifications capture common patterns for checking predicates before executing handlers

Lab 1
Create a simple 1-model RoR app with basic CRUD MVC
Create database Create the table using migrations Create scaffolding using generator

Find and fix a bug in the code provided to you :-o Hack sessions, forums will help

Vous aimerez peut-être aussi