building bulletproof views

Post on 09-Feb-2022

9 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

* BUILDING BULLETPROOF VIEWSJOHN ATHAYDE & BRUCE WILLIAMS, LivingSocial RAILS CONF 2011 BALTIMORE, MARYLAND

*

JOHNa designer(who also develops)

*

BRUCEa developer(who also designs)

WE WORK AT

WE ARE WRITING A BOOKfor

HOW MANY HERE ARE

DEVELOPERS??

HOW MANY HERE ARE

DESIGNERS??

AGENDA

HOUR 1

*

Intro, SurveysViews are ComplexMarkup RefresherBuilding a Layout

Questions

AGENDA

HOUR 2

*

The Art of Template WritingQuestions

AGENDA

HOUR 3

*

Nailing NavigationMaintainable FormsDon’t Fear the Object

Going MobilePackaging Assets

* 1.VIEWS ARECOMPLEX

Views are still...

THE WILD WEST

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

QUICKLY CLEANEDMuch 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>

Views are still...

THE RED-HEADEDSTEPCHILD

MULTIPLE LANGUAGESMuch simpler and could be better...

➡ ERB/Ruby

➡ HTML

➡ CSS

➡ JavaScript/CoffeeScript

➡ Serialization formats

LACK OF FAMILIARITY & RULES

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

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

http://www.flickr.com/photos/lastyearsgirl_/2882447041

DESIGNERISSUES

* 2.MARKUPREFRESHER

HOW MANY HERE

USE ANDUNDERSTANDHTML5?

?

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 itXHTML 1.0

DECLARE A DOCTYPEAnd then develop to it

HTML 4.01 StrictHTML 4.01 Transitional

HTML 4.01 FramesetXHTML 1.0 Strict

XHTML 1.0 TransitionalXHTML 1.0 Frameset

XHTML 1.1HTML (5)

www.alistapart.com/articles/doctype/

XHTML 4 & HTML 4HAVE 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

HTML5HAS 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 consolidatessome other elements together (acronym/abbr)

HIERARCHYWhere it’s at.

<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 HIERARCHYNot semantic, everything is the same.

NO HIERARCHYNot semantic, everything is the same.

<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

“SEMANTIC”HTML Tags used for meaning

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>

!TIP:

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

STYLECSS Overview and Legacy Hacks

http://www.flickr.com/photos/prettycooljewels/4351797285/

<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 CORNERSVintage like...

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 */}

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;}

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

DON’T FIGHT THE BROWSEREmbrace it!

html { overflow-y: scroll;}

ID vs. CLASSThere should be a standard

#this_is_an_id

.this-is-a-class

!TIP:

Embrace the browser and its flaws. Design to the power of the medium. (SCROLLING IS NOT A FLAW)

SHORTENINGLearn to write concise css

http://www.flickr.com/photos/tomsaint/3456155628/

CSS SHORTHANDCombine pieces together

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

becomes...

margin: 4px 2px 4px 8px;

application.css

CSS SHORTHANDCombine 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

CSS SHORTHANDCombine pieces together

fontbackgroundmarginborderpaddinglist

CAN BE USED FOR:

Reduce stylesheet size and improve readability.

AND WILL HELP YOU

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

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

!TIP:

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

BUT GUYS,

WHY DOESTHIS MATTER?

?

WEB ACCESSIBILITYTake care of your users

GOOGLE IS A BLIND USER

START SMALLCover the basics all the time.

www.w3.org/TR/WCAG10/full-checklist.html

WEB ACCESSIBILITYERB 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}”

WEB ACCESSIBILITY=Well Formed HTML

Not just an afterthought.

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

* 3.BUILDINGA LAYOUT

A STORY OF A LAYOUTDefault

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

CHARSETCombine 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

TARGET MSIEBy 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

TARGET MSIEBy version

section#page { margin: 0 auto; width: 960px;}

.ie7 section#page { overflow: hidden;}

http://codefluency.com/post/1100393830/ie-conditional-tag-plugin

application.css

RECOGNIZE NEW ELEMENTSHTML5 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/

RESET.CSSGet 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;

    outline:0;

    font-size:100%;

    vertical-align:baseline;

    background:transparent;

}

....etc...

reset.css

CALLING STYLESHEETSPitfalls of :all

<%= stylesheet_link_tag :all %>

Can load things in alpha order, meaning reset happens after

Xapplication.html.erb

CALLING STYLESHEETSManually calling them

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

application.html.erb

CALLING STYLESHEETSDefining :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

FRAMEWORKS/BOILERPLATECombine pieces together

html5boilerplate.com

Build Your Own

Framework

BUILD YOUROWN

FRAMEWORK

http

://www.flick

r.com/p

hoto

s/dysto

pos/3

15312

589/

HIERARCHY OF H TAGSYou can reuse the H1. Really.

html|-body| |-header| | |-h1| |-article| | |-h1| | |-section| | | |-header| | | | |-h1| | | |-p| | | |-h2

QUESTIONSAND ANSWERS

AGENDA

HOUR 2

*

The Art of Template WritingQuestions

*

WELCOMEBACK!

* 4.THE ART OF TEMPLATEWRITING

IMPROVINGREADABILITY

http://www.flickr.com/photos/bluefootedbooby/389279217/

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

FORMATTING

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

Don’t indent with hard tabs

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

Indent

Better:

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

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

TIDYBecause templating code gets messy.

http://www.flickr.com/photos/muehlinghaus/3564021462/

FORMATTING

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

Output formatting isn’t your job

