Rails Philip Ritchey slides generously gifted by Jeff Huang.

Slides:



Advertisements
Similar presentations
Testing Relational Database
Advertisements

Presenter: James Huang Date: Sept. 29,  HTTP and WWW  Bottle Web Framework  Request Routing  Sending Static Files  Handling HTML  HTTP Errors.
When Things Go Wrong: Debugging (Engineering Software as a Service §4.5) © 2013 Armando Fox & David Patterson, all rights reserved 1.
PHP (2) – Functions, Arrays, Databases, and sessions.
Concepts of Database Management Sixth Edition
Associations: Mechanics (ESaaS §5.3) © 2013 Armando Fox & David Patterson, all rights reserved.
DRYing Out MVC (ESaaS §5.1) © 2013 Armando Fox & David Patterson, all rights reserved.
Ruby on Rails. What is Ruby on Rails? Ruby on Rails is an open source full-stack web framework. It is an alternative to PHP/MySQL. It can render templates,
1 PHP and MySQL. 2 Topics  Querying Data with PHP  User-Driven Querying  Writing Data with PHP and MySQL PHP and MySQL.
Chapter 4 The Relational Model 3: Advanced Topics Concepts of Database Management Seventh Edition.
Moodle (Course Management Systems). Assignments 1 Assignments are a refreshingly simple method for collecting student work. They are a simple and flexible.
Ruby on Rails Your first app. Rails files app/ Contains the controllers, models, views and assets for your application. You’ll focus on this folder for.
Management Information Systems MS Access MS Access is an application software that facilitates us to create Database Management Systems (DBMS)
Lecture 11 Rails Topics SaaSSaaS Readings: SaaS book Ch February CSCE 740 Software Engineering.
MS Access 2007 Management Information Systems 1. Overview 2  What is MS Access?  Access Terminology  Access Window  Database Window  Create New Database.
Diagnostic Pathfinder for Instructors. Diagnostic Pathfinder Local File vs. Database Normal operations Expert operations Admin operations.
BIT 286: Web Applications Lecture 10 : Thursday, February 5, 2015 ASP.Net Form Submission.
Java server pages. A JSP file basically contains HTML, but with embedded JSP tags with snippets of Java code inside them. A JSP file basically contains.
8 Chapter Eight Server-side Scripts. 8 Chapter Objectives Create dynamic Web pages that retrieve and display database data using Active Server Pages Process.
PHP: Further Skills 02 By Trevor Adams. Topics covered Persistence What is it? Why do we need it? Basic Persistence Hidden form fields Query strings Cookies.
BIT 286: Web Applications Lecture 04 : Thursday, January 15, 2015 ASP.Net MVC -
Hello SaaS: Rails from Zero to CRUD CS 169 Spring 2012 Armando Fox, David Patterson, Koushik Sen.
Emdeon Office Batch Management Services This document provides detailed information on Batch Import Services and other Batch features.
Chapter 1 Getting Started with ASP.NET Objectives Why ASP? To get familiar with our IDE (Integrated Development Environment ), Visual Studio. Understand.
Managing State Chapter 13.
DRYing Out MVC (ESaaS §5.1)
Development Environment
Project Management: Messages
Working in the Forms Developer Environment
© 2013 Armando Fox & David Patterson, all rights reserved
GO! with Microsoft Office 2016
Practical Office 2007 Chapter 10
Chapter 19 PHP Part III Credits: Parts of the slides are based on slides created by textbook authors, P.J. Deitel and H. M. Deitel by Prentice Hall ©
Database application MySQL Database and PhpMyAdmin
Testing and Debugging.
Server Concepts Dr. Charles W. Kann.
mysql and mysql workbench
GO! with Microsoft Access 2016
Cross-Site Forgery
Associations: Mechanics (ESaaS §5.3)
Dynamic SQL Writing Efficient Queries on the Fly
PHP / MySQL Introduction
Error Handling Summary of the next few pages: Error Handling Cursors.
MVC Framework, in general.
Ruby on Rails by Manik Juneja
What is Cookie? Cookie is small information stored in text file on user’s hard drive by web server. This information is later used by web browser to retrieve.
Phil Tayco Slide version 1.0 Created Oct 2, 2017
Creating and Modifying Queries
Ruby on Rails by Manik Juneja
Topics Introduction to File Input and Output
Number and String Operations
Teaching slides Chapter 8.
Exploring Microsoft® Access® 2016 Series Editor Mary Anne Poatsy
Lecture 1: Multi-tier Architecture Overview
Programming in JavaScript
Teaching slides Chapter 6.
Programming in JavaScript
Tonga Institute of Higher Education IT 141: Information Systems
Technical Integration Guide
Tonga Institute of Higher Education IT 141: Information Systems
Test Cases, Test Suites and Test Case management systems
Topics Introduction to File Input and Output
Security Principles and Policies CS 236 On-Line MS Program Networks and Systems Security Peter Reiher.
PHP-II.
Mobile Programming Dr. Mohsin Ali Memon.
Cross Site Request Forgery (CSRF)
Database management systems
Presentation transcript:

Rails Philip Ritchey slides generously gifted by Jeff Huang

Hello Rails (Engineering Software as a Service §4) Getting started on Rails: http://guides.rubyonrails.org/getting_started.html

Connecting Arch. Concepts to Rails Apps Gemfile Rakefile app /models/, views/, controllers/ /helpers /assets/stylesheets/application.css config /routes.rb /database.yml db /development.sqlite3, test.sqlite3 /migrate/ log /development.log, test.log Gemfile: which libraries does app depend on, so can ensure same versions installed in production. Gem is a Ruby library file. Rakefile: you've already used 'rake routes' in book and lecture examples. Rake is Ruby make.

