Presentation is loading. Please wait.

Presentation is loading. Please wait.

Forms (Engineering Software as a Service §4.6)

Similar presentations


Presentation on theme: "Forms (Engineering Software as a Service §4.6)"— Presentation transcript:

1 Forms (Engineering Software as a Service §4.6)
© 2013 Armando Fox & David Patterson, all rights reserved

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

3 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

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

5 END

6 Which of these would be valid for generating the form that, when submitted, would call the Create New Movie action? = form_tag movies_path do end %form{:action => movies_path, :method => :post} %form{:action => '/movies', :method => 'post'} This will create a form with the /movies path and HTTP method post. Does the same thing. Movies_path evaluates to ‘/movies’. Does the same thing, since we can use the symbol :post or string ‘post’. Ultimately it will be converted to a string in HTML. All of the above is correct. All of the above

7 END

8 © 2013 Armando Fox & David Patterson, all rights reserved
Redirection, the Flash and the Session (Engineering Software as a Service §4.7) © 2013 Armando Fox & David Patterson, all rights reserved

9 Receiving the Form A neat trick: use debugger to inspect what’s going on start with rails server --debugger insert debugger where you want to stop details & command summary: ESaaS §4.7 NOTE: params[:movie] is a hash, because of the way we named form fields Conveniently, just what Movie.create! wants use ruby-debug to help understand what's going on - set a breakpoint right at the top of MoviesController#create, show that you can print out params[:movies], etc

10 What View Should Be Rendered for Create Action?
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” 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).

11 Flash & Session 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.

12 END

13 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

14 END

15 Administrivia Get account for “public projects” on PivotalTracker.com, add me, TA Public projects are free Do not get 30 day evaluation account Get account on CodeClimate.com Provides code analysis Free for an open source (OSS) repo Sign in with your GitHub account

16 END

17 Finishing CRUD (Engineering Software as a Service §4.8)
© 2013 Armando Fox & David Patterson, all rights reserved

18 Edit/Update Pair is Analogous to New/Create Pair
What’s the same? 1st action retrieves form, 2nd action submits it “submit” uses redirect (to show action for movie) rather than rendering its own view What’s different? Form should appear with existing values filled in: retrieve existing Movie first Form action uses PUT rather than POST

19 Destroy is Easy Remember, destroy is an instance method
Find the movie first...then destroy it Send user back to Index def destroy @movie = Movie.find(params[:id]) @movie.destroy flash[:notice] = "Movie deleted." redirect_to movies_path end Note is still in memory after it is destroyed, so can still apply title method.

20 END

21 If you set an instance variable in a controller method, its value will be retained for how long?
This request and all subsequent requests Only this request and the next request Only this request - once the view is rendered, the variable is reset to nil The instance variable will be retained as long as the controller instance is retained. The controller instance is available while the view is rendered (so the view can use the controller instance variables). If you want to save a value, you need to put it in the database or session[]. False. The controller instance is associated with this request. This might be confused with session[]. False. That is the flash[]. True. The controller instance is only active until the view is rendered. False. Ruby does not have static instance variables, in that the instance variables are only local to the instance and so disappear with the instance. It depends on whether the instance variable was declared static

22 END

23 © 2013 Armando Fox & David Patterson, all rights reserved
Fallacies, Pitfalls, and Perspectives on SaaS-on-Rails (Engineering Software as a Service § ) © 2013 Armando Fox & David Patterson, all rights reserved

24 Fat Controllers & Fat Views
Really easy to fall into “fat controllers” trap Controller is first place touched in your code Temptation: start coding in controller method Fat views “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

25 Designing for Service-Oriented Architecture
A benefit of thin controllers & views: easy to retarget your app to SOA Typically, SOA calls will expect XML or JSON (JavaScript Object Notation, looks like nested hashes) instead of HTML A trivial controller change accomplishes this We have talked about benefits of SOA, and how proper MVC separation makes it easier to turn a user-facing app into a service. Pastebin example shows use of controllers' respond_to() method to turn RottenPotatoes into a RESTful service.

26 END

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

28 END

29 © 2013 Armando Fox & David Patterson, all rights reserved
Introducing Cucumber & Capybara (Engineering Software as a Service §7.6) © 2013 Armando Fox & David Patterson, all rights reserved

