* BULLETPROOF VIEWS JOHN ATHAYDE & BRUCE WILLIAMS , - - PowerPoint PPT Presentation

bulletproof views john athayde bruce williams
SMART_READER_LITE
LIVE PREVIEW

* BULLETPROOF VIEWS JOHN ATHAYDE & BRUCE WILLIAMS , - - PowerPoint PPT Presentation

BUILDING * BULLETPROOF VIEWS JOHN ATHAYDE & BRUCE WILLIAMS , LivingSocial RAILS CONF 2011 BALTIMORE, MARYLAND JOHN a designer (who also develops) * BRUCE a developer (who also designs) * WE WORK AT WE ARE WRITING A BOOK for ?


slide-1
SLIDE 1

*

BUILDING BULLETPROOF VIEWS

JOHN ATHAYDE & BRUCE WILLIAMS, LivingSocial

RAILS CONF 2011 BALTIMORE, MARYLAND
slide-2
SLIDE 2

*

JOHN

a designer

(who also develops)
slide-3
SLIDE 3

*

BRUCE

a developer

(who also designs)
slide-4
SLIDE 4

WE WORK AT

slide-5
SLIDE 5

WE ARE WRITING A BOOK for

slide-6
SLIDE 6

HOW MANY HERE ARE

DEVELOPERS?

?

slide-7
SLIDE 7

HOW MANY HERE ARE

DESIGNERS?

?

slide-8
SLIDE 8

BOTH?

http://www.flickr.com/photos/clydeorama/5451312303/
slide-9
SLIDE 9

AGENDA

HOUR 1

* Intro, Surveys Views are Complex Markup Refresher Building a Layout Questions

slide-10
SLIDE 10

AGENDA

HOUR 2

* The Art of Template Writing Questions

slide-11
SLIDE 11

AGENDA

HOUR 3

* Nailing Navigation Maintainable Forms Don’t Fear the Object Going Mobile Packaging Assets

slide-12
SLIDE 12

*

1. VIEWS ARE COMPLEX

slide-13
SLIDE 13

Views are still...

THE WILD WEST

slide-14
SLIDE 14 <ol class="narrow_playlist"> <% playlist.listings.each_with_index do |listing, i| -%> <% if i < show %> <% composition = listing.composition %> <% position = i %> <li class="playlist_item"> <table class="playlist_listing"> <tr> <td class="listing_rank_col"> <%= position+1 %> </td> <td class="listing_title_col"> <h6><%#= link_to awesome_truncate(composition.title,30), label_release_track_path(composition.label.slug, composition.release.slug), :title => composition.title %></h6> <h6><%= link_to awesome_truncate(composition.title,30), composition_path(composition), :title => composition.title %></h6> </td> <td class="listing_icon_col" rowspan="3"> <ul class="playlist_site_actions"> <li class="preview"> <a class="sm2_link" href="<%= composition.preview_url %>"> <span class="hide_me">Preview</span>&nbsp;&nbsp;&nbsp; </a> <%= link_to_function(image_tag("icon_speaker_preview.png", :alt => "Preview", :title => "Preview"), "") %> </li> <% if !composition.member_contributed %> <li class="add_to_cart"> <% if composition.network? %> <%= link_to image_tag("button_playlist_download.gif", :alt => "Download"), downloads_path(:audio_item_id => composition.audio_items.first.id), :method => :post%> <% else %> <%= link_to(image_tag("cart.gif", :alt => "Add to cart", :title => "Add to cart"), selections_path(:salable_id => composition.id, :salable_type => composition.class.name), :method => :post) %> <% end -%> </li> <% end -%> </ul> </td> </tr> <tr> <td></td> <td><h6 class="artist"><%= linked_profiles(composition) %></h6></td> </tr> <tr> <td></td> <td> <ul class="playlist_social_actions"> <%= render :partial => "/ratings/rating_list", :locals => {:class_name => composition.class.superclass.name, :ratable => composition}%> <li> <%= link_to_function(image_tag("icon_load_in_player.png", :alt => "Add to Playlist",:title => "Add to Playlist"), "addCompositionToPlayerPlaylist(#{composition.id})") %> </li> </ul> </td> </tr> </table> </li> <% end %> <% end -%> </ol>
slide-15
SLIDE 15

QUICKLY CLEANED

Much simpler and could be better...