Rails as an MVC Framework Model, View, Controller Relational Database Persistence: mysql or sqlite3 tables models/*.rb Subclasses of ActiveRecord::Base, an object-relational mapping layer your app Logic: your code & Rack appserver controllers/*.rb Rails routing views/*.html.haml Rails rendering Presentation: WEBrick, Apache, etc. Subclasses of ActionView Subclasses of ApplicationController Client: Firefox

A Trip Through a Rails App Routes (in routes.rb) map incoming URL’s to controller actions and extract any optional parameters Route’s “wildcard” parameters (eg :id), plus any stuff after “?” in URL, are put into params[] hash accessible in controller actions Controller actions set instance variables, visible to views Subdirs and filenames of views/ match controllers & action names Controller action eventually renders a view app/controllers/movies_controller.rb def show id = params[:id] @mv=Movie.find(id) end app/views/movies/show.html.haml %li Rating: = @mv.rating config/routes.rb GET /movies/:id {:action=>'show',:controller=>'movies'}

Rails Philosophy Convention over configuration If naming follows certain conventions, no need for config files MoviesController#show in movies_controller.rb  views/movies/show.html.haml Don’t Repeat Yourself (DRY) mechanisms to extract common functionality Both rely heavily on Ruby features: introspection and metaprogramming blocks (closures) modules (mix-ins)

Why must every interaction with a SaaS app eventually cause something to be rendered? Because of convention over configuration ☐ Because HTTP is a request-reply protocol ☐ Because Model-View-Controller implies that every action renders its own View ☐ False. Not in general. True. False. Two views could be called by same action. False. All of the above ☐

Homework 1 RSpec & Unit Tests Let’s talk Just a little bit about Testing – a lot more later

Edsger Dijkstra Brian Kernighan Testing can never demonstrate the _____ of errors in software, only their _______ Edsger Dijkstra absence presence Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it. Brian Kernighan Brian Kernighan, co-author of the C language book, many UNIX utilities Turing Award winner Edsger Dijkstra, "father of object-oriented programming" Seen in bathrooms at Google Inc. as part of "Testing on the Toilet" program

Testing Today Before Today/Agile developers finish code, some ad-hoc testing “toss over the wall to Quality Assurance [QA]” QA staff manually poke at software Today/Agile testing is part of every Agile iteration developers test their own code testing tools & processes highly automated QA/testing group improves testability & tools

Testing Today Before developers finish code, some ad-hoc testing “toss over the wall to Quality Assurance [QA]” QA people manually poke at software Today/Agile testing is part of every Agile iteration developers responsible for testing own code testing tools & processes highly automated; QA/testing group improves testability & tools Software Quality is the result of a good process, rather than the responsibility of one specific group

BDD+TDD: The Big Picture Behavior-Driven Design (BDD) develop user stories (the features you wish you had) to describe how app will work via Cucumber, user stories become acceptance tests and integration tests Test-Driven Development (TDD) step definitions for a new story, may require new code to be written TDD says: write unit & functional tests for that code first, before the code itself that is: write tests for the code you wish you had

Cucumber & RSpec Cucumber describes behavior via features & scenarios (behavior driven design) RSpec tests individual modules that contribute to those behaviors (test driven development) Failing (red) Cucumber step Failing (red) RSpec test Passing (green) RSpec test 1st step is thinking of “code you wish you had”, assuming methods that if existed would make it a perfect match to the user story. Rather than inside – out (start with building blocks and compose to provide desired functionality), go from users outside-in, to reduce wasted coding; e.g., until get to user view, won’t know what you really need. Especially Web apps, since easy to see what the user is doing. Both cycles involve taking small steps and listening to the feedback you get from the tools. We start with a failing step (red) in Cucumber (the outer cycle). To get that step to pass, we’ll drop down to RSpec (the inner cycle) and drive out the underlying code at a granular level (red/green/refactor). At each green point in the RSpec cycle, we’ll check the Cucumber cycle. If it is still red, the resulting feedback should guide us to the next action in the RSpec cycle. If it is green, we can jump out to Cucumber, refactor if appropriate, and then repeat the cycle by writing a new failing Cucumber step. Passing (green) Cucumber step

Unit Tests Should Be FIRST Fast Independent Repeatable Self-checking Timely

Unit Tests Should Be FIRST Fast: run (subset of) tests quickly (since you’ll be running them all the time) Independent: no tests depend on others, so can run any subset in any order Repeatable: run N times, get same result (to help isolate bugs and enable automation) Self-checking: test can automatically detect if passed (no human checking of output) Timely: written about the same time as code under test (with TDD, written first!)

RSpec, a Domain-Specific Language for Testing DSL: small programming language that simpifies one task at expense of generality Examples: regex, SQL RSpec tests are called specs or examples Run the tests in one file: rspec filename Red failing, Green passing, Yellow pending Much better: running autotest Opinions vary on whether to test the views Our position: views are user-facing, so use user stories to test => Cucumber

☐ Only (a) Only (b) Both (a) and (b) Neither (a) nor (b) Which kinds of code can be tested Repeatably and Independently? a) Code that relies on randomness (e.g. shuffling a deck of cards) b) Code that relies on time of day (e.g. run backups every Sunday at midnight) Only (a) ☐ Only (b) ☐ ☐ Both (a) and (b) Cannot be tested repeatably. (You might be able to do a statistical analysis). Cannot be tested independently (unless you can stub the system clock). False True Neither (a) nor (b) ☐

Databases & Migrations (Engineering Software as a Service §4.2)

Your Customer Data is Golden! How do we avoid messing it up when experimenting/developing new features? How do we track and manage schema changes ? …the answer to both is automation! Database schema – organization of database

Multiple Environments, Multiple Databases Rails solution: development, production and test environments each have own DB Different DB types appropriate for each! How to make changes to DB, since will have to repeat changes on production DB? Rails solution: migration - script describing changes, portable across DB types

Migration Advantages Can identify each migration, and know which one(s) applied and when Many migrations can be created to be reversible Can manage with version control Automated == reliably repeatable Theme: don’t do it - automate it specify what to do, create tools to automate Development, test, production used for configuration

Meet a Code Generator rails generate migration CreateMovies Note, this just creates the migration. We must apply it. Apply migration to development: rake db:migrate Apply migration to production: heroku rake db:migrate Applying migration also records in DB itself which migrations have been applied http://pastebin.com/VYwbc5fq Rake the same as rails - starting from Rails 5 commands

Rails Cookery #1 Augmenting app functionality == adding models, views, controller actions To add a new model to a Rails app: (or change/add attributes of an existing model) Create a migration describing the changes: rails generate migration (gives you boilerplate) Apply the migration: rake db:migrate If new model, create model file app/models/model.rb Schema – tables and their contents

How can language features simplify design & implementation of design patterns? In this case, Active Record, which “bridges the gap” between in-memory Ruby objects & their stored representation in a database

Models: ActiveRecord Basics (Engineering Software as a Service §4.3)

CRUD in SQL “Ted” Codd Structured Query Language (SQL) is the query language used by RDBMS’s Rails generates SQL statements at runtime, based on your Ruby code 4 basic operations on a table row: Create, Read, Update attributes, Delete INSERT INTO users (username, email, birthdate) VALUES ("fox", "Fox@cs.berkeley.edu", "1968-05-12"), "patterson", "pattrsn@cs.berkeley.edu", "????") SELECT * FROM users WHERE (birthdate BETWEEN "1987-01-01" AND “2000-01-01”) UPDATE users SET email = "armandofox@gmail.com" WHERE username="fox" DELETE FROM users WHERE id=1

The Ruby Side of a Model Subclassing from ActiveRecord::Base “connects” a model to the database provides CRUD operations on the model Database table name derived from model’s name: Moviemovies Database table column names are getters & setters for model attributes Observe: the getters and setters do not simply modify instance variables! http://pastebin.com/ruu5y0D8 “getters” and “setters” do “lazy” evaluation, so you can compose several queries before they are executed on the DB – increases performance, but hidden from you Be careful of data types. String defaults to 255 chars, text to 64K. Long movie titles: Night of the Day of the Dawn of the Son of the Bridge of the Return of the Revenge of the Terror of the Attack of the Evil, Mutant, Alien, Flesh Eating, Hellbound, Zombified Living Dead Part 2: In Shocking 3-D - 169 chars Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb - 59 chars

Creating: new ≠ save Must call save or save! on an AR model instance to actually save changes to DB '!' version throws exception if operation fails create just combines new and save Once created, object acquires a primary key (id column in every AR model table) if x.id is nil or x.new_record? is true, x has never been saved These behaviors inherited from ActiveRecord:: Base - not true of Ruby objects in general Primary key is unique, even if you destroy the object and create another one, the primary key is not recycled.

Read: Finding Things in DB class method where selects objects based on attributes Movie.where("rating='PG'") Movie.where('release_date < :cutoff and rating = :rating', :rating => 'PG', :cutoff => 1.year.ago) Movie.where("rating=#{rating}") # BAD IDEA! Can be chained together efficiently kiddie = Movie.where("rating='G'") old_kids_films = kiddie.where("release_date < ?",30.years.ago) Pull out fields from strings, avoid SQL injection attacks

Read: find_* find by id: Movie.find(3) # exception if not found Movie.find_by_id(3) # nil if not found dynamic attribute-based finders: Movie.find_all_by_rating('PG') Movie.find_by_rating('PG') Movie.find_by_rating!('PG') Methods named for table columns Find_by_rating! – throw exception if not found Find_by_rating – return Nil if not found Find_by_* now deprecated, due to performance issues

Update: Two Ways Let m=Movie.find_by_title('The Help') Modify attributes, then save object m.release_date='2011-Aug-10' m.save! Update attributes on existing object m.update_attributes :release_date => '2011-Aug-10' Transactional: either all attributes are updated, or none are Save! – throws exception on failure

Deleting is Straightforward Note! destroy is an instance method m = Movie.find_by_name('The Help') m.destroy There’s also delete, which doesn’t trigger lifecycle callbacks - (so, avoid it) Once an AR object is destroyed, you can access but not modify in-memory object m.title = 'Help' # FAILS

Summary: ActiveRecord Intro Subclassing from ActiveRecord::Base “connects” a model to database C (save/create), R (where, find), U (update_attributes), D (destroy) Convention over configuration maps: model name to DB table name getters/setters to DB table columns Object in memory ≠ row in database! save must be used to persist destroy doesn’t destroy in-memory copy

may be undefined or implementation-dependent Suppose we’ve done movie = Movie.where("title='Amelie'") Then another app changes the movie’s title in the database table directly. Just after that instant, the value of movie: will be updated automatically because an ActiveRecord model “connects” your app to the database ☐ will be updated automatically because of ActiveRecord’s use of metaprogramming ☐ will not be updated automatically, but can be updated manually by re-executing movie = Movie.where("title='Amelie'") ☐ False. DB table doesn’t know about in-memory object. False. True. Have to pull update from DB. General issue is that want to minimize DB reads/writes. ☐ may be undefined or implementation-dependent

Controllers & Views (Engineering Software as a Service §4.4)

Rails Cookery #2 To add a new action to a Rails app Create route in config/routes.rb if needed Add the action (method) in the appropriate app/controllers/*_controller.rb Ensure there is something for the action to render in app/views/model/action.html.haml

MVC Responsibilities Model: methods to get/manipulate data Movie.where(...), Movie.find(...) Controller: get data from Model, make available to View def show @movie = Movie.find(params[:id]) end View: display data, allow user interaction Show details of a movie (description, rating) But… What else can user do from this page? How does user get to this page? Instance variables set in Controller available in View Absent other info, Rails will look for app/views/movies/show.html.haml %B – full month name, capitalized case %Y – full year (year with century, can be negative) %d – zero-padded day of month, 01..31 http://pastebin.com/kZCB3uNj

How We Got Here: URI helpers link_to movie_path(3) def show @movie = Movie.find(params[:id]) end index. html. haml http://guides.rubyonrails.org/routing.html <a href="/movies/3">...</a> GET /movies/:id {:action=>"show", :controller=>"movies"} params[:id]3

What Else Can We Do? How about letting user return to movie list? RESTful URI helper to the rescue again: movies_path with no arguments links to Index action =link_to 'Back to List', movies_path ESaaS, Fig. 4.7

Forms (Engineering Software as a Service §4.6)

Dealing with Forms Creating a resource usually takes 2 interactions new: Retrieve blank form create: Submit filled form How to generate/display? How to get values filled in by user? What to “return” (render)?

Rails Cookery #3 To create a new submittable form: Identify the action that serves the form itself Identify the action that receives submission Create routes, actions, views for each Form elements’ name attributes will appear as keys in params[] Helpers provided for many common elements

Creating the Form Anatomy of a form in HTML the action and method attributes (i.e., the route) only named form inputs will be submitted Generating the form in Rails often can use URI helper for action, since it’s just the URI part of a route (still need method) form field helpers (see api.rubyonrails.org) generate conveniently-named form inputs Use Erb or Haml http://pastebin.com/k8Y49EhE - how the form helpers are used to create a form - naming form fields as "movie[title]", "movie[rating]", etc. means you end up with params[:movies] as a hash ready to pass to create or update_attributes. This can lead to a security issue, discussed in Chapter 12. http://pastebin.com/3dGWsSq8

Erb or Haml Haml Erb Erb – very general, works for any text format e.g., HTML, JavaScript, but more verbose Haml is cleaner, there is another one – SLIM For your project, either is Ok, even mixed

When Things Go Wrong: Debugging (Engineering Software as a Service §4

Debugging SaaS Can Be Tricky “Terminal” (STDERR) not always available Errors early in flow may manifest much later URIroutecontrollermodelviewrender Error may be hard to localize/reproduce if affects only some users, routes, etc. What Dev? Prd? Printing to terminal (“printf debugging”) ✔ Logging Interactive debugging Dev == development Prd == production

RASP Debugging is a fact of life. Read the error message. Really read it. Ask a colleague an informed question. Search using StackOverflow, a search engine, etc. Especially for errors involving specific versions of gems, OS, etc. Post on StackOverflow, class forums, etc. Others are as busy as you. Help them help you by providing minimal but complete information

Reading Ruby Error Messages The backtrace shows you the call stack (where you came from) at the stop point A very common message: undefined method 'foo' for nil:NilClass Often, it means an assignment silently failed and you didn’t error check: @m = Movie.find_by_id(id) # could be nil @m.title # will fail: 'undefined method' find_by_* now deprecated, too much performance hit, use find() which throws exception, which you must handle

Instrumentation (a.k.a. “Printing the values of things”) In views: = debug(@movie) = @movie.inspect In the log, usually from controller method: logger.debug(@movie.inspect) In log/development.log Use debug, info, warn, error, fatal methods to control amount of logging Don’t just use puts or printf! It has nowhere to go when in production. Debug – render object in YAML Inspect – render object value as string YAML – YAML Ain’t Markup Language - human friendly data serialization standard for all programming languages, converts object to readable form Adjust amount of logging depending on development, testing, production using log level, logger.debug, logger.info, logger.warn, logger.error, logger.fatal. Default level is debug in debugging mode, and info in test and production mode (so info, warn, error, fatal are written out).

If you use puts or printf to print debugging messages in a production app: Your app will raise an exception and grind to a halt ☐ Your app will continue, but the messages will be lost forever ☐ Your app will continue, and the messages will go into the log file ☐ False True, they are written to the console, which does not exist, unless stdout, stderr are redirected to a file. False, only if you use logging methods or redirect stdout/stderr. False. The SaaS gods will strike you down in a fit of rage ☐

Search: Use the Internet to Answer Questions Search for it “How do I format a date in Ruby?” “How do I add Rails routes beyond CRUD?” Check the documentation http://api.rubyonrails.org, complete searchable Rails docs http://ruby-doc.org, complete searchable Ruby docs (including standard libraries) Check StackOverflow http://stackoverflow.com Don’t feel need to invest in reference books unless you find yourself looking at them a lot.

Fat Views (No!) No! That’s for model, controller, helpers “All I need is this for-loop” “....and this extra code to sort the list of movies differently” “...and this conditional, in case user is not logged in” No! That’s for model, controller, helpers The view should focus on displaying content and facilitating user input The controller should focus on mediating between the view and the model and set up necessary variables, e.g., @movies = Movie.all

Only (a) and (b) Only (b) Only (b) and (c) Only (a) and (c) Which steps are ALWAYS required when adding a new action 'foo' to the Movie model of a Rails app: (a) Ensure there is a template to render in app/views/movies/foo.html.haml (or .html.erb, etc) (b) Ensure a route exists in config/routes.rb (c) Implement helper method to generate necessary route-helper URIs Only (a) and (b) ☐ Only (b) ☐ By convention over configuration, the foo action will render foo.html.xxx. A route is needed to specify a controller action. Helper methods are optional. They generate URIs, but the original URIs can be used. In the Rails framework, helper methods are automatically generated. True. False. Only (b) and (c) ☐ ☐ Only (a) and (c)

DRYing Out MVC (ESaaS §5.1)

Don’t Repeat Yourself – But How? Goal: enforce that movie names must be less than 40 characters Call a “check” function from every place in app where a Movie might get created or edited? That’s not DRY! How do we DRY out cross-cutting concerns: Logically centralized, but may appear multiple places in implementation? NOTE: We are not solving the problem of how to abbreviate longer movie names. Most likely solution would be to truncate, or let user try to abbreviate.

Aspect-Oriented Programming Advice is a specific piece of code that implements a cross-cutting concern Pointcuts are the places you want to “inject” advice at runtime Advice+Pointcut = Aspect Goal: DRY out your code

Rails Example: Validations Specify declaratively in model class Validation is advice in AOP sense Many places in app where a model could be modified/updated Including indirectly via associations! Don’t want model validation code in all these places So where are the pointcuts? http://pastebin.com/2GtWshSb AOP – Aspect-Oriented Computing declare a simple validation, then violate it using script/console look at errors, errors.full_messages, errors.on(‘last_name’), errors.on(‘first_name’) look at errors.empty? on a valid object INTERACTIVELY: when validation fails: redirect/retry a view, AND, warn the user what the errors were. validates_uniqueness_of: show flow thru controller, error message printing, redirection modify the error msg validates_numericality_of; allow_nil vs allow_blank create a migration to add a ‘major’ field change edit, index, new, show to account for new field validates_inclusion_of - we’ll come back to it later with select

Model Lifecycle Callbacks Allows Pre and Post Operations Validation automatically happens here • or when you call valid? • if fail, save will fail model.errors is an ActiveRecord::Errors object with cool behaviors of its own • See Screencast 7.1.1 BUGS: "existing record" and "new record" labels swapped; "update_attribute" doesn't check validations

Example: Controller Filters Filters declared in a controller also apply to its subclasses Corollary: filters in ApplicationController apply to all controllers A filter can change the flow of execution by calling redirect_to or render You should add something to the flash to explain to the user what happened, otherwise it will manifest as a “silent failure” http://pastebin.com/ybP6Ece1

Validations vs. Filters Advice (DRYness) Check invariants on model Check conditions for allowing controller action to run Pointcut AR model lifecycle hooks Before and/or after any public controller method Can change execution flow? No Yes Can define advice in arbitrary function? Yes; shortcuts provided for common cases Yes, must provide function Info about errors? Each model object has associated errors object Capture in flash[], session[], or instance variable AR - ActiveRecord Pattern: provide object to capture error state at any given call site Con: Can make code harder to debug

Summary So Far Aspect-oriented programming (AOP) is a way of DRYing out cross-cutting concerns Ruby doesn’t have fully-general AOP, but Rails provides some “predefined” pointcuts Validations check or assert pre/post conditions at key points during model lifecycle Controller filters check or assert pre/post conditions related to controller actions And can change control flow (redirect, render) Partials DRY out views (though not AOP)

Session By default, cookies store entire contents of session & flash session[]: like a hash that persists forever reset_session nukes the whole thing session.delete(:some_key), like a hash By default, cookies store entire contents of session & flash Alternative: store Rails sessions in DB table (Search “rails session use database table”) Another alternative: store sessions in a “NoSQL” storage system, like memcached  in prev lecture we briefly mentioned "NoSQL" storage solutions like sharding. session storage is a great use case for this since there is no sharing.  hence, valuable that rails provides a SEPARATE abstraction for it.

Flash Idiom: redirect user to a more useful page e.g., list of movies, if create is successful e.g., New Movie form, if unsuccessful Redirect triggers a whole new HTTP request How to inform user why they were redirected? Solution: flash[]—quacks like a hash that persists until end of next request flash[:notice] conventionally for information flash[:warning] conventionally for “errors” Redirect_to flash notice will send a redirect to the user’s browser telling it to re-request a new URL. Then the browser will send a new request to that URL and it will go through the action for that URL Render render will render a particular view using the instance variables available in the action. some actions, like create and update, COULD render their own view but that wouldn't be very helpful for the user, so it's idiomatic to use redirect_to to send the user elsewhere. since redirect starts a new http request, we need flash[] . “errors” in quotes because if, e.g., Movie creation fails, the *user* may have made an error but your *app* is working correctly. hence the name 'warning' note, you can put other keys in flash[], these are just the conventional ones. For New Movie form, note that movie was created, or note errors - movie already exists in RP (perhaps with different rating/date), or else the date is illegal (February 30).

True - knock yourself out! Ben Bitdiddle says: “You can put arbitrary objects (not just “simple” ones like ints and strings) into the session[].” What do you think? True - knock yourself out! ☐ True - but a bad idea! ☐ False, because you can’t put arbitrary objects into a hash ☐ False. See the pitfall at the end of Chapter 4. True. See the pitfall at the end of Chapter 4. False. The session operates like a hash, which are key/value pairs, and the value can be any Ruby object type. False. The session can be used like a hash. False, because session[] isn’t really a hash, it just quacks like one ☐

Single Sign-On and Third-Party Authentication (ESaaS §5.2)

Third-Party Authentication Goal: What are my Facebook friends reading on the NY Times site? NY Times needs to be able to access your Facebook info …but you don’t want to reveal your Facebook password to NY Times! How can we do this? => Third-party authentication

How Does It Work? (Concepts) Building block: tamper-evident secure token Using cryptography, I create a string that: Only I can decrypt (decode) I can detect if it’s been tampered with No one else could have created it without knowing my secret key Usually, string just contains a “handle” to valuable info that I store myself Receive string => I know I can “trust” the handle

Third-Party Authentication with Twitter & RottenPotatoes 1. “Login with Twitter” 2. Redirect to Twitter login page 3. “OK to authorize this app?” Logos shown for educational purposes only and are the intellectual property of their owners.

Third-Party Authentication with Twitter & RottenPotatoes 4. Yes, please give away my personal info 7. “Welcome, Armando” 5. Redirect to RP callback page with access token RP – RottenPotatoes When you click the "Authorize" button, it creates an "Access Token" and an "Access Token Secret". These are like passwords, but they only allow Tweetgif to access your account and do the things you've allowed it to do. 6. Here’s a token that proves I’m allowed to know this user’s name Logos shown for educational purposes only and are the intellectual property of their owners.

Which is true about third-party authentication between a requester and a provider? Once completed, the requester can do anything you can do on the provider ☐ If your credentials on the requester are compromised, your credentials on the provider are also compromised ☐ ☐ If the provider revokes access, the requester no longer has any of your info 1. False. It depends on which privileges were granted to the requester. 2. False. The login happens on the provider. The requester only receives a token. 3. False. The provider cannot take back what the requester has in storage. 4. True. The token can expire. Access can be time-limited to expire on a pre-set date ☐

Cross-Site Request Forgery Alice has logged in bank.com and wishes to transfer $100 to Bob GET http://bank.com/transfer.do?acct=BOB&amount=100 HTTP/1.1 Maria, an attacker, wants to trick Alice into sending the money to her instead <a href="http://bank.com/transfer.do?acct=MARIA&amount=100000">View my Pictures!</a> Alice is tricked to click Maria’s link (with valid cookies) Also work for POST using forms & JavaScripts Nonce – number used once When accessing the attack link to example.com, the browser would also always automatically send any existing cookies for that domain, which makes CSRF attacks to execute hostile actions as long as the user is logged in to that website at the time of the attack. XSRF: can forge request – change password? Download??... ?? Can it get user’s information?? https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)

Cross-Site Request Forgery Preventions: include session nonce (a secret and unique value) with every request csrf_meta_tags in layouts/application.html.haml protect_from_forgery in ApplicationController Rails form helpers automatically include nonce in forms token="KbyUmhTLMpYj7CD2di7JKP1P3qmLlkPt" Nonce – number used once When accessing the attack link to example.com, the browser would also always automatically send any existing cookies for that domain, which makes CSRF attacks to execute hostile actions as long as the user is logged in to that website at the time of the attack. XSRF: can forge request – change password? Download??... ?? Can it get user’s information?? GET http://bank.com/transfer.do?acct=BOB&amount=100&token HTTP/1.1

SQL Injection View: = text_field_tag 'name' App: Moviegoer.where("name='#{params[:name]}'") Evil user fills in: BOB'); DROP TABLE moviegoers; -- Executes this SQL query: SELECT * FROM moviegoers WHERE (name='BOB'); DROP TABLE moviegoers; --' Solution: Moviegoer.where("name=?", params[:name]) xkcd.com/327

Associations & Foreign Keys (ESaaS §5.3)

Reviews for RottenPotatoes Simple model: “I give it 4 potatoes out of 5” Goal: easily represent the concept that movie has many reviews The code we’d like to write…but how? http://pastebin.com/gU1hqm77

Cartesian Product table 'artists' table 'reviews' id name desc artist_id 10 Justin 30 "Terrible" 12 11 Shakira 31 "Passable" Britney 32 "Please" Cartesian product: artists JOIN reviews artists.id artists.name reviews.id reviews.desc reviews.artist_id Filtered Cartesian product: artists JOIN reviews ON artists.id = reviews.artist_id

Expressing “Has Many” in Terms of Relational DB Model foreign key (FK) in one table refers to the primary key (PK) of another table reviews id* movie_id potatoes movies id title rating release_date Primary Key is the unique value (in the first column) associated with each table row, e.g. the id.

Databases 101 joins are queries that combine records from 2 or more tables using PKs and FKs reviews id movie_id ... movies id ... PK – primary key FK – foreign key Cartesian product SELECT * FROM movies, reviews WHERE movies.id = reviews.movie_id

Which statement is false regarding Cartesian products as a way of representing relationships? You can represent one-to-one relationships as well as one-to-many relationships ☐ ☐ You can represent many-to-many relationships The size of the full Cartesian product is independent of the join criteria ☐ 1. True. If only one foreign key matches a primary key. 2. True. See Figure 5.12. 3. True. The size of a full join depends on the size of the tables in the join, e.g. tables with M and N rows produces MxN rows. The join criteria may filter this way down. 4. False. You can filter based on attribute values. The natural join is all the rows in the two tables with common attribute values. It is not limited to primary and foreign keys. You can only filter based on primary or foreign key (id) columns ☐

ActiveRecord Association Support (ESaaS §5.3)

ActiveRecord Associations Allows manipulating DB-managed associations more Rubyistically After setting things up correctly, you don't have to worry (much) about keys and joins class Movie < ActiveRecord::Base has_many :reviews end class Review < ActiveRecord::Base belongs_to :movie The :: is a unary operator that allows: constants, instance methods and class methods defined within a class or module, to be accessed from anywhere outside the class or module. Remember: in Ruby, classes and methods may be considered constants too. You need just to prefix the :: Const_name with an expression that returns the appropriate class or module object. If no prefix expression is used, the main Object class is used by default. ActiveRecord::Base references the Base class methods in ActiveRecord “The foreign key belongs to me” 92

Basic Idea… reviews table gets a foreign key (FK) field that has PK of Movie the review is about Dereference movie.reviews == perform database join (lazily) to find reviews where movie_id == movie.id Dereference review.movie == look up one movie whose PK id == review.movie_id Note! Must add FK fields using a migration! http://pastebin.com/hfvramxQ

Association Proxy Methods Now you can say: @movie.reviews # Enumerable of reviews And also go the other way: @review.movie # what movie is reviewed? You can add new reviews for a movie: @movie = Movie.where("title='Fargo'") @movie.reviews.build(:potatoes => 5) @movie.reviews.create(:newspaper=>'Chronicle', ...) # how are these different from just new() & create()? @movie.reviews << @new_review # instantly updates @new_review's FK in database! @movie.reviews.find(:first,:conditions => '...') These methods are actually lazy.

Any of (a), (b), or (c) would be equally suitable ☐ Which Ruby language mechanisms would be appropriate for implementing associations that can be used by ActiveRecord models? (a) build behaviors into ActiveRecord::Base (b) put behaviors in their own Module (c) put behaviors in their own Class Only (a) ☐ (a) or (b), but not (c) ☐ A – this puts associations into the ActiveRecord::Base class. B – This puts associations into a module that can be used by any ActiveRecord class, e.g. ActiveRecord::Migration. They are in fact implemented as a module ActiveRecord::Associations, included by ActiveRecord::Base. C – This puts associations into their own ActiveRecord class. 1. True. But A is not very DRY. 2. True. C is not very DRY, since must include that class everywhere. 3. False. It uses modules. 4. False. C is not very DRY. ☐ (a) or (c), but not (b) Any of (a), (b), or (c) would be equally suitable ☐

Associations: Mechanics (ESaaS §5.3)

How Does It Work? Models must have attribute for foreign key of owning object e.g., movie_id in reviews table ActiveRecord manages this field in both database & in-memory AR object Don’t manage it yourself! Harder to read May break if database schema doesn’t follow Rails conventions

Rails Cookery #4 To add a one-to-many association: Add has_many to owning model and belongs_to to owned model Create migration to add foreign key to owned side that references owning side Apply migration rake db:test:prepare to regenerate test database schema

We can say movie.reviews, but review.movie won’t work ☐ Suppose we have setup the foreign key movie_id in reviews table. If we then add has_many :reviews to Movie, but forget to put belongs_to :movie in Review, what happens? We can say movie.reviews, but review.movie won’t work ☐ We will get a database error when trying to save a Review ☐ 1. True. The has_many will set up the getter in Movie, but there will be no getter in Review. 2. True. The foreign key won’t be set. 3. True. We won’t have the foreign key, and the movie name is not unique. 4. True ☐ We will have no way of determining which movie a given review is associated with All of the above ☐

Through-Associations (ESaaS §5.4)

Many-to-Many Associations Scenario: Moviegoers rate Movies a moviegoer can have many reviews but a movie can also have many reviews Why can’t we use has_many & belongs_to? Solution: create a new AR model to model the multiple association

Many-to-Many moviegoer: has_many :reviews movie: has_many :reviews id reviews moviegoer_id movie_id number movies id ... moviegoer: has_many :reviews movie: has_many :reviews review: belongs_to :moviegoer belongs_to :movie How to get all movies reviewed by some moviegoer?

has_many :through moviegoers id reviews moviegoer_id movie_id ... movies id ... moviegoer: has_many :reviews has_many :movies, :through => :reviews movie: has_many :reviews has_many :moviegoers, :through => :reviews reviews: belongs_to :moviegoer belongs_to :movie Why is there no review_id field in the movie table?

Through Now you can do: My potato scores for R-rated movies @user.movies # movies rated by user @movie.users # users who rated this movie My potato scores for R-rated movies @user.reviews.select { |r| r.movie.rating == 'R' } Example: show => vouchers

has_many :through moviegoers id reviews moviegoer_id movie_id ... movies id ... @user.movies SELECT * FROM movies JOIN moviegoers ON reviews.moviegoer_id = moviegoers.id JOIN movies ON reviews.movie_id = movies.id Why is there no review_id field in the movie table?

Which of these, if any, is NOT a correct way of saving a new association, given m is an existing movie: Review.create!(:movie_id=>m.id, :potatoes=>5) ☐ r = m.reviews.build(:potatoes => 5) r.save! ☐ ☐ m.reviews << Review.new(:potatoes=>5) m.save! 1. Works. But definitely not desirable. 2. Works. The save is needed. 3. Works. Adds new review to list of m’s reviews and saves it. The save is redundant. 4. All work. All will work ☐

Shortcut: Has and Belongs to Many (habtm) join tables express a relationship between existing model tables using FKs Join table has no primary key because there’s no object being represented! movie has_and_belongs_to_many :genres genre has_and_belongs_to_many :movies @movie.genres << Genre.find_by_name('scifi') http://pastebin.com/tTVGtNLx movies id name ...etc. genres id description genres_movies genre_id movie_id

Rules of Thumb If you can conceive of things as different real-world objects, they should probably be distinct models linked through an association If you don’t need to represent any other aspect of a M-M relationship, use habtm Otherwise, use has_many :through

HABTM Naming Conventions M-M relationship naming convention: if a Bar has_and_belongs_to_many :foos then a Foo has_and_belongs_to_many :bars and the database table is the plural AR names in alphabetical order bars_foos

Faculty has-many appointments, Student has-many appointments ☐ We want to model students having appointments with faculty members. Our model would include which relationships: Faculty has-many appointments, Student has-many appointments ☐ Faculty HABTM Students, Students HABTM Faculty ☐ ☐ Faculty belongs-to appointment, Student belongs-to appointment 1. True. Similar to a movie has-many reviews and a moviegoer has-many reviews. 2. False. We want appointments as a separate object. 3. False. The belongs-to goes the other way. 4. False. Faculty has-many students through appointments. Faculty has-many appointments, through Students ☐

RESTful Routes for Associations (ESaaS §5.5)

Creating/Updating Through-Associations When creating a new review, how to keep track of the movie and moviegoer with whom it will be associated? Need this info at creation time But route helpers like new_movie_path (provided by resources :movies in routes file) only “carry around” the ID of the model itself

Nested RESTful Routes in config/routes.rb: resources :movies becomes resources :movies do resources :reviews end Nested Route: access reviews by going ”through” a movie

Nested RESTful Routes available as params[:movie_id] Why do you need movie_id when creating a review? => nested RESTful routes manage it for you by making it part of URL. but don't HAVE to do it this way. available as params[:movie_id] available as params[:id]

ReviewsController#create # POST /movies/1/reviews # POST /movies/1/reviews.xml def create # movie_id because of nested route @movie = Movie.find(params[:movie_id]) # build sets the movie_id foreign key automatically @review = @movie.reviews.build(params[:review]) if @review.save flash[:notice] = 'Review successfully created.' redirect_to(movie_reviews_path(@movie)) else render :action => 'new' end to what action does movie_reviews_path point? why is it OK to render :action => 'new' on failure?

ReviewsController#new # GET /movies/1/reviews/new def new # movie_id because of nested route @movie = Movie.find(params[:movie_id]) # new sets movie_id foreign key automatically @review ||= @movie.reviews.new @review = @review || @movie.reviews.new end Another possibility: do it in a before-filter to what action does movie_reviews_path point? why is it OK to render :action => 'new' on failure? See http://www.rubyinside.com/what-rubys-double-pipe-or-equals-really-does-5488.html for a discussion of a ||= b. It is not exactly the same as a = a || b. If a is not nil/false, no action is taken, otherwise a = b. So it acts as a test and set operator – if @review is nil (no review has been created), then one is created (@movies.reviews.new). before_filter :lookup_movie def lookup_movie @movie = Movie.find_by_id(params[:movie_id]) || redirect_to movies_path, :flash => {:alert => "movie_id not in params"} end

Views %h1 Edit = form_tag movie_review_path(@movie,@review), :method => :put do |f| ...Will f create form fields for a Movie or a Review? = f.submit "Update Info" = link_to 'All reviews for this movie', movie_reviews_path(@movie) Remember, these are for convenience. Invariant is: review when created or edited must be associated with a movie.

If we also have moviegoer has_many reviews, can we use moviegoer_review_path() as a helper? Yes, it should work as-is because of convention over configuration ☐ Yes, but we must declare reviews as a nested resource of moviegoers in routes.rb ☐ ☐ No, because there can be only one RESTful route to any particular resource 1. False. 2. True. See Figure 5.19. /moviegoers/:moviegoer_id/reviews 3. False. You can have several routes to the same resource. 4. False. This is not a through association, as reviews belong-to moviegoers. No, because having more than one through-association involving Reviews would lead to ambiguity ☐

DRYing Out Queries with Reusable Scopes (ESaaS §5.6)

“Customizing” Associations with Declarative Scopes Movies appropriate for kids? Movies with at least N reviews? Movies with at least average review of N? Movies recently reviewed? Combinations of these?

Scopes Can Be “Stacked” Movie.for_kids.with_good_reviews(3) Movie.with_many_fans.recently_reviewed Scopes are evaluated lazily! http://pastebin.com/BW40LAHX

Where do database queries happen? 1 # in controller: 2 def good_movies_for_kids 3 @m = Movie.for_kids.with_good_reviews(3) 4 end 5 # in view: 6 - @m.each do |movie| 7 %p= pretty_print(movie) Where do database queries happen? Line 3 only ☐ Lines 6-7 only ☐ The ActiveRecord database queries are lazy. The only time that the access is required is when the value must be known, which is in lines 6 (determine whether there are any such movies and get the next one) and line 7 (print it out). False True True. If no movies are returned, then line 7 is never executed. ☐ Line 3 AND lines 6-7 Depends on return value of for_kids ☐

Associations Wrap-Up (ESaaS §5.7-5.9)

Associations Wrap-Up Associations are part of application architecture provides high-level, reusable association constructs that manipulate RDBMS foreign keys Mix-ins allow Associations mechanisms to work with any ActiveRecord subclass Proxy methods provide Enumerable-like behaviors A many-fold association quacks like an Enumerable Proxy methods are an example of a design pattern Nested routes help you maintain associations RESTfully - but they’re optional, and not magic

Elaboration: DataMapper Data Mapper associates separate mapper with each model Idea: keep mapping independent of particular data store used => works with more types of databases Used by Google AppEngine Con: can’t exploit RDBMS features to simplify complex queries & relationships

Referential Integrity What if we delete a movie with reviews? movie_id field of those reviews then refers to nonexistent primary key another reason primary keys are never recycled Various possibilities depending on app... delete those reviews? has_many :reviews, :dependent => :destroy make reviews “orphaned”? (no owner) has_many :reviews, :dependent => :nullify Can also use lifecycle callbacks to do other things (e.g., merging)

Testing Referential Integrity it "should nuke reviews when movie deleted" do @movie = @movie.create!(...) @review = @movie.reviews.create!(...) review_id = @review.id @movie.destroy end lambda { Review.find(review_id) }.should raise_error(ActiveRecord::RecordNotFound)

If using the DataMapper pattern and you want to do one-to-many associations, you can expect: to have to write the association methods yourself ☐ ☐ Better scalability ☐ Worse scalability 1. True. The mapper methods provide the associations. 2. True. You can use noSQL databases. 3. True. If the associations are well-mapped to relational databases, then it might be very fast, especially with custom RDBMS hardware. 4. True. All of the above are possible ☐

Advanced Topics Single-Table Inheritance (STI) & Polymorphic Associations Self-referential has_many :through Many declarative options on manipulating associations (like validations) To learn (much) more: http://guides.rubyonrails.org/association_basics.html The Rails Way, Chapter 9