30 User Stories => Acceptance Tests?
Wouldn’t it be great to automatically map 3x5 card user stories into tests for user to decide if accept the app? How would you match the English text to test code? How could you run the tests without a human in the loop to perform the actions? Need to map from user stories to acceptance tests for test-driven design

31 Cucumber: Big Idea Tests from customer-friendly user stories
Acceptance: ensure satisfied customer Integration: ensure interfaces between modules consistent assumptions, communicate correctly Cucumber meets halfway between customer and developer User stories are not code, so clear to customer and can be used to reach agreement Also not completely freeform, so can connect to real tests

32 Example User Story 1 Feature ≥1 Scenarios / Feature
Feature: User can manually add movie Scenario: Add a movie Given I am on the RottenPotatoes home page When I follow "Add new movie" Then I should be on the Create New Movie page When I fill in "Title" with "Men In Black" And I select "PG-13" from "Rating" And I press "Save Changes" Then I should be on the RottenPotatoes home page And I should see "Men In Black" 1 Feature ≥1 Scenarios / Feature 3 to 8 Steps / Scenario Note that mapping from 3x5 cards to Cucumber can be erroneous, ultimately need user to test. This scenario assumes I can leave the movie date blank.

33 Cucumber User Story, Feature, and Steps
User story: refers to single feature Feature: ≥1 scenarios that show different ways a feature is used Keywords Feature and Scenario identify respective components Kept in .feature files Scenario: steps that describe scenario Step definitions: Ruby code to test steps Kept in X_controller.rb files

34 5 Step Keywords Given steps represent state of world before event: preconditions When steps represent event e.g., simulate user pushing a button Then steps represent expected postconditions; check if true / 5. And & But extend previous step

35 Steps => Step Definitions via Regular Expressions
Regexes match English phrases in steps of scenarios to step definitions! Given /^(?:|I )am on the (.+)$/ “I am on the Rotten Potatoes home page” Step definitions (Ruby code) likely use captured string “Rotten Potatoes home page” (?: re) Groups regular expressions without remembering matched text. (?:|I ) will match “I “ and throw it away or match nothing, so strings starting with “I am on ...” will match, and so will “am on …” will match as well.

36 More on “Cuke” Need to install Cucumber Gem
Just for test and development environment, not for production environment When Cucumber installed, it creates commonly used step definitions Need a test database to run app Then edit .features file to add features

37 Fake User to Try Scenarios?
Need tool that pretends to be the user to follow scenarios of user stories Capybara simulates browser Can interact with app to receive pages Parse the HTML Submit forms as a user would Capybara – rat the size of a dog, largest rodent in the world, native to South America Webrat used to be tool for Ruby acceptance testing for web applications

38 END

39 Which is FALSE about Cucumber and Capybara?
1. Step definitions are in Ruby, and are similar to method calls, while steps are in English and are similar to method definitions 2. A Feature has one or more Scenarios, which are composed typically of 3 to 8 Steps 3. Steps use Given for current state, When for actions, and Then for consequences of actions False. Step definitions are like method definitions, and steps are like method calls. True. Cucumber matches step definitions to scenario steps using regexes, and Capybara pretends to be a user that interacts with the SaaS app accordingly 4.

40 END

41 Running Cucumber and Capybara (Engineering Software as a Service §7.7)
© 2013 Armando Fox & David Patterson, all rights reserved

42 Red-Yellow-Green Analysis
Cucumber colors steps Green for passing Yellow for not yet implemented Red for failing (then following steps are Blue) Goal: Make all steps green for pass (Hence green vegetable for name of tool)

43 Demo Add feature to cover existing functionality
Note: This example is doing it in wrong order – should write tests first Just done for pedagogic reasons (Or can look at screencast: Time for demo in Fall 2012: 10.5 minutes (need to be smoother – turn on mirroriing – practice window selection, copy and paste (or memorize short cuts. Like a navigator ) Type in password saasbook. Start with clear window and center window Mention preconditions, actions, postconditions Create .feature file (Addmovie.feature) Tells you want to do Copies the error message name Find route via rake route Have terminal windows already open for 1) In rottenpotatoes 2) Addmovie.feature in editor in features directory 3) paths.rb in editor in features/support directory Go to VM, tab show feature going to try to run with Cuke Go to Tab to run Cuke on AddMovie.feature Note error message, copy the missing page Go to routes.rb to edit when /^the RottenPotatoes home page/ Go to rake routes to find index mapping '/movies' Try again Go to routes.rb to edit  when /^the Create New Movie page/ Go to rake routes to find new mapping  '/movies/new' Try again – all should pass