<ol class="latest_uploads"> <% playlist.listings.each_with_index do |listing, i| -%> <% if i < show %> <% composition = listing.composition %> <% position = i %> <li class="playlist_item"> <p class="track_title"><%= link_to awesome_truncate(composition.title,30), composition_path(composition), :title => composition.title %></p> <p class="sm2_link"><a href="<%= composition.preview_url %>"><span class="hide_me">Preview</span>&nbsp;&nbsp;&nbsp;</a> <% if composition.network? %> <%= link_to image_tag("button_playlist_download.gif", :alt => "Download"), downloads_path(:audio_item_id => composition.audio_items.first.id), :method => :post%> <% else %> <%= link_to(image_tag("cart.gif", :alt => "Add to cart", :title => "Add to cart"), selections_path(:salable_id => composition.id, :salable_type => composition.class.name), :method => :post) %> <% end -%></p> <p class="artist_name"><%= linked_profiles(composition) %></p> <ul class="playlist_social_actions"> <%= render :partial => "/ratings/rating_list", :locals => {:class_name => composition.class.superclass.name, :ratable => composition}%> <li> <%= link_to_function(image_tag("icon_load_in_player.png", :alt => "Add to Playlist",:title => "Add to Playlist"), "addCompositionToPlayerPlaylist(#{composition.id})") %> </li> </ul> </li> <% end %> <% end -%> </ol>
slide-16
SLIDE 16

Views are still...

THE RED-HEADED STEPCHILD

slide-17
SLIDE 17

MULTIPLE LANGUAGES

Much simpler and could be better...

➡ ERB/Ruby ➡ HTML ➡ CSS ➡ JavaScript/CoffeeScript ➡ Serialization formats

slide-18
SLIDE 18

LACK OF FAMILIARITY & RULES

slide-19
SLIDE 19

“This is really long. I should break it into partials.”

slide-20
SLIDE 20

“This is really complex. I should make it a helper.”

slide-21
SLIDE 21 http://www.flickr.com/photos/lastyearsgirl_/2882447041

DESIGNER ISSUES

slide-22
SLIDE 22

*

2. MARKUP REFRESHER

slide-23
SLIDE 23

HOW MANY HERE

USE AND UNDERSTAND HTML5?

?

slide-24
SLIDE 24

WHICH VERSION?

HTML5 vs HTML4 vs XHTML Newest (under development) standard. More semantic elements and other technologies

HTML5

Pretty standard.

HTML4

XML version of HTML4, most browsers didn’t respect it

XHTML 1.0

slide-25
SLIDE 25

DECLARE A DOCTYPE

And then develop to it

HTML 4.01 Strict HTML 4.01 Transitional HTML 4.01 Frameset XHTML 1.0 Strict XHTML 1.0 Transitional XHTML 1.0 Frameset XHTML 1.1 HTML (5)

www.alistapart.com/articles/doctype/
slide-26
SLIDE 26

XHTML 4 & HTML 4

HAVE THESE ELEMENTS

html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, font, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td

slide-27
SLIDE 27

HTML5

HAS SOME NEW SEMANTIC TOYS

section, article, header, hgroup, nav, footer, aside, time, figure, figcaption

Legacy support requires Modernizr or a javascript shiv. and removes things like frameset and consolidates some other elements together (acronym/abbr)

slide-28
SLIDE 28

HIERARCHY

Where it’s at.

slide-29
SLIDE 29 <div class="headline">This is a page headline.</div> <div class="subhead">This is a subhead</div> <div class="body">This is body text and it goes on for miles and miles. I like cheese.</div> <div class="list">This is going to be a list of items:<br />
  • Item 1<br />
  • Item 2<br />
  • Item 3<br />
</div>

NO HIERARCHY

Not semantic, everything is the same.

slide-30
SLIDE 30

NO HIERARCHY

Not semantic, everything is the same.

slide-31
SLIDE 31 <hgroup> <h1>This is a page headline.</h1> <h2>This is a subhead</h2> </hgroup> <section id=”page”> <p>This is body text and it goes on for miles and miles. I like cheese.</p> <p>This is going to be a list of items:</p> <ul> <li>Item 1</li> <li>Item 2</li> <li>Item 3</li> </ul> </section>

“SEMANTIC”

HTML Tags used for meaning

slide-32
SLIDE 32

“SEMANTIC”

HTML Tags used for meaning

slide-33
SLIDE 33

USE THE ELEMENTS!

Lists are lists. Not divs with bullets.

<% @unit.unit_client_scans.each do |client_scan| %> &nbsp;• <%= client_scan.value %><br /> <% end %> <ul class=”scans”> <% @unit.unit_client_scans.each do |client_scan| %> <li><%= client_scan.value %></li> <% end %> </ul>
slide-34
SLIDE 34

!

TIP:

Mark up content for the meaning, NOT the layout. Use the semantic elements.

slide-35
SLIDE 35

VALIDATE!

Valid HTML makes for better coding

validator.w3.org
slide-36
SLIDE 36

STYLE

CSS Overview and Legacy Hacks

http://www.flickr.com/photos/prettycooljewels/4351797285/
slide-37
SLIDE 37 <div class="b"> <div class="l"> <div class="r"> <div class="bl"> <div class="br"> <div class="tl"> <div class="tr box"> <%= content %> </div> </div> </div> </div> </div> </div> </div>

ROUNDED CORNERS

Vintage like...

slide-38
SLIDE 38

ROUNDED CORNERS

<div class=”box-to-be-rounded”> <%= content %> </div>