Use one of these (or similar) as middleware

Zen CodingUse 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/

!TIP:

Know your team. YTMV

TOOLSWhat do we use for each situation?

http://www.flickr.com/photos/anotherphotograph/3571242832/

REFACTORING TOOLS

Extract part of a template (usually for reuse)PARTIALS

Extract complex logicHELPERS

Extract complex, wrap content, View DSLsBLOCK HELPERS

Reduce helper spaghetti code, encapsulate state, ease testingVIEW CLASSES

QUERYING, ORDERING, LIMITING

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

Don’t do this.

X

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

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

!TIP:

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

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

LOCAL ASSIGNMENT

def index # ... other stuff @number_available = Pet.available.countend

Instead, use an instance varaible

pets_controller.rb

LOCAL ASSIGNMENT

def number_available @number_available ||= Pet.available.countend

Or instead, use a helper

pets_helper.rb

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 quickfeeling of what the page contains.

X

CONDITIONAL CONTENT

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

Better, split to files

pets_helper.rb

<%= risk_assesment %>

show.html.erb

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 quickfeeling of what the page contains.

X

CONDITIONAL CONTENT

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

Better, extract condition toa meaningful helper

_pet.html.erb

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 theseindex.html.erb

X

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

http://www.flickr.com

/photos/lambdacz/4

725437313

RENDERING(There are many ways to do it.)

RENDERING

render partial: 'pet'

Without an object

Renders #{controller_name}/_pet.html.erb

render partial: 'pets/pet'

Always renders pets/_pet.html.erb

RENDERING

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

With a collection of objects

With a manual loop:

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

!TIP:

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

SETTING THE PAGE TITLEThe Instance Variable Method

Xdogs_controller.rb

def show @dog = Dog.find(params[:id]) @page_title = @dog.nameend

<section id='content'> <header id=‘page_header’> <h1><%= @page_title %></h1> </header> <%= yield %></section>

application.html.erb

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

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

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

BLOCK HELPERS

http://www.flickr.com

/photos/icco/5237319

440/

!TIP:

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

QUESTIONSAND ANSWERS

AGENDA

HOUR 3

*

NavigationForming Forms

Don’t Fear the ObjectGoing Mobile

Packaging Assets

*

WELCOMEBACK!

* 5.NAILINGNAVIGATION

WHAT NAVIGATION DOES

➡ Tells users where they can go

➡ Reminds them where they are

WHERE ARE WE?The Instance Variable Method

Xdogs_controller.rb

def show @dog = Dog.find(params[:id]) @current_tab = :dogsend

<ul> <li class=’<%= nav_class(:dogs) %>’> <%= link_to ‘Dogs’, dogs_path %> </li> ...</ul>

def nav_class(tab) ‘active’ if tab == @current_tabend

application_helper.rb

_nav.html.erb

WHERE ARE WE?The Manual Method

X_nav.html.erb

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

WHERE ARE WE?The CSS Method

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

IT’S THE ACTION TEMPLATE’S

RESPONSIBILITY.

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.erbHooking it in

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

application.html.erb

USING GOOSE

nav.css

Styling

nav#main_nav ul { height: 30px; overflow: 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;}

USING GOOSE

show.html.erb

In Action Templates

<% nav_at ‘Dogs’ %>

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

* 6.MAINTAINABLEFORMS

FORM_FOR vs FORM_TAG

SHOW ME!

Search

HTML5 FORM ELEMENTS<input type=”search” name=”search”>

diveintohtml5.org/forms.html

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

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

* 6.DON’T FEARTHE OBJECT

YOU MAKE CLASSES FOR THE MODEL

LAYER

YOU MAKE CLASSES FOR THE

CONTROLLER LAYER

WHAT ABOUT THE VIEW LAYER?

ENCAPSULATION

http://www.flickr.com

/photos/shuffle-art/28102475

09/

HELPER CLASSEncapsulates 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

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

HYBRIDController -> View

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

pets_controller.rb

!TIP:

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

* 8.GOINGMOBILE

MOBILE USERINTERACTION

http://www.flickr.com

/photos/yourdon/359975

3183/

MOBILE USER INTERACTION

➡ Task focused

➡ What are they trying to do?

➡ Fat-fingering (easy targets)

➡ Sometimes low bandwidth

CSS3 MEDIA QUERIES

Client-side smart rendering via conditional CSS

RESPONSIVE DESIGN

www.alistapart.com/articles/responsive-web-design

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 quickfeeling of what the page contains.

application.css

@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

MOBILE SPECIFIC

TEMPLATEShttp://www.flickr.com

/photos/goincase/49738

479

49/

Is it a mobile request?

MOBILE-SPECIFIC TEMPLATES

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

application_controller.rb

SET A MIME TYPE

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

mime_types.rb

FILTER THE REQUEST

before_filter :prepare_mobile_request!

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

application_controller.rb

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

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 endend

application_controller.rb

LET USERS DECIDEdef prepare_mobile_request! if mobile_request? && preferred_view == :mobile request.format = :mobile endend

def preferred_view if mobile_request? session[:preferred_view] || :mobile else :standard endend

helper_method :preferred_view

application_controller.rb

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

!TIP:

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

* 9.PACKAGINGASSETS

WHY DOES IT LOAD SLOW?YSlow and PageSpeed

UNUSED SELECTORSUI side: Dust Me Selectors

sitepoint.com/dustmeselectors

UNUSED SELECTORSGem: 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

A MORE ROBUST METHODthat 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

FINAL Q&AFIRE WHEN READY.

top related