<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>2Paths</title>
	<atom:link href="http://www.2paths.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.2paths.com</link>
	<description>Custom Software Technical Architecture, Design and Development in Vancouver, BC, Canada</description>
	<lastBuildDate>Mon, 21 Jun 2010 20:53:06 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Cassandra Key-Value Store Primer</title>
		<link>http://www.2paths.com/2010/06/07/cassandra-key-value-store-primer/</link>
		<comments>http://www.2paths.com/2010/06/07/cassandra-key-value-store-primer/#comments</comments>
		<pubDate>Mon, 07 Jun 2010 17:56:55 +0000</pubDate>
		<dc:creator>Omar</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Featured]]></category>
		<category><![CDATA[Technology]]></category>
		<category><![CDATA[cassandra]]></category>
		<category><![CDATA[Grails]]></category>
		<category><![CDATA[key-value store]]></category>
		<category><![CDATA[nosql]]></category>
		<category><![CDATA[thrift]]></category>

		<guid isPermaLink="false">http://www.2paths.com/?p=1286</guid>
		<description><![CDATA[The &#8220;no-sql&#8221; movement has been gaining strength over recent years. There is no denying that relational databases have their role and are highly effective in many cases. However, there are times when the scalability and availability of a relational database can become an issue. The &#8220;no-sql&#8221; movement is hoping to provide an alternative option to [...]]]></description>
			<content:encoded><![CDATA[<p>The &#8220;no-sql&#8221; movement has been gaining strength over recent years. There is no denying that relational databases have their role and are highly effective in many cases. However, there are times when the scalability and availability of a relational database can become an issue. The &#8220;no-sql&#8221; movement is hoping to provide an alternative option to relational databases. These options allow for the creation of data stores that do not necessarily require the creation of fixed schemas or the joining of tables and tend to focus on scaling horizontally. </p>
<p><strong>Apache Cassandra</strong><br />
<a href="http://cassandra.apache.org/">Cassandra</a> is a fairly recent addition to the &#8220;no-sql&#8221; movement. Initially developed by Facebook, Cassandra was open sourced in 2008 and is now housed and maintained by Apache.</p>
<blockquote><p>Cassandra is a highly scalable, eventually consistent, distributed, structured key-value store. Cassandra brings together the distributed systems technologies from Dynamo  and the data model from Google&#8217;s BigTable. Like Dynamo, Cassandra is eventually consistent. Like BigTable, Cassandra provides a ColumnFamily-based data model richer than typical key/value systems.</p>
<p>Cassandra was open sourced by Facebook in 2008, where it was designed by Avinash Lakshman (one of the authors of Amazon&#8217;s Dynamo) and Prashant Malik ( Facebook Engineer ). In a lot of ways you can think of Cassandra as Dynamo 2.0 or a marriage of Dynamo and BigTable. Cassandra is in production use at Facebook but is still under heavy development.</p></blockquote>
<p><strong>Data Model</strong><br />
The key difference between Cassandra and many of the other key-value stores is that it possesses the concept of a &#8220;Super Column.&#8221; For more information on what exactly a super column is (and an overview of the Cassandra data model in general) read the following blog post &#8220;<a href="http://arin.me/blog/wtf-is-a-supercolumn-cassandra-data-model">WTF is a SuperColumn? An Intro to the Cassandra Data Model</a>&#8221;</p>
<p><strong>Performance</strong><br />
Many may be curious about the performance of these key value stores. We found an article that compared <a href="http://blog.medallia.com/2010/05/choosing_a_keyvalue_storage_sy.html">Cassandra to Voldemort</a></p>
<p><strong>Interacting with Cassandra</strong><br />
Cassandra is developed in java however it does not allow access via JDBC. Instead it provides access to it&#8217;s repository via the &#8220;<a href="http://incubator.apache.org/thrift/">Apache Thrift</a>&#8221; framework which basically allows developers in any major language to gain access to a given service (in this case the Cassandra data store). For those like me who are used to JDBC, the thrift interface can be a bit confusing given that there isn&#8217;t much documentation for them. The Java client made available on the Cassandra website is rather rudimentary with no support for things like transactions, therefore many people choose to make use of a library called <a href="http://prettyprint.me/2010/02/23/hector-a-java-cassandra-client/">Hector</a> that provides many of the features necessary for any production worthy application making use of Cassandra.</p>
<p><strong>The Project </strong><br />
What I wanted to to do was use the Cassandra data store on a pet project that I&#8217;m working on in my spare time. I intend to use the datastore to do the following: </p>
<ol>
<li>Store messages being sent from one user to another</li>
<li>Track queries made by a given user</li>
<li>Store user complaints</li>
</ol>
<p><strong>Installation</strong><br />
Installation of the server is easy enough. Install Java 1.6 if you haven&#8217;t already. Edit <code>/bin/cassandra.in.sh</code> and change <code>JAVA_HOME</code> if you run a different version of java as your default<code> JAVA_HOME</code>. By default the server has an RMI port set to 8080, you can change the value in the <code>/bin/cassandra.in.sh</code> file as well.</p>
<p><strong>Configuration</strong><br />
Edit <code>/bin/storage-conf.xml</code> to define various key spaces and column families. The concept of a Keyspace is similar to the schema in a relational database, where as the ColumnFamily is analogous to a table. In defining the ColumnFamilies you are defining the key groupings and how they are sorted. Therefore, I defined the following:</p>
<pre class="brush: xml">
  &lt;Keyspaces&gt;
    &lt;Keyspace Name=&quot;Nikahfied&quot;&gt;

      &lt;ColumnFamily Name=&quot;Queries&quot;
                    Comment=&quot;User queries&quot;/&gt;

      &lt;ColumnFamily Name=&quot;Complaints&quot;
                    Comment=&quot;User complaints&quot;/&gt;

      &lt;ColumnFamily Name=&quot;Messages&quot;
                    ColumnType=&quot;Super&quot;
                    CompareWith=&quot;UTF8Type&quot;
                    CompareSubcolumnsWith=&quot;UTF8Type&quot;
                    RowsCached=&quot;10000&quot;
                    KeysCached=&quot;50%&quot;
                    Comment=&quot;Message threads between users of the system&quot;/&gt;

    &lt;/Keyspace&gt;
  &lt;/Keyspaces&gt;
</pre>
<p>I would be creating a schema named Nikahfied and three table are Queries, Complaints and Messages. As you will notice, there is no real structure to the ColumnFamilies other than what type of column and the CompareWith strategy as outlined in the &#8220;<a href="http://arin.me/blog/wtf-is-a-supercolumn-cassandra-data-model">WTF is a SuperColumn? An Intro to the Cassandra Data Model</a>&#8221; article. In this case we have one super column and two regular columns. In code I will ensure that each of the rows fore each ColumnFamily contain the same content however there is nothing forcing you to do so. As you will notice, we can configure each of the ColumnFamiles individually. Messages will need to handle heavy reads and writes where as Queries and Complaints will be more write intensive. Messages therefore have a caching scheme defined where as the others do not as they will primarily handle writes rather than reads.</p>
<p>When initially defining the column structure I was a bit confused. I was taking the definition of Cassandra as a &#8220;key value store&#8221; literally and was confused about there being a primary key and the secondary keys. When defining a basic Column I though it would simple have a key and a value. So things would be defined as <code>Nikahfied.&lt;ColumnFamily&gt;.key = value</code> where this is actually not the case. For a basic ColumnFamily, the structure is as follows: <code>Nikahfied.&lt;ColumnFamily&gt;.&lt;Primary key&gt;.&lt;Secondary Key&gt; = value</code>. In the case of a super column, this goes one level further <code>Nikahfied.&lt;ColumnFamily&gt;.&lt;Primary key&gt;.&lt;Secondary Key&gt;.&lt;Tertiary Key&gt; = value</code>. That had me puzzled for some time&#8230;upon re-reading the &#8220;<a href="http://arin.me/blog/wtf-is-a-supercolumn-cassandra-data-model">WTF is a SuperColumn? An Intro to the Cassandra Data Model</a>&#8221; article I realized my mistake so I though I&#8217;d point it out to anyone else who was confused about this construct.</p>
<p><strong>Data Structures:</strong><br />
<span style="text-decoration:underline">Queries:</span><br />
The structure of queries would be simple enough. I would store each query by date/time. To track which queries the user made when.</p>
<p><code>Nikahfied.Queries.UserId.&lt;Date/Time&gt; = &lt;Query&gt;</code></p>
<p><span style="text-decoration:underline">Complaints:</span><br />
There is only one type of complaint wherein a user can complain about another user. So I wanted to track who received the complaint, who complained and when. The structure of a complaint would be defined as follows:<br />
<code>Nikahfied.Complaints.&lt;Violator Id&gt;.&lt;Complainant Id&gt; = &lt;Date/Time&gt;</code></p>
<p><span style="text-decoration:underline">Messages:</span><br />
If there was ever a case to use a super column, a messages would be it. A user has a number of threads that contain one or more message. A user can receive and send messages. Messages themselves are the same, I simply need to track which threads contain messages that a user sent. I also need to keep track of which threads have unread messages within them. My initial design was the following:<br />
<code>Nikahfied.Messages.RecipientId.threads = A JSON Object that contains Threads defined in JSON Objects themselves. A thread being a group of<br />
                              .sent_ids = An array of thread id's that contain messages that the user has sent<br />
                              .unread_ids = An array of thread ids that contain unread messages</code></p>
<p>However, with this structure there was no need for a super column and I was determined on making use of one <img src='http://www.2paths.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> . So I expanded out the thread structure as follows:</p>
<p><code>Nikahfied.Messages.RecipientId.threads.&lt;thread id&gt; = A JSON Object that contains an individual Thread<br />
                              .sent_ids = An array of thread id's that contain messages that the user has sent<br />
                              .unread_ids = An array of thread ids that contain unread messages</code></p>
<p>Now that we have the content structure defined I now need to create a hook between my application and the Cassandra key value store. For development I have decided to use the Grails Framework to speed up development. I have included the Hector library to access the framework but needed to define a CassandraService to be the central point of contact between various controllers and other services to the Cassandra key store. Both Cassandra&#8217;s Thrift interface and Hector have pretty poor documentation so I created a Grails service that makes the interaction with Cassandra a bit clearer (at least to me). <span style="text-decoration:underline;font-weight:bold">Note:</span> You will need to be using Java 1.6.</p>
<pre class="brush: groovy">
import me.prettyprint.cassandra.service.CassandraClientPool
import me.prettyprint.cassandra.service.CassandraClientPoolFactory
import me.prettyprint.cassandra.service.CassandraClient
import me.prettyprint.cassandra.service.Keyspace
import org.apache.cassandra.thrift.Column;
import org.apache.cassandra.thrift.ColumnPath;
import org.apache.cassandra.thrift.NotFoundException;

import org.apache.cassandra.thrift.SuperColumn

class CassandraService {

    boolean transactional = true

    def servers=[&quot;localhost:9160&quot;]
    def defaultKeyspace=&quot;Nikahfied&quot;
    private static final String NOT_FOUND = &quot;&quot;

    private execute(keyspaceName=defaultKeyspace,block){
        CassandraClientPool pool = CassandraClientPoolFactory.INSTANCE.get();
        CassandraClient client = pool.borrowClient(servers);

        try {
            Keyspace keyspace = client.getKeyspace(keyspaceName)
            return block(keyspace)
        } finally {
            pool.releaseClient(client);
        }
    }

    /**
     * Get a single super column
     * @param columnFamily
     * @param secondaryKey
     * @param key primary key
     * @return Matching super column
     */
    public SuperColumn getSuperColumn(String columnFamily, String key, String secondaryKey) {
        ColumnPath cp = new ColumnPath(columnFamily)
        cp.setSuper_column(secondaryKey.bytes)
        return execute {Keyspace keyspace -&amp;gt;
            SuperColumn sc = null;
            try {
                sc = keyspace.getSuperColumn(key, cp)
            } catch (NotFoundException nfe) {
                sc = null;
            }
            return sc
        }
    }

    /**
     * Get multiple super columns
     * @param columnFamily
     * @param secondaryKey
     * @param keys primary keys
     * @return matching super columns
     */
    public Map multigetSuperColumn(String columnFamily, List keys, String secondaryKey) {
        ColumnPath cp = new ColumnPath(columnFamily)
        cp.setSuper_column(secondaryKey.bytes)
        return execute {Keyspace keyspace -&amp;gt;
            Map scMap = keyspace.multigetSuperColumn(keys, cp)
            return scMap
        }
    }

    /**
     * Get multiple columns
     * @param secondaryKey secondaryKey
     * @param keys primary keys
     * @param columnFamily column family
     * @return results if any
     */
    public Map multigetColumn(List keys, String secondaryKey, String columnFamily) {
    	ColumnPath cp = new ColumnPath(columnFamily)
        cp.setColumn(secondaryKey.bytes)
        return execute {Keyspace keyspace -&amp;gt;
            Map cMap = keyspace.multigetColumn(keys, cp)
            return cMap
        }
    }

    /**
     * Get a single column and it&#039;s values
     * @param columnFamily
     * @param key primary key
     * @return matching column
     */
    public Column getColumn(String columnFamily, String key, String secondaryKey) {
        def cp = new ColumnPath(columnFamily)
        cp.setColumn(secondaryKey.bytes)
        return execute {Keyspace keyspace -&amp;gt;
            Column c = null;
            try {
                c = keyspace.getColumn(key, cp)
            } catch (NotFoundException nfe) {
                c = null;
            }
            return c
        }
    }

    /**
     * Sets the new value for this column path
     * @param cf Column family
     * @param secondaryKey secondary key
     * @param key primary key
     * @param value value
     */
    def setColumnPathValue(String cf, String key, String secondaryKey, String value){
        def cp = new ColumnPath(cf)
        cp.setColumn(secondaryKey.bytes)
        return setColumnValue(cp, key, value)
    }

    /**
     * Sets the new super column value for this column path
     * @param cf Column Family
     * @param sc Super Key Id
     * @param secondaryKey Secondary Key
     * @param key primary key
     * @param value value
     * @return the value of the Column requested
     */
    def setColumnPathValue(String cf, String sc, String key, String secondaryKey, String value){
        def cp = new ColumnPath(cf)
        cp.setSuper_column(sc.bytes)
        cp.setColumn(secondaryKey.bytes)
        return setColumnValue(cp, key, value)
    }

    /**
     * Set a regular column
     * @param cp Column path
     * @param key primary key
     * @param value value
     */
    private setColumnValue(ColumnPath cp, String key, String value){
        return execute{ Keyspace keyspace -&amp;gt;
            keyspace.insert(key, cp, value.bytes)
        }
    }

    /**
     * Batch insert content for a give key and it&#039;s values
     * @param key primary key
     * @param columnMap column key values
     * @param superColumnMap super column key values
     */
    public batchInsert(String key, Map&amp;lt;String, List&amp;gt; columnMap, Map&amp;lt;String, List&amp;gt; superColumnMap) {
    	return execute{ Keyspace keyspace -&amp;gt;
        	keyspace.batchInsert(key, columnMap, superColumnMap)
    	}
    }

}
</pre>
<p>Using this class I have created a MessageService, ComplaintsService and a QueriesService that interact with the CassandraService to retrieve and store data. There is an adjoining Message and Complaints controller where as the QueriesService is referenced by the SearchController. Hopefully this blog post will provide you with a better understanding of how to get started with Cassandra the CassandraService class should provide you with a jump start on integrating Cassandra into an existing Grails or Java project.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.2paths.com/2010/06/07/cassandra-key-value-store-primer/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Leadership Excellence</title>
		<link>http://www.2paths.com/2009/11/09/leadership-excellence/</link>
		<comments>http://www.2paths.com/2009/11/09/leadership-excellence/#comments</comments>
		<pubDate>Mon, 09 Nov 2009 23:38:35 +0000</pubDate>
		<dc:creator>Kari</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Culture]]></category>
		<category><![CDATA[People]]></category>
		<category><![CDATA[communication]]></category>
		<category><![CDATA[leadership]]></category>

		<guid isPermaLink="false">http://www.2paths.com/?p=1268</guid>
		<description><![CDATA[About a year ago, we purchased a great learning package called &#8220;Lessons in Leadership&#8221; from the Business Source.  Besides receiving a Book Library (similar to the Coles&#8217; Notes) of the top Leadership and Financial Success books written, we also recieved five &#8211; 90 minute video modules  which include leadership sessions from five leadership gurus.
I once [...]]]></description>
			<content:encoded><![CDATA[<p>About a year ago, we purchased a great learning package called &#8220;Lessons in Leadership&#8221; from the Business Source.  Besides receiving a Book Library (similar to the Coles&#8217; Notes) of the top Leadership and Financial Success books written, we also recieved five &#8211; 90 minute video modules  which include leadership sessions from five leadership gurus.</p>
<p>I once heard Stephen Covey speak years ago and the main thing I remembered is that you don&#8217;t truly learn until you teach others.  So, throughout this past year, I&#8217;ve been able to take the learnings from the materials and adapt them to our needs so that our team could improve their communication skills and continue developing their leadership qualities.</p>
<p>In September of this year, I reviewed the module entitled &#8220;Leadership Excellence&#8221; which involved John Maxwell, author of <em>The 21 Irrefutable Laws of Leadership</em> and felt our entire team, technical or otherwise, would benefit from hearing some of the key messages John was imparting about the real measures of leaders and what causes people to truly connect and be inspired to follow a leader.   I decided to take the information presented in the video segment and reorganize it to create a condensed version of the learnings and attention span of  our team (under 30 minutes).  Those that attended our lunch hour &#8220;non&#8221; Techtalk, found it insightful and and useful.</p>
<p>I also reviewed another module called &#8220;How to be a more Influential and Persuasive Leader&#8221; presented by Kevin Hogan. This module, although similar in terms of overall topic, had a completely different approach and point of view. It really highlighted a variety of tactics people, and also leaders in a workplace, can use to influence behaviour of others (children, employees, potential customers, and hiring managers).  To share this knowledge with our team, I decided to send out daily leadership nuggets by email (less than 1 minute) that team members could read, digest and use as they felt relevant.</p>
<p>A third module I reviewed called &#8220;Becoming a Better Communicator (especially in crucial conversations)&#8221; presented by Ron McMillan, was also very interesting and involved the strategies master communicators use when having &#8220;crucial conversations&#8221; with team members.  I actually prepared a half-day workshop for our Support Crew including discussion time, and exercises, and was able to use some of the supporting materials from Ron McMillan&#8217;s organization www.crucialconversations.com to analyze the team&#8217;s individual and collective strengths in having these types of tough conversation.</p>
<p>The entire &#8220;Lessons in Leadership&#8221; package is certainly worth the cost and I encourage you to check out http://thebusinesssource.com/ for other products.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.2paths.com/2009/11/09/leadership-excellence/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>one-to-many relationships in Grails forms</title>
		<link>http://www.2paths.com/2009/10/01/one-to-many-relationships-in-grails-forms/</link>
		<comments>http://www.2paths.com/2009/10/01/one-to-many-relationships-in-grails-forms/#comments</comments>
		<pubDate>Fri, 02 Oct 2009 01:35:25 +0000</pubDate>
		<dc:creator>Tim</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Featured]]></category>

		<guid isPermaLink="false">http://www.2paths.com/?p=1228</guid>
		<description><![CDATA[Here&#8217;s a scenario we see fairly often in our Grails applications.

 Parent object has a collection of Child objects
We want the Parent&#8217;s create and edit GSPs to allow us to add/remove/update associated Child objects
The controller should correctly persist changes to the collection of Child objects, including maintaining Child object ids so any other objects referencing [...]]]></description>
			<content:encoded><![CDATA[<p>Here&#8217;s a scenario we see fairly often in our <a href="http://www.grails.org/">Grails</a> applications.</p>
<ul>
<li> Parent object has a collection of Child objects</li>
<li>We want the Parent&#8217;s create and edit GSPs to allow us to add/remove/update associated Child objects</li>
<li>The controller should correctly persist changes to the collection of Child objects, including maintaining Child object ids so any other objects referencing them don&#8217;t get screwed up</li>
</ul>
<p>I found a really nice solution that avoids adding a lot of code to the controller to sift out added/changed/deleted collection members. The original page seems to have disappeared, so here are copies from <a href="http://web.archive.org/web/20080822094606/http://www.lxisoft.com/web/guest/grails">archive.org</a> (easier to read) and <a href="http://docs.google.com/gview?a=v&amp;q=cache:yNp_JSw97_gJ:www.ambersand.org/c/document_library/get_file%3Fp_l_id%3D10913%26folderId%3D11055%26name%3DDLFE-101.pdf">Google cache</a> (PDF).</p>
<p>I was disappointed that the original page is gone, and I found some small errors in the sample code, so I thought it would be nice to document here.</p>
<p>Here&#8217;s a sample project I created to go through this. Source code: <a href="http://www.2paths.com/wp-content/uploads/2009/09/one-many.tar.gz">one-many.tar.gz</a></p>
<p>The original example used Quest objects that can hold many Task objects. I&#8217;ll follow the <a href="http://www.grails.org/Quick+Start">Grails docs</a> and use Author objects that can hold many Book objects.</p>
<p>First, create the Author class.</p>
<pre class="brush: java">
    import org.apache.commons.collections.list.LazyList;
    import org.apache.commons.collections.FactoryUtils;

    class Author {

        static constraints = {
        }

        String name
        List books = new ArrayList()
        static hasMany = [ books:Book ]

        static mapping = {
            books cascade:&quot;all,delete-orphan&quot;
        }

        def getExpandableBookList() {
            return LazyList.decorate(books,FactoryUtils.instantiateFactory(Book.class))
        }

    }
</pre>
<p>(Here&#8217;s a minor correction I had to make to the original document&#8217;s code. They declared getExpandableBookList as returning a List, but that gave unknown property errors. Using a plain def fixed that.)</p>
<p>This adds a bunch of useful behaviour right away. The mapping block declares that books will be deleted when they&#8217;re removed from the Author.books collection, so we don&#8217;t need to clean up anything manually. By initializing books to an empty ArrayList when an Author object is created, and by using the getExpandableBookList() method, we can easily add and remove Book objects to the Author.books collection.</p>
<p>Next, the Book class is pretty simple.</p>
<pre class="brush: java">
    class Book {

        static constraints = {
        }

        String title
        boolean _deleted

        static transients = [ &#039;_deleted&#039; ]

        static belongsTo = [ author:Author ]

        def String toString() {
            return title
        }

    }
</pre>
<p>Nothing too fancy here, but pay attention to the _deleted property. That&#8217;s what we&#8217;ll be using to filter out Book objects that need to be removed from the Author.book collection on updates.</p>
<p>For the views, I like to combine the guts of the create and edit GSPs into a template that they can both render.</p>
<pre class="brush: html">
    &lt;div class=&quot;dialog&quot;&gt;
        &lt;table&gt;
            &lt;tbody&gt;
                &lt;tr class=&quot;prop&quot;&gt;
                    &lt;td valign=&quot;top&quot; class=&quot;name&quot;&gt;&lt;label for=&quot;name&quot;&gt;Name:&lt;/label&gt;&lt;/td&gt;
                    &lt;td valign=&quot;top&quot; class=&quot;value ${hasErrors(bean:authorInstance,field:&#039;name&#039;,&#039;errors&#039;)}&quot;&gt;
                        &lt;input type=&quot;text&quot; id=&quot;name&quot; name=&quot;name&quot; value=&quot;${fieldValue(bean:authorInstance,field:&#039;name&#039;)}&quot;/&gt;
                    &lt;/td&gt;
                &lt;/tr&gt;
                &lt;tr class=&quot;prop&quot;&gt;
                    &lt;td valign=&quot;top&quot; class=&quot;name&quot;&gt;&lt;label for=&quot;books&quot;&gt;Books:&lt;/label&gt;&lt;/td&gt;
                    &lt;td valign=&quot;top&quot; class=&quot;value ${hasErrors(bean:authorInstance,field:&#039;books&#039;,&#039;errors&#039;)}&quot;&gt;
                        &lt;g:render template=&quot;books&quot; model=&quot;[&#039;authorInstance&#039;:authorInstance]&quot; /&gt;
                    &lt;/td&gt;
                &lt;/tr&gt;
            &lt;/tbody&gt;
        &lt;/table&gt;
    &lt;/div&gt;
</pre>
<p>That uses _books.gsp to render the editable list of books.</p>
<pre class="brush: html">
&lt;script type=&quot;text/javascript&quot;&gt;
    var childCount = ${authorInstance?.books.size()} + 0;

    function addChild() {
        var htmlId = &quot;book&quot; + childCount;
        var deleteIcon = &quot;${resource(dir:&#039;images/skin&#039;, file:&#039;database_delete.png&#039;)}&quot;;
        var templateHtml = &quot;&lt;div id=&#039;&quot; + htmlId + &quot;&#039; name=&#039;&quot; + htmlId + &quot;&#039;&gt;\n&quot;;
        templateHtml += &quot;&lt;input type=&#039;text&#039; id=&#039;expandableBookList[&quot; + childCount + &quot;].title&#039; name=&#039;expandableBookList[&quot; + childCount + &quot;].title&#039; /&gt;\n&quot;;
        templateHtml += &quot;&lt;span onClick=&#039;$(\&quot;#&quot; + htmlId + &quot;\&quot;).remove();&#039;&gt;&lt;img src=&#039;&quot; + deleteIcon + &quot;&#039; /&gt;&lt;/span&gt;\n&quot;;
        templateHtml += &quot;&lt;/div&gt;\n&quot;;
        $(&quot;#childList&quot;).append(templateHtml);
        childCount++;
    }
&lt;/script&gt;

&lt;div id=&quot;childList&quot;&gt;
    &lt;g:each var=&quot;book&quot; in=&quot;${authorInstance.books}&quot; status=&quot;i&quot;&gt;
        &lt;g:render template=&#039;book&#039; model=&quot;[&#039;book&#039;:book,&#039;i&#039;:i]&quot;/&gt;
    &lt;/g:each&gt;
&lt;/div&gt;
&lt;input type=&quot;button&quot; value=&quot;Add Book&quot; onclick=&quot;addChild();&quot; /&gt;
</pre>
<p>And that uses _book.gsp to render the individual records. It&#8217;s a bit overkill to call out to another template for only a few lines of HTML, but that&#8217;s how the original example did it and I&#8217;ll do the same for consistency.</p>
<pre class="brush: html">
    &lt;div id=&quot;book${i}&quot;&gt;
        &lt;g:hiddenField name=&#039;expandableBookList[${i}].id&#039; value=&#039;${book.id}&#039;/&gt;
        &lt;g:textField name=&#039;expandableBookList[${i}].title&#039; value=&#039;${book.title}&#039;/&gt;
        &lt;input type=&quot;hidden&quot; name=&#039;expandableBookList[${i}]._deleted&#039; id=&#039;expandableBookList[${i}]._deleted&#039; value=&#039;false&#039;/&gt;
        &lt;span onClick=&quot;$(&#039;#expandableBookList\\[${i}\\]\\._deleted&#039;).val(&#039;true&#039;); $(&#039;#book${i}&#039;).hide()&quot;&gt;&lt;img src=&quot;${resource(dir:&#039;images/skin&#039;, file:&#039;database_delete.png&#039;)}&quot; /&gt;&lt;/span&gt;
    &lt;/div&gt;
</pre>
<p>Here&#8217;s where I changed a bit more from the original example. I used <a href="http://jquery.com/">jQuery</a> because the selectors make things easy. Basically we render the books from the already-persisted author object, and keep track (using the _deleted field) of any that the user wants to remove. We also keep track of new objects to add.</p>
<p>One of the reasons I really liked this technique was how little impact there is on the controller. We just need to add this to the update method in AuthorController.</p>
<pre class="brush: java">
    def update = {
        def authorInstance = Author.get( params.id )
        if(authorInstance) {
            if(params.version) {
                // ... version locking stuff
            }
        authorInstance.properties = params
        def _toBeDeleted = authorInstance.books.findAll {it._deleted}
        if (_toBeDeleted) {
            authorInstance.books.removeAll(_toBeDeleted)
        }
        // ... etc.
</pre>
<p>The original example added similar code to the save method, but I don&#8217;t think it&#8217;s required for new objects (since they don&#8217;t have any already-persisted books to delete, only new books to create) so I only put it in the update method. I also changed it from find{} to findAll{} to guarantee that we get a list, and checked that we have objects to remove before calling the removeAll().</p>
<p>And it works great! Let&#8217;s look at some screenshots of the application in action.</p>
<p>First, we can create a new author and add some books right here instead of creating them separately and then matching them up.</p>
<p><img src="http://www.2paths.com/wp-content/uploads/2009/09/1.png" alt="Create Author" width="285" height="383" class="alignnone size-full wp-image-1232" /></p>
<p>Hit &#8220;Create&#8221; and it creates the Author and Book objects.</p>
<p><img src="http://www.2paths.com/wp-content/uploads/2009/09/2.png" alt="Show Author" width="281" height="354" class="alignnone size-full wp-image-1232" /></p>
<p>Edit the author we just created and see how we get a form that looks the same.</p>
<p><img src="http://www.2paths.com/wp-content/uploads/2009/09/3.png" alt="Edit Author" width="285" height="373" class="alignnone size-full wp-image-1232" /></p>
<p>However, it&#8217;s worth noting that the books displayed here are the already-persisted ones, so the form is keeping track of their ids and whether we should keep them or delete them on update. Let&#8217;s delete the first one and add two more new books.</p>
<p><img src="http://www.2paths.com/wp-content/uploads/2009/09/5.png" alt="Edit Author" width="279" height="400" class="alignnone size-full wp-image-1232" /></p>
<p>Now when we hit &#8220;Update&#8221; the controller has to be smart enough to remove that first book from the Author.books collection, then create two new Book objects and add them to the collection. And naturally, it is.</p>
<p><img src="http://www.2paths.com/wp-content/uploads/2009/09/6.png" alt="Show Author" width="286" height="376" class="alignnone size-full wp-image-1232" /></p>
<p>In addition to creating and destroying Book objects, we can update them. For example, let&#8217;s change the title of that first book to be the long version.</p>
<p><img src="http://www.2paths.com/wp-content/uploads/2009/09/7.png" alt="Edit Author" width="291" height="402" class="alignnone size-full wp-image-1232" /></p>
<p>No problem!</p>
<p><img src="http://www.2paths.com/wp-content/uploads/2009/09/8.png" alt="Edit Author" width="553" height="386" class="alignnone size-full wp-image-1232" /></p>
<p>So that&#8217;s one-to-many relationships in Grails forms. I hope it&#8217;s useful.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.2paths.com/2009/10/01/one-to-many-relationships-in-grails-forms/feed/</wfw:commentRss>
		<slash:comments>17</slash:comments>
		</item>
		<item>
		<title>Show me the data!  Seminar on Innovative Approaches to Turn Statistics into Knowledge 2009 (OECD, US Census Bureau, World Bank)</title>
		<link>http://www.2paths.com/2009/07/28/show-me-the-data-seminar-on-innovative-approaches-to-turn-statistics-into-knowledge-2009-oecd-us-census-bureau-world-bank/</link>
		<comments>http://www.2paths.com/2009/07/28/show-me-the-data-seminar-on-innovative-approaches-to-turn-statistics-into-knowledge-2009-oecd-us-census-bureau-world-bank/#comments</comments>
		<pubDate>Wed, 29 Jul 2009 00:37:33 +0000</pubDate>
		<dc:creator>Aaron</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Conferences]]></category>
		<category><![CDATA[Featured]]></category>
		<category><![CDATA[Technology]]></category>
		<category><![CDATA[aaron.gladders]]></category>
		<category><![CDATA[barcamp.vancouver]]></category>
		<category><![CDATA[bcv09]]></category>
		<category><![CDATA[bls]]></category>
		<category><![CDATA[linked.data]]></category>
		<category><![CDATA[michal.urbanski]]></category>
		<category><![CDATA[OECD]]></category>
		<category><![CDATA[semantic web]]></category>
		<category><![CDATA[us.bureau.of.labor.statistics]]></category>
		<category><![CDATA[us.census.bureau]]></category>
		<category><![CDATA[world.bank]]></category>

		<guid isPermaLink="false">http://www.2paths.com/?p=1162</guid>
		<description><![CDATA[(Update 20091005 &#8211; I gave the same talk at the US Bureau of Labor Statistics and Barcamp Vancouver 2009, in case you end up on this page from either source)
We recently presented at the Turning Statistics Into Knowledge conference, jointly organized by the US Census Bureau, the OECD and the World Bank, on July 15-16 [...]]]></description>
			<content:encoded><![CDATA[<p>(Update 20091005 &#8211; I gave the same talk at the US Bureau of Labor Statistics and Barcamp Vancouver 2009, in case you end up on this page from either source)</p>
<p>We recently presented at the <a href="http://www.oecd.org/progress/ict/statknowledge">Turning Statistics Into Knowledge</a> conference, jointly organized by the <a href="http://www.census.gov/">US Census Bureau</a>, the <a href="http://www.oecd.org/">OECD</a> and the <a href="http://www.worldbank.org/">World Bank</a>, on July 15-16 in DC at the Census Bureau.  Here is a <a href='http://www.2paths.com/wp-content/uploads/2009/07/tsik.zip'>pdf</a> of it including our speaking notes, or you can click through it below, sadly without notes (goto the slideshare site to see the notes).</p>
<div style="width:425px;text-align:left" id="__ss_1776637"><a style="font:14px Helvetica,Arial,Sans-serif;display:block;margin:12px 0 3px 0;text-decoration:underline;" href="http://www.slideshare.net/2Paths/show-me-the-data-turning-statistics-into-knowledge-2009" title="Show me the Data!  Turning Statistics Into Knowledge 2009">Show me the Data!  Turning Statistics Into Knowledge 2009</a><object style="margin:0px" width="425" height="355"><param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=20090715-turningstatisticsintoknowledgerevised-090727160953-phpapp01&#038;stripped_title=show-me-the-data-turning-statistics-into-knowledge-2009" /><param name="allowFullScreen" value="true"/><param name="allowScriptAccess" value="always"/><embed src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=20090715-turningstatisticsintoknowledgerevised-090727160953-phpapp01&#038;stripped_title=show-me-the-data-turning-statistics-into-knowledge-2009" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"></embed></object>
<div style="font-size:11px;font-family:tahoma,arial;height:26px;padding-top:2px;">View more <a style="text-decoration:underline;" href="http://www.slideshare.net/">presentations</a> from <a style="text-decoration:underline;" href="http://www.slideshare.net/2Paths">2Paths</a>.</div>
</div>
<p>I promised a number of people links to non-technical to technical primers, here we go:</p>
<p><strong>Non-technical</strong></p>
<ol>
<li>Very straight-forward overview, very good for sharing with non-technical people:<object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/OGg8A2zfWKg&#038;hl=en&#038;fs=1&#038;"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/OGg8A2zfWKg&#038;hl=en&#038;fs=1&#038;" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"></embed></object></li>
<li>Very quick one-pager, &#8220;<a href="http://ajbadd.wordpress.com/2009/06/09/the-semantic-web-a-low-fat-no-nonsense-introduction/">The “Semantic Web”: a low-fat, no-nonsense introduction</a>&#8221; by <a href="http://twitter.com/ajubdeen">@ajubdeen</a></li>
<li><a href="http://www.sciam.com/article.cfm?id=the-semantic-web&#038;print=true">Original article</a> from <a href="http://www.w3.org/People/Berners-Lee/">Tim Berners-Lee</a> showing his vision.</li>
<li>CNN <a href="http://www.cnn.com/2008/TECH/12/17/db.semanticweb/index.html?eref=rss_tech">Making sense of the &#8217;semantic Web&#8217;</a></li>
<li><a href="http://www.ted.com/talks/tim_berners_lee_on_the_next_web.html">Tim Berners-Lee 2009 TED talk on Linked Data</a></li>
</ol>
<p><strong>Semi-technical</strong></p>
<ol>
<li>Largely non-technical, injects a few buzzwords, &#8220;<a href="http://www.slideshare.net/mediasemanticweb/quick-linked-data-introduction">Quick Linked Data Introduction</a>&#8221; by <a href="http://mhausenblas.blogr.com/stories/">Michael Hausenblas</a></li>
<li><a href="http://www.readwriteweb.com/archives/understanding_the_new_web_era_web_30_linked_data_s.php">Understanding the New Web Era: Web 3.0, Linked Data, Semantic Web</a> by <a href="http://www.readwriteweb.com/archives/author/richard-macmanus-1.php">Richard MacManus</a>, summarizing a multi-part overview by <a href="http://twitter.com/gregboutin">Greg Boutin</a></li>
</ol>
<p><strong>Technical</strong></p>
<ol>
<li>An <strong>excellent</strong> overview &#8220;<a href="http://www.slideshare.net/mediasemanticweb/linked-data-michael-hausenblas-2009-03-05">Linked Data Tutorial</a>&#8221; by <a href="http://mhausenblas.blogr.com/stories/">Michael Hausenblas</a></li>
<li><a href="http://www.slideshare.net/iandavis/30-minute-guide-to-rdf-and-linked-data">30 Minute Guide to RDF and Linked Data</a> by <a href="http://twitter.com/iand">Ian Davis</a> and <a href="http://twitter.com/tommyh">Tom Heath</a></li>
</ol>
<p>After that, you&#8217;re off to the races!</p>
<p>(but as I said, just send us an email if you have questions or wanted to talk about it some more)</p>
]]></content:encoded>
			<wfw:commentRss>http://www.2paths.com/2009/07/28/show-me-the-data-seminar-on-innovative-approaches-to-turn-statistics-into-knowledge-2009-oecd-us-census-bureau-world-bank/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Groovy meetup  &#8211; Boats.com and SpringSource Groovy plugin for Eclipse</title>
		<link>http://www.2paths.com/2009/07/24/groovy-meetup-boatscom-and-springsource-groovy-plugin-for-eclipse/</link>
		<comments>http://www.2paths.com/2009/07/24/groovy-meetup-boatscom-and-springsource-groovy-plugin-for-eclipse/#comments</comments>
		<pubDate>Fri, 24 Jul 2009 16:14:43 +0000</pubDate>
		<dc:creator>Tim</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Grails]]></category>
		<category><![CDATA[groovy]]></category>

		<guid isPermaLink="false">http://www.2paths.com/?p=1149</guid>
		<description><![CDATA[Last night was a great Groovy meetup at the Boats.com office here in downtown Vancouver. I got to use the new Burrard Bridge dedicated bike lanes, which is always fun, but it was the meetup content that I really enjoyed.
Yuri from Boats.com went through their experience of using Groovy and Grails for some real projects.

Using [...]]]></description>
			<content:encoded><![CDATA[<p>Last night was a great <a href="http://groovyvan.wordpress.com/2009/07/12/vancouver-groovy-grails-meetup-july/">Groovy meetup</a> at the <a href="http://groovyvan.wordpress.com/2009/07/12/vancouver-groovy-grails-meetup-july/">Boats.com</a> office <a href="http://maps.google.ca/maps?f=q&amp;source=s_q&amp;hl=en&amp;geocode=&amp;q=1111+Melville+Street,+Vancouver,+Canada&amp;sll=49.891235,-97.15369&amp;sspn=33.830834,78.046875&amp;ie=UTF8&amp;ll=49.286647,-123.122213&amp;spn=0.008328,0.019054&amp;z=16&amp;iwloc=A">here</a> in downtown Vancouver. I got to use the new <a href="http://www.cbc.ca/canada/british-columbia/story/2009/07/13/bc-burrard-bridge-trial.html">Burrard Bridge dedicated bike lanes</a>, which is always fun, but it was the meetup content that I really enjoyed.</p>
<p>Yuri from Boats.com went through their experience of using <a href="http://groovy.codehaus.org/">Groovy</a> and <a href="http://www.grails.org/">Grails</a> for some real projects.</p>
<ul>
<li>Using Grails didn&#8217;t increase developer productivity, but it&#8217;s not the fault of Grails. Most of the holdups are non-technical &#8211; waiting for information, scope creep, redefining specs, etc.</li>
<li>Many tutorials show you can whip up a <a href="http://en.wikipedia.org/wiki/Create,_read,_update_and_delete">CRUD</a> application in 5 minutes, but he doesn&#8217;t know anyone in the business of writing CRUD applications. Real applications involve a lot more, and Grails is certainly useful, but it&#8217;s not a simple &#8220;grails generate-my-whole-app&#8221; command like some of the tutorials seem to indicate.</li>
<li>There are lots of good plugins for UI elements, but you have to be careful to avoid Javascript hell. One plugin might use <a href="http://developer.yahoo.com/yui/">YUI</a>, another might use <a href="http://jquery.com/">jQuery</a>, another might use <a href="http://www.prototypejs.org/">Prototype</a>, etc. If you&#8217;re not careful, your web page might suddenty balloon to over a megabyte just for the sake of some rounded corners and accordion widgets. Not really surprising, but just something to be aware of.</li>
<li>Groovy and Grails add some layers of abstraction and they don&#8217;t come for free, so make sure your development machine has enough CPU and RAM (and especially disk IO speed, in my opinion) to handle it.</li>
<li>Because Java is mature, there are lots of nice power tools available for code analysis, etc. Groovy/Grails doesn&#8217;t have much of that yet.</li>
<li>Dynamic languages are cool and it was rewarding to try out.</li>
<li>Advertising their use of Groovy/Grails helped attract talented people.</li>
</ul>
<p>That last point was interesting. I had only thought about the stack from a technical/productivity point of view, but it turns out there&#8217;s a good human justification for trying it out. If you can bait people with shiny dynamic languages, it can pay off by increasing your developer talent pool.</p>
<p>Next, Andrew from <a href="http://www.springsource.com/">SpringSource</a> presented the Groovy plugin for <a href="http://www.eclipse.org/">Eclipse</a> that they hope to release very soon. Here at 2Paths, about half of us are using Eclipse for Grails development and about half are using <a href="http://www.jetbrains.com/idea/">IntelliJ IDEA</a>. Andrew walked through a demo showing off a bunch of work they&#8217;ve been doing.</p>
<ul>
<li>Support for lots of mixing between Java and Groovy. He declared a Groovy class in one file, then created a Java subclass in another file, and then a Groovy subclass of the Java subclass in a third file. Inheritance worked nicely and  the autocomplete magic appeared to be sufficiently magical.</li>
<li>There&#8217;s some code inspection happening so autocomplete knows about things like the findBy* methods on your Groovy objects. Declare a Foo property on the Bar class and autocomplete will know about Bar.findByFoo() automatically. Lots of incremental compilation happening everywhere.</li>
<li>Lots of improvements in communication between the Java and Groovy compilers. Apparently the old plugin managed the stub compilation with lots of temporary files, so it was constantly writing to and reading from the disk. The new plugin couples the two worlds better and does a lot in RAM, so it should be faster and not thrash the disk so much.</li>
<li>A working debugger! Conditional breakpoints work, but you have to watch out for all the casting that happens under the hood when checking values. There are still some bugs in setting and removing breakpoints, but it looks good so far. Andrew talked about how he&#8217;s still trying to decide how much of the Groovy stack frames and object decorations to show. They show up in IDEA, so you have to wade through many incomprehensibly named caller levels between your own method calls, but it&#8217;s all there in case you really want to know.</li>
<li>The plugin will be available from the same place on <a href="http://codehaus.org/">The Codehaus</a> as the old plugin and it should be a standard easy install. Once they have something they can release, they&#8217;re going to throw it over the fence to be an open source project and encourage outsiders to contribute.</li>
<li>They fiddle with the Eclipse compiling backend by patching in some behaviour that&#8217;s basically &#8220;if (groovy) { do new_thing } else { do normal_thing }&#8221; so the plugin will work with Eclipse 3.4.2 only. They&#8217;ve done a lot of testing to make sure their patch doesn&#8217;t hurt normal Java projects, but that version dependency could become frustrating.</li>
</ul>
<p>Personally, I switched to IDEA because of the debugging support, which works well in both running the application and the tests. I said that we&#8217;re pretty evenly split between IDEs at 2Paths and we compare notes every morning in a contest to see whose tools are the most annoying. It looks like this new plugin may push Eclipse back a few points on the annoying scale. Good work, Andrew and company.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.2paths.com/2009/07/24/groovy-meetup-boatscom-and-springsource-groovy-plugin-for-eclipse/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Grails 1.1 Testing &#8211; Unit, Integration &amp; Functional</title>
		<link>http://www.2paths.com/2009/07/20/grails-11-testing-unit-integration-functional/</link>
		<comments>http://www.2paths.com/2009/07/20/grails-11-testing-unit-integration-functional/#comments</comments>
		<pubDate>Mon, 20 Jul 2009 21:50:28 +0000</pubDate>
		<dc:creator>dkoo</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[Technology]]></category>
		<category><![CDATA[Under the hood]]></category>
		<category><![CDATA[antbuilder]]></category>
		<category><![CDATA[controllerunittestcase]]></category>
		<category><![CDATA[functional test]]></category>
		<category><![CDATA[functional testing]]></category>
		<category><![CDATA[Grails]]></category>
		<category><![CDATA[grails 1.1]]></category>
		<category><![CDATA[grails testing]]></category>
		<category><![CDATA[grailsunittestcase]]></category>
		<category><![CDATA[groovy]]></category>
		<category><![CDATA[groovytestcase]]></category>
		<category><![CDATA[htmlunit]]></category>
		<category><![CDATA[integration test]]></category>
		<category><![CDATA[integration testing]]></category>
		<category><![CDATA[mockdomain]]></category>
		<category><![CDATA[unit test]]></category>
		<category><![CDATA[unit testing]]></category>

		<guid isPermaLink="false">http://www.2paths.com/?p=1138</guid>
		<description><![CDATA[Last Friday I gave a presentation to the team here at 2Paths on the testing capabilities in Grails 1.1.
As you Grails die-hards know, Grails 1.1 introduced the new GrailsUnitTestCase class which is a very useful extension of GroovyTestCase with support for mocking the dynamic properties and methods that Grails injects into various types of classes [...]]]></description>
			<content:encoded><![CDATA[<p>Last Friday I gave a <a href="http://www.slideshare.net/dkoo761/grails-11-testing-unit-integration-functional">presentation</a> to the team here at 2Paths on the testing capabilities in Grails 1.1.</p>
<p>As you Grails die-hards know, Grails 1.1 introduced the new <a href="http://grails.org/doc/1.1.1/guide/9.%20Testing.html#9.1%20Unit%20Testing">GrailsUnitTestCase</a> class which is a very useful extension of <a href="http://groovy.codehaus.org/Unit+Testing">GroovyTestCase</a> with support for mocking the dynamic properties and methods that Grails injects into various types of classes (domain, controller &amp; taglibs). Getting the team up to speed on this change was the original purpose of the presentation, but I decided to expand it to cover the full spectrum of testing capabilities in Grails 1.1 for those who might not have much exposure to it previously (including me!)</p>
<p>The presentation covered:</p>
<p>- the benefits &amp; drawbacks of each type of testing: <a href="http://en.wikipedia.org/wiki/Unit_testing">unit testing</a>, <a href="http://en.wikipedia.org/wiki/Integration_testing">integration testing</a>, <a href="http://en.wikipedia.org/wiki/Functional_testing">functional testing</a>.</p>
<p>- details on the new GrailsUnitTestCase and it&#8217;s extensions (ControllerUnitTestCase and TagLibUnitTestCase)</p>
<p>- a few &#8220;gotchas&#8221; &#8211; such as using mockDomain with inheritance hierarchies</p>
<p>- WebTest goodness &#8211; test organization &amp; re-use, Groovy step, passing params between Groovy &amp; WebTest contexts, a little dive into using HtmlUnit to test AJAX</p>
<p>- general recommendations for what types of tests to create for each artifact (domain classes, controllers, services, etc)</p>
<p>Here&#8217;s a link to the presentation on SlideShare.net (where you can view it online or download it):</p>
<div style="width: 425px; text-align: left;"><a title="Grails 1.1 Testing - Unit, Integration &amp; Functional" href="http://www.slideshare.net/dkoo761/grails-11-testing-unit-integration-functional">Grails 1.1 Testing &#8211; Unit, Integration &amp; Functional</a>  </p>
<div style="font-size: 11px; font-family: tahoma,arial; height: 26px; padding-top: 2px;">View more <a href="http://www.slideshare.net/">presentations</a> from <a href="http://www.slideshare.net/dkoo761">Dave Koo</a>.</div>
</div>
<p>Happy testing!<br />
Dave</p>
]]></content:encoded>
			<wfw:commentRss>http://www.2paths.com/2009/07/20/grails-11-testing-unit-integration-functional/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>memcached and Grails</title>
		<link>http://www.2paths.com/2009/07/16/memcached-and-grails/</link>
		<comments>http://www.2paths.com/2009/07/16/memcached-and-grails/#comments</comments>
		<pubDate>Fri, 17 Jul 2009 01:16:30 +0000</pubDate>
		<dc:creator>Tim</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Technology]]></category>
		<category><![CDATA[Under the hood]]></category>
		<category><![CDATA[Utilities]]></category>
		<category><![CDATA[continuous integration]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[Grails]]></category>
		<category><![CDATA[memcached]]></category>
		<category><![CDATA[scalability]]></category>

		<guid isPermaLink="false">http://www.2paths.com/?p=1122</guid>
		<description><![CDATA[I put together a Grails app to try out memcached. Like a lot of things in Grails, it was quite simple to integrate memcached once I found the right magic words. hibernate-memcached works nicely and has clear setup instructions.
Install memcached, which is simple if you use a Mac and have MacPorts installed.
$ sudo port install [...]]]></description>
			<content:encoded><![CDATA[<p>I put together a <a href="http://www.grails.org/">Grails</a> app to try out <a href="http://www.danga.com/memcached/">memcached</a>. Like a lot of things in Grails, it was quite simple to integrate memcached once I found the right magic words. <a href="http://code.google.com/p/hibernate-memcached/">hibernate-memcached</a> works nicely and has clear setup instructions.</p>
<p>Install memcached, which is simple if you use a Mac and have <a href="http://www.macports.org/">MacPorts</a> installed.</p>
<pre>$ sudo port install memcached</pre>
<p>Get the <a href="http://spymemcached.googlecode.com/files/memcached-2.3.1.jar">memcached jar</a> , the <a href="http://code.google.com/p/hibernate-memcached/downloads/list">hibernate-memcached jar</a> and the <a href="http://jdbc.postgresql.org/">PostgreSQL JDBC driver jar</a>, then  add them to the project.</p>
<pre>$ cp memcached-2.3.1.jar hibernate-memcached-1.2.jar postgresql-8.4-701.jdbc3.jar &lt;project_dir&gt;/lib/</pre>
<p>Configure memcached as the second-level cache by adding something like this to your DataSource.groovy.</p>
<pre><span class="pln">hibernate </span><span class="pun">{</span><span class="pln">
    cache</span><span class="pun">.</span><span class="pln">use_second_level_cache </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</span><span class="pln">
    cache</span><span class="pun">.</span><span class="pln">use_query_cache </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">true</span><span class="pln">
    cache</span><span class="pun">.</span><span class="pln">provider_class </span><span class="pun">=</span><span class="pln"> </span><span class="str">'com.googlecode.hibernate.memcached.MemcachedCacheProvider'</span><span class="pln">
    memcached </span><span class="pun">{</span><span class="pln">
        servers </span><span class="pun">=</span><span class="pln"> </span><span class="str">"localhost:11211"</span><span class="pln">
    </span><span class="pun">}</span><span class="pln">
</span><span class="pun">}</span></pre>
<p>I wrote a simple test to generate a whole bunch of domain objects and shove them in the database, then go back and fetch them all again. I set the test to run the insert/load loops in batches of 1, 10, 100, 1,000, 2,000, 4,000, 7,000, and 10,000 objects. I expected that inserting the objects would take a little longer because of the memcached overhead but that loading the objects would be faster because they could be loaded from memcached instead of going out to the database.</p>
<p>I tested three persistence backend configurations.</p>
<ul>
<li><a href="http://en.wikipedia.org/wiki/HSQLDB">HSQLDB</a>, the default for new Grails projects and when running unit/integration tests. This is an in-memory database only, so data aren&#8217;t persisted permanently.</li>
<li><a href="http://en.wikipedia.org/wiki/PostgreSQL"> PostgreSQL</a> for a full-blown RDBMS setup. I set dbCreate to &#8220;create-drop&#8221; in <a href="http://docs.codehaus.org/display/GRAILS/Quick+Start">DataSource.groovy</a> so that each test started with an empty database.</li>
<li>PostgreSQL with memcached enabled. I should point out that in this test I had both PostgreSQL and memcached running on the same machine (my laptop) as the application, so it&#8217;s really not taking advantage of memcached&#8217;s parallelism strengths.</li>
</ul>
<p>My expectations held true for the inserts. The memcached case is a little slower but not that much.</p>
<div id="attachment_1123" class="wp-caption alignnone" style="width: 806px"><img class="size-full wp-image-1123 " title="db-compare-inserts" src="http://www.2paths.com/wp-content/uploads/2009/07/db-compare-inserts.png" alt="db-compare-inserts" width="796" height="516" /><p class="wp-caption-text">Comparison of object insert times using HSQL, PostgreSQL, and memcached+PostgreSQL for persistence backend</p></div>
<p>When I tested the load times, I was surprised. It looked like memcached wasn&#8217;t speeding things up at all. I was also surprised to see that it took less time to load 10,000 objects from the data store than 7,000 objects. I ran the test a few times to rule out the possibility of a load spike from something else running at the same time, but I got similar results every time.</p>
<div id="attachment_1124" class="wp-caption alignnone" style="width: 806px"><img class="size-full wp-image-1124 " title="db-compare-loads" src="http://www.2paths.com/wp-content/uploads/2009/07/db-compare-loads.png" alt="Comparison object load times using HSQL, PostgreSQL, and memcached+PostgreSQL for persistence backend" width="796" height="516" /><p class="wp-caption-text">Comparison of object load times using HSQL, PostgreSQL, and memcached+PostgreSQL for persistence backend</p></div>
<p>I then tried to improve my test setup by running memcached on a second machine (my desktop, connected by gigabit ethernet to the laptop). Using the PostgreSQL + memcached configuration for each test this time, I looked at three more scenarios.</p>
<ul>
<li>PostgreSQL running locally on the laptop, memcached running locally on the laptop.</li>
<li>PostgreSQL running locally on the laptop, memcached running remotely on the desktop.</li>
<li>PostgreSQL running locally on the laptop, memcached running on both the laptop and the desktop.</li>
</ul>
<p>There&#8217;s some overhead in going out to the network to retrieve data instead of fetching it from a process running locally, so I expected the memcached local instance to be faster than the memcached remote instance. I expected the third test, with two instances of memcached running in parallel, to be fastest because it splits the load between the two instances and frees up some CPU time on the laptop. Things didn&#8217;t play out that way, though.</p>
<div id="attachment_1125" class="wp-caption alignnone" style="width: 806px"><img class="size-full wp-image-1125" title="local-remote-inserts" src="http://www.2paths.com/wp-content/uploads/2009/07/local-remote-inserts.png" alt="Comparison of object insert times for various memcache setups" width="796" height="516" /><p class="wp-caption-text">Comparison of object insert times for various memcache setups</p></div>
<div id="attachment_1126" class="wp-caption alignnone" style="width: 806px"><img class="size-full wp-image-1126" title="local-remote-loads" src="http://www.2paths.com/wp-content/uploads/2009/07/local-remote-loads.png" alt="Comparison of object load times for various memcache setups" width="796" height="516" /><p class="wp-caption-text">Comparison of object load times for various memcache setups</p></div>
<p>I have to be honest that these were not great tests. For one, I should really have run those insert/load loops in parallel instead of sequentially. I&#8217;m guessing that the test spent a lot of time stalled waiting for data when it could have been sending out more requests. I would also like to test with a large group of memcached servers and a remote database, perhaps even a cluster of app servers with a load balancer to try the full meal deal. The <a href="http://code.google.com/p/memcached/wiki/FAQ#Memcached_is_not_faster_than_my_database._Why?">memcached FAQ</a> explains that running everything on one machine will not show off memcached&#8217;s scalability.</p>
<p>However, this gave me the opportunity to get my hands dirty with memcached and learn a few things.</p>
<ul>
<li>Integrating memcached is so simple and the performance penalty of just running it locally during development is so small that there&#8217;s no reason not to enable it.</li>
<li>It&#8217;s dead simple to install and set up. We could , for example, easily  install it on a bunch of test servers and run multiple instances on different ports &#8211; one for each developer (to prevent us from clobbering each other&#8217;s data).</li>
<li>Measuring performance and scalability realistically is tricky. It&#8217;s one thing to hand wave and say that running multiple instances of a database or a cache will speed things up, but it takes some work to set up a realistic simulation of high traffic application performance.</li>
<li>Adding memcached doesn&#8217;t automatically turn performance up to <a href="http://www.youtube.com/watch?v=EbVKWCpNFhY">11</a>. I need to learn more about what&#8217;s going on under the hood with my domain objects and how they&#8217;re being persisted through memcached.</li>
</ul>
<p>I look forward to using memcached more in the future and learning how to really take advantage of it.</p>
<p><strong>Update (2009-07-24)</strong>: Ray pointed out in the <a href="#comment-8792">comments</a> that I should have enabled <a href="http://www.grails.org/GORM+-+Mapping+DSL">caching</a> in my domain objects under test. I added &#8220;static mapping = { cache true }&#8221; to my domain class definition and re-ran the tests to compare the effect under 4 scenarios.</p>
<ul>
<li>no domain object caching (as before)</li>
<li>domain object caching with memcached running locally</li>
<li>domain object caching with memcached running remotely</li>
<li>domain object caching with memcached running both locally and remotely</li>
</ul>
<p>Not much difference with the inserts.</p>
<div id="attachment_1145" class="wp-caption alignnone" style="width: 806px"><img class="size-full wp-image-1145" title="cache-true-inserts" src="http://www.2paths.com/wp-content/uploads/2009/07/cache-true-inserts.png" alt="Comparison of object insert times with domain object caching enabled and disabled" width="796" height="516" /><p class="wp-caption-text">Comparison of object insert times with domain object caching enabled and disabled</p></div>
<p>However, the load times are slightly better with memcached enabled during heavy activity! The advantage disappears when I add the overhead of traversing the network, but it&#8217;s a start. Thanks for the pointer, Ray!</p>
<div id="attachment_1146" class="wp-caption alignnone" style="width: 806px"><img class="size-full wp-image-1146" title="cache-true-loads" src="http://www.2paths.com/wp-content/uploads/2009/07/cache-true-loads.png" alt="Comparison of object load times with domain object caching enabled and disabled" width="796" height="516" /><p class="wp-caption-text">Comparison of object load times with domain object caching enabled and disabled</p></div>
]]></content:encoded>
			<wfw:commentRss>http://www.2paths.com/2009/07/16/memcached-and-grails/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Getting [On&#124;Off] the CouchDB?</title>
		<link>http://www.2paths.com/2009/06/30/getting-onoff-the-couchdb/</link>
		<comments>http://www.2paths.com/2009/06/30/getting-onoff-the-couchdb/#comments</comments>
		<pubDate>Wed, 01 Jul 2009 00:13:39 +0000</pubDate>
		<dc:creator>Garrett</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[Technology]]></category>
		<category><![CDATA[couchdb]]></category>
		<category><![CDATA[database]]></category>
		<category><![CDATA[MapReduce]]></category>
		<category><![CDATA[schema-free]]></category>

		<guid isPermaLink="false">http://www.2paths.com/?p=1066</guid>
		<description><![CDATA[Based on my recent experimenting with CouchDB I have conflicted feelings of whether to get on, or stay off the couch. I am intrigued by the notion of a schema-free document-based database, and how applications could be created to leverage such a technology. I am also reserved about its benefits to my current work environment. [...]]]></description>
			<content:encoded><![CDATA[<p>Based on my recent experimenting with <a href="http://couchdb.apache.org/" target="_blank">CouchDB</a> I have conflicted feelings of whether to get on, or stay off the couch. I am intrigued by the notion of a schema-free document-based database, and how applications could be created to leverage such a technology. I am also reserved about its benefits to my current work environment. Most of the relational data storage I&#8217;ve dealt with has been handled by Spring/Hibernate and now Grails/Hibernate. Does CouchDB buy anything that isn&#8217;t already easy to do with methodology like <a href="http://www.grails.org/GORM" target="_blank">GORM</a>? Let me attempt to dissect some of the pros and cons of CouchDB.</p>
<h3><em>PROS</em></h3>
<p>Probably the number one mentioned feature by anyone talking about CouchDB is that it&#8217;s schema-free. That means that each document is its own structure, that everything is based on a key-value pairing, where the value is typically a document, but can also be a base type. This is pretty cool when dealing with describing real world objects, as one can be more descriptive and hierarchical in storing the data. Also, updating your application&#8217;s domain model is much like evolution, as soon as you need something added to the model, just add it.</p>
<p>CouchDB&#8217;s RESTful HTTP service implementation is great for the many applications being created for today&#8217;s web. Send Javascript queries/requests to the service, and get JSON formatted data in return. This is great, since many front-end technologies can decipher JSON, and the format itself is even human readable if need be.</p>
<p>There is support for interacting with CouchDB data offline. One can access and edit data while not connected, and when back online can merge and update changes made while offline. This kind of support out of the box makes CouchDB a prime candidate for  web based applications like blogs, wikis, bug reporting, CRM, etc.</p>
<p>Another quality of CouchDB is its MapReduce implementation for handling large data sets. I&#8217;m going to redirect you to the author&#8217;s notes for this one: <a href="http://damienkatz.net/2008/02/incremental_map.html" target="_blank">MapReduce</a> and <a href="http://labs.google.com/papers/mapreduce.html" target="_blank">Google&#8217;s definition</a>.</p>
<h3><em>CONS</em></h3>
<p>Transaction management is non-existent. Relational database managements systems (RDBMS) provide mechanisms for things like transaction management, particularly unique constraints and locking. CouchDB is based on being &#8216;eventually consistent&#8217; and &#8216;available&#8217; (See the <a href="http://books.couchdb.org/relax/eventual-consistency" target="_blank">CAP Theorem section</a> for more), which although great for many things, doesn&#8217;t lend itself to transaction management.</p>
<p>Data security is also a concern. A somewhat old article describes this issue quite well: <a href="http://www.automatthew.com/2008/01/planned-security-model-for-couchdb.html" target="_blank">Planned Security Model For CouchDB</a>. The CouchDB team has been working on <a href="http://jchrisa.net/drl/_design/sofa/_show/post/couchdb_edge__security_and_vali" target="_blank">security and validation</a> but are still early in their development.</p>
<p>To a less important degree, but still worth mentioning, is that personally I really don&#8217;t find the out-of-the-box UI for CouchDB to be all that useful. It feels clunky to navigate your key/document pairs, and too feature-light for managing the database.</p>
<h3><em>Conclusion</em></h3>
<p>If you&#8217;re looking to create a document based web application, then this may be a worthwhile technology to use. CouchDB has a number of very impressive features. However, for now at least, I believe I&#8217;ll be staying off the couch.</p>
<hr />
<h5><em>Aside</em>:</h5>
<p>When testing out CouchDB I went through the process of manually installing the application. I had to resolve the dependencies, and deal with compilation errors on my own. I kind of wish I&#8217;d found <a href="http://jan.prima.de/~jan/plok/archives/142-CouchDBX-Revival.html" target="_blank">this site</a> earlier. Follow that link if you&#8217;re a MacOSX user and want an installer for CouchDB.</p>
<p>For me, this was pretty much the  only useful tutorial on getting started with CouchDB: <a href="http://jan.prima.de/~jan/plok/archives/108-Programming-CouchDB-with-Javascript.html" target="_blank">Programming CouchDB with Javascript</a>.</p>
<hr />
<h5><em>Resources</em>:</h5>
<ul>
<li><a href="http://couchdb.apache.org/" target="_blank">http://couchdb.apache.org/</a></li>
<li><a href="http://books.couchdb.org/relax/" target="_blank">http://books.couchdb.org/relax/</a></li>
<li><a href="http://www.eflorenzano.com/blog/tag/couchdb/" target="_blank">http://www.eflorenzano.com/blog/tag/couchdb/</a></li>
<li><a href="http://labs.google.com/papers/mapreduce.html" target="_blank">http://labs.google.com/papers/mapreduce.html</a></li>
<li><a href="http://damienkatz.net/2008/02/incremental_map.html" target="_blank">http://damienkatz.net/2008/02/incremental_map.html</a></li>
<li><a href="http://jan.prima.de/~jan/plok/archives/142-CouchDBX-Revival.html" target="_blank">http://jan.prima.de/~jan/plok/archives/142-CouchDBX-Revival.html</a></li>
<li><a href="http://www.automatthew.com/2008/01/planned-security-model-for-couchdb.html" target="_blank">http://www.automatthew.com/2008/01/planned-security-model-for-couchdb.html</a></li>
<li><a href="http://jchrisa.net/drl/_design/sofa/_show/post/couchdb_edge__security_and_vali" target="_blank">http://jchrisa.net/drl/_design/sofa/_show/post/couchdb_edge__security_and_vali</a></li>
<li><a href="http://jan.prima.de/~jan/plok/archives/108-Programming-CouchDB-with-Javascript.html" target="_blank">http://jan.prima.de/~jan/plok/archives/108-Programming-CouchDB-with-Javascript.html</a></li>
<li><a href="http://www.infoq.com/CouchDB" target="_blank">http://www.infoq.com/CouchDB</a></li>
<li><a href="http://www.infoq.com/news/2007/11/the-rdbms-is-not-enough" target="_blank">http://www.infoq.com/news/2007/11/the-rdbms-is-not-enough</a></li>
<li><a href="http://www.infoq.com/news/2008/11/Database-Martin-Fowler" target="_blank">http://www.infoq.com/news/2008/11/Database-Martin-Fowler</a></li>
<li><a href="http://code.google.com/p/couchdb-lounge/wiki/SettingUpTwoCouchInstances" target="_blank">http://code.google.com/p/couchdb-lounge/wiki/SettingUpTwoCouchInstances</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.2paths.com/2009/06/30/getting-onoff-the-couchdb/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Product Pitches and PhDs, Oh My!</title>
		<link>http://www.2paths.com/2009/06/23/semtech2009-dev-perspective/</link>
		<comments>http://www.2paths.com/2009/06/23/semtech2009-dev-perspective/#comments</comments>
		<pubDate>Tue, 23 Jun 2009 22:36:35 +0000</pubDate>
		<dc:creator>Michal</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[Events]]></category>
		<category><![CDATA[semantic-web conference semtech2009]]></category>

		<guid isPermaLink="false">http://www.2paths.com/?p=1010</guid>
		<description><![CDATA[Aaron and I got back last week from the 2009 Semantic Technology Conference, aka Semtech2009, and after four solid days of twittering and forgetting people&#8217;s names and awkward conversation, I figured I should comment on what was cool and what was forgettable before everything shifts from the first category to the second.
The basic conference elements [...]]]></description>
			<content:encoded><![CDATA[<p>Aaron and I got back last week from the 2009 Semantic Technology Conference, aka <a href="http://www.semtech2009.com/">Semtech2009</a>, and after four solid days of twittering and forgetting people&#8217;s names and awkward conversation, I figured I should comment on what was cool and what was forgettable before everything shifts from the first category to the second.</p>
<p>The basic conference elements were all good: it felt well-run, there was a good mix of people and the sessions were more-or-less mostly-kinda interesting. The conference itself was in San Jose, so&#8230; it was warm outside, I guess? We weren&#8217;t tempted to skip sessions because there might something more interesting to do? I&#8217;ll do breakdowns by category, going in decreasing order of importance.</p>
<p><strong>Keynotes</strong></p>
<p>I attended the opening keynotes on Tuesday, which <a href="http://www.semtech2009.com/session/2120/">started</a> with Thomas Tague from OpenCalais letting us know that People With Money™ (my term for it) were about to descend on our sleepy semantic backwater. He was a good speaker, and his message was well-received. What I got out of his talk was that we (as software developers) need to figure out where we can succeed in this new landscape, that there are still a lot of potentially profitable niches and that opportunities for tool developers still exist. Tuesday&#8217;s second keynote was Tom Gruber from <a href="http://www.siri.com/">Siri</a>, who gave a demo of their upcoming mobile &#8220;virtual personal assistant&#8221;. It was a pretty rad demo, and I&#8217;m very excited about the ecosystem of services that it might cultivate. Siri has the potential to be a killer app in both the mobile and semantic web spaces, and I&#8217;d love to see their execution match their vision.</p>
<p>I skipped the other two keynotes. One was a panel discussion; I find it difficult to get much out of anything in this format. The closing keynote was two gentlemen from the NY Times discussing semantic technology uptake at the Grey Lady. I probably should have gone to that one.</p>
<p><strong>Sessions</strong></p>
<p>I&#8217;m not going to go over every session,  because that&#8217;s a lot of writing that no one wants to read. The following sessions were ones I found worthwhile, so if you&#8217;re ever undecided about seeing these people, or say, booking them for <em>your</em> conference, you should just go right ahead on my say-so.</p>
<p><strong>Elisa Kendall and Deborah McGuinness</strong>: <em>Ontology 101</em> &#8211; a lot of content in this one. One of the key themes that I picked up on during the conference is that almost every way to incorporate semantic tech in an organization starts with understanding and developing ontologies. Another one is that you shouldn&#8217;t feel bad for thinking that ontologies are hard&#8230; there are people that devote a lot of time and effort to making them, and <strong>they</strong> still sometimes get it wrong.</p>
<p><strong>John Hebeler and Matthew Fisher</strong>: <em>Semantic Web Programming</em> &#8211; <strong>loved this</strong>. It had a great format, with short lab-like exercises that got progressively more complicated; we started with building simple models in Jena, and ended with using the Pellet reasoner to infer new knowledge. It was so good and well-presented that I immediately went out and bought their <a href="http://www.amazon.ca/Semantic-Web-Programming-Mike-Dean/dp/047041801X">book</a>.</p>
<p><strong>Dave Wood</strong>: <em>Active PURLs</em> &#8211; a great, no-nonsense introduction to <a href="http://purlz.org/">PURL</a>s and their in-development &#8220;active&#8221; variants. Dave, along with quite a few people at the conference, really came across as a true believer in the infrastructure of our current web. I had always thought people just made it up as they went a long, but no, a lot of thought and effort went into those early RFCs.</p>
<p><strong>Random people</strong>: <em>Visualizing RDF</em> &#8211; this sort-of panel was sort-of organized on the spur of the moment. I&#8217;m glad I went, because I got to see AllegroGraph&#8217;s <a href="http://www.franz.com/agraph/gruff/">Gruff</a> and someone else&#8217;s <a href="http://www.diagramic.com/">Diagramic</a>. Cool tools, these.</p>
<p><strong>Food</strong></p>
<p>The catered stuff was quite good for conference food. I&#8217;m going to spread the rumour that, due to certain &#8220;economic realities&#8221; (*cough*recession*cough*), the turnout was less than the organizers were expecting, and so the catering budget (which had been decided per-head much earlier) was relatively high.</p>
<p>Outside the conference, found a few places in San Jose which might be worth going to if, like us, you didn&#8217;t rent a car and don&#8217;t have enough time to trek to San Fran.</p>
<p><a href="http://www.yelp.com/biz/mezcal-san-jose">Mezcal</a> &#8211; featured mezcalitas (good) and pan-fried grasshoppers (less good). Get the lightest-coloured molé&#8230; best of their three.</p>
<p><a href="http://www.yelp.com/biz/angelous-cafe-and-grill-san-jose">Angelou&#8217;s</a> &#8211; went here while on a burrito hunt. (To a Canadian, America is the Promised Land of the Burrito). I ordered their Super Burrito. It was Merely Okay (A Bit on the Salty Side).</p>
<p><a href="http://www.yelp.com/biz/hanuman-thai-cuisine-san-jose">Hanuman</a> &#8211; ordered the curried duck (&#8220;Thai spicy&#8221;). It was fantastically tasty, but it was not Thai spicy.</p>
<p><strong>Conclusion?</strong></p>
<p>This year had a good mix of people from the business, acedemic and technical communities. As expected, there was lots of networking, not all of it involving Twitter or Cat-5. They say next year will be even more business-focussed, but since grad students love to travel, expect to see them too. And maybe us, as well.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.2paths.com/2009/06/23/semtech2009-dev-perspective/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>jQuery Tidbits</title>
		<link>http://www.2paths.com/2009/06/23/jquery-tidbits/</link>
		<comments>http://www.2paths.com/2009/06/23/jquery-tidbits/#comments</comments>
		<pubDate>Tue, 23 Jun 2009 21:12:29 +0000</pubDate>
		<dc:creator>Garrett</dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[ajax]]></category>
		<category><![CDATA[DOM]]></category>
		<category><![CDATA[dynamic web]]></category>
		<category><![CDATA[HTML]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[javascript library]]></category>
		<category><![CDATA[jquery]]></category>
		<category><![CDATA[web application]]></category>

		<guid isPermaLink="false">http://www.2paths.com/?p=1032</guid>
		<description><![CDATA[My experience with JavaScript has lead me to using Ajax frameworks and libraries such as Mootools, ExtJs, and now jQuery. The jQuery library has been around long enough to become stable, fast, and lightweight for dealing with those annoying problems all web application developers have to handle.
The core competency of jQuery is it&#8217;s ability to [...]]]></description>
			<content:encoded><![CDATA[<p>My experience with JavaScript has lead me to using Ajax frameworks and libraries such as <a title="Mootools" href="http://mootools.net/" target="_blank">Mootools</a>, <a title="ExtJs" href="http://extjs.com/" target="_blank">ExtJs</a>, and now <a title="jQuery" href="http://jquery.com" target="_blank">jQuery</a>. The jQuery library has been around long enough to become stable, fast, and lightweight for dealing with those annoying problems all web application developers have to handle.</p>
<p>The core competency of jQuery is it&#8217;s ability to manage and manipulate the HTML <a title="DOM" href="http://www.w3.org/DOM/" target="_blank">DOM</a>. This portion of the library alone makes the small 20kb library fully worth the download. Targeting an element for assignment while manipulating anything within a document is as easy as:</p>
<pre>var element = $('#idOfTheElement')</pre>
<p><a style="font-size: smaller;" href="http://docs.jquery.com/Selectors/id#id" target="_blank">(API for selectors)</a></p>
<p>More than just HTML document manipulation, jQuery has a very simple and clean way to implement event triggers and handlers.</p>
<pre>// trigger
$("#idOfElement").click(function () {
   $("#idOfElementThatIsListening").<strong class="selflink">trigger</strong>('click');
});
// handler
$("#idOfElementThatIsListening").<strong class="selflink">bind</strong>("click", function(e){
   alert("click event happened")
});</pre>
<p>(<a style="font-size: smaller;" href="http://docs.jquery.com/Events" target="_blank">API for events</a>)</p>
<p>This is just  a taste of what jQuery can do for your web application development. I recommend giving it a test drive if you haven&#8217;t already. The online <a href="http://docs.jquery.com/Main_Page" target="_blank">API and documentation</a> is thorough and well constructed. The user community is chock-full of enthusiasts with useful plug-ins.</p>
<p>This will be my go-to JavaScript library for most applications for the foreseeable future.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.2paths.com/2009/06/23/jquery-tidbits/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