CSS3 Solution

.box-to-be-rounded { border: 1px solid #ccc;
  • webkit-border-radius: 5px; /* Safari, Chrome */
  • moz-border-radius: 5px; /* Firefox */
border-radius: 5px; /* IE9, Opera 10.5 */ }
slide-39
SLIDE 39

REQUIRED FIELDS

<%= f.label :first_name %> <span class=”required”>*</span><br /> <%= f.text_field :first_name %>

Love the form you’re with

span.required {color: red;}
slide-40
SLIDE 40

REQUIRED FIELDS

<%= f.label :first_name, :class => “required” %> <%= f.text_field :first_name %>

Progressively enhance!

label { display: block; } label.required { color: red; } label.required:after { content: “*”; }

Not supported in MSIE 7 and below, 8 does not accept images for content

www.quirksmode.org/css/beforeafter.html
slide-41
SLIDE 41

DON’T FIGHT THE BROWSER

Embrace it!

html {

  • verflow-y: scroll;

}

slide-42
SLIDE 42

ID vs. CLASS

There should be a standard

#this_is_an_id .this-is-a-class

slide-43
SLIDE 43

!

TIP:

Embrace the browser and its flaws. Design to the power of the medium.

(SCROLLING IS NOT A FLAW)
slide-44
SLIDE 44

SHORTENING

Learn to write concise css

http://www.flickr.com/photos/tomsaint/3456155628/
slide-45
SLIDE 45

CSS SHORTHAND

Combine pieces together

margin-top: 4px; margin-right: 2px; margin-bottom: 4px; margin-left: 8px;

becomes...

margin: 4px 2px 4px 8px;

application.css

slide-46
SLIDE 46

CSS SHORTHAND

Combine pieces together

background-color: #333; background-image: url(“../images/texture.png”) background-position: top left; background-repeat: repeat;

becomes...

background: #333 url(“../images/texture.png”) repeat-x top left;

application.css

slide-47
SLIDE 47

CSS SHORTHAND

Combine pieces together font background margin border padding list

CAN BE USED FOR:

Reduce stylesheet size and improve readability.

AND WILL HELP YOU

slide-48
SLIDE 48

HOW TO WRITE CSS

.button-install { margin-left: 30px; font-family: "Helvetica Neue", helvetica, arial, sans-serif; display: block; width: 250px; margin-bottom: 15px; text-align: left; height: 48px; font-size: 20px; font-weight: bold; padding:4px 0 8px 0; background-color: #67c5e6; background-image:
  • webkit-gradient(
linear, left top, left bottom, color-stop(0.23, rgb(103,197,230)), color-stop(0.81, rgb(94,149,167)) ); background-image: -moz-linear-gradient( center top, rgb(103,197,230) 23%, rgb(94,149,167) 81% ); border-radius: 8px; -webkit-border-radius: 8px; -moz-border-radius: 8px; }

Hard to read and find what you’re looking for...

slide-49
SLIDE 49

HOW TO WRITE CSS

.button-install { background-color: #67c5e6; background-image:
  • webkit-gradient(
linear, left top, left bottom, color-stop(0.23, rgb(103,197,230)), color-stop(0.81, rgb(94,149,167)) ); background-image: -moz-linear-gradient( center top, rgb(103,197,230) 23%, rgb(94,149,167) 81% );
  • moz-border-radius: 8px;
  • webkit-border-radius: 8px;
border-radius: 8px; display: block; font: bold 20px "Helvetica Neue", helvetica, arial, sans-serif; height: 48px; margin: 0 0 15px 30px; padding:4px 0 8px 0; text-align: left; width: 250px; }

Alpha order your properties, use shorthand, one per line

slide-50
SLIDE 50

!

TIP:

Write effective and concise CSS. Properties in alpha order, be specific, browser experimental calls before standard calls, and don’t use browser hacks.

slide-51
SLIDE 51

BUT GUYS,

WHY DOES THIS MATTER?

?

slide-52
SLIDE 52

WEB ACCESSIBILITY

Take care of your users

slide-53
SLIDE 53

GOOGLE IS A BLIND USER

slide-54
SLIDE 54

§508

www.section508.gov

slide-55
SLIDE 55

START SMALL

Cover the basics all the time.

www.w3.org/TR/WCAG10/full-checklist.html
slide-56
SLIDE 56

WEB ACCESSIBILITY

ERB Examples

link_to @product.name, product_path(@product), :title => “Take a look at #{@product.name}” image_tag “product_12758.png”, :alt => “#{@product.name}” link_to (image_tag “product_12758.png”, :alt => “# {@product.name}”), product_path(@product), :title => “Take a look at #{@product.name}”
slide-57
SLIDE 57

WEB ACCESSIBILITY = Well Formed HTML

Not just an afterthought.

slide-58
SLIDE 58

!

TIP:

HTML is code. Don’t be afraid to write it. Well written HTML helps users of all types access and interact with our content or application.