44 END

45 © 2013 Armando Fox & David Patterson, all rights reserved
Enhancing Rotten Potatoes Again (Engineering Software as a Service §7.8) Start at 3:00, end at 9:00, so takes 6 minutes © 2013 Armando Fox & David Patterson, all rights reserved

46 Add a Real New Feature? What if we add something harder?
e.g., includes form to fill in e.g., needs a User Interface e.g., needs to add route to connect view to controller e.g., includes both a happy path and a sad path

47 Integrated with The Movie Database (TMDb)
New Feature: Populate from TMDb, versus enter information by hand Need to add ability to search TMDb from Rotten Potatoes home page Need LoFi UI and Storyboard

48 Storyboard TMDb Figure 7.6 of Engineering Software as a Service
The home page of Rotten Potatoes, which lists all movies, will be augmented with a search box where we can type some title keywords of a movie and a “Search” button that will search TMDb for a movie whose title contains those keywords. If the search does match—the so-called “happy path” of execution—the first movie that matches will be used to “pre-populate” the fields in the Add New Movie page that we already developed If the search doesn’t match any movies—the “sad path”—we should be returned to the home page with a message informing us of this fact. Easy Story Board

49 Search TMDb User Story (Fig. 7.7 ESAAS)
Feature: User can add movie by searching in The Movie Database (TMDb) As a movie fan So that I can add new movies without manual tedium I want to add movies by looking up their details in TMDb Scenario: Try to add nonexistent movie (sad path) Given I am on the RottenPotatoes home page Then I should see "Search TMDb for a movie" When I fill in "Search Terms" with "Movie That Does Not Exist" And I press "Search TMDb" Then I should be on the RottenPotatoes home page And I should see "'Movie That Does Not Exist' was not found in TMDb." Usually start with happy path; but we’d have to implement method for searching and matching TMDb, which we’ll do in next chapter. (Have to explain both BDD and TDD, so we’ll do BDD first – so don’t do sad path) Instead, do a sad path where can’t find it, and put in dummy method that always fails to find, which makes sad path work. BDD next will go further in TDD to build the method you need. Not a good idea, but works for this chapter. In future, will go back and forth between BDD design and TDD implementation

50 Haml for Search TMDb Page (Fig. 7.8 ESAAS)
-# add to end of app/views/movies/index.html.haml: %h1 Search TMDb for a movie = form_tag :action => 'search_tmdb' do %label{:for => 'search_terms'} Search Terms = text_field_tag 'search_terms' = submit_tag 'Search TMDb' We will need a new view to go with new UI, and then include a form to fill in Haml – more concise version of Markup Language than HTML, 1st line is search movie. Need to make a name for the action, we’ll call it “search_tmdb” Label is form where fill in the information for movie when it does match Line 3: %h1 Search TMDb for a movie is the text that allows Then I should see “Search TMDb for a movie” to pass. As with any user interaction in a view, we need a controller action that will handle that Web interaction. In this case the interaction is submitting the form with search keywords. Line 5 = form_tag :action => 'search_tmdb' do says that when the form is submitted, the controller action search_tmdb will receive the form submission.

51 Haml Expansion Last Two Lines
This Haml: = text_field_tag 'search_terms' = submit_tag 'Search TMDb’ Turns into this HTML: <label for='search_terms'>Search Terms</label> <input id="search_terms" name="search_terms" type="text" /> for attribute of label tag matches id attribute of input tag, from text_field_tag helper (above) 2) the use of the HTML label tag. Figure 2.14 in Chapter 2 tells us that lines 7 and 8 (=...) will expand to the following more verbose HTML markup (<… The key is that the for attribute of the label tag matches the id attribute of the input tag, which was determined by the first argument to the text_field_tag helper called in line 8 of Figure 7.8. This correspondence allows Cucumber to determine what form field is being referenced by the name “Search Terms” in line 11 of Figure 7.7: When I fill in “Search Terms”

