jquery and ruby web frameworks
DESCRIPTION
A presentation on using jQuery with Ruby web frameworks.TRANSCRIPT
jQuery + RubyRapid Development on Steroids
Ruby
• Fun
• Elegant
• Rapid
jQuery
• Fun
• Elegant
• Rapid
Me
• On jQuery Team
• On Merb Team
• Use jQuery and Rails Daily
• Love Rapid Development
Me
• On jQuery Team
• On Merb Team
• Use jQuery and Rails Daily
• Love Rapid Development
Rails
• Simplifies:
• HTML Generation
• Database access
• API creation for your web app
• Routing
Rails
• Tries but fails at:
• Nontrivial Ajax
• Nontrivial in-browser JS (i.e. form validation)
• Shielding you from JavaScript
• Key: You can’t avoid JS through Rails
Merb
• New Ruby Web Framework
• ORM Agnostic
• JS Framework Agnostic
• Similar controller/routing to Rails
• Faster and smaller than Rails
Merb
• New Ruby Web Framework
• ORM Agnostic
• JS Framework Agnostic
• Similar controller/routing to Rails
• Faster and smaller than Rails
MVC Web Frameworks• Models encapsulate database information
• Controllers route and process requests
• Views provide the raw HTML that goes to the browser
• Rails uses helpers to simplify views
• Rails helpers spit out JS
• We want to be unobtrusive
Our Approach to JS in MVC• Typically, JS represents site-wide behavior (like CSS)
• Common markup format represents behavior
•<table class="sortable">
• Use Ruby to generate markup
• Use CSS to apply style to markup
• Use jQuery to apply behavior to markup
• Profit!
Our Approach to JS in MVC• Typically, JS represents site-wide behavior (like CSS)
• Common markup format represents behavior
•<table class="sortable">
• Use Ruby to generate markup
• Use CSS to apply style to markup
• Use jQuery to apply behavior to markup
• Profit!
Helpers in Rails
• Generate Markup
• Not JavaScript
• Remember:
• concat
• capture
def sortable_table(&block) html = content_tag(:table, :class => "sortable") do capture(&block) end concat(html, block.binding)end
Helpers in Merb
• Generate Markup
• Not JavaScript
• Remember:
• concat
• capture
def sortable_table(&block) html = tag(:table, capture(&block), :class => "sortable") concat(html, block.binding)end
Very similar to Rails
Mixing it Up• We have consistent markup
produced by our framework<table class='sortable'> <thead> <tr><th>Name</th><th>Price</th></tr> </thead> <tbody> <tr> <td>Bones: The Complete Second Season</td> <td>$40.99</td> </tr> <tr> <td>Heroes: Season 1</td> <td>$39.99</td> </tr> <tr> <td>Charmed: The Final Season</td> <td>$32.99</td> </tr> </tbody></table>
$("table.sortable").tablesorter();
jQuery Code• We have consistent
markup produced by our framework
• We can add behavior
<table class='sortable' metaData='{cssHeader: "sort-header", cssAsc: "sort-header-asc", cssDesc: "sort-header-desc"}'> <thead> <tr><th>Name</th><th>Price</th></tr> </thead> <tbody> <tr> <td>Bones: The Complete Second Season</td> <td>$40.99</td> </tr> <tr> <td>Heroes: Season 1</td> <td>$39.99</td> </tr> <tr> <td>Charmed: The Final Season</td> <td>$32.99</td> </tr> </tbody></table>
Markup Code• We have consistent
markup produced by our framework
• We can add behavior
• We can support options
class Hash def metadata data = self.map {|k,v| "#{k.js_case}:#{v.metadata}" } "{#{data.join(",")}}" endend
class String def metadata "'#{self}'" end def js_case r = camelcase r[0] = r[0].chr.downcase r endend
Markup Code• We have consistent
markup produced by our framework
• We can add behavior
• We can support options
• Via some glue code
class Symbol def js_case self.to_s.js_case endend
class Object def metadata "#{self.to_s}" endend
Markup Code• We have consistent
markup produced by our framework
• We can add behavior
• We can support options
• Via some glue code
def sortable_table(options = {}, &block) html = content_tag(:table, :class => "sortable", :metadata => options.meta_data) do capture(&block) endend
Rails Helper• We have consistent
markup produced by our framework
• We can add behavior
• We can support options
• Via some glue code
• And a Rails helper
def sortable_table(options = {}, &block) html = tag(:table, capture(&block), :class => "sortable", :meta_data => options.metadata) concat(html, block.binding)end
Merb Helper• We have consistent
markup produced by our framework
• We can add behavior
• We can support options
• Via some glue code
• And a Rails helper
• Or a Merb Helper
$("table.sortable").each(function() { $(this).tablesorter($(this).metadata()); });
Rails Helper• We have consistent
markup produced by our framework
• We can add behavior
• We can support options
• Via a Rails helper
• Or a Merb Helper
• And with a quick jQuery change...
Simple Ajax Loader
<a href="ajax_url" rel="#target" class="remote"> Load it In</a>
Markup• Use everything at your disposal
$("a.remote").click(function() { $(this.rel).load(this.href);});
jQuery• Use everything at your disposal
• Write jQuery Code
def remote_link(contents, url, update = nil) url = url_for(url) if url.is_a?(Hash) options = {:href => url} options.merge!(:rel => update) if update content_tag(:a, contents, options)end
Rails Helper• Use everything at your disposal
• Write jQuery code
• Write a Rails helper
def remote_link(contents, url_param, update = nil) url = url_param.is_a?(Hash) ? url(url_param) : url options = {:href => url} options.merge!(:rel => update) if update tag(:a, contents, options)end
Merb Helper• Use everything at your disposal
• Write jQuery code
• Write a Rails helper
• Or a Merb Helper
<%= remote_link("Hey lookey here", :controller => "foo", :action => "foo") %>
<%= remote_link("Hey lookey here", {:controller => "foo", :action => "foo"}, "#update") %>
Use Helper• Use everything at your disposal
• Write jQuery code
• Write a Rails helper
• Or a Merb Helper
• Profit!
Some Caveats• Relative URLs won't work like you expect
• Check out the <base> tag
• Application.js can get pretty big
• Check out Cascading JavaScript
• http://www.redhillonrails.org/#cascading_javascripts
The <base> Tag• <base href="http://mysite.com/foo/bar" />
• Makes all URLs (including JS) operate relative to it
• Needs to be before any other URLs are specified (top of head)
• With routing:
• /foo/bar/1 and /foo/bar should have the same relative URL
• Browsers interpret the /1 as a new directory
The <base> Tag• <base href="http://mysite.com/foo/bar" />
• Makes all URLs (including JS) operate relative to it
• Needs to be before any other URLs are specified (top of head)
• With routing:
• /foo/bar/1 and /foo/bar should have the same relative URL
• Browsers interpret the /1 as a new directory
<%= tag(:base, :href => url_for(:id => "") %> <%= self_closing_tag(:base, :href => url(:id => ""))%>#=> <base href="/controller/action/" />
Rails Merb
Summary• Rails/Merb don't have built-in helpers for jQuery
Summary• Rails/Merb don't have built-in helpers for jQuery
• jQuery is easy
Summary• Rails/Merb don't have built-in helpers for jQuery
• jQuery is easy
• Writing Ruby helpers is easy
Summary• Rails/Merb don't have built-in helpers for jQuery
• jQuery is easy
• Writing Ruby helpers is easy
• Making Ruby frameworks work with jQuery is easy
Summary• Rails/Merb don't have built-in helpers for jQuery
• jQuery is easy
• Writing Ruby helpers is easy
• Making Ruby frameworks work with jQuery is easy
• We need to share our helpers and jQuery code
Demo and Some Code
• http://10.0.2.6:4000/jquery_camp
• Give me a sec to demo it before creating things ;)