slide-59
SLIDE 59

*

3. BUILDING A LAYOUT

slide-60
SLIDE 60

A STORY OF A LAYOUT

Default

<!DOCTYPE html> <html> <head> <title></title> <%= stylesheet_link_tag :all %> <%= javascript_include_tag :defaults %> <%= csrf_meta_tag %> </head> <body> <%= yield %> </body> </html>

application.html.erb

slide-61
SLIDE 61

CHARSET

Combine pieces together

<!DOCTYPE html> <html> <head> <meta charset=”utf-8”> <title></title> <%= stylesheet_link_tag :all %> <%= javascript_include_tag :defaults %> <%= csrf_meta_tag %> </head> <body> <%= yield %> </body> </html>

application.html.erb

slide-62
SLIDE 62

TARGET MSIE

By version

<!DOCTYPE html> <!--[if lt IE 7 ]> <html lang="en" class="ie ie6"> <![endif]--> <!--[if IE 7 ]> <html lang="en" class="ie ie7"> <![endif]--> <!--[if IE 8 ]> <html lang="en" class="ie ie8"> <![endif]--> <!--[if IE 9 ]> <html lang="en" class="ie ie9"> <![endif]--> <!--[if (gt IE 9)|!(IE)]><!--> <html lang="en"> <!--<![endif]-->

Extracted into a gem by Bruce

http://codefluency.com/post/1100393830/ie-conditional-tag-plugin <%= ie_conditional_tag :html %>

application.html.erb

slide-63
SLIDE 63

TARGET MSIE

By version

section#page { margin: 0 auto; width: 960px; } .ie7 section#page {
  • verflow: hidden;
} http://codefluency.com/post/1100393830/ie-conditional-tag-plugin

application.css

slide-64
SLIDE 64

RECOGNIZE NEW ELEMENTS

HTML5 Shiv (or Shim, same thing)

<!--[if lt IE 9]> <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script> <![endif]-->

Enables the elements and fixes printing on IE

code.google.com/p/html5shiv/
slide-65
SLIDE 65 modernizr.com
slide-66
SLIDE 66

RESET.CSS

Get your browser on the same page

meyerweb.com/eric/tools/css/reset/

First showed up in 2007 Put forth by Eric Meyer NOT about defaults Brings browsers to the same base page Makes you specify everything

slide-67
SLIDE 67

RESET.CSS

Get your browser on the same page

html5doctor.com/html-5-reset-stylesheet/ tml, body, div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, abbr, address, cite, code, del, dfn, em, img, ins, kbd, q, samp, small, strong, sub, sup, var, b, i, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, figcaption, figure, footer, header, hgroup, menu, nav, section, summary, time, mark, audio, video { margin:0; padding:0; border:0;
  • utline:0;
font-size:100%; vertical-align:baseline; background:transparent; } ....etc...

reset.css

slide-68
SLIDE 68

CALLING STYLESHEETS

Pitfalls of :all

<%= stylesheet_link_tag :all %>

Can load things in alpha order, meaning reset happens after

X

application.html.erb

slide-69
SLIDE 69

CALLING STYLESHEETS

Manually calling them

<%= stylesheet_link_tag ‘reset’, ‘application’, ‘buttons’ %>

application.html.erb

slide-70
SLIDE 70

CALLING STYLESHEETS

Defining :defaults

class Application < Rails::Application config.action_view.stylesheet_expansions[:defaults] = %w( nav tipsy iconic ui-lightness/jquery-ui-1.8.11.custom jquery.customSelect dashboard shadows dataTables/css/demo_table dataTables/css/demo_page application ) end

config/application.rb

slide-71
SLIDE 71

FRAMEWORKS/BOILERPLATE

Combine pieces together

html5boilerplate.com
slide-72
SLIDE 72

Build Your Own Framework

BUILD YOUR OWN FRAMEWORK

http://www.flickr.com/photos/dystopos/315312589/
slide-73
SLIDE 73

HIERARCHY OF H TAGS

You can reuse the H1. Really.

html |-body | |-header | | |-h1 | |-article | | |-h1 | | |-section | | | |-header | | | | |-h1 | | | |-p | | | |-h2
slide-74
SLIDE 74

QUESTIONS

AND ANSWERS
slide-75
SLIDE 75

AGENDA

HOUR 2

* The Art of Template Writing Questions

slide-76
SLIDE 76

*

WELCOME BACK!

slide-77
SLIDE 77

*

4. THE ART OF TEMPLATE WRITING

slide-78
SLIDE 78

IMPROVING READABILITY

http://www.flickr.com/photos/bluefootedbooby/389279217/
slide-79
SLIDE 79

WHY WE REFACTOR

➡ Templates can be verbose ➡ Display logic can be very complex ➡ More people need to read, understand, and

modify them.

➡ They need to change more frequently, as

they’re more subjective

slide-80
SLIDE 80

FORMATTING

<html> <head> <title>...

