Presentation is loading. Please wait.

Presentation is loading. Please wait.

Rails Philip Ritchey slides generously gifted by Jeff Huang.

Similar presentations


Presentation on theme: "Rails Philip Ritchey slides generously gifted by Jeff Huang."— Presentation transcript:

1 Rails Philip Ritchey slides generously gifted by Jeff Huang

2 Hello Rails (Engineering Software as a Service §4)
Getting started on Rails:

3 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.

4 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

5 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: config/routes.rb GET /movies/:id {:action=>'show',:controller=>'movies'}

6 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)

7 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

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

9 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

10

11 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

12 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

13 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

14 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

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

16 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!)

17 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

18 ☐ 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)

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

20 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

21 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

22 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

23 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 Rake the same as rails - starting from Rails 5 commands

24 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

25 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

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

27 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, , birthdate) VALUES ("fox", " "), "patterson", "????") SELECT * FROM users WHERE (birthdate BETWEEN " " AND “ ”) UPDATE users SET = WHERE username="fox" DELETE FROM users WHERE id=1

28 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! “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 chars Dr. Strangelove or: How I Learned to Stop Worrying and Love the Bomb - 59 chars

29 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.

30 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

31 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

32 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

33 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

34 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

35 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

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

37 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

38 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,

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

40 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

41 Forms (Engineering Software as a Service §4.6)

42 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)?

43 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

44 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 - 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.

45 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

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

47 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

48 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

49 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

50 Instrumentation (a.k.a. “Printing the values of things”)
In views: = In the log, usually from controller method: 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).

51 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

52 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 complete searchable Rails docs complete searchable Ruby docs (including standard libraries) Check StackOverflow Don’t feel need to invest in reference books unless you find yourself looking at them a lot.

53 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, = Movie.all

54 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)

55 DRYing Out MVC (ESaaS §5.1)

56 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.

57 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

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

59 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

60 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”

61 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

62 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)

63 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.

64 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).

65 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

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

67 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

68 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

69 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.

70 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.

71 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

72 Cross-Site Request Forgery
Alice has logged in bank.com and wishes to transfer $100 to Bob GET HTTP/1.1 Maria, an attacker, wants to trick Alice into sending the money to her instead <a href=" 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??

73 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/1.1

74 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

75 Associations & Foreign Keys (ESaaS §5.3)

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

77 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

78 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.

79 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

80 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

81 ActiveRecord Association Support (ESaaS §5.3)

82 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

83 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!

84 Association Proxy Methods
Now you can # Enumerable of reviews And also go the other # 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 # instantly FK in database! @movie.reviews.find(:first,:conditions => '...') These methods are actually lazy.

85 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

86 Associations: Mechanics (ESaaS §5.3)

87 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

88 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

89 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

90 Through-Associations (ESaaS §5.4)

91 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

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

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

94 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

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

96 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

97 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') movies id name ...etc. genres id description genres_movies genre_id movie_id

98 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

99 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

100 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

101 RESTful Routes for Associations (ESaaS §5.5)

102 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

103 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

104 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]

105 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]) flash[:notice] = 'Review successfully created.' else render :action => 'new' end to what action does movie_reviews_path point? why is it OK to render :action => 'new' on failure?

106 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 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 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 – is nil (no review has been created), then one is created 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

107 Views %h1 Edit = form_tag :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', Remember, these are for convenience. Invariant is: review when created or edited must be associated with a movie.

108 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 /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

109 DRYing Out Queries with Reusable Scopes (ESaaS §5.6)

110 “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?

111 Scopes Can Be “Stacked”
Movie.for_kids.with_good_reviews(3) Movie.with_many_fans.recently_reviewed Scopes are evaluated lazily!

112 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 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

113 Associations Wrap-Up (ESaaS §5.7-5.9)

114 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

115 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

116 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)

117 Testing Referential Integrity
it "should nuke reviews when movie deleted" = end lambda { Review.find(review_id) }.should raise_error(ActiveRecord::RecordNotFound)

118 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

119 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: The Rails Way, Chapter 9


Download ppt "Rails Philip Ritchey slides generously gifted by Jeff Huang."

Similar presentations


Ads by Google