<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-4238792389635811701</id><updated>2012-02-16T07:29:18.125-08:00</updated><category term='Bundle'/><category term='Textmate'/><category term='movie'/><category term='Themes'/><category term='ctags'/><category term='Scala'/><category term='Lift'/><category term='compass'/><category term='sass'/><category term='Ruby'/><category term='Javascript'/><title type='text'>Sideways Coding</title><subtitle type='html'>Blog by Mads Hartmann Jensen</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://www.sidewayscoding.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4238792389635811701/posts/default'/><link rel='alternate' type='text/html' href='http://www.sidewayscoding.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Mads Hartmann Jensen</name><uri>http://www.blogger.com/profile/07989299859443129808</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_j6ul9CMlP_Q/S2qvbDr6RKI/AAAAAAAAAAM/ZVVUtZHM3Sc/S220/IMGP0163.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>14</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-4238792389635811701.post-1170257445627316574</id><published>2011-03-20T15:33:00.000-07:00</published><updated>2011-03-20T15:37:52.686-07:00</updated><title type='text'>Lifty 1.6.1</title><content type='html'>&lt;p&gt;In the past two weeks Lifty 1.6 and 1.6.1 have been released. The following issues have been fixed: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Now works with paths that contain white-space &lt;/li&gt;
&lt;li&gt;Now works on Windows&lt;/li&gt;
&lt;li&gt;Templates have been improved and updated to use Lift version 2.3-RC3&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm sorry that it has taken this long to fix these bugs. I was caught between Scala versions and
was patiently waiting for the libraries that I use to update to Scala 2.8.x. With Timothy Perrett's book 
Lift in Action nearing its publication date I couldn't afford to wait any longer so two weeks ago I finally 
chose to create a patched version of the library that caused these issues and everything should be running
smoothly now. &lt;/p&gt;

&lt;p&gt;I'm by no means done working on Lifty - I've got some great ideas which I'll be working on but I'll cover
that in another blog post. &lt;/p&gt;

&lt;p&gt;For more information on Lifty see the &lt;a href="http://lifty.github.com/" target="_blank"&gt;site&lt;/a&gt;. For install guides go &lt;a href="http://lifty.github.com/Lifty/" target="_blank"&gt;here&lt;/a&gt;

&lt;p&gt;Thank you for your patience,&lt;br /&gt;
Mads &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4238792389635811701-1170257445627316574?l=www.sidewayscoding.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.sidewayscoding.com/feeds/1170257445627316574/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.sidewayscoding.com/2011/03/lifty-161.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4238792389635811701/posts/default/1170257445627316574'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4238792389635811701/posts/default/1170257445627316574'/><link rel='alternate' type='text/html' href='http://www.sidewayscoding.com/2011/03/lifty-161.html' title='Lifty 1.6.1'/><author><name>Mads Hartmann Jensen</name><uri>http://www.blogger.com/profile/07989299859443129808</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_j6ul9CMlP_Q/S2qvbDr6RKI/AAAAAAAAAAM/ZVVUtZHM3Sc/S220/IMGP0163.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4238792389635811701.post-2992436192711471816</id><published>2011-01-19T12:47:00.000-08:00</published><updated>2011-01-19T13:10:36.653-08:00</updated><title type='text'>Introduction to type classes in Scala in 5 minutes</title><content type='html'>&lt;p&gt;The aim of this short blog post is to give you enough information about type classes (in Scala) that you won’t twitch every time someone mentions the subject on twitter. It will focus on one application of type classes: An alternative to ad-hoc polymorphism. Once you have more time I’d suggest reading the paper &lt;a href="http://ropas.snu.ac.kr/~bruno/papers/TypeClasses.pdf"&gt;Type Classes as Objects and Implicits&lt;/a&gt; to get a better understand of the subject.&lt;/p&gt;
&lt;p&gt;The above article states that type classes have two roles&lt;/p&gt;
&lt;ol&gt;
 &lt;li&gt;Define a set of requirements for the type parameters used by generic algorithms&lt;/li&gt;
 &lt;li&gt;Propagate constraints automatically making generic algorithms convenient and practical to use&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The first is achieved by defining a trait and the second by providing an implicit object which extends that trait for a given type and an implicit parameter to the generic algorithm.&lt;/p&gt;
&lt;p&gt;Now to make it a bit less abstract lets use an example. Assume you want to write a method named ‘greedy’ which picks the largest of whichever two instances of the same class you pass to the method. For this we need to have some idea of the ordering of the instances. Instead of forcing the instances to extend some type you can accomplish it with type classes.&lt;/p&gt;
&lt;pre&gt;case class MyModel(val data: Int) 

trait Ord[T] { 
  def compare (x: T, y: T): Boolean
}

implicit object ordMyModel extends Ord[MyModel] { 
  def compare (m1: MyModel, m2: MyModel) = m1.data &amp;lt;= m2.data
}
 
def greedy[T](m1: T,m2: T)(implicit ordM: Ord[T]) = 
  if (ordM.compare (m1,m2)) m2 else m1&lt;/pre&gt;
&lt;p&gt;With the above code you can invoke the following in the &lt;span class="caps"&gt;REPL&lt;/span&gt;&lt;/p&gt;
&lt;pre&gt;val m1 = new MyModel(3) 
val m2 = new MyModel(5)
greedy(m1,m2)&lt;/pre&gt;
&lt;p&gt;The nice thing about this is that if you want to be able to use the method greedy with another type you simply have to implement and implicit object which extends the Ord[T] trait. This keeps the inheritance chain of your classes clean.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4238792389635811701-2992436192711471816?l=www.sidewayscoding.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.sidewayscoding.com/feeds/2992436192711471816/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.sidewayscoding.com/2011/01/introduction-to-type-classes-in-scala.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4238792389635811701/posts/default/2992436192711471816'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4238792389635811701/posts/default/2992436192711471816'/><link rel='alternate' type='text/html' href='http://www.sidewayscoding.com/2011/01/introduction-to-type-classes-in-scala.html' title='Introduction to type classes in Scala in 5 minutes'/><author><name>Mads Hartmann Jensen</name><uri>http://www.blogger.com/profile/07989299859443129808</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_j6ul9CMlP_Q/S2qvbDr6RKI/AAAAAAAAAAM/ZVVUtZHM3Sc/S220/IMGP0163.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4238792389635811701.post-8342214398600120474</id><published>2010-11-04T17:17:00.000-07:00</published><updated>2010-11-04T17:21:09.533-07:00</updated><title type='text'>ENSIME Textmate Bunde preview</title><content type='html'>&lt;p&gt;For the last week or so I've been spending some of my spare time hacking on an ENSIME bundle for Textmate. I won't explain how to use it yet as it's still under development and everything might change from one day to another. But I do want to show you some of the features that I've already implemented. &lt;/p&gt;

&lt;p&gt;You can watch the bundle on &lt;a href="http://github.com/mads379/ensime.tmbundle"&gt;Github&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enjoy!&lt;/p&gt;

&lt;object width="425" height="344"&gt;&lt;param name="movie" value="http://www.youtube.com/v/P5fcceWKxkU?hl=en&amp;fs=1"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/P5fcceWKxkU?hl=en&amp;fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4238792389635811701-8342214398600120474?l=www.sidewayscoding.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.sidewayscoding.com/feeds/8342214398600120474/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.sidewayscoding.com/2010/11/ensime-textmate-bunde-preview.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4238792389635811701/posts/default/8342214398600120474'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4238792389635811701/posts/default/8342214398600120474'/><link rel='alternate' type='text/html' href='http://www.sidewayscoding.com/2010/11/ensime-textmate-bunde-preview.html' title='ENSIME Textmate Bunde preview'/><author><name>Mads Hartmann Jensen</name><uri>http://www.blogger.com/profile/07989299859443129808</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_j6ul9CMlP_Q/S2qvbDr6RKI/AAAAAAAAAAM/ZVVUtZHM3Sc/S220/IMGP0163.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4238792389635811701.post-767524373481906000</id><published>2010-10-24T02:37:00.000-07:00</published><updated>2010-10-24T02:42:36.444-07:00</updated><title type='text'>Lift TableSorter Widget</title><content type='html'>&lt;p&gt;I recently committed some changes to Lift's TableSorter widget which allows you to customize it's behavior in your Scala code. In this blog post I will show you how you can use the TableSorter widget in your code. I've also added this guide to the &lt;a href="https://www.assembla.com/wiki/show/liftweb/TableSorter" target="_blank"&gt;lift wiki&lt;/a&gt;&lt;/p&gt;

&lt;h2 id="about"&gt;
            About the Widget
        &lt;/h2&gt;
        &lt;p&gt;
            The TableSorter Widget is a wrapper for the jQuery Plugin &lt;a href="http://tablesorter.com/docs/" target="_blank"&gt;TableSorter&lt;/a&gt;.
        &lt;/p&gt;
        &lt;h2 id="how_to_use_it"&gt;
            How to use it
        &lt;/h2&gt;
        &lt;h3 id="the_code"&gt;
            The code
        &lt;/h3&gt;
        &lt;p&gt;
            Lets start with some example code
        &lt;/p&gt;
        &lt;pre&gt;
&lt;code&gt;import _root_.scala.xml.{NodeSeq, Text}
import _root_.net.liftweb.http.SHtml._
import _root_.net.liftweb.util.Helpers._
import _root_.net.liftweb.widgets.tablesorter.{TableSorter, DisableSorting, Sorting, Sorter}

class TableSorterDemo {
  
  val headers = (0, DisableSorting()) :: (3,Sorter("currency")) :: Nil
  val sortList = (3,Sorting.DSC) :: Nil
  
  val options = TableSorter.options(headers,sortList)
  
  def render(xhtml: NodeSeq) :NodeSeq = {
    TableSorter("#myTable", options)
  }  
}&lt;/code&gt;
&lt;/pre&gt;
        &lt;p&gt;
            The most import bit is this &lt;code&gt;TableSorter("#myTable2", options)&lt;/code&gt; which will convert the DOM Node that matches the the CSS selector ‘#myTable’ into a sortable table. So the first argument is a CSS selector and the second is your configuration of the table. You can leave out the second argument if you just want the default configuration.
        &lt;/p&gt;
        &lt;p&gt;
            You create the configuration of the table by calling the &lt;code&gt;TableSorter.options&lt;/code&gt; with a List of (int x TableSorterOption) and a List of (int x Sorting). Here’s what the headers and sortList configure.
        &lt;/p&gt;
        &lt;p&gt;
            The headers is a List of (int x TableSorterOption) which tells the table how it should sort the data in that column. You can disable sorting of that column by using DisableSorting() or you can specify a sorter using the Sorter class. Some of the possible options for the Sorter are: “shortDate, usLongDate, percent, isoDate, url, ipAddress, currency, digit, text, shortDate, time, metadata” but check the official plugin site for more information.
        &lt;/p&gt;
        &lt;p&gt;
            The sortList is a List of (int x Sorting) tuples which decides what the default sorting of the table should be (Ascending or Descending). You could add another tuple to the list and that would be the secondary sorting.
        &lt;/p&gt;
        &lt;h3 id="the_markup"&gt;
            The Markup
        &lt;/h3&gt;
        &lt;p&gt;
            The widgets will only work if you have a table with a &amp;lt;thead&amp;gt; tag. The following is an example from test source of the lift-widgets project.
        &lt;/p&gt;
        &lt;pre&gt;