Don’t indent with hard tabs

slide-81
SLIDE 81

FORMATTING

<% pets.each do |pet| %> <li> <%= pet.name %> </li> <% end %>

Indent Better:

<% pets.each do |pet| %> <li> <%= pet.name %> </li> <% end %>
slide-82
SLIDE 82

FORMATTING

<p> We believe that many dogs are unnecessarily abandoned because of easily solved, unwanted behavior. To combat this, we offer behavior classes to <strong>all<strong> adopters, and all individuals requiring help with their dogs. </p>

Clean up big blocks of text Still try to keep line length <= 80 characters per line

slide-83
SLIDE 83

TIDY

Because templating code gets messy.

http://www.flickr.com/photos/muehlinghaus/3564021462/
slide-84
SLIDE 84

FORMATTING

$ gem install rack-tidy $ gem install rack-tidy-ffi

Output formatting isn’t your job Use one of these (or similar) as middleware

slide-85
SLIDE 85 http://www.flickr.com/photos/miltonkeynesman/5359120312/
slide-86
SLIDE 86

Zen Coding

Use a tool, not a generator

div#page>img.logo+ul#navigation>li*5>a <div id="page"> <img src=”” class="logo" /> <ul id="navigation"> <li><a href=""></a></li> <li><a href=""></a></li> <li><a href=""></a></li> <li><a href=""></a></li> <li><a href=""></a></li> </ul> </div> PROJECT FILES: code.google.com/p/zen-coding/
slide-87
SLIDE 87

!

TIP:

Know your team. YTMV

slide-88
SLIDE 88

TOOLS

What do we use for each situation?

http://www.flickr.com/photos/anotherphotograph/3571242832/
slide-89
SLIDE 89

REFACTORING TOOLS

Extract part of a template (usually for reuse)

PARTIALS

Extract complex logic

HELPERS

Extract complex, wrap content, View DSLs

BLOCK HELPERS

Reduce helper spaghetti code, encapsulate state, ease testing

VIEW CLASSES

slide-90
SLIDE 90

QUERYING, ORDERING, LIMITING

<% Pet.all.order(:adoption_date).each do |pet| %> <%# Use pet %> <% end %>

Don’t do this.

X

slide-91
SLIDE 91

QUERYING, ORDERING, LIMITING

def index @pets_by_adoption = Pet.all.order(:adoption_date) end

Instead, set an instance variable

pets_controller.rb

<% @pets_by_adoption.each do |pet| %> <%# Use pet %> <% end %>

_adoption_list.html.erb

slide-92
SLIDE 92

QUERYING, ORDERING, LIMITING

def pets_by_adoption @pets_by_adoption = Pet.all.order(:adoption_date) end

Or instead, use a helper

pets_helper.rb

<% pets_by_adoption.each do |pet| %> <%# Use pet %> <% end %>

_adoption_list.html.erb

slide-93
SLIDE 93

!

TIP:

Avoid capital letters in your template code (configuration constants and model classes).

slide-94
SLIDE 94

LOCAL ASSIGNMENT

<% number_available = Pet.available.count %> <% available, unavailable = pets_by_status %> <% next_month = Date.today >> 1 %>

Don’t do this, ever. Never. At no time... Messy, noisy, undocumentable, easy to accidentally remove

X

_availability.html.erb

slide-95
SLIDE 95

LOCAL ASSIGNMENT

def index # ... other stuff @number_available = Pet.available.count end

Instead, use an instance varaible

pets_controller.rb

slide-96
SLIDE 96

LOCAL ASSIGNMENT

def number_available @number_available ||= Pet.available.count end

Or instead, use a helper

pets_helper.rb

slide-97
SLIDE 97

CONDITIONAL CONTENT

<% if @pet.at_risk? <%# Lots of content %> <% else %> <%# Different lots of content %> <% end %>

Avoid long if/else clauses If lengthy, this makes it hard to get a quick feeling of what the page contains.

X

slide-98
SLIDE 98

CONDITIONAL CONTENT

def risk_assessment if @pet.at_risk? render partial: 'at_risk' else render partial: 'not_at_risk' end end

Better, split to files

pets_helper.rb

<%= risk_assesment %>

show.html.erb

slide-99
SLIDE 99

CONDITIONAL CONTENT

<% if current_user.admin? || current_user.can?(:edit_pet) || pet.owner == current_user %> <%= render partial: 'form', locals: {pet: pet} %> <% end %>

Avoid long and complex conditions If lengthy, this makes it hard to get a quick feeling of what the page contains.

X

slide-100
SLIDE 100

CONDITIONAL CONTENT

<% if can_edit_pet?(pet) %> <%= render partial: 'form', locals: {pet: pet} <% end %>

Better, extract condition to a meaningful helper

_pet.html.erb

slide-101
SLIDE 101

MANUAL LOOPS

