+ All Categories
Home > Documents > Slides from lecture - Ruby on Rails Short Course: Just Enough Ruby

Slides from lecture - Ruby on Rails Short Course: Just Enough Ruby

Date post: 19-Jan-2015
Category:
Upload: aabbccddeeee
View: 954 times
Download: 3 times
Share this document with a friend
Description:
 
Popular Tags:
30
UC Berkeley More Rails: Multi-Model, HABTM, Controller & View tricks CS 98-10/CS 198-10 Web 2.0 Programming Using Ruby on Rails Armando Fox
Transcript
Page 1: Slides from lecture - Ruby on Rails Short Course: Just Enough Ruby

UC Berkeley

More Rails: Multi-Model, HABTM, Controller & View

tricksCS 98-10/CS 198-10

Web 2.0 Programming Using Ruby on RailsArmando Fox

Page 2: Slides from lecture - Ruby on Rails Short Course: Just Enough Ruby

Outline (and suggested Rails book

chapters*)• Review Lab 2• Review multi-model associations (Ch. 18)

– has_and_belongs_to_many– has_many :through

• Model validations (19.1) & lifecycle callbacks (19.2)

• More controller (Ch. 21) & view (Ch. 22) tricks

• Lab 3: add login & administrator actions to Lab 2

• Project pitches*Chapters refer to 2nd ed. of Agile Web Development With Rails

Page 3: Slides from lecture - Ruby on Rails Short Course: Just Enough Ruby

Lab 2

• UI question: add student to course, or course to student?

• Note, scaffolding won’t automatically work for multi-model associations

• http://www.rubyonrails.org/doc is your friend

Page 4: Slides from lecture - Ruby on Rails Short Course: Just Enough Ruby

Review: Testing

• What is...– a unit test?– a functional test?– an integration test?

• How do you “simulate” user’s actions in a controller test?

Page 5: Slides from lecture - Ruby on Rails Short Course: Just Enough Ruby

Review: Simple Associations

• What will the tables be called?• What additional column(s) will need to be present (besides those for attributes)?

• Why do we have to declare the association in both directions?

class Professor < ActiveRecord::Base has_many :courses...class Course < ActiveRecord::Base belongs_to :professor

Page 6: Slides from lecture - Ruby on Rails Short Course: Just Enough Ruby

Associations and the magic of “duck

typing”

• What if there’s more than one professor whose last name is Fox?

• What if ror doesn’t validate?– What would be a safe workaround?

• What does the delete do?• Will ror be saved?

ror = Course.new(:name => "Ruby on Rails", :ccn => 99999)cs61a = Course.find_by_name("Structure and Interp...")p = Professor.find_by_last_name("Fox")p.courses << cp.courses.delete(cs61a)p.save!

Page 7: Slides from lecture - Ruby on Rails Short Course: Just Enough Ruby

Caveat!

• This won’t work! why not?• General rule: saving the “parent” object saves the child(ren)...but not vice versa

• Fix: save the children separately

ror = Course.find_by_name("Ruby on Rails")p = Professor.new(:last_name=>"Sobel", :first_name=>"Will")cs61a.professor = pcs61a.save!

Page 8: Slides from lecture - Ruby on Rails Short Course: Just Enough Ruby

Has and Belongs to Many

• Symmetric relationship between tables– can use the has_many methods “from either end”– why do you need a separate join table for this?

• Recall: simple join table– Compared to regular ActiveRecord model table?– How to create in raw SQL DDL vs. with migrations– C/C tells us how to name it.

class Student << ActiveRecord::Base has_and_belongs_to_many :courses...class Course << ActiveRecord::Base has_and_belongs_to_many :students

courses_students

course_id

student_id

Page 9: Slides from lecture - Ruby on Rails Short Course: Just Enough Ruby

Adding attributes to a join table

• You can add other columns to a table– but beware, it might be better to make it a full model

