Your SlideShare is downloading. ×
Slides from lecture - Ruby on Rails Short Course: Just Enough Ruby
Upcoming SlideShare
Loading in...5

Thanks for flagging this SlideShare!

Oops! An error has occurred.

Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

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


Published on

  • Be the first to comment

  • Be the first to like this

No Downloads
Total Views
On Slideshare
From Embeds
Number of Embeds
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

No notes for slide


  • 1. 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
  • 2. 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
  • 3. Lab 2 • UI question: add student to course, or course to student? • Note, scaffolding won’t automatically work for multi-model associations • is your friend
  • 4. Review: Testing • What is... – a unit test? – a functional test? – an integration test? • How do you “simulate” user’s actions in a controller test?
  • 5. 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
  • 6. 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 = => "Ruby on Rails", :ccn => 99999) cs61a = Course.find_by_name("Structure and Interp...") p = Professor.find_by_last_name("Fox") << c!
  • 7. 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 =>"Sobel", :first_name=>"Will") cs61a.professor = p!
  • 8. 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
  • 9. 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<<c => courses_students course_id student_id date_added
  • 10. 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"
  • 11. Additional find options for multi-model students = 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 ?
  • 12. 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?
  • 13. 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?
  • 14. 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
  • 15. Callbacks Allows Pre and Post Operations
  • 16. 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("--#{}-- #{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?
  • 17. 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
  • 18. 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]) Why does this work?
  • 19. 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
  • 20. More sophisticated tag helpers: dropdown menus • Here are some examples • Many more helpers, read the documentation!
  • 21. 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
  • 22. 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
  • 23. 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!
  • 24. 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)
  • 25. 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?
  • 26. 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
  • 27. 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:[:notice] – visible to views as well as controller • Strictly speaking, could use session & clear it out yourself
  • 28. 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
  • 29. 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
  • 30. 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 1. Add an “is administrator” flag to Student model using a migration 2. 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