&lt;code&gt;&amp;lt;lift:TableSorterDemo /&amp;gt;
&amp;lt;table id="myTable" class="tablesorter"&amp;gt;
    &amp;lt;thead&amp;gt;
        &amp;lt;tr&amp;gt;
            &amp;lt;th&amp;gt;Last Name&amp;lt;/th&amp;gt;
            &amp;lt;th&amp;gt;First Name&amp;lt;/th&amp;gt;
            &amp;lt;th&amp;gt;Email&amp;lt;/th&amp;gt;
            &amp;lt;th&amp;gt;Due&amp;lt;/th&amp;gt;
            &amp;lt;th&amp;gt;Web Site&amp;lt;/th&amp;gt;
        &amp;lt;/tr&amp;gt;
    &amp;lt;/thead&amp;gt;
    &amp;lt;tbody&amp;gt;
        &amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt;Smith&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;John&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;jsmith@gmail.com&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;$50.00&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;http://www.jsmith.com&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt;Bach&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;Frank&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;fbach@yahoo.com&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;$50.00&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;http://www.frank.com&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt;Doe&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;Jason&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;jdoe@hotmail.com&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;$100.00&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;http://www.jdoe.com&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
        &amp;lt;tr&amp;gt;
            &amp;lt;td&amp;gt;Conway&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;Tim&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;tconway@earthlink.net&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;$50.00&amp;lt;/td&amp;gt;
            &amp;lt;td&amp;gt;http://www.timconway.com&amp;lt;/td&amp;gt;
        &amp;lt;/tr&amp;gt;
    &amp;lt;/tbody&amp;gt;
&amp;lt;/table&amp;gt;&lt;/code&gt;
&lt;/pre&gt;
        &lt;h3 id="table_sort_a_table_on_the_go"&gt;
            Table Sort a table on the go
        &lt;/h3&gt;
        &lt;p&gt;
            By using &lt;code&gt;TableSorter.jsRender("#myTable3", options).cmd&lt;/code&gt; you get a JsCmd that will convert a regular table to a TableSorter table on the fly.
        &lt;/p&gt;
        &lt;p&gt;
            Here’s an example of how to use it in a snippet
        &lt;/p&gt;
        &lt;pre&gt;
&lt;code&gt; def demo3(xhtml: NodeSeq) :NodeSeq = {
    bind("demo3", xhtml, 
      "btn" -&amp;gt; a( () =&amp;gt; TableSorter.jsRender("#myTable3", options).cmd, Text("TableSorter that table!") ))
  }&lt;/code&gt;
&lt;/pre&gt;
        &lt;p&gt;
            This will convert a tag like this &amp;lt;demo3:btn /&amp;gt; into an ajax link which will convert the table that match the CSS selector ‘#myTable3’ into a sortable table.
        &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4238792389635811701-767524373481906000?l=www.sidewayscoding.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.sidewayscoding.com/feeds/767524373481906000/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.sidewayscoding.com/2010/10/lift-tablesorter-widget.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4238792389635811701/posts/default/767524373481906000'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4238792389635811701/posts/default/767524373481906000'/><link rel='alternate' type='text/html' href='http://www.sidewayscoding.com/2010/10/lift-tablesorter-widget.html' title='Lift TableSorter Widget'/><author><name>Mads Hartmann Jensen</name><uri>http://www.blogger.com/profile/07989299859443129808</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_j6ul9CMlP_Q/S2qvbDr6RKI/AAAAAAAAAAM/ZVVUtZHM3Sc/S220/IMGP0163.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4238792389635811701.post-2166730794889924547</id><published>2010-08-31T09:31:00.001-07:00</published><updated>2010-08-31T11:35:45.771-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scala'/><category scheme='http://www.blogger.com/atom/ns#' term='Textmate'/><title type='text'>Using TextMate for Scala Development</title><content type='html'>&lt;p&gt;I’ve been developing Scala using TextMate for a couple of months now. It’s been a pleasure but every once in a while I’ve stumbled upon features that were missing that would improve the experience. I couldn’t resist the urge to implement the most interesting of these features so while working on my &lt;a href="http://lifty.github.com/" title="GSoC Project"&gt;GSoC Project&lt;/a&gt;, Lift and a commercial Scala project I set out to develop a &lt;a href="http://github.com/mads379/SBT-Console-Textmate-Plugin" title="TextMate SBT Plugin"&gt;SBT Plugin&lt;/a&gt; and a &lt;a href="http://github.com/mads379/scala.tmbundle" title="Scala Bundle"&gt;Scala Bundle&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this blog post I’ll guide you through the installing progress, show you the robes and you’ll be off happily hacking Scala using TextMate shortly after that.&lt;/p&gt;

&lt;p&gt;Just to get you excited I've taken some screen shots&lt;/p&gt;

&lt;object width="400" height="300"&gt; &lt;param name="flashvars" value="offsite=true&amp;lang=en-us&amp;page_show_url=%2Fphotos%2Fmads_hartmann%2Fsets%2F72157624849848718%2Fshow%2F&amp;page_show_back_url=%2Fphotos%2Fmads_hartmann%2Fsets%2F72157624849848718%2F&amp;set_id=72157624849848718&amp;jump_to="&gt;&lt;/param&gt; &lt;param name="movie" value="http://www.flickr.com/apps/slideshow/show.swf?v=71649"&gt;&lt;/param&gt; &lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;embed type="application/x-shockwave-flash" src="http://www.flickr.com/apps/slideshow/show.swf?v=71649" allowFullScreen="true" flashvars="offsite=true&amp;lang=en-us&amp;page_show_url=%2Fphotos%2Fmads_hartmann%2Fsets%2F72157624849848718%2Fshow%2F&amp;page_show_back_url=%2Fphotos%2Fmads_hartmann%2Fsets%2F72157624849848718%2F&amp;set_id=72157624849848718&amp;jump_to=" width="400" height="300"&gt;&lt;/embed&gt;&lt;/object&gt;

&lt;h2&gt;Installation&lt;/h2&gt;

&lt;h3 id="scroll_to_here"&gt;The bundle&lt;/h3&gt;

&lt;p&gt;Installing and configuring the bundle is a three step process. First off download &amp;amp; install it by typing the following in your terminal (requires git):&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;git clone git://github.com/mads379/scala.tmbundle.git
open scala.tmbundle&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then add the shell variable &lt;code&gt;SCALA_HOME&lt;/code&gt; in &lt;code&gt;TextMate -&amp;gt; Preferences... -&amp;gt; Advanced -&amp;gt; Shell Variables&lt;/code&gt; to the root of your Scala installation. For me this is &lt;pre&gt;&lt;code&gt;/Users/Mads/dev/programming_languages/scala-2.8.0.final&lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;

&lt;p&gt;Some of the features of the bundle uses ctags, so please add the following to your ~/.ctags file (If you haven’t got one, simply create one)&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;--langdef=scala
--langmap=scala:.scala
--regex-scala=/^[ \t]&lt;em&gt;class[ \t]+([a-zA-Z0-9_]+)/\1/c,classes/
--regex-scala=/^[ \t]&lt;/em&gt;trait[ \t]+([a-zA-Z0-9&lt;em&gt;]+)/\1/t,traits/
--regex-scala=/^[ \t]*type[ \t]+([a-zA-Z0-9&lt;/em&gt;]+)/\1/T,types/
--regex-scala=/^[ \t]&lt;em&gt;def[ \t]+([a-zA-Z0-9_\?]+)/\1/m,methods/
--regex-scala=/^[ \t]&lt;/em&gt;val[ \t]+([a-zA-Z0-9&lt;em&gt;]+)/\1/C,constants/
--regex-scala=/^[ \t]*var[ \t]+([a-zA-Z0-9&lt;/em&gt;]+)/\1/l,local variables/
--regex-scala=/^[ \t]&lt;em&gt;package[ \t]+([a-zA-Z0-9_.]+)/\1/p,packages/
--regex-scala=/^[ \t]&lt;/em&gt;case class[ \t]+([a-zA-Z0-9&lt;em&gt;]+)/\1/c,case classes/
--regex-scala=/^[ \t]*final case class[ \t]+([a-zA-Z0-9&lt;/em&gt;]+)/\1/c,case classes/
--regex-scala=/^[ \t]&lt;em&gt;object[ \t]+([a-zA-Z0-9_]+)/\1/o,objects/
--regex-scala=/^[ \t]&lt;/em&gt;private def[ \t]+([a-zA-Z0-9_]+)/\1/pd,defs/&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And that’s it! For more information about the bundle check out the &lt;a href="http://github.com/mads379/scala.tmbundle" title="Github project"&gt;Github project&lt;/a&gt;. If you haven’t done so already feel free to ‘watch’ the bundle. It encourages me when I see a new watcher ;)&lt;/p&gt;

&lt;h3&gt;The plugin&lt;/h3&gt;

&lt;p&gt;Now, for the bundle just visit &lt;a href="http://github.com/mads379/SBT-Console-Textmate-Plugin/downloads" title="this site"&gt;this site&lt;/a&gt; and download the newest version of the plugin. At the time of writing this is Terminal-BETA-2.0.zip. Once you’ve downloaded it simply unzip it and double click the Terminal.tmplugin file. &lt;/p&gt;

&lt;p&gt;You also have to add the &lt;code&gt;SBT_PATH&lt;/code&gt; shell variable in TextMate. It has to point to a shell script that starts SBT without colors, here’s an example:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;java -Xmx1512M -XX:+CMSClassUnloadingEnabled -Dsbt.log.noformat=true  -XX:MaxPermSize=256m -jar &lt;code&gt;dirname $0&lt;/code&gt;/sbt-launch-0.7.4.jar “$@”&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;My SBT path is set like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;SBT_PATH = /Users/Mads/dev/tools/sbt/sbtnocolors&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now relaunch TextMate (I’m pretty sure this is necessary) and open the TextMate preference. The plugin should’ve added a new tab called SBT Console. In this view you can change the colors of error messages etc. so it matches your current theme. &lt;/p&gt;

