DRYing Out MVC (ESaaS §5.1) © 2013 Armando Fox & David Patterson, all rights reserved 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? Background & History: GO TO & COME FROM • CACM, 1968 Letter to Editor 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 http://pastebin.com/2GtWshSb • 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? Model Lifecycle Callbacks Allows Pre and Post Operations movie.create movie.update_attributes movie.save (new record) movie.save (existing record) before_validation before_validation before_validation_on_create before_validation_on_update Run validations Run validations movie. rb after_validation after_validation after_validation_on_create after_validation_on_update before_save before_save before_create before_update INSERT INTO movies... Validation automatically happens here UPDATE movies... after_create after_update after_save after_save • 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 Example: Controller Filters • Filters declared in a controller also apply to http://pastebin.com/ybP6Ece1 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” Validations vs. Filters Validation Filter 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 Yes; shortcuts provided arbitrary function? for common cases Yes, must provide function Info about errors? Capture in flash, session, or instance variable Each model object has associated errors object Con: Can make code harder to debug 10 Single Sign-On and Third-Party Authentication (ESaaS §5.2) © 2013 Armando Fox & David Patterson, all rights reserved 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 Logos shown for educational purposes only and are the intellectual property of their owners. 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 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. How Does It Work? (MVC) • Model session as its own entity – session controller creates and deletes session, handles interaction with authentication provider • Once user is authenticated, we need a local users model to represent him/her – session remembers primary key (ID) of “currently authenticated user” • OmniAuth gem helps a lot by providing uniform API to different “strategies” 21 Associations & Foreign Keys (ESaaS §5.3) © 2013 Armando Fox & David Patterson, all rights reserved 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' id name 10 11 12 Justin Shakira Britney table 'reviews' id desc 30 31 32 "Terrible" "Passable" "Please" artist_id 12 11 10 Cartesian product: artists JOIN reviews artists.id 10 10 10 artists.name Justin Justin Justin reviews.id 30 31 32 reviews.desc reviews.artist_id "Terrible" 12 "Passable" 11 "Please" 10 11 11 11 12 Shakira Shakira Shakira Britney 30 31 32 30 "Terrible" "Passable" "Please" "Terrible" 12 11 10 12 12 12 Britney Britney 31 32 "Passable" "Please" 11 10 Filtered Cartesian product: artists JOIN reviews ON artists.id = reviews.artist_id artists.id 10 11 12 artists.name Justin Shakira Britney reviews.id 32 31 30 reviews.desc reviews.artist_id "Please" 10 "Passable" 11 "Terrible" 12 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 movies id* id movie_id title potatoes rating release_date Databases 101 • joins are queries that combine records from 2 or more tables using PKs and FKs reviews movies id id movie_id ... ... Cartesian SELECT * product FROM movies, reviews WHERE movies.id = reviews.movie_id 29 ActiveRecord Association Support (ESaaS §5.3) © 2013 Armando Fox & David Patterson, all rights reserved 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 foreign key belongs33to me” end 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 => '...') 36 Administrivia • Hold team meetings next week – Meet with your team in ETB 2005 during Tue and Thu class time • TAs will circulate to get project status update from you, provide advice • Highlight any issues you have so far!
© Copyright 2020