<ul id='pets'> <% @pets.each do |pet| %> <li> <%= image_tag pet.image.url(:thumb) %> <%= pet.name %> <%= pet_stats(pet) %> <li> <% end %> </ul>

Minimize these

index.html.erb

X

slide-102
SLIDE 102

MANUAL LOOPS

<ul id='pets'> <%= render @pets %> </ul>

Break into partials

index.html.erb

<li> <%= image_tag pet.image.url(:thumb) %> <%= pet.name %> <%= pet_stats(pet) %> <li>

_pet.html.erb

slide-103
SLIDE 103 http://www.flickr.com/photos/lambdacz/4725437313

RENDERING

(There are many ways to do it.)

slide-104
SLIDE 104

RENDERING

render partial: 'pet'

Without an object Renders #{controller_name}/_pet.html.erb

render partial: 'pets/pet'

Always renders pets/_pet.html.erb

slide-105
SLIDE 105

RENDERING

render partial: 'pet', locals: {pet: pet}

With an object Using :locals

<p><%= pet.description %></p>

Renders _pet.html.erb Assigns pet local variable

slide-106
SLIDE 106

RENDERING

render partial: 'pet', object: comment

With an object Using :object

<p><%= pet.description %></p>

Also renders _pet.html.erb Assigns pet local variable

slide-107
SLIDE 107

RENDERING

render pet

With an object Using an implicit partial Checks the object’s .class.name (“Pet”), renders

pets/_pet.html.erb, and assigns the object to

the local pet variable.

slide-108
SLIDE 108

RENDERING

<% @pets.each do |pet| %> <%= render pet %> <% end %>

With a collection of objects With a manual loop:

slide-109
SLIDE 109

RENDERING

render partial: 'pet', collection: @pets

With a collection of objects Using :collection Renders _pet.html.erb for each object in @pets, assigning each as the pet local variable

slide-110
SLIDE 110

RENDERING

render @pets

With a collection of objects Using an implicit partial Checks the object’s .class.name (“Pet”), renders

pets/_pet.html.erb, and assigns each object to

the pet local variable.

slide-111
SLIDE 111

RENDERING

render [a_cat, a_dog, another_dog]

With a collection of objects If we had Cat, and Dog single table inheritance subclasses of Pet, with this render:

cats/_cat.html.erb (with cat set to a_cat) dogs/_dog.html.erb (with dog set to a_dog) dogs/_dogs.html.erb (with dog set to another_dog)

slide-112
SLIDE 112

!

TIP:

Using implicit partial rendering means you don’t have to CARE which partial gets rendered.

slide-113
SLIDE 113

SETTING THE PAGE TITLE

The Instance Variable Method

X

dogs_controller.rb

def show @dog = Dog.find(params[:id]) @page_title = @dog.name end <section id='content'> <header id=‘page_header’> <h1><%= @page_title %></h1> </header> <%= yield %> </section>

application.html.erb

slide-114
SLIDE 114

SETTING THE PAGE TITLE

<% content_for :page_header do %> <h1><%= @pet.name %></h1> <% end %>

show.html.erb

<section id='content'> <% if content_for?(:page_header) %> <header id=‘page_header’><%= yield :page_header %></header> <% end %> <%= yield %> </section>

application.html.erb

The content_for Method

slide-115
SLIDE 115

SETTING THE PAGE TITLE

def page_title(title) content_for(:page_header, content_tag(:h1, title)) end

pets_helper.rb

<% page_title @pet.name %>

show.html.erb

The content_for Method

slide-116
SLIDE 116

!

TIP:

Use content_for in your action templates to define content that is conceptually related to the action, but structurally located elsewhere in the layout.

slide-117
SLIDE 117

BLOCK HELPERS

http://www.flickr.com/photos/icco/5237319440/
slide-118
SLIDE 118

!

TIP:

Don’t code generate before you have determined what your common design elements are.

slide-119
SLIDE 119

QUESTIONS

AND ANSWERS
slide-120
SLIDE 120

AGENDA

HOUR 3

* Navigation Forming Forms Don’t Fear the Object Going Mobile Packaging Assets

slide-121
SLIDE 121

*

WELCOME BACK!

slide-122
SLIDE 122

*

5. NAILING NAVIGATION

slide-123
SLIDE 123

WHAT NAVIGATION DOES

➡ Tells users where they can go ➡ Reminds them where they are

slide-124
SLIDE 124

WHERE ARE WE?

The Instance Variable Method

X

dogs_controller.rb

def show @dog = Dog.find(params[:id]) @current_tab = :dogs end <ul> <li class=’<%= nav_class(:dogs) %>’> <%= link_to ‘Dogs’, dogs_path %> </li> ... </ul> def nav_class(tab) ‘active’ if tab == @current_tab end

application_helper.rb _nav.html.erb

slide-125
SLIDE 125

WHERE ARE WE?

The Manual Method

X

_nav.html.erb

<ul> <li> <%= link_to_unless_current("Home", action: ‘home’, controller: ‘pages’) %> </li> ... </ul>
slide-126
SLIDE 126