52 Try Cucumber? If try Cucumber, it fails Missing the route
Also MoviesController#search_tmdb is controller action that should receive form, yet not in movies_controller.rb Should use Test Driven Development (future lecture) to implement method search_tmdb Instead, to finish sad path, add fake controller method that always fails

53 Trigger Fake Controller When Form is POSTed (Fig. 7.9)
# add to routes.rb, just before or just after 'resources :movies' : # Route that posts 'Search TMDb' form post '/movies/search_tmdb' we have to make sure there is a route to this new controller action. Above is the line you must add to config/ routes.rb to add a form submission (POST) route to that action

54 Fake Controller Method: Will Fail Finding Movie (Fig. 7.9)
# add to movies_controller.rb, anywhere inside # 'class MoviesController < ApplicationController': def search_tmdb # hardwired to simulate failure flash[:warning] = "'#{params[:search_terms]}' was not found in TMDb." redirect_to movies_path end For now, use fix It to fail, which let’s us complete the sad path. This “fake” controller method always behaves as if no matches were found. It retrieves the keywords typed by the user from the params hash (as we saw in Chapter 3), stores a message in the flash[], and redirects the user back to the list of movies. Recall from Chapter 3 that we added code to app/views/layouts/application.html.haml to display the contents of the flash on every view.

55 END

56 Which statement is TRUE?
1. Usually you complete the Behavior Driven Design phase with Cucumber before starting the Test Driven Development phase with RSpec 2. Usually you code the sad paths first 1. False. We usually iterate, going back and forth between BDD & TDD. See Figure 7.1. 2. False. We usually code the happy path first. This allows us to get the story basics implemented first. 3. True. That is the example; we put in a dummy method to let us move ahead. 4. False. 3 are true. 3. A sad path can pass without having code written needed to make a happy path pass 4. None of the above is true

57 END

58 Running Rotten Potatoes Again (Engineering Software as a Service §7.8)
7 minutes, + 2 minutes Q&A © 2013 Armando Fox & David Patterson, all rights reserved

59 Demo Add feature to search for movie in TMDb
Note: This will be a sad path, in that won’t find it Will use fake method (until future when implement it using TDD) (Or can look at screencast: When adding a feature, usually need to write new UI, new step definitions, and new controller method. Be sure to save edits. Use tabs from pastebin First step OK, since on RP page {Then I should see ``Search TMDb for a movie'’} should fail (red), because we haven't yet added this text to the home page Try to run cuke on new feature: cucumber features/search_tmdb.feature Says 1 passed, rest failed 2. In book says to fix that view, need to copy code from Figure 7.7, which is in PasteBin. It modifies app/views/movies/index.html.haml. If use vi, be sure to put in paste mode - :set paste (:set nopaste changes mode) Try to run cuke Now complains no route 3. In book says to fix route, need to copy code from Figure 7.9 (top). It modifes config/routes.rb 4. We will also set dummy controller method now from Figure 7.9 (bottom). It modifies app/controllers/movies_controller.rb Try to run cuke, all steps green

60 Happy Path of TMDb Find an existing movie, should return to Rotten Potatoes home page But some steps same on sad path and happy path How to make it DRY? Background means steps performed before each scenario

61 TMDb w/2 Scenarios: Background (Fig. 7.10)
Feature: User can add movie by searching for it in The Movie Database (TMDb) As a movie fan So that I can add new movies without manual tedium I want to add movies by looking up their details in TMDb Background: Start from the Search form on the home page Given I am on the RottenPotatoes home page Then I should see "Search TMDb for a movie” Scenario: Try to add nonexistent movie (sad path) When I fill in "Search Terms" with "Movie That Does Not Exist" And I press "Search TMDb" Then I should be on the RottenPotatoes home page And I should see "'Movie That Does Not Exist' was not found in TMDb.” Scenario: Try to add existing movie (happy path) When I fill in "Search Terms" with "Inception" And I should see "Inception”

62 Cucumber Summary New feature => UI for feature, write new step definitions, even write new methods before Cucumber can color steps green Usually do happy paths first Background lets us DRY out scenarios of same feature BDD/Cucumber test behavior; TDD/RSpec in folllowing chapter is how write methods to make all scenarios pass

63 END

64 And in Conclusion Cucumber – “magically” maps 3x5 card user stories onto acceptance tests and integration tests for the application


Download ppt "Forms (Engineering Software as a Service §4.6)"

Similar presentations


Ads by Google