Flash Player 9 (or above) is needed to view presentations.
We have detected that you do not have it on your computer. To install it, go here.

Like this presentation? Why not share!

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






Total Views
Views on SlideShare
Embed Views



0 Embeds 0

No embeds



Upload Details

Uploaded via as Microsoft PowerPoint

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
Post Comment
Edit your comment

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

  • More Rails: Multi-Model, HABTM, Controller & View tricks CS 98-10/CS 198-10 Web 2.0 Programming Using Ruby on Rails Armando Fox
  • 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
  • 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
    View slide
  • Review: Testing
    • What is...
      • a unit test?
      • a functional test?
      • an integration test?
    • How do you “simulate” user’s actions in a controller test?
    View slide
  • 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
  • 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 => &quot;Ruby on Rails&quot;, :ccn => 99999) cs61a = Course.find_by_name(&quot;Structure and Interp...&quot;) p = Professor.find_by_last_name(&quot;Fox&quot;) p.courses << c p.courses.delete(cs61a) p.save!
  • 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(&quot;Ruby on Rails&quot;) p = Professor.new(:last_name=>&quot;Sobel&quot;, :first_name=>&quot;Will&quot;) cs61a.professor = p cs61a.save!
  • 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 student_id course_id courses_students
  • 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(&quot;Ruby on Rails&quot;) s<<c s.courses.push_with_attributes(:date_added => Date.today) date_added student_id course_id courses_students
  • 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(&quot;Bodik&quot;) p = s.professors last_name=Bodik id=3 students professor_id=7 id=45 student_id=3 courses last_name=&quot;Fox&quot; id=7 professors
  • Additional find options for multi-model
    • students = Student.find(:all,
    • :conditions=>['graduation_date < ?',Date.parse(&quot;6/1/08&quot;)],
    • :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 &quot;courses&quot; ?
      • what kind of a method call is Date.parse ?
  • 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?
  • 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?
  • 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
  • Callbacks Allows Pre and Post Operations
  • Another way to do passwords
    • # Encrypts some data with the salt.
    • def self.encrypt(password, salt)
    • Digest::SHA1.hexdigest(&quot;--#{salt}--#{password}--&quot;)
    • end
    • def before_save
    • return nil if password.blank?
    • self.salt = Digest::SHA1.hexdigest(&quot;--#{Time.now.to_s}--#{login}--&quot;) 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?
  • 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 values  HTML elements (e.g. menus)
      • HTML form input  assignment 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
  • 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=&quot;student_last_name&quot; name=&quot;student[last_name]&quot; size=&quot;30&quot; type=&quot;text&quot; value=&quot;Fox&quot; />
    • In controller method, can then say:
    • stu = Student.new(params[:student])
    • Why does this work?
  • model-specific form tag helpers
    • Recall: <input type=&quot;text&quot; id=&quot;student_last_name&quot; name=&quot;student[last_name]&quot; />
    • 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
  • More sophisticated tag helpers: dropdown menus
    • Here are some examples
    • Many more helpers, read the documentation!
  • 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
  • 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
  • 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=&quot;fieldWithErrors&quot;>
      • error_messages_for (in 'form' partial) wraps @student.errors (set by ActiveRecord validation callbacks) with <div id=&quot;errorExplanation&quot;>
    • 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!
  • 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)
  • 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?
  • 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
  • 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 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
  • 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
  • More General Filters
    • Code blocks that can go before, after or around controller actions; return Boolean
      • before_filter :filter_method_name
      • before_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
  • Lab 3
    • 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
    • Add an “is administrator” flag to Student model using a migration
    • 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