WHERE ARE WE?

The CSS Method

X

application.html.erb

<body class="<%= controller_name %> <%= action_name %>">

nav.css

body.dogs li#dogs_tab { background: #fff; } body.dogs li#dogs_tab a { color: #000; }

_nav.html.erb

<ul> <li id=‘dogs_tab’> <%= link_to("Dogs", dogs_path) %> </li> ... </ul>

nav.css

slide-127
SLIDE 127

IT’S THE ACTION TEMPLATE’S RESPONSIBILITY.

slide-128
SLIDE 128

*

GOOSE

a simple navigation gem

GITHUB: github.com/bruce/goose
slide-129
SLIDE 129

USING GOOSE

<% content_for :main_nav do %> <nav id='main-nav'> <ul> <%= nav_to 'Home', root_path %> <%= nav_to 'Dogs', dogs_path %> <%= nav_to 'Cats', dogs_path %> <%= nav_to 'Contact Us', about_path %> </ul> </nav> <% end %>

nav/_main.html.erb

Hooking it in

<header> <%= yield :main_nav %> </header>

application.html.erb

slide-130
SLIDE 130

USING GOOSE

nav.css

Styling

nav#main_nav ul { height: 30px;
  • verflow: hidden;
} nav#main_nav ul li { color: #999; float: left; margin-right: 3px; padding: 5px 10px; } nav#main_nav ul li.active { color: #000; font-weight: bold; }
slide-131
SLIDE 131

USING GOOSE

show.html.erb

In Action Templates

<% nav_at ‘Dogs’ %>
slide-132
SLIDE 132

!

TIP:

Driving the state of the page from the action template gives you flexibility and means you know exactly where to look to make changes.

slide-133
SLIDE 133

*

6. MAINTAINABLE FORMS

slide-134
SLIDE 134

FORM_FOR vs FORM_TAG

slide-135
SLIDE 135

SHOW ME!

slide-136
SLIDE 136

Email

HTML5 FORM ELEMENTS

Will affect keyboard layout on iOS devices

<input type=”email” name=”email”> diveintohtml5.org/forms.html
slide-137
SLIDE 137

Search

HTML5 FORM ELEMENTS

<input type=”search” name=”search”> diveintohtml5.org/forms.html
slide-138
SLIDE 138

Date

HTML5 FORM ELEMENTS

Only in Opera right now

<input type=”date” name=”date”> diveintohtml5.org/forms.html
slide-139
SLIDE 139

FORMTASTIC

<%= form_for @adoption_application do |f| %> <fieldset> <legend>Your Name</legend> <ol> <li> <%= f.label :first_name, "First Name" %> <%= f.text_field :first_name %> </li> <li> <%= f.label :last_name, "Last Name" %> <%= f.text_field :last_name %> </li> </ol> </fieldset> <% end %>

Before...

slide-140
SLIDE 140

FORMTASTIC

<%= semantic_form_for @adoption_application do |f| %> <%= f.inputs “Your Name” do %> <%= f.input :first_name, label: ‘First Name’ %> <%= f.input :last_name, label: ‘Last Name’ %> <% end %> ... <% end %>

After

slide-141
SLIDE 141

*

6. DON’T FEAR THE OBJECT

slide-142
SLIDE 142

YOU MAKE CLASSES FOR THE MODEL LAYER

slide-143
SLIDE 143

YOU MAKE CLASSES FOR THE CONTROLLER LAYER

slide-144
SLIDE 144

WHAT ABOUT THE VIEW LAYER?

slide-145
SLIDE 145

ENCAPSULATION

http://www.flickr.com/photos/shuffle-art/2810247509/
slide-146
SLIDE 146

HELPER CLASS

Encapsulates state, accesses template

class PetHistory def initialize(template, pet) @template = template @pet = pet end # ... def to_s # @template.render, etc end end def history @history ||= PetHistory.new(self, @pet) end

lib/pet_history.rb pets_helper.rb

slide-147
SLIDE 147

HYBRID

Controller -> View

class PetSort delegate :params, to: :@controller delegate :each, to: :records def initializer(controller) @controller = controller end def field params[:field] || :name end def order params[:order] || :desc end private def records # TODO: make sure field & order are on whitelist Pet.order("#{field} #{order}") end end

lib/pet_sort.rb

slide-148
SLIDE 148

HYBRID

Controller -> View

def sort @sort ||= PetSort.new(self) end helper_method :sort

pets_controller.rb

slide-149
SLIDE 149

!

TIP:

View classes allow you to more easily test complex logic (instantiate, stub out the controller, template, etc).

slide-150
SLIDE 150

*

8. GOING MOBILE

slide-151
SLIDE 151

MOBILE USER INTERACTION

http://www.flickr.com/photos/yourdon/3599753183/
slide-152
SLIDE 152

MOBILE USER INTERACTION

