<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <title>McWong</title>
    <link rel="alternate" type="text/html" href="http://www.themcwongs.com/mcblog/" />
    <link rel="self" type="application/atom+xml" href="http://www.themcwongs.com/mcblog/atom.xml" />
    <id>tag:www.themcwongs.com,2008-03-02:/mcblog//1</id>
    <updated>2008-05-15T02:14:10Z</updated>
    
    <generator uri="http://www.sixapart.com/movabletype/">Movable Type Open Source 4.1</generator>

<entry>
    <title>How J2EE set architecture back a ways</title>
    <link rel="alternate" type="text/html" href="http://www.themcwongs.com/mcblog/2008/05/how-j2ee-set-architecture-back.html" />
    <id>tag:www.themcwongs.com,2008:/mcblog//1.43</id>

    <published>2008-05-15T01:43:46Z</published>
    <updated>2008-05-15T02:14:10Z</updated>

    <summary><![CDATA[I'll start this off by saying this is pure opinion.&nbsp; I don't have any statistics.&nbsp; Instead I have some gut intuition based on numerous "enterprise Java" projects within my company, and through observing the development community.I'm currently involved in architecting...]]></summary>
    <author>
        <name>McWong</name>
        
    </author>
    
        <category term="opinion" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="architecture" label="architecture" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="asynchronous" label="asynchronous" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="erlang" label="erlang" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="j2ee" label="j2ee" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="messaging" label="messaging" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="rdbms" label="rdbms" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="ruby" label="ruby" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="scala" label="scala" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://www.themcwongs.com/mcblog/">
        <![CDATA[I'll start this off by saying this is pure opinion.&nbsp; I don't have any statistics.&nbsp; Instead I have some gut intuition based on numerous "enterprise Java" projects within my company, and through observing the development community.<br /><br />I'm currently involved in architecting a next generation systems management framework.&nbsp; It has fairly hefty requirements... monitor and manage millions of objects distributed across the globe in real time.&nbsp; Take out the "millions" bit, and this isn't so bad.&nbsp; There are a bunch of standards out there (JMX, SNMP), there are some off the shelf tools, and you can knock something together pretty quickly that covers a reasonable subset of equipment for small installs.&nbsp; But we need to do better.&nbsp;&nbsp; We need to manage more than just "most" of the equipment, and we need to do it in some of the world's biggest data centers.&nbsp; And as I talk about this with my peers from other groups,&nbsp; I'm getting more convinced that J2EE has poisoned a good chunk of the current generation of architects.<br /><br />I know this isn't how J2EE &nbsp;has to work, but in most projects, you start them like this...<br />1) Build your object model, use magic to map it to the DB<br />2) Build your business logic<br />3) Build your front end<br />4) Ship it!<br /><br />And there you are. &nbsp;You have a nice central database so you don't have to worry about distributed data so much. &nbsp;It's all nice and safe on the disk. &nbsp;You have some adapters to get stuff in and out, and a cozy UI that serves it all up to the user. &nbsp;And it scales... for as much load as you can throw at your desktop.<br /><br />Then you take it to some place big. &nbsp;And it just flat out doesn't work. &nbsp;It's not a matter of getting a bigger database, because you just can't scale it up past a certain point. You can cluster, but now you need a professional services group to install your product, and the performance gains to be had aren't crystal clear. <br /><br />It becomes a matter of deciding what goes in there. &nbsp;And when, and how. &nbsp;But looking forward to these issues is met with fierce resistance in design meetings... Building distributed systems is much harder than building something around a central store. &nbsp;Objections are typically based on reading marketing propaganda from database vendors. &nbsp;Responses of the form "well, we'll partition the data when we hit that load. &nbsp;Or use a cluster... yeah, a cluster will fix everything" are extremely painful to work through. &nbsp;After all, some "expert" from VendorX says it will work. Who are you, my peer, to assume you know more than me? &nbsp;<br /><br />But guess what? &nbsp;Each of these objectors has a product in the field, based a central database, handling far less load than our targets, and none of them scale well.<br /><br />Scaling out is beating scaling up. &nbsp;Processors are going multi-core, not taking huge leaps forward in the GHz war. &nbsp;Scala and Erlang are gaining in popularity, and with them a different model of parallelism (not new, just different) than that offered by J2EE. &nbsp;The actor model embraced by both of those languages is primarily targeted at small processes within a service. &nbsp;But as you look at larger chunks of the architecture, I think the sanest way forward is to embrace a similar model. &nbsp;<br /><br />ESB has been a buzzword for years now. &nbsp;I don't buy all the marketing hype around it, but at its core there is a model that the current generation of architects needs to get a handle on. &nbsp;It's very similar in concept to the Erlang/Scala actor model. &nbsp;Yes, it's harder to design around asynchronous messages and a large number of distributed components. &nbsp;But that's how you handle the big problems. &nbsp;And that's why you hire good developers to work on them, not just some random CS grad who happens to be able to regurgitate the latest Spring book. &nbsp;<br /><br />I'm not saying there isn't a place for the traditional J2EE buildout (rails has a similar model, and works wonderfully for small-medium sized projects). &nbsp;And in fact, even with these asynchronous systems there are almost certainly services that need a DB backing them. &nbsp;But there are other tools that should be in your toolbelt.<br /><br />My suggestion to Java architects today would be to pick up another language that fosters a totally different architecture. &nbsp;I'm currently biased towards Ruby, Erlang or Scala. &nbsp;There are no guarantees that any of these scale better than Java (and it's fairly easily arguable that Ruby doesn't). &nbsp;But any of these will make you see the world in a different way, and allow you to more effectively consider and evolve your current architecture.<br /> ]]>
        
    </content>
</entry>

<entry>
    <title>Scala Lift Off - Static Companion to Ruby?</title>
    <link rel="alternate" type="text/html" href="http://www.themcwongs.com/mcblog/2008/05/scala-lift-off-static-companio.html" />
    <id>tag:www.themcwongs.com,2008:/mcblog//1.42</id>

    <published>2008-05-13T05:42:04Z</published>
    <updated>2008-05-13T06:50:44Z</updated>

    <summary><![CDATA[So I went to the last half of Scala Lift Off on Saturday (only half, because the first half was taken up by my final MBA class.&nbsp; Ever.).&nbsp; I went primarily out of curiosity, not knowing much about Scala or...]]></summary>
    <author>
        <name>McWong</name>
        
    </author>
    
        <category term="scala" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="lift" label="lift" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="rails" label="rails" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="ruby" label="ruby" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="scala" label="scala" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://www.themcwongs.com/mcblog/">
        <![CDATA[So I went to the last half of <a href="http://www.scala-lang.org/">Scala</a> <a href="http://scalaliftoff.com/liftoff/">Lift Off</a> on Saturday (only half, because the first half was taken up by my final MBA class.&nbsp; Ever.).&nbsp; I went primarily out of curiosity, not knowing much about Scala or Lift.&nbsp; The main draw was the built in comet support for Lift, which seems to not be a focus in other frameworks... at least not for Rails.&nbsp; We currently use <a href="http://juggernaut.rubyforge.org/">Juggernaut</a> for comet support, but depending on flash is something of a liability (see: iPhone), and Juggernaut itself isn't as smoothly integrated with Rails as i'd like.<br /><br />I came away extremely impressed.&nbsp; Scala is relatively unheralded in the world of alternative JVM languages (see Groovy, Jython and JRuby publicity), but shows a lot of promise.&nbsp; It's a functional language with an expressive syntax that allows you to easily create code that looks DSL-ish.&nbsp; These are the primary features that drew me to Ruby (ok, Ruby isn't a functional language, but you can sorta fake it).&nbsp; But Scala has a better integration story with existing Java libraries, is strongly typed, and has a stronger functional bent.<br /><br />I'm a big believer in the right tool for the job, and as such don't fall into a pure-dynamic or pure-static language camp.&nbsp; I also don't fall into a single language camp. I really enjoy Ruby for quick prototyping, and love Rails for quick prototyping of webapps, and maintaining a nimble production face on web applications.&nbsp; But Rails falls down when I need to run background processing.&nbsp; The times I think hardest about moving back to a Java webapp environment are when I need to go write something that doesn't just receive a web request and terminate.&nbsp; This is where concurrency issues get painful to deal with, Ruby daemons/DRb are painful, and starting up a whole Rails env for simple processing is rough.<br /><br />So I'm hoping Scala/Lift fills that void.&nbsp; I'm mentally sketching out a replacement of our background processing jobs (Twitter integration, email processing, etc.) with Scala, and in particular the <a href="http://debasishg.blogspot.com/2006/11/threadless-concurrency-on-jvm-aka-scala.html">Actors</a> library.&nbsp; These are relatively simple processing tasks, and should give me a decent feel for the language.&nbsp; It should also improve the stability and scalability of our background processing.&nbsp; It may also yield some reasonable libraries to contribute back (Scala Twitter library, Scala ActiveRecord bridge).&nbsp; <br /><br />Once I have that nailed down, an evaluation of how Lift can/should fit into the framework is in order... or maybe I'll have to start my Rettiwt side project based on Lift.<br /><br /><br /> ]]>
        
    </content>
</entry>

<entry>
    <title>System and Organizational Scaling - the Enterprise View</title>
    <link rel="alternate" type="text/html" href="http://www.themcwongs.com/mcblog/2008/04/system-and-organizational-scal.html" />
    <id>tag:www.themcwongs.com,2008:/mcblog//1.41</id>

    <published>2008-04-30T15:40:18Z</published>
    <updated>2008-04-30T17:21:56Z</updated>

    <summary><![CDATA[Albert Wenger put up a good post talking about the challenges faced by their startup portfolio, and how a vertical approach to subsystem division helps scaling the organization.&nbsp; In fact the Web 2.0 landscape is very reflective of this approach.&nbsp;...]]></summary>
    <author>
        <name>McWong</name>
        
    </author>
    
        <category term="organization" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="development" label="development" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="organization" label="organization" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="scale" label="scale" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://www.themcwongs.com/mcblog/">
        <![CDATA[Albert Wenger put up a good <a href="http://continuations.wenger.us/post/33100418">post</a> talking about the challenges faced by their startup portfolio, and how a vertical approach to subsystem division helps scaling the organization.&nbsp; In fact the Web 2.0 landscape is very reflective of this approach.&nbsp; 10 years ago if Yahoo needed an authorization framework they would have built it themselves.&nbsp; Today people use oAuth.&nbsp; Flickr, Twitter, Delicious, Campfire, Meebo, S3... all very focused services that delegate non-core functionality to another service where possible.&nbsp; For those things that are duplicated across services (web frameworks, database backends), nobody cares if they're different... it's hidden behind the service.<br /><br />This is very attractive for the startup ecosystem.&nbsp; The tangible results have been products that are cheaper to launch, quicker to market, and easy to adapt to customer feedback.&nbsp; But how does it apply to the enterprise?<br /><br /><b>Issue 1:<br /><br /></b>For better or worse, in large engineering organizations people tend to care that the common horizontal components are indeed common.&nbsp; If you work in a 100+ person engineering organization, and the rest of the team is using Spring + Struts for web development, it's usually a tough sell to start using Rails.&nbsp; If the organizations insists on this homogeneity, your vertically
sliced org is cross cut by the commonality police, hampering the
agility of the vertical team.&nbsp; There are good reasons for this.&nbsp; Somebody needs to support and test the thing.&nbsp; If nobody else can deal with what you just built, it doesn't have a path to market, and is therefore useless.&nbsp; <br /><br /><b>Issue 2:<br /><br /></b>It's easier to add 1 person or 1 feature to an existing service than it is to spin up a whole new service team.&nbsp; Most organizations usually don't have the luxury of five new reqs to apply to a service.&nbsp; And they're also hesitant to carve out five people from existing teams to spin up a new service.&nbsp; So teams and services are usually built by accretion.&nbsp; The result is usually a gradual march to collapse, as services become bloated and so difficult to maintain they need to be replaced.<br /><br /><b>Issue 3:<br /><br /></b>The organization only has capacity for a limited number of products.&nbsp; A startup of 5-10 people can fully support the development, launch, and marketing of a vertical service.&nbsp; In a large organization, the amount of infrastructure required to take a product to market means that unless you have a multi-million dollar revenue stream guaranteed in year 1 your chances of getting marketing, sales, training, doc, etc. spun up to support you are very small.&nbsp; That means actual releases are larger than individual services.<br /><br /><b>Issue 4:<br /><br /></b>Coordination challenges across vertical services.&nbsp; This ties in to issue 3.&nbsp; Because a product release is a composition of multiple services, there is a desire for tight coordination across these services.&nbsp; Contrast this to the Web 2.0 world.&nbsp; Basecamp uses Amazon's S3 storage service.&nbsp; They are under no illusion that they can call the shots on S3's feature roadmap.&nbsp; And even more important, they would not plan for a release based on features that S3 has not committed to.&nbsp; In the enterprise, these rules don't apply.&nbsp; Feature roadmaps for a collection of services are determined at the same time, and management hopes to coalesce them all into product at a predefined future date.<br /><br /><b>Possible Solutions<br /><br /></b>To address issue 1, the organization should focus on service capability, not underlying implementation.&nbsp; To support this, service teams will need to be self sufficient, and should make implementation decisions as a <i>team</i>.&nbsp; It needs to be tested.&nbsp; Involve quality engineering in the technology selection.&nbsp; It needs to be doced.&nbsp; Involve doc.&nbsp; At the end of the day the customer rarely cares if you use VB or Python to get them their value.<br /><br />To address issue 2, the entire organization needs to be focused on keeping services focused on their core function.&nbsp; When somebody needs feature X, run through the list of services.&nbsp; Teams should be willing to say no to features not because they don't
have capacity, but because the feature corrupts the purpose of the
service. If the feature doesn't fit with any project, spin up a new service rather than accrete it onto an existing project.&nbsp; This requires the organization to be flexible, to be willing to work on new things, and to give up old responsibilities.&nbsp; It also demands a rejection of empire building.&nbsp; You should take more pride in your project being small, focused, and absolutely fantastic at what it does, rather than measuring your worth by the number of people on your project.&nbsp; <br /><br />Issues 3 and 4 are the stickiest.&nbsp; In the enterprise the opportunity for experimentation is much lower than it is for a consumer web offering.&nbsp; Perhaps one option is to create a "labs" organization, that releases products for free for use by bleeding edge customers.&nbsp; Services that are vetted through the "labs" channel could be productized at a later date.&nbsp; <br /><br />Coordination problems could be solved by eliminating planning that spans services. Take a snapshot of service capabilities, and plan from there.&nbsp; This likely slows down development to a certain extent, but also makes individual plans more predictable, and significantly reduces coordination challenges.<br /><b><br /></b><b>Wrapup&nbsp; <br /><br /></b>While challenges exist in the enterprise, I think it's worthwhile to look at how we can make vertical slicing of the org work.&nbsp; The advantages realized by Web 2.0 companies are compelling, and we definitely have challenges with our current horizontally structured groups.<br />  ]]>
        
    </content>
</entry>

<entry>
    <title>Transparent PNGs in IE w/ Rails</title>
    <link rel="alternate" type="text/html" href="http://www.themcwongs.com/mcblog/2008/04/transparent-pngs-in-ie-w-rails.html" />
    <id>tag:www.themcwongs.com,2008:/mcblog//1.40</id>

    <published>2008-04-27T20:58:10Z</published>
    <updated>2008-04-27T21:10:29Z</updated>

    <summary><![CDATA[I've been working on Kebima for several months now, using Firefox and Linux/OSX.&nbsp; Chalk it up to not doing enough research, but I just figured transparent PNGs worked in IE.&nbsp; Oh well.&nbsp; They don't.&nbsp; At least not in 6 and...]]></summary>
    <author>
        <name>McWong</name>
        
    </author>
    
        <category term="rails" scheme="http://www.sixapart.com/ns/types#category" />
    
        <category term="ruby" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="design" label="design" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="ie" label="ie" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="png" label="png" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="rails" label="rails" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="ruby" label="ruby" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://www.themcwongs.com/mcblog/">
        <![CDATA[I've been working on <a href="http://www.kebima.com/">Kebima</a> for several months now, using Firefox and Linux/OSX.&nbsp; Chalk it up to not doing enough research, but I just figured transparent PNGs worked in IE.&nbsp; Oh well.&nbsp; <a href="http://support.microsoft.com/kb/294714">They don't</a>.&nbsp; At least not in 6 and earlier.&nbsp; So began my mission to get them to work.<br /><br />I was already using a lightbox package that uses the technique mentioned in the MS support article, but I didn't want to have to apply a div-specific solution for every png on my page... and since i'm using the <a href="http://www.famfamfam.com/lab/icons/silk/">silk</a> icon set, this would mean a lot a lot of specificity.<br /><br />Googling gets you a lot of results.&nbsp; The first hit is actually pretty good, in that it states that the script isn't maintained, but points you to <a href="http://24ways.org/2007/supersleight-transparent-png-in-ie6">24 Ways</a>, which has a pretty good solution.&nbsp; This likely works out of the box for normal web development (I did run into one bug... for some reason the section of the script that sets root on line 17 failed... I took out the bit that allowed you to limit the div the script was applied to).&nbsp; However rails likes to throw timestamps on the end of images, so instead of '/images/icon.png' you get '/images/icon.png?<span class="attribute-value">1209327623'</span><br /><br />Because the 24 Ways script is looking for an img tag whose src attribute ends with '.png', this makes things not work.&nbsp;  My solution was to add a regex to match rails style image srcs<br /><br />&nbsp;&nbsp;&nbsp; var png_pattern&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; = /\.png(\?\d*)?$/i;<br /><br />And then match against that in fnLoadPngs<br /><br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // background pngs<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; if (obj.currentStyle.backgroundImage.match(png_pattern) !== null) {<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; bg_fnFixPng(obj);<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // image elements<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; if (obj.tagName=='IMG' &amp;&amp; obj.src.match(png_pattern) !== null){<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; el_fnFixPng(obj);<br />&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br /><br />And it worked.&nbsp; Plus I got a pretty decent workout running up and down the stairs between Mac and PC.<br />  ]]>
        
    </content>
</entry>

<entry>
    <title>Twitter, Jabber, Stability - Should Twitter be more ejabberd and less rails?</title>
    <link rel="alternate" type="text/html" href="http://www.themcwongs.com/mcblog/2008/04/twitter-jabber-stability-shoul.html" />
    <id>tag:www.themcwongs.com,2008:/mcblog//1.39</id>

    <published>2008-04-26T01:21:49Z</published>
    <updated>2008-04-26T01:34:30Z</updated>

    <summary><![CDATA[So I switched our Twitter integration for Kebima from using the HTTP interface to using the XMPP interface.&nbsp; We really wanted real time updates, and polling just seems so barbaric.&nbsp; I found some code on how to create a twitter...]]></summary>
    <author>
        <name>McWong</name>
        
    </author>
    
        <category term="ruby" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="api" label="api" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="jabber" label="jabber" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="twitter" label="twitter" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="xmpp" label="xmpp" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://www.themcwongs.com/mcblog/">
        <![CDATA[So I switched our Twitter integration for <a href="http://www.kebima.com/">Kebima</a> from using the HTTP interface to using the XMPP interface.&nbsp; We really wanted real time updates, and polling just seems so barbaric.&nbsp; I found some code on <a href="http://dominiek.com/articles/2008/2/15/how-to-build-a-twitter-agent">how to create a twitter bot</a> and got the conversion made surprisingly fast.&nbsp; It's still ugly because auto-following has to be done through HTTP, but in a few hours I had a pretty simple bot going.<br /><br />Then we went to the Web 2.0 Expo and tried it out.&nbsp; And it didn't work.&nbsp; Turns out Twitter's Jabber replies were delayed or somesuch... probably fallout of Twitter's other greyout problems this week.&nbsp; But it got me thinking...<br /><br />If you had to build twitter from scratch, how thin a veneer over XMPP could you do it with?&nbsp; At its heart, Twitter is a message router.&nbsp; There are interfaces with SMS systems, HTTP, and Jabber.&nbsp; But messages come in, messages go out.&nbsp; I've heard they run XMPP under the hood to handle this, but I've also heard they run rails for a good chunk of functionality.&nbsp; I'm a rails fan as well, but from what I can see, perhaps there's too much rails and not enough ejabberd in the mix.<br /><br />Conceptually it seems like you could set this up as a set of processes that each act as an internal Jabber client for an individual twitter user.&nbsp; The process is responsible for receiving messages<br /><ol><li>pushing them to SMS or the user's Jabber client</li><li>Building the web page that people visit</li><li>Handling API calls as they come in</li><li>Dispatching messages to followers<br /></li></ol>In fact conceptually you could set up three clients, each to handle one of these jobs.&nbsp; Each of these processes could in essence be a fully functional twitter service for an individual user.<br /><br />In addition to this you need gateways for SMS and the HTTP API, but it seems like those could be scaled out fairly easily as they're not user specific.&nbsp; The SMS gateway is just going to build an XMPP message and dispatch it to the matching twitter client.&nbsp; The HTTP gateway is doing the same thing.<br /><br />The beauty of this is that it's naturally sharded.&nbsp; No shared data between users.&nbsp; You have to parse messages for @responses and route them to appropriate destinations, but it seems like that could easily be written as an ejabberd plugin.&nbsp;  <br /><br />Google has proven this scales to a fairly large user base.&nbsp; Is Twitter already beyond that scale, or am I missing something?<br /><br />Regardless, we'll be pushing ahead with our plans to add direct GTalk integration for our app.&nbsp; It may go down at times as well, but it seems more robust than Twitter's infrastructure at this point. <br />  ]]>
        
    </content>
</entry>

<entry>
    <title>Standalone ActiveRecord and SQLite3</title>
    <link rel="alternate" type="text/html" href="http://www.themcwongs.com/mcblog/2008/03/standalone-activerecord-and-sq.html" />
    <id>tag:www.themcwongs.com,2008:/mcblog//1.10</id>

    <published>2008-03-05T23:50:33Z</published>
    <updated>2008-03-06T00:00:38Z</updated>

    <summary>I recently wanted to set up a quick daemon process that used ActiveRecord outside the rails framework. I also wanted it to use sqlite, just to keep install/dependencies simple. I found not so good documentation on how to do this......</summary>
    <author>
        <name>McWong</name>
        
    </author>
    
        <category term="ruby" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="activerecord" label="activerecord" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="ruby" label="ruby" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://www.themcwongs.com/mcblog/">
        <![CDATA[I recently wanted to set up a quick daemon process that used ActiveRecord outside the rails framework. I also wanted it to use sqlite, just to keep install/dependencies simple.  I found not so good documentation on how to do this... and after a few missteps it turns out it's not that hard.  The only gem dependencies are activerecord and sqlite3-ruby.  You'll also need sqlite working. Code follows
<br/>
<code>
<pre>
require 'rubygems'
require 'sqlite3'
require 'activerecord'

# connect to database.  This will create one if it doesn't exist
MY_DB_NAME = ".my.db"
MY_DB = SQLite3::Database.new(MY_DB_NAME)

# get active record set up
ActiveRecord::Base.establish_connection(:adapter =&gt; 'sqlite3', :database =&gt; MY_DB_NAME)

# create your AR class
class Update &lt; ActiveRecord::Base

end

# do a quick pseudo migration.  This should only get executed on the first run
if !Update.table_exists?
  ActiveRecord::Base.connection.create_table(:updates) do |t|
    t.column :account_name, :string
    t.column :last_update_time, :timestamp
    t.column :last_update_id, :integer
  end
end</pre> 
</code>
]]>
        

    </content>
</entry>

<entry>
    <title>Scaling and Deploying Rails: It&apos;s not Java. Or PHP.</title>
    <link rel="alternate" type="text/html" href="http://www.themcwongs.com/mcblog/2008/01/scaling-and-deploying-rails-it.html" />
    <id>tag:www.themcwongs.com,2008:/mcblog//1.6</id>

    <published>2008-01-09T05:14:12Z</published>
    <updated>2008-03-02T05:14:38Z</updated>

    <summary><![CDATA[Recently there have been a slew of posts complaining that rails doesn't scale and is hard to deploy.&nbsp; I think the root of this is that people come to rails after experience with Java, PHP or similar frameworks.&nbsp;&nbsp; Rails is...]]></summary>
    <author>
        <name>McWong</name>
        
    </author>
    
        <category term="rails" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="rails" label="rails" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="ruby" label="ruby" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://www.themcwongs.com/mcblog/">
        <![CDATA[Recently there have been a slew of posts complaining that rails doesn't scale and is hard to deploy.&nbsp; I think the root of this is that people come to rails after experience with Java, PHP or similar frameworks.&nbsp;&nbsp; Rails is neither of these.&nbsp; It is heavier than PHP, and it seems that this makes its fit with the typical PHP deployment methods slow at best.&nbsp; At the same time it has an entirely different deployment model than Java web applications.&nbsp; The Java application containers are heavier, and designed to support a much more highly available application within&nbsp; that JVM instance.<br /><br />But rails still has its sweet spot.&nbsp; So why would I use rails instead of Java? It's not as light as PHP, but from a development perspective it's usually close enough.&nbsp; Bouncing webrick or mongrel takes a second or two, and for most changes I don't even need to do this.&nbsp; To accomplish the same code/test cycle in Java takes&nbsp; at least 10 times as long, often much worse.&nbsp; At the same time rails offers.&nbsp; Plus I can do a lot of things I can't do with Java, thanks to dynamic typing, better metaprogramming facilities, and things like the rails console.&nbsp; Plus, from a hosting perspective you can get much better deals on hosting a rails application than a full Java servlet environment, which tells you something about the relative difficulty of hosting rails and J2EE.<br /><br />Why would I use it instead of PHP?&nbsp; The framework does enough magic that getting an application up and running is faster than raw PHP (disclaimer:&nbsp; I haven't used any of the newer PHP frameworks like CakePHP . Maybe they're more on par with rails here.).&nbsp; MVC is nicely broken out.&nbsp; Ruby itself is a wonderful language to develop in.&nbsp; Yes, you're going to have to do more work around deployment.&nbsp; I can't imagine going live using FastCGI, SCGI, or that sort of thing.&nbsp; But learn how to set up a mongrel cluster.&nbsp; That seems to work.<br /><br />I think Java and PHP still have their places as well.&nbsp; Java provides much better concurrency in a single process, better built-in capabilities for high availability, security, and a larger library/tool ecosystem.&nbsp; Enterprises know how to deploy and maintain Java applications, and in general feel more comfortable putting those in production than a rails system.&nbsp; PHP is lighter weight, will likely scale better out of the box, and is better understood by most hosting companies.<br /><br />At the end of the day the decision comes down to what you're building and where you're deploying.&nbsp; I think rails works in wide range of situations, but there is an equally wide range where PHP or Java may be a better choice.&nbsp; ]]>
        
    </content>
</entry>

<entry>
    <title>Handling Multiple User Authentication With ActiveResource Clients</title>
    <link rel="alternate" type="text/html" href="http://www.themcwongs.com/mcblog/2008/01/handling-multiple-user-authent.html" />
    <id>tag:www.themcwongs.com,2008:/mcblog//1.5</id>

    <published>2008-01-06T05:13:31Z</published>
    <updated>2008-03-02T05:14:03Z</updated>

    <summary>I&apos;m in the process of building a REST service using rails and ActiveResource. On the server side everything was fairly straightforward, but I hit a snag when I moved to the web tier. For various reasons I wanted to have...</summary>
    <author>
        <name>McWong</name>
        
    </author>
    
        <category term="rails" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="activeresource" label="activeresource" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="rails" label="rails" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="ruby" label="ruby" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://www.themcwongs.com/mcblog/">
        <![CDATA[I'm in the process of building a REST service using rails and ActiveResource. On the server side everything was fairly straightforward, but I hit a snag when I moved to the web tier. For various reasons I wanted to have two separate servers, one providing the REST service and the other serving HTML content. The rub is that ActiveResource assumes that credentials are a class level attribute. I'd like the calls to the ActiveResource client to use the credentials of the currently logged on user, but there doesn't appear to be a straightforward way to do this out of the box. Metaprogramming to the rescue!<br /><br />Using the REST example from my last post, I extended the client in the following way.<br /><br />require 'activeresource'<br /><br />module Sample<br />&nbsp; module Client<br />&nbsp;&nbsp;&nbsp; class API<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # Creates a module that serves as an ActiveResource<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # client for the specified user<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; #<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; def self.create_api(login = nil, password = nil)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # specify the site.&nbsp; Default to no credentials<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @url_base = "http://localhost:3000"<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @url_base = "http://#{login}:#{password}@localhost:3000" if login<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # build a module name.&nbsp; This assumes that logins are unique.<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # it also assumes they're valid ruby module names when capitalized<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @module = login ? login.capitalize : "Default"<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class_eval &lt;&lt;-"end_eval",__FILE__, __LINE__<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; module #{@module}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class Post &lt; ActiveResource::Base<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self.site = "#{@url_base}"<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class Comment &lt; ActiveResource::Base<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self.site = "#{@url_base}/posts/:post_id"<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # return the module, not the last site String<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end_eval<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end<br />&nbsp;&nbsp;&nbsp; end<br />&nbsp; end<br />end<br /><br />This new version allows you to create an api for a specific user. You can squirrel this away and use it for specific users, allowing you to use multiple connections to your rest service. A quick irb session follows<br /><br />&gt;&gt; require 'ares_sample_client'<br />=&gt; ["Sample"]<br />&gt;&gt; api = Sample::Client::API.create_api<br />=&gt; Sample::Client::API::Default<br />&gt;&gt; p = api::Post.find(1)<br />=&gt; #&lt;Sample::Client::API::Default::Post:0xb715af74 @attributes={"updated_at"=&gt;Wed Jan 09 02:36:34 UTC 2008, "id"=&gt;1, "content"=&gt;"The first post", "user_id"=&gt;1, "created_at"=&gt;Wed Jan 09 02:36:34 UTC 2008}, @prefix_options={}&gt;<br />&gt;&gt; p = api::Post.create(:content =&gt; "should fail")<br />ActiveResource::UnauthorizedAccess: Failed with 401 Unauthorized<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; from /usr/lib/ruby/gems/1.8/gems/activeresource-2.0.2/lib/active_resource/connection.rb:125:in `handle_response'<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; from /usr/lib/ruby/gems/1.8/gems/activeresource-2.0.2/lib/active_resource/connection.rb:112:in `request'<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; from /usr/lib/ruby/gems/1.8/gems/activeresource-2.0.2/lib/active_resource/connection.rb:101:in `post'<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; from /usr/lib/ruby/gems/1.8/gems/activeresource-2.0.2/lib/active_resource/base.rb:803:in `create'<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; from /usr/lib/ruby/gems/1.8/gems/activeresource-2.0.2/lib/active_resource/base.rb:636:in `save_without_validation'<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; from /usr/lib/ruby/gems/1.8/gems/activeresource-2.0.2/lib/active_resource/validations.rb:262:in `save'<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; from /usr/lib/ruby/gems/1.8/gems/activeresource-2.0.2/lib/active_resource/base.rb:339:in `create'<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; from /usr/lib/ruby/gems/1.8/gems/activesupport-2.0.2/lib/active_support/core_ext/object/misc.rb:28:in `returning'<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; from /usr/lib/ruby/gems/1.8/gems/activeresource-2.0.2/lib/active_resource/base.rb:339:in `create'<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; from (irb):4<br />&gt;&gt; auth_api = Sample::Client::API.create_api('test1','test1')<br />=&gt; Sample::Client::API::Test1<br />&gt;&gt; p = auth_api::Post.find(1)<br />=&gt; #&lt;Sample::Client::API::Default::Post:0xb713dde8 @attributes={"updated_at"=&gt;Wed Jan 09 02:36:34 UTC 2008, "id"=&gt;1, "content"=&gt;"The first post", "user_id"=&gt;1, "created_at"=&gt;Wed Jan 09 02:36:34 UTC 2008}, @prefix_options={}&gt;<br />&gt;&gt; p = auth_api::Post.create(:content =&gt; "should succeed!")<br />=&gt; #&lt;Sample::Client::API::Test1::Post:0xb713312c @attributes={"updated_at"=&gt;Thu Jan 10 04:01:53 UTC 2008, "id"=&gt;7, "content"=&gt;"should succeed!", "user_id"=&gt;nil, "created_at"=&gt;Thu Jan 10 04:01:53 UTC 2008}, @prefix_options={}&gt;<br />&gt;&gt; <br /><br /> ]]>
        
    </content>
</entry>

<entry>
    <title>Dealing with Maven bloat and complexity</title>
    <link rel="alternate" type="text/html" href="http://www.themcwongs.com/mcblog/2008/01/dealing-with-maven-bloat-and-c.html" />
    <id>tag:www.themcwongs.com,2008:/mcblog//1.3</id>

    <published>2008-01-05T05:04:37Z</published>
    <updated>2008-03-02T05:05:15Z</updated>

    <summary>I recently came across an article questioning whether Maven is too complex and bloated. The short answer is it can be. If you aren&apos;t taking some steps up front to deal with some of Maven&apos;s quirks, the benefit it provides...</summary>
    <author>
        <name>McWong</name>
        
    </author>
    
        <category term="maven" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="maven" label="maven" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://www.themcwongs.com/mcblog/">
        <![CDATA[I recently came across an article questioning whether Maven is too complex and bloated. The short answer is it can be. If you aren't taking some steps up front to deal with some of Maven's quirks, the benefit it provides can quickly be outweighed by the administrative overhead and performance hit its project management scheme imposes.<br /><br />At my job we have a fairly sophisticated Maven project. It involves custom code generation plugins, cross-platform C++ compiles and deployments, assemblies, you name it. The number of engineers working on this project is probably around 80, and the number of Maven projects for this system is probably around 50.<br /><br />It's not the most beautiful system in the world, but at the end of the day a lot of Maven's benefits are realized. Your average developer can check out an individual component from src, run "mvn test", and be reasonably assured<br />that things will compile, run, and execute unit tests, with minimal up front configuration of the build environment.<br /><br />This is no small feat. It didn't start off easy. Maven is complicated. It's large. Documentation could no doubt be better. But at the end of the day, we're in a better place than we would have been with ant or make. Some of the steps we've taken to make things easier follow.<br /><br />&nbsp;&nbsp; 1. Create your own repository. If you want to be able to do repeatable builds, don't let your projects access any repositories outside your control. Poms get broken, sites go offline... this causes all sorts of chaos. We ended up with a single repository server, but multiple repositories. The salient ones are maven-releases, maven-snapshots, our-releases, and our-snapshots. We put the artifacts we needed into maven-releases and maven-snapshots manually. We added profiles that can be enabled to get to the public repositories, but these are never enabled on our automated build machines.<br />&nbsp;&nbsp; 2. Use inheritance correctly. It's really tempting to use pom inheritance to capture project structure. This is what module tags are for. Pom inheritance is to allow you to apply similar configurations easily (like Java inheritance). It took us a long time to unwind this mess. We have a base-java pom that sets up all the reports we want to run for java, a base-model one to handle domain models,<br />&nbsp;&nbsp; 3. Fight the urge to tightly couple your large project. Maven leads you to fine grained componentization, which leads to a looser coupling of components in the build/release sense. There is a natural tendency to be uncomfortable with this (what's really going into my final build), but fight the urge to make it one giant system that gets built from the ground up. Executing releases is a nightmare with one giant system.<br />&nbsp;&nbsp; 4. Use version ranges where possible. This makes dealing with #3 easier. The odds that a component needs that specific release of a component (especially if you're doing agile and releasing every 30 days) is pretty slim. Most just need the latest. Also get familiar with the dependency convergence report.<br />&nbsp;&nbsp; 5. Make sure people understand what Maven goals are necessary for doing work. If lots of people are complaining about site generation taking too long, you have a clue that people don't get this. Your average developer should be running test and install, very rarely site.<br />&nbsp;&nbsp; 6. Decide on a versioning scheme up front, and make sure you can execute on it. Nothing is more frustrating than realizing it takes two weeks to get all your poms revved to the next revision. The maven release plugin has been fairly unreliable, but it leads you into a set of best practices that work even if you're taking the steps manually.<br /><br />After taking these steps, the nearly universal sentiment is that while Maven is indeed complicated, it's a step forward when compared to ant/make. At the end of the day, designing a build process for large systems is difficult and rarely gets the attention from the development org it deserves. Maven doesn't make it dirt simple, but it makes the overall management of the build system simpler. ]]>
        
    </content>
</entry>

<entry>
    <title>Selling Automated Testing to Management</title>
    <link rel="alternate" type="text/html" href="http://www.themcwongs.com/mcblog/2008/01/selling-automated-testing-to-m.html" />
    <id>tag:www.themcwongs.com,2008:/mcblog//1.2</id>

    <published>2008-01-05T05:03:04Z</published>
    <updated>2008-03-02T05:04:27Z</updated>

    <summary>I don&apos;t think there are that many developers out there these days that don&apos;t think that automated unit testing is a good idea. Most seem to agree that automated functional and integration testing are worthwhile as well, even if they&apos;re...</summary>
    <author>
        <name>McWong</name>
        
    </author>
    
        <category term="agile" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="agile" label="agile" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="testing" label="testing" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://www.themcwongs.com/mcblog/">
        <![CDATA[I don't think there are that many developers out there these days that don't think that automated unit testing is a good idea. Most seem to agree that automated functional and integration testing are worthwhile as well, even if they're generally harder to achieve. Being able to be reasonably confident that the change you just made didn't disrupt far flung parts of the project just by executing a test suite is great. However it's not a tangible, quantifiable thing you can sell to management.<br /><br />There are generally two cases where you need to sell the effort involved in building and maintaining these suites. The first (and easier) is when you already have the suites in place, but need to invest in maintaining them. This is an easier sell because usually management will have seen the benefit and/or the ongoing expense is fairly small, and justifying the ongoing effort isn't all that bad. I'll throw new development in here as well... writing the suites as you go usually isn't as bad as retrofitting suites onto an existing code base.<br /><br />Which brings us to the second case. When you have a legacy product, adding automated test suites is usually time consuming at best, and technically challenging in most cases. Your legacy product probably wasn't built in a way that encourages automated testing. The interfaces may not be there, it's tough to run in a test sandbox, etc. If you want to get automated tests working, you're probably facing a significant investment in people terms, and could be facing some infrastructure expenditures as well.<br /><br />The wrong way to pitch this is to go to management, roll out your proposal and say that you need to spend $500k over the next three months building something that provides purely qualitative benefit. Unfortunately most attempts at quantification are difficult. You can talk about improved quality, improved productivity, increased agility... but at the end of the day that $500k is staring them down and making everybody in the room uncomfortable.<br /><br />A better way is to talk about it in terms that can be strictly quantified, and the easiest one I've found is around regression testing. Figuring out how much you spend each year on regression testing is usually pretty straightforward.<br /><br />&nbsp;&nbsp; 1. Write down how many times a year you execute a full regression test<br />&nbsp;&nbsp; 2. Write down many people a single test cycle takes<br />&nbsp;&nbsp; 3. Write down how long it takes<br />&nbsp;&nbsp; 4. Figure out how much a tester's time is worth.<br /><br />Doing the math hear should give you a dollar amount for your regression expenditure. The next step is simpler, all though less precise. Estimate how much you think you could cut the time you got for #3. From what I've seen this should be on the order of 50%-80%. This gives you a total savings realized from automated testing.<br /><br />A quick example may help<br /><br />&nbsp;&nbsp; 1. We release twice a year, and execute on average two regression test cycles per release (one beta, one GA)<br />&nbsp;&nbsp; 2. Each test requires four test engineers<br />&nbsp;&nbsp; 3. The test cycle takes four weeks<br />&nbsp;&nbsp; 4. Each tester costs the org $100k a year<br /><br />this means we have a total cost of 4*4*(4/52)*100k which is about $123k. If I can cut my regression test time to 1 week, I save at least $92k per year. Keep the last two words in mind... that's per year. Forever.<br /><br />Keep in mind this is a minimum. You still have all the intangible benefits. But $92k/year is something the business folks can plug into their project valuation tools and evaluate. When we ran these numbers for our project it became clear that dedicating people full time to build out automated test suites for existing projects made perfect financial as well as technical sense. ]]>
        
    </content>
</entry>

<entry>
    <title>Quick REST with ActiveResource and rails
</title>
    <link rel="alternate" type="text/html" href="http://www.themcwongs.com/mcblog/2008/01/quick-rest-with-activeresource.html" />
    <id>tag:www.themcwongs.com,2008:/mcblog//1.4</id>

    <published>2008-01-03T05:10:42Z</published>
    <updated>2008-03-02T05:13:00Z</updated>

    <summary><![CDATA[&nbsp;&nbsp;&nbsp; Posted by McWong 53 days agoThis walkthrough shows the creation of a simple REST API using ActiveResource. It includes a nested model, authorization scheme, and a client library. The intent is not to exhaustively explore ActiveResource, but to show...]]></summary>
    <author>
        <name>McWong</name>
        
    </author>
    
        <category term="rails" scheme="http://www.sixapart.com/ns/types#category" />
    
    <category term="rails" label="rails" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="ruby" label="ruby" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://www.themcwongs.com/mcblog/">
        <![CDATA[&nbsp;&nbsp;&nbsp; <br />Posted by McWong 53 days ago<br />This walkthrough shows the creation of a simple REST API using ActiveResource. It includes a nested model, authorization scheme, and a client library. The intent is not to exhaustively explore ActiveResource, but to show how to quickly get up and running with a reasonable REST API.<br /><br />Step 1: create an application<br /><br />rails ares-demo<br /><br />I'm using rails 2.0.2 here, but I don't think anything is version specific.<br /><br />Step 2: install two great rest plugins<br />Resource This is a plugin that adds default rest-based actions to a controller. It has so-so support for nested resources... you won't be able to use it out of the box for HABTM relationships for instance, but for strict hierarchies it works well, and really beats hand coding the controllers. You can install it with<br /><br />script/plugin install http://jnewland.com/svn/public/ruby/rails/plugins/resource_this/<br /><br />Restful Authentication is derived from acts_as_authenticated, and adds a rest-ish session controller, as well as the usual controller helpers (login_required, etc.) You can install it with<br /><br />script/plugin install http://svn.techno-weenie.net/projects/plugins/restful_authentication/<br /><br />Step 3: setup restful authentication<br />This bit is easy. Just run the generator to get your migrations for users created, along with a couple other things. I honestly didn't dig into this too much, it just works.<br /><br />./script/generate authenticated user sessions<br /><br />Step 4: create application model<br />Next up is generating your model. We'll do a simple one. Posts contain many Comments. Both Posts and Comments belong to a user.<br /><br />./script/generate scaffold post<br />./script/generate scaffold comment<br /><br />Next set up your migrations. db/migrate/002_create_posts.rb should contain<br /><br />class CreatePosts &lt; ActiveRecord::Migration<br />&nbsp; def self.up<br />&nbsp;&nbsp;&nbsp; create_table :posts do |t|<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; t.column :content, :text<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; t.column :user_id, :integer<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; t.timestamps<br />&nbsp;&nbsp;&nbsp; end<br />&nbsp; end<br /><br />&nbsp; def self.down<br />&nbsp;&nbsp;&nbsp; drop_table :posts<br />&nbsp; end<br />end<br /><br />and db/migrate/003_create_comments.rb should contain<br /><br />class CreateComments &lt; ActiveRecord::Migration<br />&nbsp; def self.up<br />&nbsp;&nbsp;&nbsp; create_table :comments do |t|<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; t.column :content, :text<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; t.column :user_id, :integer<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; t.column :post_id, :integer<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; t.timestamps<br />&nbsp;&nbsp;&nbsp; end<br />&nbsp; end<br /><br />&nbsp; def self.down<br />&nbsp;&nbsp;&nbsp; drop_table :comments<br />&nbsp; end<br />end<br /><br />Next you'll need to add the relationships to your model classes. Add the following bit to the front of app/models/user.rb<br /><br />class User &lt; ActiveRecord::Base<br />&nbsp; # custom relationships<br />&nbsp; has_many :posts<br />&nbsp; has_many :comments<br /><br />app/models/post.rb should look like<br /><br />class Post &lt; ActiveRecord::Base<br />&nbsp; belongs_to :user<br />&nbsp; has_many :comments<br />end<br /><br />and app/models/comment.rb should look like<br /><br />class Comment &lt; ActiveRecord::Base<br />&nbsp; belongs_to :user<br />&nbsp; belongs_to :post<br />end<br /><br />Now run migrate to get your database all configured<br /><br />rake db:migrate<br /><br />Step 5: populate dummy data<br />Next we'll want to get some default data in there. Execute script/console and run through the following<br /><br />&gt;&gt; u1 = User.new(:login =&gt; "test1", :email =&gt; "test1@foo.com", :password =&gt; "test1", :password_confirmation =&gt; "test1")<br />=&gt; #&lt;User id: nil, login: "test1", email: "test1@foo.com", crypted_password: nil, salt: nil, created_at: nil, updated_at: nil, remember_token: nil, remember_token_expires_at: nil&gt;<br />&gt;&gt; u1.save<br />=&gt; true<br />&gt;&gt; u2 = User.new(:login =&gt; "test2", :email =&gt; "test2@foo.com", :password =&gt; "test2", :password_confirmation =&gt; "test2")<br />=&gt; #&lt;User id: nil, login: "test2", email: "test2@foo.com", crypted_password: nil, salt: nil, created_at: nil, updated_at: nil, remember_token: nil, remember_token_expires_at: nil&gt;<br />&gt;&gt; u2.save<br />=&gt; true<br />&gt;&gt; p = Post.new(:user =&gt; u1, :content =&gt; "The first post")<br />=&gt; #&lt;Post id: nil, content: "The first post", user_id: 1, created_at: nil, updated_at: nil&gt;<br />&gt;&gt; p.save<br />=&gt; true<br />&gt;&gt; c = Comment.new(:user =&gt; u2, :post =&gt; p, :content =&gt; "first!")<br />=&gt; #&lt;Comment id: nil, content: "first!", user_id: 2, post_id: 1, created_at: nil, updated_at: nil&gt;<br />&gt;&gt; c.save<br />=&gt; true<br />&gt;&gt;<br /><br />Step 6: resource_this!<br />Now the boring part us over. So far we have a basic rails app. Now we get to the REST part! You're three edits away... Change app/controllers/posts_controller.rb to the following<br /><br />class PostsController &lt; ApplicationController<br />&nbsp; resource_this<br />end<br /><br />Change app/controllers/comments_controller.rb to the following<br /><br />class CommentsController &lt; ApplicationController<br />&nbsp; resource_this :nested =&gt; [:post]<br />end<br /><br />And finally update config/routes.rb<br /><br />ActionController::Routing::Routes.draw do |map|<br />&nbsp; map.resources :posts do |post|<br />&nbsp;&nbsp;&nbsp; post.resources :comments<br />&nbsp; end<br /><br />&nbsp; map.resources :users<br /><br />&nbsp; map.resource :session<br />end<br /><br />That's it! You're ready to look through your API<br /><br />Step 7: browse<br />Start up your server with script/server. You'll need to clean up the erb views if you want to use the HTML interface, but your xml interface should be working just fine. Try the following urls for verification<br /><br />&nbsp;&nbsp;&nbsp; * http://localhost:3000/posts/1.xml<br />&nbsp;&nbsp;&nbsp; * http://localhost:3000/posts/1/comments.xml<br /><br />Step 8: lock it down<br />So far our service is open to anybody. We'll add very simple authentication at this stage... we'll require a valid user for anything other than show and index requests. All we have to do for this is include AuthenticatedSystem in our application controller, and add a before_filter. Make app/controllers/application.rb look like the following<br /><br />class ApplicationController &lt; ActionController::Base<br />&nbsp; include AuthenticatedSystem<br />&nbsp; before_filter :login_required, :only =&gt; [:create,:edit,:new,:update,:destroy]<br />&nbsp; helper :all # include all helpers, all the time<br /><br />&nbsp; # See ActionController::RequestForgeryProtection for details<br />&nbsp; # Uncomment the :secret if you're not using the cookie session store<br />&nbsp; protect_from_forgery # :secret =&gt; 'b845cf981afbfef06600e5d7f23e0fed'<br />end<br /><br />Step 9: create client lib<br />We could test this with curl or a browser, but the real fun of ActiveResource is getting to use ruby objects. So... we'll create a simple client api. Create a new file in lib called 'ares_sample_client.rb' and add the following code<br /><br />require 'activeresource'<br /><br />module Sample<br />&nbsp; module Client<br />&nbsp;&nbsp;&nbsp; class API<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class Post &lt; ActiveResource::Base<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self.site = "http://localhost:3000"<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end<br />&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class Comment &lt; ActiveResource::Base<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self.site = "http://localhost:3000"<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end<br />&nbsp;&nbsp;&nbsp; end<br />&nbsp; end<br />end<br /><br />Now we can play with our creation using the console. Make sure your server is running on port 3000, and startup script/console.<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&gt;&gt; require 'ares_sample_client.rb'<br />=&gt; ["Sample"]<br />&gt;&gt; posts = Sample::Client::API::Post.find(:all)<br />=&gt; [#&lt;Sample::Client::API::Post:0xb710bec4 @attributes={"updated_at"=&gt;Wed Jan 09 02:36:34 UTC 2008, "id"=&gt;1, "content"=&gt;"The first post", "user_id"=&gt;1, "created_at"=&gt;Wed Jan 09 02:36:34 UTC 2008}, @prefix_options={}&gt;]<br />&gt;&gt; <br /><br />&gt;&gt; p = Sample::Client::API::Post.create<br />ActiveResource::UnauthorizedAccess: Failed with 401 Unauthorized<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; from /usr/lib/ruby/gems/1.8/gems/activeresource-2.0.2/lib/active_resource/connection.rb:125:in `handle_response'<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; from /usr/lib/ruby/gems/1.8/gems/activeresource-2.0.2/lib/active_resource/connection.rb:112:in `request'<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; from /usr/lib/ruby/gems/1.8/gems/activeresource-2.0.2/lib/active_resource/connection.rb:101:in `post'<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; from /usr/lib/ruby/gems/1.8/gems/activeresource-2.0.2/lib/active_resource/base.rb:803:in `create'<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; from /usr/lib/ruby/gems/1.8/gems/activeresource-2.0.2/lib/active_resource/base.rb:636:in `save_without_validation'<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; from /usr/lib/ruby/gems/1.8/gems/activeresource-2.0.2/lib/active_resource/validations.rb:262:in `save'<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; from /usr/lib/ruby/gems/1.8/gems/activeresource-2.0.2/lib/active_resource/base.rb:339:in `create'<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; from /usr/lib/ruby/gems/1.8/gems/activesupport-2.0.2/lib/active_support/core_ext/object/misc.rb:28:in `returning'<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; from /usr/lib/ruby/gems/1.8/gems/activeresource-2.0.2/lib/active_resource/base.rb:339:in `create'<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; from (irb):3<br />&gt;&gt; <br /><br />Our API works, and the authentication is as it should be. We didn't specify credentials, so we can't actually create a post. Let's fix up our client lib to add credentials<br /><br />module Sample<br />&nbsp; module Client<br />&nbsp;&nbsp;&nbsp; class API<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class Post &lt; ActiveResource::Base<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self.site = "http://test1:test1@localhost:3000"<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class Comment &lt; ActiveResource::Base<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self.site = "http://test1:test1@localhost:3000"<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; end<br />&nbsp;&nbsp;&nbsp; end<br />&nbsp; end<br />end<br /><br />Note that credentials apply to the whole class... this makes the API fairly easy to use, but makes using multiple users difficult. Anyway, with our authentication in place, let's try again<br /><br />&gt;&gt; p = Sample::Client::API::Post.create(:content =&gt; "Should succeed", :user_id =&gt; 1)<br />=&gt; #&lt;Sample::Client::API::Post:0xb71b81d8 @attributes={"updated_at"=&gt;Wed Jan 09 02:55:22 UTC 2008, "id"=&gt;3, "content"=&gt;"Should succeed", "user_id"=&gt;1, "created_at"=&gt;Wed Jan 09 02:55:22 UTC 2008}, @prefix_options={}&gt;<br />&gt;&gt; p.save<br />=&gt; true<br />&gt;&gt; <br /><br />Sweet! Now we can not only browse posts and comments, but create them as well! <br />]]>
        
    </content>
</entry>

</feed>