s = Student.find_by_ucb_sid(99999)

c = Course.find_by_name("Ruby on Rails")s<<cs.courses.push_with_attributes(:date_added => Date.today)

courses_students

course_id

student_id

date_added

Page 10: Slides from lecture - Ruby on Rails Short Course: Just Enough Ruby

has_many :through

• If a student has_many courses and a course belongs_to professor, you could argue that a student has_many professors

class Student << ActiveRecord::Base has_many :professors, :through => :courses...s = Student.find_by_last_name("Bodik")p = s.professors

students

id=3

last_name=Bodik

courses

id=45

student_id=3

professor_id=7

professors

id=7

last_name="Fox"

Page 11: Slides from lecture - Ruby on Rails Short Course: Just Enough Ruby

Additional find options for multi-

modelstudents = Student.find(:all, :conditions=>['graduation_date < ?',Date.parse("6/1/08")], :include => :courses

You can also specify limits and offsets, and oh so much more

• :include - Prefetches joined tables. Discuss.

• Just to make sure you’re on your toes...– why did I use the “array form” of :conditions clause?

– does it matter if I say :courses or 'courses' or "courses"?

– what kind of a method call is Date.parse ?

Page 12: Slides from lecture - Ruby on Rails Short Course: Just Enough Ruby

Model Validations

• A validation asserts the conditions under which some attribute of a model has a valid value

• Why not do this in (e.g.) controller when new model object is submitted for creation?

Page 13: Slides from lecture - Ruby on Rails Short Course: Just Enough Ruby

Keeping validations with models

• model lifecycle specifies well-defined callbacks for ActiveRecord manipulation– allows keeping validation semantics with the model– allows keeping validation code separate from mainline

• are those macros, language keywords, or what?

Page 14: Slides from lecture - Ruby on Rails Short Course: Just Enough Ruby

How would you use these?

• Note convention: save! vs. save (also create, update, ...)

• Scaffolding provides a default use via a view helper method errors_for

Page 15: Slides from lecture - Ruby on Rails Short Course: Just Enough Ruby

Callbacks

Allows Pre and Post Operations

Page 16: Slides from lecture - Ruby on Rails Short Course: Just Enough Ruby

Another way to do passwords

# Encrypts some data with the salt.def self.encrypt(password, salt) Digest::SHA1.hexdigest("--#{salt}--#{password}--")end

def before_save return nil if password.blank? self.salt = Digest::SHA1.hexdigest("--#{Time.now.to_s}--

#{login}--") if new_record? self.crypted_password = encrypt(password)end

Encrypt a password before saving the record

• Update fails if filter returns nil• Note use of:

– instance/class methods: password, encrypt– predicate methods: blank?, new_record?

Page 17: Slides from lecture - Ruby on Rails Short Course: Just Enough Ruby

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 (Don’t Repeat Yourself) support– Layouts capture common page content at application level, model level, etc. (app/views/layouts/)

– Partials capture reusable/parameterizable view patterns

Page 18: Slides from lecture - Ruby on Rails Short Course: Just Enough Ruby

Helper Methods for Input & Output

• Here is a simple view ...– Anatomy: <% code %> <%= output %>– Sanity check: why do we use <% and not <%= for tag helpers?

• What about ActiveRecord model-specific 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" />

• In controller method, can then say:stu = Student.new(params[:student])

Why does this work?

Page 19: Slides from lecture - Ruby on Rails Short Course: Just Enough Ruby

model-specific form tag helpers

• 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

Page 20: Slides from lecture - Ruby on Rails Short Course: Just Enough Ruby

More sophisticated tag helpers: dropdown

menus• Here are some examples• Many more helpers, read the documentation!

Page 21: Slides from lecture - Ruby on Rails Short Course: Just Enough Ruby

Partials

• Reusable chunk of a view– e.g., one line of a Student table– e.g., form to display/capture Student info that can be used as part of Edit, Show, Create,...

– file naming convention: the partial foo for model bar is in app/views/bar/_foo.rhtml

• default partial form generated by scaffolding– so edit.rhtml (the edit view) is really trivial, and differs minimally from new.rhtml

– but both of them set the local variable student– can set additional local variables for partial using:locals => {:var => value, :var => value }

– can use the option :object => var to set it from an object other than @student

Page 22: Slides from lecture - Ruby on Rails Short Course: Just Enough Ruby

What about a collection?

• Common idiom:@students.each do |student|render :partial => 'student'

• Captured by:render :partial => :student, :collection => @students

– other options allow specifying “divider” template

Page 23: Slides from lecture - Ruby on Rails Short Course: Just Enough Ruby

Putting it all together: Validation error reporting in

views: CSS+HTML+Rails• form partial sets ID, class of specific elements– text_field helper conditionally wraps HTML element in <div class="fieldWithErrors">

– error_messages_for (in 'form' partial) wraps @student.errors (set by ActiveRecord validation callbacks) with <div id="errorExplanation">

• Default layout for class (app/views/layouts/students.rhtml)– generated by script/generate scaffold student– pulls in stylesheet scaffold.css (generic scaffolding styles) that define visual appearance for element ID errorExplanation and class fieldWithErrors

Yow!

Page 24: Slides from lecture - Ruby on Rails Short Course: Just Enough Ruby

Controller: rendering

• So far you’ve relied mostly on the implicit rendering done after controller method– Convention over configuration: implicit render looks for template matching controller method name and renders with default layouts (model, app)

• But we can do other things instead...

– exactly one render permitted from controller method

– in fact, required; why? (hint: MVC)

Page 25: Slides from lecture - Ruby on Rails Short Course: Just Enough Ruby

Controller tricks

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

Page 26: Slides from lecture - Ruby on Rails Short Course: Just Enough Ruby

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, it’s not really a hash, but it quacks like one

– Managed at dispatch level using cookies– You can keep full-blown objects there, or just id’s (primary keys) of database records

– Deploy-time flag lets sessions be stored in filesystem, DB table, or distributed in-memory hash table

Page 27: Slides from lecture - Ruby on Rails Short Course: Just Enough Ruby

The Flash

• Problem: I’m 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 variablesRails 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

Page 28: Slides from lecture - Ruby on Rails Short Course: Just Enough Ruby

Controller predicates: verify

• A declarative way to assert various preconditions on calling controller methods

• You can check selectively (:only, :except) for...– HTTP request type (GET, POST, Ajax XHR)– Presence of a key in the flash or the session– Presence of a key in params[]

• And if the check fails, you can...– redirect_to somewhere else– add_to_flash a helpful message

• A simple example in our simple controller

Page 29: Slides from lecture - Ruby on Rails Short Course: Just Enough Ruby

More General Filters

• Code blocks that can go before, after or around controller actions; return Booleanbefore_filter :filter_method_namebefore_filter { |controller| ... }before_filter ClassName

– options include :only,:except, etc.– multiple filters allowed; calls provided to prepend

or append to filter chain– subclasses inherit filters but can use skip_filter

methods to selectively disable them• If any before-filter returns false, chain halted &

controller action method won’t be invoked– so filter should redirect_to, render, or otherwise

deal with the request• Simple example: authentication

Page 30: Slides from lecture - Ruby on Rails Short Course: Just Enough Ruby

Lab 3

1. Add logins and passwords to Lab 2– Use virtual instance methods or model callbacks

to implement passwords– Add unit tests to check password functionality– Add functional tests to check login being

enforced– Note: for projects, you’ll probably use

acts_as_authenticated which we’ll describe later

2. Add an “is administrator” flag to Student model using a migration

3. Restrict certain controller actions (your choice) to admin only

• Redirect to login page with a helpful message (hint: use the flash) if violated

• Add functional test to check that only admin can do those actions


Recommended