➡ Task focused ➡ What are they trying to do? ➡ Fat-fingering (easy targets) ➡ Sometimes low bandwidth

slide-153
SLIDE 153

CSS3 MEDIA QUERIES

slide-154
SLIDE 154

Client-side smart rendering via conditional CSS

RESPONSIVE DESIGN

www.alistapart.com/articles/responsive-web-design
slide-155
SLIDE 155

Client-side smart rendering via conditional CSS

RESPONSIVE DESIGN

@media screen and (max-width: 800px) { } @media screen and (max-width: 640px) { } @media only screen and (min-device-width: 320px) and (max-device-width: 480px) { }

If lengthy, this makes it hard to get a quick feeling of what the page contains.

application.css

slide-156
SLIDE 156 www.mediaqueri.es
slide-157
SLIDE 157

@media QUERIES PITFALLS

➡ All content is loaded, even if it’s not shown ➡ Interaction is different on mobile ➡ Doesn’t get into the capabilities of the

mobile device

slide-158
SLIDE 158

MOBILE SPECIFIC TEMPLATES

http://www.flickr.com/photos/goincase/4973847949/
slide-159
SLIDE 159

Is it a mobile request?

MOBILE-SPECIFIC TEMPLATES

def mobile_request? request.user_agent =~ /iP(?:one|ad|od)/ end helper_method :mobile_request?

application_controller.rb

slide-160
SLIDE 160

SET A MIME TYPE

Mime::Type.register_alias "text/html", :mobile

mime_types.rb

slide-161
SLIDE 161

FILTER THE REQUEST

before_filter :prepare_mobile_request! def prepare_mobile_request! if mobile_request? request.format = :mobile end end

application_controller.rb

slide-162
SLIDE 162

Specific to mobile

LAYOUT FILE

<!DOCTYPE html> <html lang="en"> <head> <title>Dam App: Mobile Edition!</title> </head> <body> <%= yield %> <%= render partial: 'layouts/footer' %> </body> </html>

application.mobile.erb

slide-163
SLIDE 163

LET USERS DECIDE

before_filter :set_preferred_view! before_filter :prepare_mobile_request! def set_preferred_view! if mobile_request? case params[:prefer_view] when 'standard' session[:preferred_view] = :standard when 'mobile' session[:preferred_view] = :mobile end end end

application_controller.rb

slide-164
SLIDE 164

LET USERS DECIDE

def prepare_mobile_request! if mobile_request? && preferred_view == :mobile request.format = :mobile end end def preferred_view if mobile_request? session[:preferred_view] || :mobile else :standard end end helper_method :preferred_view

application_controller.rb

slide-165
SLIDE 165

LET USERS DECIDE

def link_to_prefer_view(name) link_to_unless(preferred_view == name, "#{name.capitalize} View", prefer_view: name) end

application_helper.rb

<% if mobile_request? %> <%= link_to_prefer_view :mobile %> | <%= link_to_prefer_view :standard %> <% end %>

_footer.html.erb

slide-166
SLIDE 166

MOBILE FU

http://www.flickr.com/photos/mfl/2978031449/
slide-167
SLIDE 167
slide-168
SLIDE 168

!

TIP:

Figure out what your users need and then build to that. The technology follows the use case.

slide-169
SLIDE 169

*

9. PACKAGING ASSETS

slide-170
SLIDE 170

WHY DOES IT LOAD SLOW?

YSlow and PageSpeed

slide-171
SLIDE 171

UNUSED SELECTORS

UI side: Dust Me Selectors

sitepoint.com/dustmeselectors
slide-172
SLIDE 172

UNUSED SELECTORS

Gem: Deadweight

# lib/tasks/deadweight.rake require 'deadweight' Deadweight::RakeTask.new do |dw| dw.mechanize = true dw.root = 'http://staging.example.com' dw.stylesheets = %w( /stylesheets/style.css ) dw.pages = %w( / /page/1 /about ) dw.pages << proc { fetch('/login') form = agent.page.forms.first form.username = 'username' form.password = 'password' agent.submit(form) fetch('/secret-page') } dw.ignore_selectors = /hover|lightbox|superimposed_kittens/ end www.github.com/aanand/deadweight
slide-173
SLIDE 173

A MORE ROBUST METHOD

that covers javascript too...

“Jammit is an industrial strength asset packaging library for Rails, providing both the CSS and JavaScript concatenation and compression that you'd expect, as well as YUI Compressor and Closure Compiler compatibility, ahead-of- time gzipping, built-in JavaScript template support, and optional Data-URI / MHTML image and font embedding.” documentcloud.github.com/jammit
slide-174
SLIDE 174

Concat, Convert, Minify assets

RAILS 3.1 SPROCKETS

vendor/assets app/assets

slide-175
SLIDE 175

FINAL Q&A

FIRE WHEN READY.
slide-176
SLIDE 176

*

SLIDES: www.therailsview.com/railsconf2011

THANKYou

BOOK: www.therailsview.com/book