&lt;h2&gt;Using it&lt;/h2&gt;

&lt;h3&gt;The bundle&lt;/h3&gt;

&lt;p&gt;Like any other TextMate bundle this bundle includes a language definition which tells TextMate how it should highlight the file, indention rules, comment rules etc. The syntax definition for this bundle have been greatly improved by Paul Phillips. Paul Phillips works on the Scala compiler (to my knowledge) so the syntax highlighting is pretty precise. &lt;/p&gt;

&lt;p&gt;Besides the basic language definition it comes with a set of snippets also. The best way to explore these is by hitting ctrl+escape in a .scala file and check out the menus. &lt;/p&gt;

&lt;p&gt;Now for the fun part. The bundle also comes with a set of commands. I’ll go through the most awesome ones now.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Navigation (⌘⇧c)&lt;/strong&gt; &lt;br&gt;
This is my all-time favorite. This will display a pretty list of all the traits/classes/objects/types in the project. Simply pick the one you want and press enter and Textmate will jump to the appropriate line in the file where you declare that class/trait (etc.). This command requires you to have an index file of the project. To create one hit ⎇⌃⌘t.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Comments&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Javadoc for line (⌃⇧d)&lt;/strong&gt; &lt;br&gt;
This will analyze the the current line and add the appropriate documentation for the line (i.e. correct @param etc.)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;New javadoc line (shift-enter in comment scope)&lt;/strong&gt; &lt;br&gt;
This will create a new correctly indented comment line.&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Refactoring&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Organize imports (⌃⇧o)&lt;/strong&gt; &lt;br&gt;
This will take the current selection and organize the imports alphabetically.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reformat Document (⌃⇧h)&lt;/strong&gt; &lt;br&gt;
This will reformat the current document using Scalariform (http://github.com/mdr/scalariform).&lt;/li&gt;
&lt;/ul&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;The plugin&lt;/h3&gt;

&lt;p&gt;The plugin is of course only relevant to you if you’re using SBT. But who am I kidding. Of course you’re using SBT.&lt;/p&gt;

&lt;p&gt;Open a TextMate project - so just any folder that contains a SBT project. Now hit ⌃⇧1 to toggle the SBT console. Now you can start an interactive SBT session by typing &lt;code&gt;sbt shell&lt;/code&gt; or you can run a single command by typing &lt;code&gt;sbt compile&lt;/code&gt; or whatever command you want. You can toggle focus between the document and console by hitting ⌘⇧1&lt;/p&gt;

&lt;p&gt;If any errors/warnings happen it will link to the appropriate line in the file that contains an error. Try removing a { or similar to try it out.&lt;/p&gt;

&lt;p&gt;Thats it. Thanks for Reading, &lt;br&gt;
&lt;em&gt;Mads Hartmann&lt;/em&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4238792389635811701-2166730794889924547?l=www.sidewayscoding.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.sidewayscoding.com/feeds/2166730794889924547/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.sidewayscoding.com/2010/08/using-textmate-for-scala-development.html#comment-form' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4238792389635811701/posts/default/2166730794889924547'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4238792389635811701/posts/default/2166730794889924547'/><link rel='alternate' type='text/html' href='http://www.sidewayscoding.com/2010/08/using-textmate-for-scala-development.html' title='Using TextMate for Scala Development'/><author><name>Mads Hartmann Jensen</name><uri>http://www.blogger.com/profile/07989299859443129808</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_j6ul9CMlP_Q/S2qvbDr6RKI/AAAAAAAAAAM/ZVVUtZHM3Sc/S220/IMGP0163.jpg'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4238792389635811701.post-4211687652497972443</id><published>2010-07-31T03:01:00.000-07:00</published><updated>2010-07-31T03:05:24.091-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scala'/><category scheme='http://www.blogger.com/atom/ns#' term='Lift'/><title type='text'>It's Chat App Time</title><content type='html'>&lt;p&gt;&lt;strong&gt;This is now the "Getting started" page on &lt;a href="http://www.liftweb.com/docs/getting_started.html" target="_blank"&gt;Lift's website&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I have the honor to represent the demo that David Pollak gives all the time, it's the ubiquitous Lift chat app.&lt;/p&gt;
    
&lt;p&gt;In this article I'll show how you can create a comet-enabled chat application using Lift. I will show all the code you need to get it working and walk through the lines step by step to give you an understanding of what's happening. In the end I'll show how to enhance the application with some extra functionality and a few effects.&lt;/p&gt;
    
&lt;p&gt;Before we begin I want to say a quick word about comet in case it's the first you've heard of it. Comet describes a model where the client sends a request to the server. The request is hanging till the server has something interesting to response. As soon as the server responses another request is made. The idea is to give the impression that the server is notifying the client of changes on the server.&lt;/p&gt;

&lt;p&gt;To get started install the &lt;a href="http://code.google.com/p/simple-build-tool/"&gt;Simple Build Tool&lt;/a&gt; (aka sbt) and download the 
&lt;a href="http://github.com/lift/lift_sbt_prototype/tarball/Lift_20"&gt;TAR&lt;/a&gt; or &lt;a href="http://github.com/lift/lift_sbt_prototype/zipball/Lift_20"&gt;Zip&lt;/a&gt; of the default Lift project and un-tar or un-zip the file.&lt;/p&gt;
    
&lt;p&gt;Now &lt;em&gt;cd&lt;/em&gt; into the new folder and type &lt;code&gt;sbt update&lt;/code&gt; to grab the dependencies.&lt;/p&gt;
    
&lt;p&gt;Next, spark up your editor of choice and create the &lt;em&gt;src/main/scala/code/comet/Chat.scala&lt;/em&gt; file. Put the following code into Chat.scala:&lt;/p&gt;
    
&lt;pre&gt;&lt;code&gt;package code.comet&lt;br&gt;  &lt;br&gt;import net.liftweb._&lt;br&gt;import http._&lt;br&gt;import actor._&lt;br&gt;  &lt;br&gt;object ChatServer extends LiftActor with ListenerManager {&lt;br&gt;  &lt;br&gt;  private var messages = List("Welcome")&lt;br&gt;  &lt;br&gt;  def createUpdate = messages&lt;br&gt;  &lt;br&gt;  override def lowPriority = {&lt;br&gt;    case s: String =&amp;gt; messages ::= s ; updateListeners()&lt;br&gt;  }&lt;br&gt;}&lt;br&gt;  &lt;br&gt;class Chat extends CometActor with CometListener {  &lt;br&gt;  &lt;br&gt;  private var msgs: List[String] = Nil  &lt;br&gt;  &lt;br&gt;  def registerWith = ChatServer  &lt;br&gt;  &lt;br&gt;  override def lowPriority = {&lt;br&gt;    case m: List[String] =&amp;gt; msgs = m; reRender(false)  &lt;br&gt;  }  &lt;br&gt;  &lt;br&gt;  def render = {  &lt;br&gt;    &amp;lt;div&amp;gt;&lt;br&gt;    &amp;lt;ul&amp;gt;&lt;br&gt;    {&lt;br&gt;      msgs.reverse.map(m =&amp;gt; &amp;lt;li&amp;gt;{m}&amp;lt;/li&amp;gt;)&lt;br&gt;    }&lt;br&gt;    &amp;lt;/ul&amp;gt;&lt;br&gt;    &amp;lt;lift:form&amp;gt;&lt;br&gt;    {&lt;br&gt;      SHtml.text("", s =&amp;gt; ChatServer ! s)&lt;br&gt;    }&lt;br&gt;    &amp;lt;input type="submit" value="Chat"/&amp;gt;&lt;br&gt;    &amp;lt;/lift:form&amp;gt;&lt;br&gt;    &amp;lt;/div&amp;gt;&lt;br&gt;  }&lt;br&gt;}&lt;/code&gt;&lt;/pre&gt;
  
&lt;p&gt;In your &lt;em&gt;src/main/webapp/index.html&lt;/em&gt; file, put the tag: &lt;code&gt;&amp;lt;lift:comet type="Chat"/&amp;gt;&lt;/code&gt; and run the app by typing the following in your console:&lt;/p&gt;
  
&lt;pre&gt;&lt;code&gt;sbt ~jetty-run&lt;/code&gt;&lt;/pre&gt;

Now browse to &lt;em&gt;http://localhost:8080&lt;/em&gt; with multiple browsers and you have your chat app. Pretty cool huh? Lets walk through the code to figure out how it all fits together.
    
&lt;pre&gt;&lt;code&gt;object ChatServer extends LiftActor with ListenerManager {&lt;br&gt;  &lt;br&gt;  private var messages = List("Welcome")&lt;br&gt;  &lt;br&gt;  def createUpdate = messages&lt;br&gt;  &lt;br&gt;  override def lowPriority = {&lt;br&gt;    case s: String =&amp;gt; messages ::= s ; updateListeners()&lt;br&gt;  }&lt;br&gt;}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We're doing a couple of things here. In the first line we're defining a chat server as an object (singleton) that's a &lt;a href="http://main.scala-tools.org/mvnsites/liftweb-2.0/framework/scaladocs/net/liftweb/actor/LiftActor.html" target="_blank"&gt;LiftActor&lt;/a&gt; and that can manage listeners by mixing in the &lt;a href="http://main.scala-tools.org/mvnsites/liftweb-2.0/framework/scaladocs/net/liftweb/http/ListenerManager.html" target="_blank"&gt;ListenerManager&lt;/a&gt; trait.&lt;/p&gt;

&lt;p&gt;In the implementation of our ChatServer we're creating a private list of strings that we'll use to store the messages posted by the clients. 
The createUpdate method is called when the updateListeners method needs a message to send to the subscribed Actors. Here's it's simply returning all the messages posted to the server.&lt;/p&gt;

&lt;p&gt;Lastly we're overriding the lowPriority method where we're pattern matching against the messages sent to us. If the message is a string we're simply adding it to the list of messages and telling all the listeners that something happened by invoking the updateListeners method which we got by mixing in the ListenerManager trait.&lt;/p&gt;

&lt;p&gt;lowPriority is just one of three methods (lowPriority, mediumPriority, hightPriority) you can override to process the messages. As the names may suggest the three methods lets you prioritize your messages.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class Chat extends CometActor with CometListener {&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Here we're defining our chat component that knows how to push updates to the browser and interact with the ChatServer.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;private var msgs: List[String] = Nil&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This is where we'll store our local state.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def registerWith = ChatServer&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Here the component is registrering itself with the ChatSever so it will get notified of any changes. &lt;/p&gt;

&lt;pre&gt;&lt;code&gt;override def lowPriority = {&lt;br&gt;  case m: List[String] =&amp;gt; msgs = m; reRender(false)&lt;br&gt;}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This is where we implement how our component will handle the messages from our ChatServer. We're simply updating our local state (msgs) and invoking reRender(false). false tells Lift that we don't want to rerender the entire page but just the comet component.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;def render = {&lt;br&gt;  &amp;lt;div&amp;gt;&lt;br&gt;  &amp;lt;ul&amp;gt;&lt;br&gt;  {&lt;br&gt;    msgs.reverse.map(m =&amp;gt; &amp;lt;li&amp;gt;{m}&amp;lt;/li&amp;gt;)&lt;br&gt;  }&lt;br&gt;  &amp;lt;/ul&amp;gt;&lt;br&gt;  &amp;lt;lift:form&amp;gt;&lt;br&gt;  {&lt;br&gt;    // a &amp;lt;lift:form&amp;gt; is an Ajax form.  Define our&lt;br&gt;    // input box that sends the message to the chat server&lt;br&gt;    SHtml.text("", s =&amp;gt; ChatServer ! s)&lt;br&gt;  }&lt;br&gt;  &amp;lt;input type="submit" value="Chat"/&amp;gt;&lt;br&gt;  &amp;lt;/lift:form&amp;gt;&lt;br&gt;  &amp;lt;/div&amp;gt;&lt;br&gt;}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Here we're telling the component how to render itself. But, &lt;strong&gt;OMG&lt;/strong&gt;... we've mixed view logic with our Scala code, gaaakkk... sputter... barf. Yes, Lift allows you to mix view into your business logic, but it's a choice. Here's how we can break the code out to separate the view from the logic.  The critical thing to keep in mind is that we did not change our logic at all in order to achieve the separation.&lt;/p&gt;

&lt;p&gt;First, let's update our view (&lt;em&gt;src/main/webapp/index.html&lt;/em&gt;) to:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;lift:comet type="Chat"&amp;gt;&lt;br&gt;  &amp;lt;ul&amp;gt;&lt;br&gt;    &amp;lt;chat:line&amp;gt;&lt;br&gt;      &amp;lt;li&amp;gt;&amp;lt;chat:msg/&amp;gt;&amp;lt;/li&amp;gt;&lt;br&gt;    &amp;lt;/chat:line&amp;gt;&lt;br&gt;  &amp;lt;/ul&amp;gt;&lt;br&gt;  &amp;lt;lift:form&amp;gt;&lt;br&gt;    &amp;lt;chat:input/&amp;gt;&lt;br&gt;    &amp;lt;input type="submit" value="chat"/&amp;gt;&lt;br&gt;  &amp;lt;/lift:form&amp;gt;&lt;br&gt;&amp;lt;/lift:comet&amp;gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The view now contains the definition of the layout with "bind points" where dynamic content will be inserted.&lt;/p&gt;

&lt;p&gt;Next, let's update our Chat component's render method.  Replace the def render = line through the end of the file (except the closing brace) with:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;// import NodeSeq... yes, this can be done inside any scope in Scala&lt;br&gt;import scala.xml.NodeSeq&lt;br&gt;&lt;br&gt;def render =&lt;br&gt;    bind("chat", // the namespace for binding&lt;br&gt;         "line" -&amp;gt; lines _, // bind the function lines&lt;br&gt;         "input" -&amp;gt; SHtml.text("", s =&amp;gt; ChatServer ! s))&lt;br&gt;&lt;br&gt;private def lines(xml: NodeSeq): NodeSeq =&lt;br&gt;  msgs.reverse.flatMap(m =&amp;gt; bind("chat", xml, "msg" -&amp;gt; m))&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Instead of using inline xhtml we're using the bind method. You'll find the bind method in the &lt;a href="http://main.scala-tools.org/mvnsites/liftweb-2.0/framework/scaladocs/net/liftweb/util/BindHelpers$object.html" target="_blank"&gt;BindHelpers&lt;/a&gt; trait and it is used to bind real content to the binding points of your templates.&lt;/p&gt;

&lt;p&gt;In this code we're binding content to tags with the namespace &lt;em&gt;chat&lt;/em&gt;. The last argument of bind is repeatable and accepts &lt;a href="http://main.scala-tools.org/mvnsites/liftweb-2.0/framework/scaladocs/net/liftweb/util/BindHelpers.BindParam.html" target="_blank"&gt;BindParam&lt;/a&gt;. So the second and third line of the bind statement are really instances of BindParam and should be read: "replace the tag &lt;code&gt;&amp;lt;chat:line&amp;gt;&lt;/code&gt; with the return value of invoking lines" and "replace the tag &lt;code&gt;&amp;lt;chat:input&amp;gt;&lt;/code&gt; with the return value of invoking the text method on the SHtml object". &lt;br /&gt; You might notice that we aren't specifying which xml to bind to in our bind statements like we've done previously. This is because the markup passed to the comet component when it is instantiated (i.e. every child node of &lt;code&gt;&amp;lt;lift:comet type=&amp;quot;Chat&amp;quot;&amp;gt;&lt;/code&gt;) is kept around and that's what we're binding against.&lt;/p&gt;

&lt;p&gt;the method lines simply takes a NodeSeq and returns a NodeSeq. It reverses the list of messages so the newest message will be at the end of the list and flatMaps the list of messages into the proper xhtml by using the bind method. Note that we have to use flatMap instead of map, because map would result in a Seq[NodeSeq] instead of the expeced NodeSeq (Seq[Node]).&lt;/p&gt;

&lt;p&gt;The text method of the &lt;a href="http://main.scala-tools.org/mvnsites/liftweb-2.0/framework/scaladocs/net/liftweb/http/SHtml$object.html" target="_blank"&gt;SHtml&lt;/a&gt; object returns an input field. The method takes two normal arguments and a repeatable one. The first is the initial value of the input field and the second is a function that takes a string and returns Any (&lt;em&gt;(String) =&amp;gt; Any&lt;/em&gt;). This function will be invoked when the form is submitted. Our function takes a string s and sends that string to our ChatServer using the bang (!) method.&lt;/p&gt;

&lt;p&gt;I hope this has helped you gain a better understanding of what the code does. Now, it would be quite dull if all I did was recite the demo that David has done numerous times - Lets see if I can't spice the demo application up a bit to provide some extra functionality.&lt;/p&gt;

&lt;p&gt;Lets add the following to our application&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;It should be possible to delete messages&lt;/li&gt;
  &lt;li&gt;When a message is deleted/added it should fade out/in&lt;/li&gt;
&lt;/ul&gt;


&lt;p&gt;To achieve this lets start editing the view (&lt;em&gt;src/main/webapp/index.html&lt;/em&gt;) so it looks like this:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&amp;lt;lift:comet type=&amp;quot;Chat&amp;quot;&amp;gt;
  &amp;lt;ul id=&amp;quot;ul_dude&amp;quot;&amp;gt;
    &amp;lt;chat:line&amp;gt;
      &amp;lt;li&amp;gt;&amp;lt;chat:msg/&amp;gt; &amp;lt;chat:btn/&amp;gt;&amp;lt;/li&amp;gt;
    &amp;lt;/chat:line&amp;gt;
  &amp;lt;/ul&amp;gt;
  &amp;lt;lift:form&amp;gt;
    &amp;lt;chat:input/&amp;gt;
    &amp;lt;input type=&amp;quot;submit&amp;quot; value=&amp;quot;chat&amp;quot;/&amp;gt;
  &amp;lt;/lift:form&amp;gt;
&amp;lt;/lift:comet&amp;gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Not a whole lot change in the view, we simply added a new tag &lt;code&gt;&amp;lt;chat:btn/&amp;gt;&lt;/code&gt; and added an id attribute to the &lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt; tag. So lets fast forward to the exciting part. Changing the &lt;em&gt;ChatServer&lt;/em&gt; and &lt;em&gt;Chat&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Start by adding the following imports to your &lt;em&gt;src/main/scala/code/comet/Chat.scala&lt;/em&gt; file:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;import js._
import JsCmds._
import js.jquery.JqJsCmds.{AppendHtml, FadeOut, Hide, FadeIn}
import java.util.Date
import scala.xml._&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now that we've got the right classes imported lets start looking at the actual code, again in your &lt;em&gt;src/main/scala/code/comet/Chat.scala&lt;/em&gt; file add the following:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;sealed trait ChatCmd

object ChatCmd {
  implicit def strToMsg(msg: String): ChatCmd =
    new AddMessage(Helpers.nextFuncName, msg, new Date)
}

final case class AddMessage(guid: String, msg: String, date: Date) extends ChatCmd
final case class RemoveMessage(guid: String) extends ChatCmd&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;In the two last lines we're creating two case classes (the final keyword means you can't subclass them). We're going to send instances of these classes between the client and server instead of strings as we did earlier. We're also creating an object named ChatCmd which has an implicit conversion from string to AddMessage as this will simplify the code in Chat as we'll be able to send the string the user entered in the input field and let the implicit conversion do the work of instantiating an instance of AddMessage.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;Helpers.nextFuncName&lt;/code&gt; simply creates a unique string based on the current time and a random String generation. We use it here as a unique id for both AddMessage and RemoveMessage.&lt;/p&gt;

&lt;p&gt;Now lets take a look how we need to change the ChatServer:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;object ChatServer extends LiftActor with ListenerManager {
  
  private var messages: List[ChatCmd] = List("Welcome")
  
  def createUpdate = messages
  
  override def lowPriority = {
    case s: String =&gt; messages ::= s ; updateListeners()
    case d: RemoveMessage =&gt; messages ::= d ; updateListeners()
  }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Instead of using a list of strings to store our messages we're using a list of ChatCmd (both AddMessage and RemoveMessage are subclasses of ChatCmd). The lowPriority message has also changed a bit. We're pattern matching against the message and if it's a string we simply add it to the list of ChatCmd ... but wait ... String isn't a subclass of ChatCmd so surely this doesn't compile. Oh, but it does, this is where our implicit conversion from String to AddMessage comes in handy. The compiler does notice that String isn't a subclass of ChatCmd but before it starts complaining it checks if there is any implicit conversion in scope that might be able to solve the type problem and in this case there is.&lt;/p&gt;

&lt;p&gt;Finally lets take a look at the Chat. Replace the following implementation of Chat with the one currently in your file:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class Chat extends CometActor with CometListener {  
  private var msgs: List[ChatCmd] = Nil  
  private var bindLine: NodeSeq = Nil

  def registerWith = ChatServer  
  
  override def lowPriority = {
    case m: List[ChatCmd] =&gt; {
      val delta = m diff msgs
      msgs = m
      updateDeltas(delta)
    }
  }  

  def updateDeltas(what: List[ChatCmd]) {
    partialUpdate(what.foldRight(Noop) {
      case (m: AddMessage , x) =&gt;
        x &amp; CmdPair(AppendHtml("ul_dude", doLine(m)), 
                    CmdPair(Hide(m.guid), FadeIn(m.guid, TimeSpan(0),TimeSpan(500))))
      case (RemoveMessage(guid), x) =&gt;
        x &amp; CmdPair(FadeOut(guid,TimeSpan(0),TimeSpan(500)),
                    After(TimeSpan(500),Replace(guid, NodeSeq.Empty)))
    })
  }

  def render =
    bind("chat", // the namespace for binding
         "line" -&gt; lines _, // bind the function lines
         "input" -&gt; SHtml.text("", s =&gt; ChatServer ! s)) // the input
  
  private def lines(xml: NodeSeq): NodeSeq = {
    bindLine = xml
    val deleted = Set((for {
      RemoveMessage(guid) &lt;- msgs
    } yield guid) :_*)

    for {
      m @ AddMessage(guid, msg, date) &lt;- msgs.reverse if !deleted.contains(guid)
      node &lt;- doLine(m)                              
    } yield node
  }

  private def doLine(m: AddMessage): NodeSeq =
    bind("chat", addId(bindLine, m.guid),
         "msg" -&gt; m.msg,
         "btn" -&gt; SHtml.ajaxButton("delete", 
                                   () =&gt; {
                                     ChatServer ! 
                                     RemoveMessage(m.guid)
                                     Noop}))
              

  private def addId(in: NodeSeq, id: String): NodeSeq = in map {
    case e: Elem =&gt; e % ("id" -&gt; id)
    case x =&gt; x
  }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Bam, If you restart the jetty server and browse to http://localhost:8080 after pasting in the above code you should have a chat application with fancy fading messages and the ability to delete old messages. I hope this is enough to keep your motivated as we walk through the code. Lets take it from the top:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;private var msgs: List[ChatCmd] = Nil  
private var bindLine: NodeSeq = Nil

def registerWith = ChatServer&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We're declaring a list of ChatCmd which we'll use as our local state (ChatCmd instead of String) and a NodeSeq called bindLine which I'll talk about later when we're using it. We're still registering with ChatServer.&lt;/p&gt; 

&lt;pre&gt;&lt;code&gt;override def lowPriority = {
  case m: List[ChatCmd] =&gt; {
    val delta = m diff msgs
    msgs = m
    updateDeltas(delta)
  }
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Again, the lowPriority method is the one that deals with the messages sent from the ChatServer. We're pattern matching against the messages and if it's a list of ChatCmd we're calculating the difference between the new list and our local state using the diff method on List and store the result in the variable delta. Then we're replacing our local state with the new list and finally we call updateDeltas with delta. Now lets take a look at what updateDeltas actually does:&lt;/p&gt; 

&lt;pre&gt;&lt;code&gt;def updateDeltas(what: List[ChatCmd]) {
  partialUpdate(what.foldRight(Noop) {
    case (m: AddMessage , x) =&gt;
      x &amp; CmdPair(AppendHtml("ul_dude", doLine(m)), 
                  CmdPair(Hide(m.guid), FadeIn(m.guid, TimeSpan(0),TimeSpan(500))))
    case (RemoveMessage(guid), x) =&gt;
      x &amp; CmdPair(FadeOut(guid,TimeSpan(0),TimeSpan(500)),
                  After(TimeSpan(500),Replace(guid, NodeSeq.Empty)))
  })
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We're calling partialUpdate which is declared in CometActor and takes a &lt;a href="http://main.scala-tools.org/mvnsites/liftweb-2.0/framework/scaladocs/net/liftweb/http/js/JsCmd.html" target="_blank"&gt;JsCmd&lt;/a&gt; as it's only argument. As the name may suggest partialUpdate is used to do partial updates of your comet component. The exciting part of updateDeltas is how we convert a List[ChatCmd] into a JsCmd. Lets take a look.&lt;/p&gt;

&lt;p&gt;We declared &lt;em&gt;what&lt;/em&gt; as an argument of updateDeltas. We're calling foldRight on &lt;em&gt;what&lt;/em&gt; which is a method on List that has the following method signature &lt;code&gt;foldRight [B](z : B)(f : (A, B) =&gt; B) : B&lt;/code&gt;. Unless you're used to reading Scala code this doesn't help you much, so here's the explanation of foldRight from the Scala Library Documentation: &lt;q&gt;Combines the elements of this list together using the binary function f, from right to left, and starting with the value z&lt;/q&gt;.&lt;/p&gt;

&lt;p&gt;In our case we're currying it with Noop which extends JsCmd and basically is an empty javascript statement. In the binary function we're pattern matching against the arguments which is the current element from the list starting from the right-most element and the cumulated value of foldRight so far.&lt;/p&gt;

&lt;p&gt;If the element is an instance of AddMessage we're doing a couple of things, first off we're chaining javascripts calls using &lt;a href="http://main.scala-tools.org/mvnsites/liftweb-2.0/framework/scaladocs/net/liftweb/http/js/JsCmds/CmdPair.html" target="_blank"&gt;CmdPair&lt;/a&gt; which takes two arguments of type JsCmd. The left-most argument well be invoked before the right-most one. We're also using the  AppendHtml object declared in &lt;a href="http://main.scala-tools.org/mvnsites/liftweb-2.0/framework/scaladocs/net/liftweb/http/js/jquery/JqJsCmds$object.html" target="_blank"&gt;JqJsCmds&lt;/a&gt; which has an apply method &lt;code&gt;def apply(uid: String, content: NodeSeq): JsCmd&lt;/code&gt; that takes the id of the node to append html to, in this case it's our UL tag with the id &lt;em&gt;ul_dude&lt;/em&gt;. The second argument is the NodeSeq to append. In this case we're calling the doLine method with our instance of AddMessage. Well go through that method in due time don't worry. For the second argument of the first CmdPair we're chaining together another CmdPair where we're hiding the html we've just created using the &lt;a href="http://main.scala-tools.org/mvnsites/liftweb-2.0/framework/scaladocs/net/liftweb/http/js/jquery/JqJsCmds/Hide.html" target="_blank"&gt;Hide&lt;/a&gt; class and then we're using the &lt;a href="http://main.scala-tools.org/mvnsites/liftweb-2.0/framework/scaladocs/net/liftweb/http/js/jquery/JqJsCmds/FadeIn.html" target="_blank"&gt;FadeIn&lt;/a&gt; object to fade in the message. Had this not been a demo I would probably have added a css class to the newly created messages with the display property set to none and then simply fade in the messages ones added.&lt;/p&gt;

&lt;p&gt;In the second match statement we're using Scala Extractors to fetch the value guid of RemoveMessage. For more information about extractors read &lt;a href="http://www.scala-lang.org/node/112" target="_blank"&gt;this&lt;/a&gt;. If the unapply method on RemoveMessage was successful (i.e. returned Some) we're using CmdPair once more. First we're fading out the message using &lt;a href="http://main.scala-tools.org/mvnsites/liftweb-2.0/framework/scaladocs/net/liftweb/http/js/jquery/JqJsCmds/FadeOut.html" target="_blank"&gt;FadeOut&lt;/a&gt; and then we're creating an instance of &lt;a href="http://main.scala-tools.org/mvnsites/liftweb-2.0/framework/scaladocs/net/liftweb/http/js/JsCmds/After.html" target="_blank"&gt;After&lt;/a&gt; which allows us to invoke a JsCmd after waiting for the amount of time specified in the instantiation of After. The JsCmd we're handing to After is an instance of &lt;a href="http://main.scala-tools.org/mvnsites/liftweb-2.0/framework/scaladocs/net/liftweb/http/js/JsCmds/Replace.html" target="_blank"&gt;Replace&lt;/a&gt; which we use to replace the node with id guid with NodeSeq.Empty (i.e. nothing).&lt;/p&gt;

&lt;p&gt;In each case we're using the &amp; method on &lt;a href="http://main.scala-tools.org/mvnsites/liftweb-2.0/framework/scaladocs/net/liftweb/http/js/JsCmd.html" target="_blank"&gt;JsCmd&lt;/a&gt; to combine the new JsCmd with the cumulated JsCmd which means that the entire foldRight method wil results in a JsCmd that will remove all unwanted messages and add all the new ones on the fly with Javascript. Neat!&lt;/p&gt;

&lt;p&gt;The render method hasn't changed so we can skip that. The lines method however has, so lets take a look at it:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;private def lines(xml: NodeSeq): NodeSeq = {
  bindLine = xml
  val deleted = Set((for {
    RemoveMessage(guid) &lt;- msgs
  } yield guid) :_*)

  for {
    m @ AddMessage(guid, msg, date) &lt;- msgs.reverse if !deleted.contains(guid)
    node &lt;- doLine(m)                              
  } yield node
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The first thing we're doing is storing the xml in the private variable bindLine (the one I mentioned earlier). We're storing it because we need to use it in the doLine method that I'll explain next. Next we're creating a local variable &lt;em&gt;deleted&lt;/em&gt; that we'll use to store the guid of all the messages that should be deleted. We find all the deleted messages by using a for comprehension. We're yielding the guild of all the objects in msgs (our local list of messages) by using the RemoveMessage extractor (which I explained earlier) in our for-comprehension. The for-comprehension returns a List so if we pass it to Set(..) we'll get a Set with Lists instead of a Set of String. To avoid this we're using :_* which tells scala to pass each element of the list to Set as a separate argument.&lt;/p&gt;

&lt;p&gt;Next we're using yet another for-comprehension. This time we want to do something with all the instances in msgs (reversed) that isn't part of set of deleted messages. In the second line we're storing the result of invoking doLine with the message. Again we're yielding the result of the for-comprehension so the result of the for-comprehension will be NodeSeq.&lt;/p&gt;

&lt;p&gt;Finally it's time to take a look at doLine:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;private def doLine(m: AddMessage): NodeSeq =
  bind("chat", addId(bindLine, m.guid),
       "msg" -&gt; m.msg,
       "btn" -&gt; SHtml.ajaxButton("delete", 
                                 () =&gt; {
                                   ChatServer ! 
                                   RemoveMessage(m.guid)
                                   Noop}))&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We're using the bind message to bind content to nodes with the prefix chat in the NodeSeq returned by calling addId with bindLine and the guid of the AddMessage passed to doLine. We'll look at addId next. The new thing in this bind statement is the invocation of SHtml.ajaxButton(...). The AjaxButton of the &lt;a href="http://main.scala-tools.org/mvnsites/liftweb-2.0/framework/scaladocs/net/liftweb/http/SHtml$object.html" target="_blank"&gt;SHtml object&lt;/a&gt; takes two arguments, the text of the button and a function that takes zero arguments and returns a JsCmd that will get invoked when the button is clicked. In this case we're setting the value of the button to delete and the function sends a RemoveMessage with the guid of the current message to the ChatServer followed by Noop.&lt;/p&gt;

&lt;p&gt;Now lets take a look at the very last method:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;private def addId(in: NodeSeq, id: String): NodeSeq = in map {
  case e: Elem =&gt; e % ("id" -&gt; id)
  case x =&gt; x
}&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;It takes a NodeSeq and a String. It simply matches against the NodeSeq: If it's an Elem (scala.xml.Elem) it simply adds the attribute id with the value of the argument id. If it's anything else (well it has to be NodeSeq or a subclass of NodeSeq or else the compiler would have complained) it just returns that.&lt;/p&gt;

&lt;p&gt;And thats it! I hope this have given you some taste of what Lift is able to do. If you have any feedback please don't hesitate to communicate it to the &lt;a href="http://groups.google.com/group/liftweb" target="_blank"&gt;community&lt;/a&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4238792389635811701-4211687652497972443?l=www.sidewayscoding.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.sidewayscoding.com/feeds/4211687652497972443/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.sidewayscoding.com/2010/07/i-have-honor-to-represent-demo-that.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4238792389635811701/posts/default/4211687652497972443'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4238792389635811701/posts/default/4211687652497972443'/><link rel='alternate' type='text/html' href='http://www.sidewayscoding.com/2010/07/i-have-honor-to-represent-demo-that.html' title='It&apos;s Chat App Time'/><author><name>Mads Hartmann Jensen</name><uri>http://www.blogger.com/profile/07989299859443129808</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_j6ul9CMlP_Q/S2qvbDr6RKI/AAAAAAAAAAM/ZVVUtZHM3Sc/S220/IMGP0163.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4238792389635811701.post-8661504590741913078</id><published>2010-05-03T14:03:00.000-07:00</published><updated>2010-05-04T01:34:13.715-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='Textmate'/><title type='text'>Textmate Javascript API</title><content type='html'>&lt;p&gt;TextMate has a lot of really cool APIs that you might not be aware of. The biggest one as far as I know is the Ruby wrapper which is really impressive. I'll write a post about that on a later date. &lt;/p&gt;
&lt;p&gt;In this post I'll cover the Javascript wrapper. This is how the LaTex Bundle allows you to typeset/clean/etc. your tex files by clicking on buttons. &lt;/p&gt;
&lt;h4&gt;How to use it&lt;/h4&gt;
&lt;p&gt;When you display output as HTML you have access to the TextMate javascript object. One very interesting method of the TextMate object is the system method which enables you to run system (shell) commands from Javascript.&lt;/p&gt;
&lt;p&gt;Here's an example from the &lt;a href="http://wiki.github.com/mads379/Textmate-Javascript-Console-Bundle/" target="_blank"&gt;Javascript Console bundle&lt;/a&gt; I'm currently working on: &lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var JsConsoleHelper = (function() {

 var helper = {};
 
 helper.openGlobalConfig = function() { 
  var cmd = 'cd "${TM_BUNDLE_SUPPORT}"; mate jsconsole_config.yaml',
    myCommand = TextMate.system(cmd);
  myCommand.onreadoutput = function(str) { console.log("read: " + str); };
  myCommand.onreaderror = function(str) { console.log("error: " + str); };
 };
 
 helper.openProjectConfig = function() {
  var cmd = 'cd "${TM_PROJECT_DIRECTORY:-$TM_DIRECTORY}"; mate jsconsole_config.yaml',
    myCommand = TextMate.system(cmd);
  myCommand.onreadoutput = function(str) { console.log("read: " + str); };
  myCommand.onreaderror = function(str) { console.log("error: " + str); };
 };
 
 return helper;
 
})();&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The system function returns a RuntimeObject. It has hooks for handling output and error output. In my example I currently just write to the console but what you would most likely want to do is update the DOM in some way to notify the user of what's going on. &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4238792389635811701-8661504590741913078?l=www.sidewayscoding.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.sidewayscoding.com/feeds/8661504590741913078/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.sidewayscoding.com/2010/05/textmate-javascript-api.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4238792389635811701/posts/default/8661504590741913078'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4238792389635811701/posts/default/8661504590741913078'/><link rel='alternate' type='text/html' href='http://www.sidewayscoding.com/2010/05/textmate-javascript-api.html' title='Textmate Javascript API'/><author><name>Mads Hartmann Jensen</name><uri>http://www.blogger.com/profile/07989299859443129808</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_j6ul9CMlP_Q/S2qvbDr6RKI/AAAAAAAAAAM/ZVVUtZHM3Sc/S220/IMGP0163.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4238792389635811701.post-4432458043618931211</id><published>2010-03-25T13:30:00.000-07:00</published><updated>2010-03-26T02:12:20.417-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Textmate'/><category scheme='http://www.blogger.com/atom/ns#' term='Themes'/><title type='text'>Novel TextMate theme</title><content type='html'>&lt;p&gt;I'm addicted to changing my TextMate theme and recently I've run out of themes to switch between - So I though wth, I'll create my own. &lt;/p&gt;

&lt;p&gt;However as I don't have a single design bone in my body I was just picking random colours and the result was pretty hideous - So I though it might be better to get 'inspiration' somewhere else and I picked one of the standard themes of Terminal.app&lt;/p&gt;

&lt;strong&gt;Comments&lt;/strong&gt;
&lt;cite&gt;"that theme makes programming look like a walk in the park some late afternoon with your best friend &amp; toffee #goodstuff" &lt;br/&gt; &lt;a href="www.twitter.com/pokho" target="_blank"&gt;@pokho&lt;/a&gt;&lt;/cite&gt;

&lt;p&gt;Right now I've only created Novel but I reckon It won't be long before I give Red Sands a shot too. Take a look:&lt;/p&gt;

  &lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_j6ul9CMlP_Q/S6vNuFAYXGI/AAAAAAAAABQ/LwkCYUhtlzw/s1600/scala.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 303px;" src="http://2.bp.blogspot.com/_j6ul9CMlP_Q/S6vNuFAYXGI/AAAAAAAAABQ/LwkCYUhtlzw/s400/scala.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5452677965363567714" /&gt;&lt;/a&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_j6ul9CMlP_Q/S6vNt-7G_6I/AAAAAAAAABI/oIRmpfCABxU/s1600/sass.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 338px;" src="http://3.bp.blogspot.com/_j6ul9CMlP_Q/S6vNt-7G_6I/AAAAAAAAABI/oIRmpfCABxU/s400/sass.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5452677963730845602" /&gt;&lt;/a&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_j6ul9CMlP_Q/S6vNtkJ0D1I/AAAAAAAAABA/LIdpzFUXIxI/s1600/js.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 304px;" src="http://4.bp.blogspot.com/_j6ul9CMlP_Q/S6vNtkJ0D1I/AAAAAAAAABA/LIdpzFUXIxI/s400/js.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5452677956544761682" /&gt;&lt;/a&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_j6ul9CMlP_Q/S6vNtEdlSwI/AAAAAAAAAA4/dIWG7sNpHKM/s1600/html.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 347px;" src="http://3.bp.blogspot.com/_j6ul9CMlP_Q/S6vNtEdlSwI/AAAAAAAAAA4/dIWG7sNpHKM/s400/html.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5452677948037745410" /&gt;&lt;/a&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_j6ul9CMlP_Q/S6vNsz38sCI/AAAAAAAAAAw/kP_-1bNQI9o/s1600/css.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 340px;" src="http://1.bp.blogspot.com/_j6ul9CMlP_Q/S6vNsz38sCI/AAAAAAAAAAw/kP_-1bNQI9o/s400/css.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5452677943584927778" /&gt;&lt;/a&gt;

&lt;p&gt;Grab the themes at &lt;a href="http://github.com/mads379/Terminal-TextMate-Themes" target="_blank"&gt;github&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As always, feel free to fork me and help out :) &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4238792389635811701-4432458043618931211?l=www.sidewayscoding.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.sidewayscoding.com/feeds/4432458043618931211/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.sidewayscoding.com/2010/03/novel-textmate-theme.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4238792389635811701/posts/default/4432458043618931211'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4238792389635811701/posts/default/4432458043618931211'/><link rel='alternate' type='text/html' href='http://www.sidewayscoding.com/2010/03/novel-textmate-theme.html' title='Novel TextMate theme'/><author><name>Mads Hartmann Jensen</name><uri>http://www.blogger.com/profile/07989299859443129808</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_j6ul9CMlP_Q/S2qvbDr6RKI/AAAAAAAAAAM/ZVVUtZHM3Sc/S220/IMGP0163.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_j6ul9CMlP_Q/S6vNuFAYXGI/AAAAAAAAABQ/LwkCYUhtlzw/s72-c/scala.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4238792389635811701.post-4823636401266274550</id><published>2010-03-03T14:53:00.000-08:00</published><updated>2010-03-03T15:31:41.989-08:00</updated><title type='text'>Textmate Lift bundle v0.2</title><content type='html'>&lt;p&gt;I finally found the time to sit down and hack a bit more on the Lift bundle for TextMate - I've also created a little teaser-video to show you what it's capable of&lt;/p&gt; 
&lt;p&gt;I forgot to show that it's capable of inferring the type of the following variable 'test' also. &lt;/p&gt;
&lt;pre&gt;&lt;code&gt;var test = new WhatEver
test.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So you don't have to declare the type ':WhatEver' if you're using the new keyword&lt;/p&gt; 

&lt;object width="560" height="340"&gt;&lt;param name="movie" value="http://www.youtube.com/v/V0-AgmrwvpQ&amp;hl=en_US&amp;fs=1&amp;"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/V0-AgmrwvpQ&amp;hl=en_US&amp;fs=1&amp;" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="560" height="340"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4238792389635811701-4823636401266274550?l=www.sidewayscoding.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.sidewayscoding.com/feeds/4823636401266274550/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.sidewayscoding.com/2010/03/textmate-lift-bundle-v02.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4238792389635811701/posts/default/4823636401266274550'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4238792389635811701/posts/default/4823636401266274550'/><link rel='alternate' type='text/html' href='http://www.sidewayscoding.com/2010/03/textmate-lift-bundle-v02.html' title='Textmate Lift bundle v0.2'/><author><name>Mads Hartmann Jensen</name><uri>http://www.blogger.com/profile/07989299859443129808</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_j6ul9CMlP_Q/S2qvbDr6RKI/AAAAAAAAAAM/ZVVUtZHM3Sc/S220/IMGP0163.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4238792389635811701.post-7980787527320036506</id><published>2010-02-22T02:59:00.000-08:00</published><updated>2010-05-04T01:04:48.726-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scala'/><category scheme='http://www.blogger.com/atom/ns#' term='Textmate'/><category scheme='http://www.blogger.com/atom/ns#' term='ctags'/><title type='text'>Lift Textmate bundle now with primitive code-completion</title><content type='html'>&lt;p&gt;It's now been two weeks since I said I would have developed further on the Lift TextMate bundle in one week so I though I'd better start writing ;) &lt;/p&gt;

&lt;p&gt;Last time I said I would implement the following features&lt;/p&gt;
&lt;ol&gt;
 &lt;li&gt;Primitive autocompletion for methods of the current objet. This is only realistic when the type of the object has been explicitly stated in the document&lt;/li&gt;
 &lt;li&gt;Snippet output&lt;/li&gt;
 &lt;li&gt;Images in the drop-down list that indicates if it's a function, object or a class&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And so far I've managed to get #1 and #2 done, I'm fairly happy with the result even though it's not that  clever when it comes to working out the type of a variable - I'll be teaching it a thing or two before the next release&lt;/p&gt;

&lt;h4&gt;What is planned for the future: &lt;/h4&gt;
&lt;ul&gt;
 &lt;li&gt;Improving the type interference (it's extremely lightweight now)&lt;/li&gt;
 &lt;li&gt;Teach it about packages to help your organize/add/remove imports&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;NB:&lt;/strong&gt; Ctags was created to help the developer navigate source files and as such it doesn't record each function in each Class/Object/Trait but instead each function in each of the files it analyzes. This means that you might get some weird suggestion if there's more than one class in each file. But don't worry the compiler won't allow you to do anything illegal :)&lt;/p&gt;

&lt;p&gt;I'll be rolling out my own static analyzer when I get the time (hopefully soon) to address this issue&lt;/p&gt;

&lt;h4&gt;Requirements&lt;/h4&gt;
&lt;ul&gt;
 &lt;li&gt;If you don't have it already install &lt;a href="http://ctags.sourceforge.net/" target="_blank"&gt;ctags&lt;/a&gt;&lt;/li&gt;
 &lt;li&gt;Add the contents of this &lt;a href="http://github.com/mads379/Lift-TextMate-Bundle/blob/master/.ctags" target="_blank"&gt;file&lt;/a&gt; to your .ctags file. If you don't have one already simple place this one in your home folder (~/)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;How to get started&lt;/h4&gt;
&lt;ul&gt;
 &lt;li&gt;Download the bundle &lt;a href="http://github.com/mads379/Lift-TextMate-Bundle" target="_blank"&gt;here&lt;/a&gt; &lt;/li&gt;
 &lt;li&gt;Add the following shell variables to TextMate &lt;strong&gt;LIFTWEB_PATH&lt;/strong&gt; - point it to the lift source code for me this is /Users/Mads/Dev/liftweb/framework/. If you don't have it download it &lt;a href="http://github.com/dpp/liftweb" target="_blank"&gt;here&lt;/a&gt; and &lt;strong&gt;SCALA_LIBRARY_PATH&lt;/strong&gt; - point it to the scala library source code for me this is /Users/Mads/Dev/scala-2.7.7.final-sources/src/library/ you can download it &lt;a href="http://www.scala-lang.org/downloads" target="_blank"&gt;here&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Create the ctags file by pressing cmd+shift+c&lt;/li&gt;
&lt;li&gt;Code, code, code - try hitting option+escape every once in a while to see if it works&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As stated above it's not very good at figuring out the type of a variable yet - in fact it only knows what type to look for when you explicitly state it like so: &lt;/p&gt;
&lt;pre&gt;val myList :Array[String] = "H,e,l,l,o".split(",")&lt;/pre&gt;
&lt;p&gt;This is not very convienient as it forces you to drop type-interference, however, if you want to get a list of the methods List has you can simply type 'List.' and hit option+escape&lt;/p&gt;

&lt;p&gt;Remember this is still extremely beta - It works for me but I can't promise it will work for you. If you find any issues feel free to fork me on &lt;a href="http://github.com/mads379" target="_blank"&gt;github&lt;/a&gt; and help me out :) &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4238792389635811701-7980787527320036506?l=www.sidewayscoding.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.sidewayscoding.com/feeds/7980787527320036506/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.sidewayscoding.com/2010/02/lift-textmate-bundle-now-with-primitive.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4238792389635811701/posts/default/7980787527320036506'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4238792389635811701/posts/default/7980787527320036506'/><link rel='alternate' type='text/html' href='http://www.sidewayscoding.com/2010/02/lift-textmate-bundle-now-with-primitive.html' title='Lift Textmate bundle now with primitive code-completion'/><author><name>Mads Hartmann Jensen</name><uri>http://www.blogger.com/profile/07989299859443129808</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_j6ul9CMlP_Q/S2qvbDr6RKI/AAAAAAAAAAM/ZVVUtZHM3Sc/S220/IMGP0163.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4238792389635811701.post-7609380484184373174</id><published>2010-02-09T12:13:00.000-08:00</published><updated>2010-02-09T12:13:40.310-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Textmate'/><category scheme='http://www.blogger.com/atom/ns#' term='sass'/><category scheme='http://www.blogger.com/atom/ns#' term='movie'/><category scheme='http://www.blogger.com/atom/ns#' term='compass'/><title type='text'>The making of http://www.robocatapps.com/stickers</title><content type='html'>&lt;p&gt;I recently coded a small one-pager for &lt;a href="http://www.robocatapps.com/"&gt;Robocat Apps&lt;/a&gt; and thought it would be fun to record the process. Slicing out the .psd file and coding the markup/css took about 1 hour and 50 minutes from scratch. The movie doesn't show the initial slicing of the photoshop file because frankly, that's now very interesting to look at ;) &lt;/p&gt;

&lt;p&gt;I'm using:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Textmate for editing&lt;/li&gt; 
&lt;li&gt;the &lt;a href="http://compass-style.org/" target="_blank"&gt;compass framework&lt;/a&gt; which has a nice list of mixins to use and to do continuous compilation of the &lt;a href="http://sass-lang.com/" target="_blank"&gt;sass code&lt;/a&gt; to css&lt;/li&gt;
&lt;li&gt;chrome (webkit) to test the site&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check it out: &lt;/p&gt;

&lt;object width="560" height="340"&gt;&lt;param name="movie" value="http://www.youtube.com/v/7VFj_HtG2Mg&amp;hl=en_US&amp;fs=1&amp;rel=0"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/7VFj_HtG2Mg&amp;hl=en_US&amp;fs=1&amp;rel=0" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="560" height="340"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4238792389635811701-7609380484184373174?l=www.sidewayscoding.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.sidewayscoding.com/feeds/7609380484184373174/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.sidewayscoding.com/2010/02/making-of-httpwwwrobocatappscomstickers.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4238792389635811701/posts/default/7609380484184373174'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4238792389635811701/posts/default/7609380484184373174'/><link rel='alternate' type='text/html' href='http://www.sidewayscoding.com/2010/02/making-of-httpwwwrobocatappscomstickers.html' title='The making of http://www.robocatapps.com/stickers'/><author><name>Mads Hartmann Jensen</name><uri>http://www.blogger.com/profile/07989299859443129808</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_j6ul9CMlP_Q/S2qvbDr6RKI/AAAAAAAAAAM/ZVVUtZHM3Sc/S220/IMGP0163.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4238792389635811701.post-5323181092408600625</id><published>2010-02-08T08:35:00.000-08:00</published><updated>2010-02-08T08:35:08.541-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scala'/><category scheme='http://www.blogger.com/atom/ns#' term='Lift'/><category scheme='http://www.blogger.com/atom/ns#' term='Textmate'/><title type='text'>Class-level autocompletion for Lift-based projects in Textmate</title><content type='html'>&lt;p&gt;I'm currently working on a bundle for Textmate that brings autocompletion for Lift-based scala projects to Textmate. It's very very primitive right now - It only knows how to complete partial typed classes and the ouput isn't snippets so you can't use tab to jump through the different arguments. &lt;/p&gt;

&lt;p&gt;I'm planning to implement the following the next week or so:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Primitive autocompletion for methods of the current objet. This is only realistic when the type of the object has been explicitly stated in the document&lt;/li&gt;
&lt;li&gt;Snippet output&lt;/li&gt;
&lt;li&gt;Images in the drop-down list that indicates if it's a function, object or a class&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I'm using:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ctags to index the classes, methods, objects etc. in every file in the lift project&lt;/li&gt;
&lt;li&gt;grep to find the lines in the ctags file that are relevant&lt;/li&gt;
&lt;li&gt;34 lines of ruby to match the class names and arguments from the lines returned by grep&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here's a small demo of it so far: &lt;/p&gt;
&lt;object width="560" height="340"&gt;&lt;param name="movie" value="http://www.youtube.com/v/_mAQOef8wts&amp;hl=en_US&amp;fs=1&amp;"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/_mAQOef8wts&amp;hl=en_US&amp;fs=1&amp;" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="560" height="340"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4238792389635811701-5323181092408600625?l=www.sidewayscoding.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.sidewayscoding.com/feeds/5323181092408600625/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.sidewayscoding.com/2010/02/class-level-autocompletion-for-lift.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4238792389635811701/posts/default/5323181092408600625'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4238792389635811701/posts/default/5323181092408600625'/><link rel='alternate' type='text/html' href='http://www.sidewayscoding.com/2010/02/class-level-autocompletion-for-lift.html' title='Class-level autocompletion for Lift-based projects in Textmate'/><author><name>Mads Hartmann Jensen</name><uri>http://www.blogger.com/profile/07989299859443129808</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_j6ul9CMlP_Q/S2qvbDr6RKI/AAAAAAAAAAM/ZVVUtZHM3Sc/S220/IMGP0163.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4238792389635811701.post-7106933689244586107</id><published>2010-02-07T03:33:00.001-08:00</published><updated>2010-05-04T01:05:19.756-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Textmate'/><category scheme='http://www.blogger.com/atom/ns#' term='Ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='ctags'/><category scheme='http://www.blogger.com/atom/ns#' term='Bundle'/><title type='text'>Creating Textmate bundles</title><content type='html'>&lt;p&gt;I know a lot of people are anticipating the release of Textmate 2 and I can understand why. The current version of Textmate is awesome, so awesome that it's hard to image that whatever Alan Oddegaard and XXXX has spent the last three years on will be nothing short of mind blowing. &lt;/p&gt;
&lt;p&gt;But to be honest I'm quite happy with Textmate 1.5.9 and ... I checked out the top 20 list of requested features and on top 5 was "Code completion for language «xyz»" - I'm sorry to say it but I doubt Textmate 2 will ship with code completion support for &lt;b&gt;your&lt;/b&gt; favorite language. So what to do then? Just dump Textmate (your favorite editor) all-together and use a bloated IDE? Don't be silly, you implement it yourself of course! This post will guide you through the process and in the end you should have a good idea of what to do next to get code completion for &lt;strong&gt;favorite&lt;/strong&gt; language in Textmate&lt;/p&gt;   
&lt;p&gt;If you're about to leave this post I'm sorry but you don't deserve code-completion - If you're not ready to do a little bit scripting you don't want it enough. &lt;/p&gt;

&lt;p&gt;The following is a small list of tools we'll use to make it a lot easier to implement&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Ctags&lt;/strong&gt;&lt;br /&gt; Exuberant Ctags is a multilanguage implementation of Ctags which supports &lt;a href="http://ctags.sourceforge.net/languages.html"&gt;41 different&lt;/a&gt; programming languages. We use it to recursively scan a folder of source files and output a list with a line for each class, method, interface &amp;lt;&amp;lt;whatever programming constructs your language uses&amp;gt;&amp;gt; &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dialog2&lt;/strong&gt;&lt;br/&gt;This is a ruby wrapper that is bundles with Textmate it's what we'll use to display the drop-down list of potential completions&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;grep&lt;/strong&gt;&lt;br /&gt; grep (global / regular expression / print) from wikipedia: The grep command searches files or standard input globally for lines matching a given regular expression, and prints them to the program's standard output.&lt;/li&gt;
&lt;li&gt;small amount of ruby scripting &lt;br/&gt; If you don't know ruby then don't worry, I don't really know it either xxx link to _why xxx should be enough to get your started (It's quite fun!)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Just to convince you that I'm not all talk and no action, here's a small video clip of the auto-completion i implemented for scala/lift&lt;/p&gt;

&lt;p&gt;Okay, great! Lets get started!&lt;/p&gt; 

&lt;h4&gt;First, create the ctags file&lt;/h4&gt;

&lt;p&gt;I hope you enjoyed reading this, if you have any questions or implemented code completion for a language, please post a comment below&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4238792389635811701-7106933689244586107?l=www.sidewayscoding.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.sidewayscoding.com/feeds/7106933689244586107/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.sidewayscoding.com/2010/02/creating-textmate-bundles.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4238792389635811701/posts/default/7106933689244586107'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4238792389635811701/posts/default/7106933689244586107'/><link rel='alternate' type='text/html' href='http://www.sidewayscoding.com/2010/02/creating-textmate-bundles.html' title='Creating Textmate bundles'/><author><name>Mads Hartmann Jensen</name><uri>http://www.blogger.com/profile/07989299859443129808</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_j6ul9CMlP_Q/S2qvbDr6RKI/AAAAAAAAAAM/ZVVUtZHM3Sc/S220/IMGP0163.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-4238792389635811701.post-4607841031742384149</id><published>2010-02-04T01:34:00.000-08:00</published><updated>2010-05-04T01:05:38.244-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Scala'/><category scheme='http://www.blogger.com/atom/ns#' term='Lift'/><title type='text'>View-first templating in Lift</title><content type='html'>&lt;p class="edit"&gt;&lt;strong&gt;EDIT:&lt;/strong&gt; I've been informed that there's an &lt;a href="http://wiki.github.com/dpp/liftweb/how-to-binding-view-content-to-code" target="_blank"&gt;article&lt;/a&gt; on github that coveres the same subject (go to 'Advanced Binding')&lt;/p&gt;
&lt;p&gt;I'm currently re-writing a project from PHP to &lt;a href="http://www.scala-lang.org/" target="_blank"&gt;Scala&lt;/a&gt; using the &lt;a href="http://www.liftweb.net/" target="_blank"&gt;Lift framework&lt;/a&gt;. I'm very new to both Scala and the Lift Framework, but I'm enjoying working with it none the less. I'm slowly getting used to the 'View-First' approach that the Lift framework uses but the other day I got stuck when I needed to iterate over a collection of data.&lt;br /&gt; 
Normally I simply inject a few lines of inline code in the template to iterate over the collection, but since you can't have logic in a template in Lift I was stuck. After searching through the &lt;a href="http://groups.google.com/group/liftweb" target="_blank"&gt;lift google group&lt;/a&gt; and playing around with a bit of code I worked out one way to do it and I thought I would share it here. I won't be explaining how the Scala code works, I'll just explain my approach&lt;/p&gt;
&lt;h4&gt;Controller-first vs. View-first approach&lt;/h4&gt;
&lt;p&gt;Before going further it might be wise to summarize the two approaches:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Controller first approach&lt;/strong&gt; This is the approach I'm used to. The controller fetches and prepares the data I want to display and makes it available to the template. The template is usually xhtml/html with inline code whenever it's necessary to iterate over a collection of data or inject some of the data prepared by the controller.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;View first approach&lt;/strong&gt; This is the templating approach used in Lift and it's named 'View-first' because it starts with the xhtml and the controller (it's called Snippet in Lift lingo) can then fill in the data in the desired places.&lt;/p&gt;
&lt;p class="edit"&gt;&lt;strong&gt;EDIT:&lt;/strong&gt; I don't view snippets as controllers at all. 
Comparing to Spring MVC for instance AFAIK controllers are the ones 
that process the requests and generate data that will be rendered (in 
MVC way). Here snippets per say never process requests although they 
typically bind user's function that in turn could be compared with 
controllers. Snippets just render dynamic markup. &lt;small&gt;Comment by Marius Danciu&lt;/p&gt;
&lt;p&gt;For a more in-depth description of the 'view-first' approach take a look at the article on the &lt;a href="http://wiki.liftweb.net/index.php?title=Lift_View_First" target="_blank"&gt;lift wiki&lt;/a&gt;&lt;/p&gt;
&lt;small&gt;The template (view)&lt;/small&gt;
&lt;pre&gt;
&amp;lt;lift:surround with=&amp;quot;default&amp;quot; at=&amp;quot;content&amp;quot;&amp;gt; 
    &amp;lt;h2&amp;gt;Welcome to your project!&amp;lt;/h2&amp;gt; 
    &amp;lt;p&amp;gt;
      &amp;lt;lift:helloWorld.howdy&amp;gt;
        &amp;lt;p&amp;gt; Welcome to helloworld at &amp;lt;howdy:date /&amp;gt; &amp;lt;/p&amp;gt; 
      &amp;lt;/lift:helloWorld.howdy&amp;gt;
    &amp;lt;/p&amp;gt; 
&amp;lt;/lift:surround&amp;gt;
&lt;/pre&gt;
&lt;p&gt;This is prettty cool, the template is pure xhtml, no inline code or other nasties. &lt;/p&gt;
&lt;small&gt;The snippet (controller)&lt;/small&gt;
&lt;pre&gt;
class HelloWorld { 
  def howdy(in :NodeSeq) :NodeSeq {
    bind("howdy", in, "date" -&gt; Text(new java.util.Date))
  } 
}
&lt;/pre&gt;
&lt;p&gt;Pretty simple, however, on the projects I work on the pages are rarely this simple. I normally have to interate over a few collections etc. so lets look at a bit more elaborate example&lt;/p&gt;
&lt;small&gt;The more ellaborate template&lt;/small&gt;
&lt;pre&gt;
&amp;lt;lift:surround with=&amp;quot;default&amp;quot; at=&amp;quot;content&amp;quot;&amp;gt;
&amp;lt;h2&amp;gt;Welcome to your project!&amp;lt;/h2&amp;gt;
 &amp;lt;lift:helloWorld.howdyAll&amp;gt;
  &amp;lt;p&amp;gt; Welcome to helloworld at &amp;lt;howdy:date /&amp;gt; &amp;lt;/p&amp;gt;  
  &amp;lt;ul&amp;gt;
   &amp;lt;howdy:users&amp;gt;
    &amp;lt;li&amp;gt;&amp;lt;user:name /&amp;gt;&amp;lt;/li&amp;gt;
   &amp;lt;/howdy:users&amp;gt;
  &amp;lt;/ul&amp;gt;
 &amp;lt;/lift:helloWorld.howdyAll&amp;gt;
&amp;lt;/lift:surround&amp;gt;
&lt;/pre&gt;
&lt;small&gt;A bit more advanced snippet&lt;/small&gt;
&lt;pre&gt;
class HelloWorld {

  def howdyAll(in: NodeSeq) :NodeSeq = {
    // list of users
    val userList :List[String] = "Arthur Dent" :: "Ford Prefect" :: "Zaphod Beeblebrox" :: Nil

    // extract the tag &amp;lt;howdy:users&amp;gt; and all of it's children
    val usersTemplate :NodeSeq = template(in, "howdy", "users") match {
      case Full(xhtml) =&gt; xhtml
      case Empty =&gt; &amp;lt;strong&amp;gt;Error&amp;lt;/strong&amp;gt;
      case Failure(msg, _, _) =&gt; &amp;lt;strong&amp;gt;Error&amp;lt;/strong&amp;gt;
    }

    // loop through each user and bind the values
    val users :NodeSeq =
      userList.flatMap { name :String =&gt;
        bind("user", usersTemplate, "name" -&gt; Text(name))
        }

    // now bind the tags &amp;lt;howdy:date&amp;gt; and &amp;lt;howdy:users&amp;gt;
    val date = new java.util.Date
    bind("howdy", in, "date" -&gt; Text(date.toString),"users" -&gt; users)
    }
}
&lt;/pre&gt;
&lt;p&gt;Nice and simply, hope it will help someone else new to Lift&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/4238792389635811701-4607841031742384149?l=www.sidewayscoding.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.sidewayscoding.com/feeds/4607841031742384149/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.sidewayscoding.com/2010/02/view-first-templating-in-lift.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/4238792389635811701/posts/default/4607841031742384149'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/4238792389635811701/posts/default/4607841031742384149'/><link rel='alternate' type='text/html' href='http://www.sidewayscoding.com/2010/02/view-first-templating-in-lift.html' title='View-first templating in Lift'/><author><name>Mads Hartmann Jensen</name><uri>http://www.blogger.com/profile/07989299859443129808</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='24' height='32' src='http://3.bp.blogspot.com/_j6ul9CMlP_Q/S2qvbDr6RKI/AAAAAAAAAAM/ZVVUtZHM3Sc/S220/IMGP0163.jpg'/></author><thr:total>0</thr:total></entry></feed>
