*
BUILDING BULLETPROOF VIEWS
JOHN ATHAYDE & BRUCE WILLIAMS, LivingSocial
RAILS CONF 2011 BALTIMORE, MARYLAND
* 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 ?
JOHN ATHAYDE & BRUCE WILLIAMS, LivingSocial
RAILS CONF 2011 BALTIMORE, MARYLANDa designer
(who also develops)a developer
(who also designs)WE WORK AT
WE ARE WRITING A BOOK for
HOW MANY HERE ARE
HOW MANY HERE ARE
AGENDA
HOUR 1
* Intro, Surveys Views are Complex Markup Refresher Building a Layout Questions
AGENDA
HOUR 2
* The Art of Template Writing Questions
AGENDA
HOUR 3
* Nailing Navigation Maintainable Forms Don’t Fear the Object Going Mobile Packaging Assets
Views are still...
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> </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...
MULTIPLE LANGUAGES
Much 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.”
HOW MANY HERE
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
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/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
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)
Where it’s at.
NO HIERARCHY
Not semantic, everything is the same.
NO HIERARCHY
Not semantic, everything is the same.
“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| %> • <%= client_scan.value %><br /> <% end %> <ul class=”scans”> <% @unit.unit_client_scans.each do |client_scan| %> <li><%= client_scan.value %></li> <% end %> </ul>Mark up content for the meaning, NOT the layout. Use the semantic elements.
Valid HTML makes for better coding
validator.w3.orgCSS Overview and Legacy Hacks
http://www.flickr.com/photos/prettycooljewels/4351797285/ROUNDED CORNERS
Vintage like...
ROUNDED CORNERS
<div class=”box-to-be-rounded”> <%= content %> </div>CSS3 Solution
.box-to-be-rounded { border: 1px solid #ccc;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.htmlDON’T FIGHT THE BROWSER
Embrace it!
html {
}
ID vs. CLASS
There should be a standard
#this_is_an_id .this-is-a-class
Embrace the browser and its flaws. Design to the power of the medium.
(SCROLLING IS NOT A FLAW)Learn to write concise css
http://www.flickr.com/photos/tomsaint/3456155628/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
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
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
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:Hard to read and find what you’re looking for...
HOW TO WRITE CSS
.button-install { background-color: #67c5e6; background-image:Alpha order your properties, use shorthand, one per line
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,
WEB ACCESSIBILITY
Take care of your users
www.section508.gov
START SMALL
Cover the basics all the time.
www.w3.org/TR/WCAG10/full-checklist.htmlWEB 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}”Not just an afterthought.
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.
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
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
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
TARGET MSIE
By version
section#page { margin: 0 auto; width: 960px; } .ie7 section#page {application.css
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/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
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;reset.css
CALLING STYLESHEETS
Pitfalls of :all
<%= stylesheet_link_tag :all %>Can load things in alpha order, meaning reset happens after
application.html.erb
CALLING STYLESHEETS
Manually calling them
<%= stylesheet_link_tag ‘reset’, ‘application’, ‘buttons’ %>application.html.erb
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 ) endconfig/application.rb
FRAMEWORKS/BOILERPLATE
Combine pieces together
html5boilerplate.comBuild Your Own Framework
HIERARCHY OF H TAGS
You can reuse the H1. Really.
html |-body | |-header | | |-h1 | |-article | | |-h1 | | |-section | | | |-header | | | | |-h1 | | | |-p | | | |-h2AGENDA
HOUR 2
* The Art of Template Writing Questions
IMPROVING READABILITY
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
Because templating code gets messy.
http://www.flickr.com/photos/muehlinghaus/3564021462/FORMATTING
$ gem install rack-tidy $ gem install rack-tidy-ffiOutput formatting isn’t your job Use one of these (or similar) as middleware
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/Know your team. YTMV
What 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 logic
HELPERS
Extract complex, wrap content, View DSLs
BLOCK HELPERS
Reduce helper spaghetti code, encapsulate state, ease testing
VIEW CLASSES
QUERYING, ORDERING, LIMITING
<% Pet.all.order(:adoption_date).each do |pet| %> <%# Use pet %> <% end %>Don’t do this.
QUERYING, ORDERING, LIMITING
def index @pets_by_adoption = Pet.all.order(:adoption_date) endInstead, 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) endOr instead, use a helper
pets_helper.rb
<% pets_by_adoption.each do |pet| %> <%# Use pet %> <% end %>_adoption_list.html.erb
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
_availability.html.erb
LOCAL ASSIGNMENT
def index # ... other stuff @number_available = Pet.available.count endInstead, use an instance varaible
pets_controller.rb
LOCAL ASSIGNMENT
def number_available @number_available ||= Pet.available.count endOr 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 quick feeling of what the page contains.
CONDITIONAL CONTENT
def risk_assessment if @pet.at_risk? render partial: 'at_risk' else render partial: 'not_at_risk' end endBetter, 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 quick feeling of what the page contains.
CONDITIONAL CONTENT
<% if can_edit_pet?(pet) %> <%= render partial: 'form', locals: {pet: pet} <% end %>Better, extract condition to a 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 these
index.html.erb
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
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
render partial: 'pet', locals: {pet: pet}With an object Using :locals
<p><%= pet.description %></p>Renders _pet.html.erb Assigns pet local variable
RENDERING
render partial: 'pet', object: commentWith an object Using :object
<p><%= pet.description %></p>Also renders _pet.html.erb Assigns pet local variable
RENDERING
render petWith 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.
RENDERING
<% @pets.each do |pet| %> <%= render pet %> <% end %>With a collection of objects With a manual loop:
RENDERING
render partial: 'pet', collection: @petsWith a collection of objects Using :collection Renders _pet.html.erb for each object in @pets, assigning each as the pet local variable
RENDERING
render @petsWith 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.
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)
Using implicit partial rendering means you don’t have to CARE which partial gets rendered.
SETTING THE PAGE TITLE
The Instance Variable Method
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
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)) endpets_helper.rb
<% page_title @pet.name %>show.html.erb
The content_for Method
Use content_for in your action templates to define content that is conceptually related to the action, but structurally located elsewhere in the layout.
Don’t code generate before you have determined what your common design elements are.
AGENDA
HOUR 3
* Navigation Forming Forms Don’t Fear the Object Going Mobile Packaging Assets
WHAT NAVIGATION DOES
➡ Tells users where they can go ➡ Reminds them where they are
WHERE ARE WE?
The Instance Variable Method
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 endapplication_helper.rb _nav.html.erb
WHERE ARE WE?
The Manual Method
_nav.html.erb
<ul> <li> <%= link_to_unless_current("Home", action: ‘home’, controller: ‘pages’) %> </li> ... </ul>WHERE ARE WE?
The CSS Method
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
IT’S THE ACTION TEMPLATE’S RESPONSIBILITY.
a simple navigation gem
GITHUB: github.com/bruce/gooseUSING 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
USING GOOSE
nav.css
Styling
nav#main_nav ul { height: 30px;USING GOOSE
show.html.erb
In Action Templates
<% nav_at ‘Dogs’ %>Driving the state of the page from the action template gives you flexibility and means you know exactly where to look to make changes.
FORM_FOR vs FORM_TAG
SHOW ME!
HTML5 FORM ELEMENTS
Will affect keyboard layout on iOS devices
<input type=”email” name=”email”> diveintohtml5.org/forms.htmlSearch
HTML5 FORM ELEMENTS
<input type=”search” name=”search”> diveintohtml5.org/forms.htmlDate
HTML5 FORM ELEMENTS
Only in Opera right now
<input type=”date” name=”date”> diveintohtml5.org/forms.htmlFORMTASTIC
<%= 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
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/2810247509/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) endlib/pet_history.rb pets_helper.rb
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 endlib/pet_sort.rb
HYBRID
Controller -> View
def sort @sort ||= PetSort.new(self) end helper_method :sortpets_controller.rb
View classes allow you to more easily test complex logic (instantiate, stub out the controller, template, etc).
MOBILE USER INTERACTION
http://www.flickr.com/photos/yourdon/3599753183/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-designClient-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
@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 TEMPLATES
http://www.flickr.com/photos/goincase/4973847949/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
SET A MIME TYPE
Mime::Type.register_alias "text/html", :mobilemime_types.rb
FILTER THE REQUEST
before_filter :prepare_mobile_request! def prepare_mobile_request! if mobile_request? request.format = :mobile end endapplication_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 end endapplication_controller.rb
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_viewapplication_controller.rb
LET USERS DECIDE
def link_to_prefer_view(name) link_to_unless(preferred_view == name, "#{name.capitalize} View", prefer_view: name) endapplication_helper.rb
<% if mobile_request? %> <%= link_to_prefer_view :mobile %> | <%= link_to_prefer_view :standard %> <% end %>_footer.html.erb
Figure out what your users need and then build to that. The technology follows the use case.
WHY DOES IT LOAD SLOW?
YSlow and PageSpeed
UNUSED SELECTORS
UI side: Dust Me Selectors
sitepoint.com/dustmeselectorsUNUSED 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/deadweightA 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/jammitConcat, Convert, Minify assets
RAILS 3.1 SPROCKETS
vendor/assets app/assets
THANKYou
BOOK: www.therailsview.com/book