<?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/"
	xmlns:georss="http://www.georss.org/georss" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:media="http://search.yahoo.com/mrss/"
	>

<channel>
	<title>A little story</title>
	<atom:link href="http://thailehuy.wordpress.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://thailehuy.wordpress.com</link>
	<description>Just another WordPress.com weblog</description>
	<lastBuildDate>Sat, 19 Feb 2011 04:05:16 +0000</lastBuildDate>
	<language>vi</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.com/</generator>
<cloud domain='thailehuy.wordpress.com' port='80' path='/?rsscloud=notify' registerProcedure='' protocol='http-post' />
<image>
		<url>http://s2.wp.com/i/buttonw-com.png</url>
		<title>A little story</title>
		<link>http://thailehuy.wordpress.com</link>
	</image>
	<atom:link rel="search" type="application/opensearchdescription+xml" href="http://thailehuy.wordpress.com/osd.xml" title="A little story" />
	<atom:link rel='hub' href='http://thailehuy.wordpress.com/?pushpress=hub'/>
		<item>
		<title>The most important lesson ^_^</title>
		<link>http://thailehuy.wordpress.com/2011/02/19/the-most-important-lesson-_/</link>
		<comments>http://thailehuy.wordpress.com/2011/02/19/the-most-important-lesson-_/#comments</comments>
		<pubDate>Sat, 19 Feb 2011 04:05:16 +0000</pubDate>
		<dc:creator>thailehuy</dc:creator>
				<category><![CDATA[Business stuffs]]></category>

		<guid isPermaLink="false">http://thailehuy.wordpress.com/?p=255</guid>
		<description><![CDATA[<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=thailehuy.wordpress.com&amp;blog=6260931&amp;post=255&amp;subd=thailehuy&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p><img src="http://26.media.tumblr.com/tumblr_lc6a12ArZm1qf15gxo1_500.png" alt="Everyone's dream" /></p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/thailehuy.wordpress.com/255/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/thailehuy.wordpress.com/255/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/thailehuy.wordpress.com/255/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/thailehuy.wordpress.com/255/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/thailehuy.wordpress.com/255/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/thailehuy.wordpress.com/255/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/thailehuy.wordpress.com/255/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/thailehuy.wordpress.com/255/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/thailehuy.wordpress.com/255/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/thailehuy.wordpress.com/255/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/thailehuy.wordpress.com/255/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/thailehuy.wordpress.com/255/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/thailehuy.wordpress.com/255/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/thailehuy.wordpress.com/255/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=thailehuy.wordpress.com&amp;blog=6260931&amp;post=255&amp;subd=thailehuy&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://thailehuy.wordpress.com/2011/02/19/the-most-important-lesson-_/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/89f133b618e8530d78bf1dc59c5f7dd3?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">thailehuy</media:title>
		</media:content>

		<media:content url="http://26.media.tumblr.com/tumblr_lc6a12ArZm1qf15gxo1_500.png" medium="image">
			<media:title type="html">Everyone's dream</media:title>
		</media:content>
	</item>
		<item>
		<title>Where Dell went wrong?</title>
		<link>http://thailehuy.wordpress.com/2010/02/04/where-dell-went-wrong/</link>
		<comments>http://thailehuy.wordpress.com/2010/02/04/where-dell-went-wrong/#comments</comments>
		<pubDate>Thu, 04 Feb 2010 04:00:05 +0000</pubDate>
		<dc:creator>thailehuy</dc:creator>
				<category><![CDATA[Business stuffs]]></category>
		<category><![CDATA[Dell]]></category>
		<category><![CDATA[went]]></category>
		<category><![CDATA[wrong]]></category>

		<guid isPermaLink="false">http://thailehuy.wordpress.com/?p=251</guid>
		<description><![CDATA[With the resignation of Kevin Rollins as Dell&#8217;s CEO, one of the oldest yet most important adages of PC industry is proven: no company can stay on top forever. Relentless competition, product commoditization, prickly customers and the aggressiveness to become number one or two in the fields always tend to bring down a company. So [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=thailehuy.wordpress.com&amp;blog=6260931&amp;post=251&amp;subd=thailehuy&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>With the resignation of Kevin Rollins as Dell&#8217;s CEO, one of the oldest yet most important adages of PC industry is proven: no company can stay on top forever. Relentless competition, product commoditization, prickly customers and the aggressiveness to become number one or two in the fields always tend to bring down a company.</p>
<p>So where did Dell go wrong?</p>
<p>In a common way of speaking, Dell succumb to the belief that its business model would always work and keep it far ahead against other competitors. It clung too narrowly to its own founding strategy, forgetting to search or develop future sources of growth.</p>
<p>While Dell indeed broaden its product lines, it never really used it lead in products direct sales to invest in new business lines, talent, or innovation to deal with the constant improvement of its competitors.</p>
<p>Dell is a textbook example of single-formula growth, &#8220;We make PCs cheap. This is what we do, and we do it a lot&#8221; said Jim Mackey, MD at Billion Dollar Growth Network.</p>
<p>Although single-formula can help a company grows very fast during certain stage, once it reaches certain point of development, single-formula simply doesn&#8217;t have the ability to expand or create new growth.</p>
<p>Together with that, one of Dell&#8217;s fatal mistakes is that it hired too many &#8220;former management&#8221; consultants like Rollins himself. While having great credentials themselves, those consultants are usually very disconnected from people and only associates themselves with their fellows. In their mind, their top priorities is how to cut back on services to reduce cost without anyone noticing.</p>
<p>Unfortunately, with the overgrowing Internet these days, customers become more educated, and they have easy access to communities sites to share their thoughts and experiences. With the changes in Dell&#8217;s way of treating its customers, there is no surprise that they will turn to other brands like Hewlett &#8220;the computer is personal again&#8221; Package, Lenovo or Acer.</p>
<p>With its secret weapon for decades, the customer trust and satisfaction, backfired on itself, Dell faced a great downfall in business.</p>
<p>Although there have been many efforts from Dell, like having Michael Dell himself in the call center answering call, customers&#8217; trust will still take a while to win back.</p>
<p>A second mistake is that Dell has no style itself. For half a decades, despite being a very profitable major PC company, Dell didn&#8217;t invest that money into researching for new product lines but rather return those as earning for stakeholders. Because of that, the company has been a boring PC maker for a very long time. While PCs and electronics consumers have become a large part of business in the recent years, Dell could only come up with TVs, but still in a very low market share, 15%.</p>
<p>Dell didn&#8217;t begin an orderly evolution during its prime time, now it is paying for the consequences.</p>
<p>Dell is also a people-intensive business that doesn&#8217;t benefit from the expertise of efficient manufacturing, making it a very hard time to move up the value chain. While other competitors like HP can reduce its products price with the advantage of being a manufacturer itself, Dell&#8217;s computer price gradually become &#8220;un-cheap&#8221; if not more expensive than others&#8217; computers. Statistic showed that in the year of 2002, Dell&#8217;s average price was $1084, while HP is only $1009. For a long time cheap price is one of Dell&#8217;s key advantages, but that advantage has faded.</p>
<p>Not being a manufacturer also works against Dell, because by no mean it can improve its PC capabilities by utilizing new technologies and such, Dell&#8217;s PCs fall out of strict customer favor quickly.</p>
<p>These are the kind of challenges that Dell has to face for years. How well its founder handles them will determine whether his legacy is building a great company that lasts or just having a great idea that ran out of steam. We will wait and see :)</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/thailehuy.wordpress.com/251/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/thailehuy.wordpress.com/251/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/thailehuy.wordpress.com/251/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/thailehuy.wordpress.com/251/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/thailehuy.wordpress.com/251/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/thailehuy.wordpress.com/251/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/thailehuy.wordpress.com/251/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/thailehuy.wordpress.com/251/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/thailehuy.wordpress.com/251/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/thailehuy.wordpress.com/251/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/thailehuy.wordpress.com/251/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/thailehuy.wordpress.com/251/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/thailehuy.wordpress.com/251/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/thailehuy.wordpress.com/251/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=thailehuy.wordpress.com&amp;blog=6260931&amp;post=251&amp;subd=thailehuy&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://thailehuy.wordpress.com/2010/02/04/where-dell-went-wrong/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/89f133b618e8530d78bf1dc59c5f7dd3?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">thailehuy</media:title>
		</media:content>
	</item>
		<item>
		<title>Dell SWOT analysis</title>
		<link>http://thailehuy.wordpress.com/2010/02/03/dell-swot-analysis/</link>
		<comments>http://thailehuy.wordpress.com/2010/02/03/dell-swot-analysis/#comments</comments>
		<pubDate>Wed, 03 Feb 2010 03:20:38 +0000</pubDate>
		<dc:creator>thailehuy</dc:creator>
				<category><![CDATA[Business stuffs]]></category>
		<category><![CDATA[analysis]]></category>
		<category><![CDATA[Dell]]></category>
		<category><![CDATA[SWOT]]></category>

		<guid isPermaLink="false">http://thailehuy.wordpress.com/?p=247</guid>
		<description><![CDATA[Dell Computer Corporation was founded in 1984 by Michael Dell with a very simple premise: computers should be built and sold directly to customers. By doing this, Dell has become one of the world&#8217;s largest PC maker as well as one of the best well known brands. Dell now employs more than 76000 people world [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=thailehuy.wordpress.com&amp;blog=6260931&amp;post=247&amp;subd=thailehuy&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Dell Computer Corporation was founded in 1984 by Michael Dell with a very simple premise: computers should be built and sold directly to customers. By doing this, Dell has become one of the world&#8217;s largest PC maker as well as one of the best well known brands. Dell now employs more than 76000 people world wide, nine manufacturing plants and provide 24/7 customer supports. The company ships approximately 140000 custom-made computers per day and has over 2 billion interactions with customers every year.</p>
<p>Dell&#8217;s Direct model enables them to interact with customers directly, providing them with fast and reasonable priced products and distribution.</p>
<p><strong>Strengths</strong></p>
<p>Direct sell models meaning that Dell build computers based on customer provided specifications, allowing customers to make their own computer interactively on Dell website or telephone. Over 85% of Dell&#8217;s sale are made through their website using this method, allowing customers to make their own computer with competitive price, since this cuts down the cost arise from retailers. </p>
<p>This method also allows more customer interaction, helping Dell provides top-notch customer service both pre-sale and post-sale.</p>
<p>Computers are made by Dell affiliated manufacturer with relatively cheap labour. This will cut down the cost of inventory managements, as a result, Dell only has to keep an average of 5 days inventory in comparison to the usual 30-40 days of its competitors like HP. This also means Dell can quickly introduce the latest relevant technology without worrying about leftover inventories.</p>
<p><strong>Weaknesses</strong></p>
<p>Custom made computers are Dell&#8217;s strength but also weakness. Because every product is built by customer, no customer can buy a pre-made Dell product like other companies with retailers system. Custom made products also might take up to several days to finish.</p>
<p>The above also means that Dell is very dependent on its suppliers, or manufacturer, which come from a wide range of countries, and very hard to control the quality consistency amongst them. There has been case of massive recall due to defective products happen in 2004 when Dell has to recall 4.4 million laptops adapter in fear of them overheating, causing electric shock or fire.</p>
<p>Dependence on suppliers and not being able to produce computers itself make Dell unable to switch supply and locked with certain core suppliers for a period of time.</p>
<p>Another of Dell weaknesses is the relationship with college student segment. Since most of students purchase their computers through their schools or institute, Dell&#8217;s Direct Model approach is obviously not popular. As a matter of fact, this segment earn only 5% of Dell&#8217;s total revenue while remaining a very potential market segment for other companies.</p>
<p>Despite being a very successful company, Dell does not hold its own trademark or patent or any copyright technology at all. All of its technologies are also used by all other industry competitors.</p>
<p><strong>Opportunities</strong></p>
<p>While there are still defects with Dell&#8217;s Direct Model, personal computers are becoming more and more necessary and commonly used, customers are getting more and more educated about computer. As their knowledge grows, they will seek out for Dell&#8217;s custom made computers the fit their need to experience additional use of computer features.</p>
<p>The overgrowing Internet also provides Dell with more opportunities since the majority of Dell businesses are done over the net.</p>
<p>Moreover, while already been one of the world&#8217;s largest computer providers, Dell&#8217;s market still has many potential areas to explore. By providing low cost, low price computers directly to retailers, Dell would gain a substantial segment of the market. And by sponsoring educations purpose, Dell could gain more popularity amongst students.</p>
<p>Together with the pursuit of diversification in technologies by introducing new products such as printers or toners, LCD televisions and other non-computing goods, Dell now can compete in a wide range of market areas.</p>
<p><strong>Threats</strong></p>
<p>Like any other companies in the ever-changing fields of computer business, Dell face the competition from rivalries existing in the PC market globally. Although Dell&#8217;s model has been proven to be effective, there is no stopping its competitors to adopt a similar, if not the same, strategy with a better suppliers.</p>
<p>Also, in global trend, the price difference among brands are getting smaller and smaller, making Dell&#8217;s main attraction, the build cost, become less obvious. There might come a time that all the price difference disappear, leaving customer choice purely dependent on the brand&#8217;s name, and as other brands can provide pre-made computers unlike Dell&#8217;s several-day-to-build custom made products, Dell is at a disadvantage here.</p>
<p>Being a globally recognized brand, Dell is also exposed to the fluctuations in the World currency exchange market. The system where orders are placed before being charged could leave the company in potential loss in part of the supply chain if there is changes in exchange rates.</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/thailehuy.wordpress.com/247/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/thailehuy.wordpress.com/247/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/thailehuy.wordpress.com/247/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/thailehuy.wordpress.com/247/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/thailehuy.wordpress.com/247/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/thailehuy.wordpress.com/247/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/thailehuy.wordpress.com/247/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/thailehuy.wordpress.com/247/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/thailehuy.wordpress.com/247/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/thailehuy.wordpress.com/247/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/thailehuy.wordpress.com/247/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/thailehuy.wordpress.com/247/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/thailehuy.wordpress.com/247/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/thailehuy.wordpress.com/247/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=thailehuy.wordpress.com&amp;blog=6260931&amp;post=247&amp;subd=thailehuy&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://thailehuy.wordpress.com/2010/02/03/dell-swot-analysis/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/89f133b618e8530d78bf1dc59c5f7dd3?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">thailehuy</media:title>
		</media:content>
	</item>
		<item>
		<title>What Is The Simplest Thing That Could Possibly Work?</title>
		<link>http://thailehuy.wordpress.com/2009/06/20/what-is-the-simplest-thing-that-could-possibly-work/</link>
		<comments>http://thailehuy.wordpress.com/2009/06/20/what-is-the-simplest-thing-that-could-possibly-work/#comments</comments>
		<pubDate>Sat, 20 Jun 2009 01:52:27 +0000</pubDate>
		<dc:creator>thailehuy</dc:creator>
				<category><![CDATA[Tech tips]]></category>
		<category><![CDATA[design]]></category>
		<category><![CDATA[productivity]]></category>
		<category><![CDATA[simplicity]]></category>
		<category><![CDATA[test]]></category>

		<guid isPermaLink="false">http://thailehuy.wordpress.com/?p=245</guid>
		<description><![CDATA[Source: http://railstips.org/2009/6/8/what-is-the-simplest-thing-that-could-possibly-work I am always amazed when I read an article from 2004 and find interesting goodies. I’m probably late to the game on a lot of these articles, as I didn’t really dive into programming as a career until 2005, but I just read The Simplest Thing that Could Possibly Work, a conversation with [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=thailehuy.wordpress.com&amp;blog=6260931&amp;post=245&amp;subd=thailehuy&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Source: <a href="http://railstips.org/2009/6/8/what-is-the-simplest-thing-that-could-possibly-work" target="_blank">http://railstips.org/2009/6/8/what-is-the-simplest-thing-that-could-possibly-work</a></p>
<div>
<p>I am always amazed when I read an article from 2004 and find interesting goodies. I’m probably late to the game on a lot of these articles, as I didn’t really dive into programming as a career until 2005, but I just read <a href="http://www.artima.com/intv/simplestP.html">The Simplest Thing that Could Possibly Work</a>, a conversation with Ward Cunningham by Bill Venners. The article was published on January 19, 2004, but it is truly timeless.</p>
<h2>The Shortest Path</h2>
<blockquote><p>Simplicity is the shortest path to a solution.</p></blockquote>
<p>“Shortest” doesn’t necessarily refer to lines of code or number of characters, but I see it more as the path that requires <strong>the least amount of complexity</strong>. As he mentions in the article, if someone releases a 20 page proof to a math problem and then later on, someone releases a 10 page proof for the same problem, the 10 page proof is not necessarily more simple.</p>
<p>The 10 page proof could use some form of mathematics that is not widely used in the community and takes some time to comprehend. This means the 10 page version could be less simple as it requires learning to understand, whereas the 20 page uses generally understood concepts.</p>
<p>I think this is a balance that we always fight with as programmers. What is simple? I can usually say simple or not simple when I look at code, but it is <strong>hard to define the rules for simplicity</strong>.</p>
<h2>Work Today Makes You Better Tomorrow</h2>
<blockquote><p>The effort you expend today to understand the code will make you a more powerful programmer tomorrow.</p></blockquote>
<p>This is one of the concepts that has made the biggest different in my programming knowledge over the past few years. The first time that I really did this was when I wrote about <a href="http://railstips.org/2006/11/18/class-and-instance-variables-in-ruby">class and instance variables</a> a few years back. Ever since then, when I come across something that I don’t understand, that I feel I should, I spend the time to understand it. I have grown immensely because of this and would recommend that you do the same if you aren’t already.</p>
<h2>Narrow What You Think About</h2>
<blockquote><p>We had been thinking about too much at once, trying to achieve too complicated a goal, trying to code it too well.</p></blockquote>
<p>This is something that I have been practicing a lot lately. You know how sometimes you just feel overwhelmed and don’t want to start a feature or project? What I’ve found is that when I feel this way it is because I’m trying to think about too much at once.</p>
<p>Ward encourages over and over in the article, think about what is the most simple <strong>possible</strong> thing that could work. Notice he did not say what is the simplest thing that <strong>would</strong> work, but rather what <strong>could</strong> work.</p>
<p>This is something that I’ve noticed recently while pairing with <a href="http://opensoul.org/">Brandon Keepers</a>. Both of us almost apologize for some of the code we first implement, as we are afraid the other will think that is all we are capable of. What is funny, is that we both realize that you have to start with <strong>something</strong> and thus never judge. It is far easier to incrementally work towards a brilliant solution than to think it in your head and instantly code it.</p>
<p><strong>Start with a test. Make the test pass. Rinse and repeat.</strong> Small, tested changes that solve only the immediate problem at hand always end up with a more simple solution than trying to do it all in one fell swoop. I’ve also found I’m more productive this way as I have less moments of wondering what to do next. The failing test tells me.</p>
<p><strong>Anyway</strong>, I thought the article was interesting enough that I would post some of the highlights here and encourage you all to read it. If you know of some oldie, but goodie articles, link them up in the comments below.</div>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/thailehuy.wordpress.com/245/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/thailehuy.wordpress.com/245/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/thailehuy.wordpress.com/245/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/thailehuy.wordpress.com/245/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/thailehuy.wordpress.com/245/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/thailehuy.wordpress.com/245/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/thailehuy.wordpress.com/245/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/thailehuy.wordpress.com/245/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/thailehuy.wordpress.com/245/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/thailehuy.wordpress.com/245/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/thailehuy.wordpress.com/245/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/thailehuy.wordpress.com/245/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/thailehuy.wordpress.com/245/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/thailehuy.wordpress.com/245/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=thailehuy.wordpress.com&amp;blog=6260931&amp;post=245&amp;subd=thailehuy&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://thailehuy.wordpress.com/2009/06/20/what-is-the-simplest-thing-that-could-possibly-work/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/89f133b618e8530d78bf1dc59c5f7dd3?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">thailehuy</media:title>
		</media:content>
	</item>
		<item>
		<title>Solve the problem, not just a problem</title>
		<link>http://thailehuy.wordpress.com/2009/06/20/solve-the-problem-not-just-a-problem/</link>
		<comments>http://thailehuy.wordpress.com/2009/06/20/solve-the-problem-not-just-a-problem/#comments</comments>
		<pubDate>Sat, 20 Jun 2009 01:47:37 +0000</pubDate>
		<dc:creator>thailehuy</dc:creator>
				<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Tech tips]]></category>
		<category><![CDATA[best practices]]></category>
		<category><![CDATA[rails]]></category>

		<guid isPermaLink="false">http://thailehuy.wordpress.com/?p=242</guid>
		<description><![CDATA[Source: http://blog.rubybestpractices.com/posts/jamesbritt/2009-04-13-solving-the-problem.rc.html So you decide you want a way for a number of people to post short articles to a Web site, and maybe allow for other people to leave comments. What do you do? That’s easy: jump to the white board and start sketching out a vast array of boxes, blobs, and arrows that [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=thailehuy.wordpress.com&amp;blog=6260931&amp;post=242&amp;subd=thailehuy&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Source: <a href="http://blog.rubybestpractices.com/posts/jamesbritt/2009-04-13-solving-the-problem.rc.html" target="_blank">http://blog.rubybestpractices.com/posts/jamesbritt/2009-04-13-solving-the-problem.rc.html</a></p>
<div>
<p>So you decide you want a way for a number of people to post short articles to a Web site, and maybe allow for other people to leave comments. What do you do? That’s easy: jump to the white board and start sketching out a vast array of boxes, blobs, and arrows that define a sophisticated content management system with multi-level admin roles, content versioning, threaded discussion boards, syntax highlighting, the works.</p>
<p>Right?</p>
<p>Don’t be too quick to judge.  It’s easy to fall into the trap of defining the wrong requirements for a project.</p>
<p>Part of the reason is that building software is (or should be) fun, and often bigger projects are more fun. There is also the tendency to think about “what if”, imagining all the things that maybe one day who knows you never can tell might be needed.</p>
<p>People also tend to think in terms of what’s familiar, of how things have been done in the past or by others.</p>
<p>There are many ways to satisfy the needs described in the first paragraph.  Some don’t require writing any software at all.</p>
<p>For the Ruby Best Practices blog, the general goals were modest. Allow a core set of people to easily add new content. Allow other people to contribute content as well, but only with the OK from someone in that core group. (<span>BTW</span>, there are eight of us in the <span>RBP</span> blog team.  See <a href="http://img509.imageshack.us/img509/2944/rbpdharmainitiative.jpg">here</a> for my take on the <span>RBP</span> team logo)</p>
<p>We wanted to allow the use of Textile, and not make people use a browser for editing. Basically, turn simple flat files into a Web site with minimal fuss.</p>
<p><a href="http://github.com/sandal/korma/tree/master">Korma</a> takes an interesting approach to building Web sites. The whole app is ~230 lines of Ruby. Its key function is to take content from a Git repository, run it through some templating, and write out the site files.</p>
<p>Relying on Git for that back end is stunningly perfect. Git provides versioning, access control, and distributed contributions.</p>
<p>It becomes the database layer common to most blogging tools. For free. Right off the bat, no need to write any admin tools or content versioning code .</p>
<p>At the heart of Korma is the <a href="http://github.com/mojombo/grit/tree/master">grit</a> gem. As the project blurb says, “Grit gives you object oriented read/write access to Git repositories via Ruby.”  Very sweet.</p>
<p>The <code>korma.rb</code> file takes two arguments. The first is required, and is the path to a git repository. The second argument is optional; it tells Korma what directory to use for the generated content, and defaults to ‘www’, relative to where you invoke the program.</p>
<p>The app uses grit to reach into the current contents of the repo and parse the needed files. Files have to be committed to be accessible to Korma.</p>
<p>There is a configuration file that describes some basic site metadata, such as the base <span>URL</span>, the site title, and the author list.  When called, <code>korma.rb</code> grabs this config file, sets some <code>Korma::Blog</code> properties, and then writes out the static files.</p>
<p>An early version of Korma used <a href="http://www.sinatrarb.com/">Sinatra</a>; basically, Korma was a lightweight Web app to serve the blog posts. But as simple as it was, it was overkill, since there was no real dynamic content. It made no sense to have the Web app regenerate the <span>HTML</span> on each request, since it changed so infrequently.</p>
<p>A next version replaced the Web app part with static files, making it a straightforward command-line program. This solved another problem: how to automate the regeneration of files. The answer: use Git’s post-commit hook to invoke the program.</p>
<p>For example:</p>
<pre>   #!/bin/sh
   # File .git/hooks/post-commit
   /usr/local/bin/ruby /home/james/data/vendor/korma/korma.rb /home/james/data/vendor/rbp-blog /home/james/data/vendor/korma/www</pre>
<p>Early versions also used Haml for site-wide templates. Not being a fan of Haml, I added in a configurable option to use Erb. It was nice and all, but it was a feature without a requirement. No one was asking for configurable templating, so that option was dropped and Erb replaced Haml as the default.</p>
<p>If you are wondering why working code was removed, consider that any code you have is something you have to maintain. As bug-free and robust as you may like to think it, no code is easier to maintain than <em>no</em> code. Configurable templating was simply not a problem we were needed to solve, and a smaller, simpler code base is more valuable than a maybe “nice to have.”</p>
<p>There was some discussion about the need or value of allowing comments. In the end they were deemed good, but there was no good argument for hosting them as part of the blog site itself. That meant a 3rd-party solution (in this case, <a href="http://disqus.com/">Disqus</a>) was perfectly acceptable. Again, a goal was to have comments, not to write a commenting system (as entertaining as that may be).</p>
<p>Using Git allows for yet another feature for free: easy one-off contributions. In many systems, allowing anyone to contribute means creating an account, granting some sort of access, managing all the new user annoyances. Or, an existing user has to play proxy, accepting content and entering it into the system on behalf of the real author. That’s work! With git, one can clone the repo, add new content, and issue a pull request back to the master branch. Anyone with commit rights to the master can then merge it in (or politely decline). No-code features <span>FTW</span>.</p>
<p>None of the blog design requirements are written in stone, and they may change tomorrow, but by identifying the real needs, addressing what was deemed essential, offloading what we could, and skipping the feature bling, we have a system that is easy to understand, easy to maintain, and easy to change.</p></div>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/thailehuy.wordpress.com/242/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/thailehuy.wordpress.com/242/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/thailehuy.wordpress.com/242/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/thailehuy.wordpress.com/242/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/thailehuy.wordpress.com/242/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/thailehuy.wordpress.com/242/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/thailehuy.wordpress.com/242/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/thailehuy.wordpress.com/242/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/thailehuy.wordpress.com/242/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/thailehuy.wordpress.com/242/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/thailehuy.wordpress.com/242/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/thailehuy.wordpress.com/242/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/thailehuy.wordpress.com/242/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/thailehuy.wordpress.com/242/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=thailehuy.wordpress.com&amp;blog=6260931&amp;post=242&amp;subd=thailehuy&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://thailehuy.wordpress.com/2009/06/20/solve-the-problem-not-just-a-problem/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/89f133b618e8530d78bf1dc59c5f7dd3?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">thailehuy</media:title>
		</media:content>
	</item>
		<item>
		<title>TDD for Greasemonkey scripts; and introducing Ninja Search JS</title>
		<link>http://thailehuy.wordpress.com/2009/06/13/tdd-for-greasemonkey-scripts-and-introducing-ninja-search-js/</link>
		<comments>http://thailehuy.wordpress.com/2009/06/13/tdd-for-greasemonkey-scripts-and-introducing-ninja-search-js/#comments</comments>
		<pubDate>Sat, 13 Jun 2009 12:24:59 +0000</pubDate>
		<dc:creator>thailehuy</dc:creator>
				<category><![CDATA[Ruby]]></category>
		<category><![CDATA[greasemonkey]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[js]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[search]]></category>
		<category><![CDATA[TDD]]></category>
		<category><![CDATA[test]]></category>

		<guid isPermaLink="false">http://thailehuy.wordpress.com/?p=240</guid>
		<description><![CDATA[Source: http://drnicwilliams.com/2009/06/07/tdd-for-greasemonkey-scripts-and-introducing-ninja-search-js/ “this article shows how I used test-driven development tools and processes on a Greasemonkey script.” Though it also includes free ninjas. When I do online banking I need to select from a large list of other people’s bank accounts to which I might like to transfer money too. It is the massive drop [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=thailehuy.wordpress.com&amp;blog=6260931&amp;post=240&amp;subd=thailehuy&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Source: <a href="http://drnicwilliams.com/2009/06/07/tdd-for-greasemonkey-scripts-and-introducing-ninja-search-js/" target="_blank">http://drnicwilliams.com/2009/06/07/tdd-for-greasemonkey-scripts-and-introducing-ninja-search-js/</a></p>
<p><em>“this article shows how I used test-driven development tools and processes on a Greasemonkey script.”</em> Though it also includes free ninjas.</p>
<div><a href="http://skitch.com/drnic/b19kn/1-long-drop-downs-hate-humans"><img style="width:200px;float:right;" src="http://img.skitch.com/20090605-fjjyx32iidgw1jtq1kbys8tbr7.preview.jpg" alt="1. Long drop downs hate humans" /></a></div>
<p>When I do online banking I need to select from a large list of other people’s bank accounts to which I might like to transfer money too. It is the massive drop down list that I must scroll through that I wish to raise issue with today. The problem of having to give other people money is probably a different discussion.</p>
<p>And take those time-zone selector drop down lists, for example, the massively long list rendered by Rails’ <code>time_zone_select</code> helper. Granted, I am thankful for you letting me choose my timezone in your web app. Though for those of us not living in the USA we must hunt for our closest city in the list. Dozens of locations, ordered by time zone and not the name of the city (see adjacent image). Unfortunately you can’t easily type a few letters of your current city to find it. Rather, you have to scroll. And if you live in the GMT+1000 time zone group (Eastern Australia), you have to scroll all the way to the bottom.</p>
<div><a href="http://skitch.com/drnic/b19kx/5-choose-from-a-small-list"><img style="width:200px;float:right;" src="http://img.skitch.com/20090605-t82418x1j76w4fm8s44cec9cr3.preview.jpg" alt="5. Choose from a small list" /></a></div>
<p>So I got to thinking I’d like a <a href="http://www.greasespot.net/">Greasemonkey</a> (for Firefox) or <a title="GreaseKit - User Scripting for all WebKit applications" href="http://8-p.info/greasekit/">GreaseKit</a> (for Safari) script that automatically converted all ridiculously long HTML drop down lists into a sexy, autocompletion text field. You could then type in “bris” and be presented with “(GMT+1000) Brisbane”, or given the less amusing banking scenario then I could type “ATO” and get the bank account details for the Australian Tax Office.</p>
<p><strong>I mean, how hard could it be?</strong></p>
<p>This article is two things: an introduction to Ninja Search JS which gives a friendly ninja for every drop down field to solve the above problem. Mostly, the rest of this article shows how I used test-driven development tools and processes on a Greasemonkey script.</p>
<h2>Introducing Ninja Search JS</h2>
<div><a href="http://drnic.github.com/ninja-search-js/"><img src="http://img.skitch.com/20090606-xy454r3b8u29hsykjs1gfhu259.jpg" alt="Ninja Search JS banner" /></a></div>
<p>Click the banner to learn about and install the awesome Ninja Search JS. It includes free ninjas.</p>
<p>Currently it is a script for Greasemonkey (FireFox) or GreaseKit (Safari). It could be dynamically installed as necessary via a bookmarklet. I just haven’t done that yet. It could also be a FireFox extension so it didn’t have to fetch remote CSS and JS assets each time.</p>
<p>Ninja Search JS uses <a href="http://github.com/rmm5t/liquidmetal">liquidmetal</a> and <a href="http://rmm5t.github.com/jquery-flexselect">jquery-flexselect</a> projects created by <a href="http://www.emacsblog.org/">Ryan McGeary</a>.</p>
<p>Most importantly of all, I think, is that I wrote it all using TDD. That is, tests first. I don’t think this is an erroneous statement given the relatively ridiculous, and unimportant nature of Ninja Search JS itself.</p>
<h2>TDD for Greasemonkey scripts</h2>
<p>I love the simple idea of Greasemonkey scripts: run a script on a subset of all websites you visit. You can’t easily do this on desktop apps, which is why web apps are so awesome &#8211; its just HTML inside <em>your</em> browser, and with Greasemoney or browser extensions you can hook into that HTML, add your own DOM, remove DOM, add events etc.</p>
<p>But what stops me writing more of them is that once you cobble together a script, you push it out into the wild and then bug reports start coming back. Or feature requests, preferably. I’d now have a code base without any test coverage, so each new change is likely to break something else. Its also difficult to isolate bugs across different browsers, or in different environments (running Ninja Search JS in a page that used <a href="http://www.prototypejs.org/">prototypejs</a> originally failed), without a test suite.</p>
<p>And the best way to get yourself a test suite is to write it before you write the code itself. I believe this to be true because I know it sucks writing tests <em>after</em> I’ve writing the code.</p>
<p>I mostly focused on unit testing this script rather than integration testing. With integration testing I’d need to install the script into Greasemonkey, then display some HTML, then run the tests. I’ve no idea how’d I’d do that.</p>
<div><a href="http://skitch.com/drnic/bunyt/testing-running"><img style="width:200px;float:right;" src="http://img.skitch.com/20090606-d7usrgr8i1qjs3ji45xfkra3hs.preview.jpg" alt="testing running" /></a></div>
<p>But I do know how to unit test JavaScript, and if I can get good coverage of the core libraries, then I should be able to slap the Greasemonkey specific code on top and do manual QA testing after that. The Greasemonkey specific code shouldn’t ever change much (it just loads up CSS and more JS code dynamically) so I feel ok about this approach.</p>
<p>For this project I used <a href="http://github.com/nkallen/screw-unit/tree/master">Screw.Unit</a> for the first time (via a modified version of the <a href="http://github.com/relevance/blue-ridge/tree/master">blue-ridge rails plugin</a>) and it was pretty sweet. Especially being able to run single tests or groups of tests in isolation.</p>
<h3>Project structure</h3>
<div><a href="http://skitch.com/drnic/bunbd/summary-of-project-structure"><img src="http://img.skitch.com/20090606-x7h9yhp9s3pt6y86r5q1mm1wf8.jpg" alt="summary of project structure" /></a></div>
<p>All the JavaScript source &#8211; including dependent libraries such as jquery and jquery-flexselect &#8211; was put into the <code>public</code> folder. This is because I needed to be able to load the files into the browser without using <code>file://</code> protocol (which was failing for me). So, I moved the entire project into my Sites folder, and added the project as a Passenger web app. I’m ahead of myself, but there is a reason I went with <code>public</code> for the JavaScript + assets folder.</p>
<p>In <code>vendor/plugins</code>, The blue-ridge rails plugin is a composite of several JavaScript libraries, including the test framework Screw.Unit, and a headless rake task to run all the tests without browser windows popping up everywhere. In my code base blue-ridge is slightly modified since my project doesn’t look like a rails app.</p>
<p>Our tests go in <code>spec</code>. In a Rails app using blue-ridge, they’d go in <code>spec/javascripts</code>, but since JavaScript is all we have in this project I’ve flattened the spec folder structure.</p>
<p>The <code>website</code> folder houses the github pages website (a git submodule to the gh-pages branch) and also the greasemonkey script and its runtime JavaScript, CSS, and ninja image assets.</p>
<h3>A simple first test</h3>
<p>For the Ninja Search JS I wanted to add the little ninja icon next to every <code>&lt;select&gt;</code> element on every page I ever visited. When the icon is clicked, it would convert the corresponding <code>&lt;select&gt;</code> element into a text field with fantastical autocompletion support.</p>
<p>For Screw.Unit, the first thing we need is a <code>spec/ninja_search_spec.js</code> file for the tests, and an HTML fixture file that will be loaded into the browser. The HTML file’s name must match to the corresponding test name, so it must be <code>spec/fixtures/ninja_search.html</code>.</p>
<p>For our first test we want the cute ninja icon to appear next to <code>&lt;select&gt;</code> drop downs.</p>
<pre>require("spec_helper.js");
require("../public/ninja_search.js"); // relative to spec folder

Screw.Unit(function(){
  describe("inline activation button", function(){
    it("should display NinjaSearch image button", function(){
      var button = $('a.ninja_search_activation');
      expect(button.size()).to(be_gte, 1);
    });
  });
});</pre>
<p>The <a href="http://github.com/karnowski/blue-ridge-tmbundle/tree/master">Blue Ridge textmate bundle</a> makes it really easy to create the <code>describe</code> (<code>des</code>) and <code>it</code> (<code>it</code>) blocks, and <code>ex</code> expands into a useful <code>expects(...).to(matcher, ...)</code> snippet.</p>
<p>The two ellipses are values that are compared by a matcher. Matchers are available via global names such as <code>equals</code>, <code>be_gte</code> (greater than or equal) etc. See the <a href="http://github.com/nathansobo/screw-unit/blob/master/javascript/lib/screw/matchers.js">matchers.js</a> file for the default available matchers.</p>
<p>The HTML fixture file is important in that it includes the sample HTML upon which the tests are executed.</p>
<pre>&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"&gt;
&lt;html xmlns="http://www.w3.org/1999/xhtml"&gt;

&lt;head&gt;
  &lt;title&gt;Ninja Search | JavaScript Testing Results&lt;/title&gt;
  &lt;link rel="stylesheet" href="screw.css" type="text/css" charset="utf-8" /&gt;
  &lt;script src="../../vendor/plugins/blue-ridge/lib/blue-ridge.js"&gt;&lt;/script&gt;
&lt;/head&gt;

&lt;body&gt;
  &lt;div&gt;
    &lt;label for="person_user_time_zone_id"&gt;Main drop down for tests&lt;/label&gt;
    &lt;select name="person[user][time_zone_id]" id="person_user_time_zone_id" style="display: inline;"&gt;
      &lt;option value="Hawaii"&gt;(GMT-10:00) Hawaii&lt;/option&gt;
      &lt;option value="Alaska"&gt;(GMT-09:00) Alaska&lt;/option&gt;
      &lt;option value="Pacific Time (US &amp; Canada)"&gt;(GMT-08:00) Pacific Time (US &amp; Canada)&lt;/option&gt;
      &lt;option value="Arizona"&gt;(GMT-07:00) Arizona&lt;/option&gt;
      &lt;option value="Mountain Time (US &amp; Canada)"&gt;(GMT-07:00) Mountain Time (US &amp; Canada)&lt;/option&gt;
      &lt;option value="Central Time (US &amp; Canada)"&gt;(GMT-06:00) Central Time (US &amp; Canada)&lt;/option&gt;
      &lt;option value="Eastern Time (US &amp; Canada)"&gt;(GMT-05:00) Eastern Time (US &amp; Canada)&lt;/option&gt;
    &lt;/select&gt;
  &lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;</pre>
<p>In its header it loads the blue-ridge JavaScript library, which in turn loads Screw.Unit and ultimately our spec.js test file (based on corresponding file name), so <code>ninja_search.html</code> will cause a file <code>spec/ninja_search_spec.js</code> to be loaded.</p>
<p>To run our first test just load up the <code>spec/fixtures/ninja_search.html</code> file into your browser.</p>
<p>Your first test will fail. But that’s ok, that’s the point of TDD. <a href="http://en.wikipedia.org/wiki/Test-driven_development">Red, green, refactor</a>.</p>
<h3>Simple passing code</h3>
<p>So now we need some code to make the test pass.</p>
<p>Create a file <code>public/ninja_search.js</code> and something like the following should work:</p>
<pre>(function($){
  $(function() {
    $('select').each(function(index) {
      var id = $(this).attr('id');

      // create the Ninja Search button, with rel attribute referencing corresponding &gt;select id="..."&gt;
      $('&gt; rel="' + id + '"&gt;ninja search&gt;/a&gt;')
      .insertAfter($(this));
    });
  });
})(jQuery);</pre>
<p>Reload your test fixtures HTML file and the test should pass.</p>
<p>Now rinse and repeat. The final suite of <a href="http://github.com/drnic/ninja-search-js/tree/master/spec">tests and fixture files</a> for Ninja Search JS are on github.</p>
<h3>Building a Greasemonkey script</h3>
<p>Typically Greasemonkey scripts are all-inclusive affairs. One JavaScript file, named <code>my_script.user.js</code>, typically does the trick.</p>
<p>I decided I wanted a thin Greasemonkey script that would dynamically load my <code>ninja-search.js</code>, and any stylesheets and dependent libraries. This would allow people to install the thin Greasemonkey script once, and I can deploy new versions of the actual code base over time without them having to re-install anything.</p>
<p>Ultimately in production, the stylesheets, images, and JavaScript code would be hosted on the intertubes somewhere. Though during development that would be long-winded and painful to push the code to a remote host just to run tests.</p>
<p>So I have three Greasemonkey scripts:</p>
<ul>
<li><code>public/ninja_search.dev.user.js</code> &#8211; loads each dependent library and asset from the local file system</li>
<li><code>public/ninja_search.local.user.js</code> &#8211; loads compressed library and asset from the local file system</li>
<li><code>public/ninja_search.user.js</code> &#8211; loads compressed library and assets from remote server</li>
</ul>
<p>Let’s ignore the optimisation of compressing dependent JavaScript libraries for the moment and just look at the <code>dev.user.js</code> and <code>user.js</code> files.</p>
<p>The two scripts differ in the target host from which they load assets and libraries. <a href="http://github.com/drnic/ninja-search-js/blob/master/public/ninja-search.dev.user.js">ninja_search.dev.user.js</a> loads them from the local machine and <a href="http://github.com/drnic/ninja-search-js/blob/master/public/ninja-search.user.js">ninja_search.user.js</a> loads them from a remote server.</p>
<p>For example <code>ninja_search.dev.user.js</code> loads local dependencies like this:</p>
<pre>require("http://ninja-search-js.local/jquery.js");
require("http://ninja-search-js.local/ninja_search.js");</pre>
<p>And <code>ninja_search.user.js</code> loads remote dependencies like this:</p>
<pre>require("http://drnic.github.com/ninja-search-js/dist/jquery.js");
require("http://drnic.github.com/ninja-search-js/dist/ninja_search.js");</pre>
<p>In the final version of <a href="http://github.com/drnic/ninja-search-js/blob/master/public/ninja-search.user.js">ninja_search.user.js</a> we load a simple, conpressed library containing jquery, our code, and other dependencies, called <a href="http://github.com/drnic/ninja-search-js/blob/master/public/ninja_search_complete.js">ninja_search_complete.js</a>.</p>
<h3>Using Passenger to server local libraries</h3>
<p>The problem with loading local JavaScript libraries using the <code>file://</code> protocol, inferred earlier, is that it doesn’t work. So if I can’t load libraries using <code>file://</code> then I must use the <code>http://</code> protocol. That means I must route the requests through Apache/Ningx.</p>
<p>Fortunately there is a very simple solution: use <a href="http://www.modrails.com/">Phusion Passenger</a> which serves a “web app’s” <code>public</code> folder automatically. That’s why all the javascript, CSS and image assets have been placed in a folder <code>public</code> instead of <code>src</code> or <code>lib</code> or <code>javascript</code>.</p>
<p>On my OS X machine, I moved the repository folder into my <code>Sites</code> folder and wired up the folder as a Passenger web app using <a href="http://github.com/alloy/passengerpane/tree/master">PassengerPane</a>. It took 2 minutes and now I had <a href="http://ninja-search.local/">http://ninja-search.local</a> as a valid base URL to serve my JavaScript libraries to my Greasemonkey script.</p>
<h3>Testing the Greasemonkey scripts</h3>
<p>I can only have one of the three Greasemonkey scripts installed at a time, so I install the <code>ninja-search.dev.user.js</code> file to check that everything is basically working inside a browser on interesting, foreign sites (outside of the unit test HTML pages).</p>
<p>Once I’ve deployed the JavaScript files and assets to the remote server I can then install the <code>ninja-search.user.js</code> file (<a href="http://drnic.github.com/ninja-search-js/dist/ninja-search.user.js">so can you</a>) and double check that I haven’t screwed anything up.</p>
<h3>Deploying via GitHub Pages</h3>
<p>The normal, community place to upload and share Greasemonkey scripts is <a href="http://userscripts.org/">userscripts.org</a>. This is great for one file scripts, though if your script includes CSS and image assets, let alone additional JavaScript libraries, then I don’t think its as helpful, which is a pity.</p>
<p>So I decided to deploy the ninja-search-js files into the project’s own GitHub Pages site.</p>
<p>After creating the GitHub Pages site using <a href="http://github.com/blog/277-pages-generator">Pages Generator</a>, I then pulled down the gh-pages branch, and then linked (via submodules) that branch into my master branch as <code>website</code> folder.</p>
<p>Something like:</p>
<pre><code>
git checkout origin/gh-pages -b gh-pages
git checkout master
git submodule add -b gh-pages git@github.com:drnic/ninja-search-js.git website
</code></pre>
<p>Now I can access the gh-pages branch from my master branch (where the code is).</p>
<p>Then to deploy our Greasemonkey script we just copy over all the <code>public</code> files into <code>website/dist</code>, and then commit and push the changes to the gh-pages branch.</p>
<pre><code>
mkdir -p website/dist
cp -R public/* website/dist/
cd website
git commit -a "latest script release"
git push origin gh-pages
cd ..
</code></pre>
<p>Then you wait very patiently for GitHub to deploy your latest website, which now contains your Greasemonkey script (<code>dist/ninja-search.user.js</code>) and all the libraries (our actual code), stylesheets and images.</p>
<h3>Summary</h3>
<p>Greasemonkey scripts might seem like small little chunks of code. But all code starts small and grows. At some stage you’ll wish you had some test coverage. And later you’ll hate yourself for ever having release the bloody thing in the first place.</p>
<p>I wrote all this up to summarise how I’d done TDD for the <a href="http://drnic.github.com/ninja-search-js/">Ninja Search JS</a> project, which is slightly different from how I added test cases to _why’s <a href="http://github.com/drnic/8cpj/tree/master">the octocat’s pajamas</a> greasemonkey script when I first started hacking with unit testing Greasemonkey scripts. The next one will probably be slightly different again.</p>
<p>I feel good about the current project structure, I liked Screw.Unit and blue-ridge, and I’m amused by my use of GitHub Pages to deploy the application itself.</p>
<p>If anyone has any ideas on how this could be improved, or done radically differently, I’d love to hear them!</p>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/thailehuy.wordpress.com/240/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/thailehuy.wordpress.com/240/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/thailehuy.wordpress.com/240/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/thailehuy.wordpress.com/240/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/thailehuy.wordpress.com/240/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/thailehuy.wordpress.com/240/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/thailehuy.wordpress.com/240/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/thailehuy.wordpress.com/240/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/thailehuy.wordpress.com/240/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/thailehuy.wordpress.com/240/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/thailehuy.wordpress.com/240/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/thailehuy.wordpress.com/240/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/thailehuy.wordpress.com/240/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/thailehuy.wordpress.com/240/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=thailehuy.wordpress.com&amp;blog=6260931&amp;post=240&amp;subd=thailehuy&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://thailehuy.wordpress.com/2009/06/13/tdd-for-greasemonkey-scripts-and-introducing-ninja-search-js/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/89f133b618e8530d78bf1dc59c5f7dd3?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">thailehuy</media:title>
		</media:content>

		<media:content url="http://img.skitch.com/20090605-fjjyx32iidgw1jtq1kbys8tbr7.preview.jpg" medium="image">
			<media:title type="html">1. Long drop downs hate humans</media:title>
		</media:content>

		<media:content url="http://img.skitch.com/20090605-t82418x1j76w4fm8s44cec9cr3.preview.jpg" medium="image">
			<media:title type="html">5. Choose from a small list</media:title>
		</media:content>

		<media:content url="http://img.skitch.com/20090606-xy454r3b8u29hsykjs1gfhu259.jpg" medium="image">
			<media:title type="html">Ninja Search JS banner</media:title>
		</media:content>

		<media:content url="http://img.skitch.com/20090606-d7usrgr8i1qjs3ji45xfkra3hs.preview.jpg" medium="image">
			<media:title type="html">testing running</media:title>
		</media:content>

		<media:content url="http://img.skitch.com/20090606-x7h9yhp9s3pt6y86r5q1mm1wf8.jpg" medium="image">
			<media:title type="html">summary of project structure</media:title>
		</media:content>
	</item>
		<item>
		<title>Sketches &#8211; Reload you code in console</title>
		<link>http://thailehuy.wordpress.com/2009/06/13/sketches-reload-you-code-in-console/</link>
		<comments>http://thailehuy.wordpress.com/2009/06/13/sketches-reload-you-code-in-console/#comments</comments>
		<pubDate>Sat, 13 Jun 2009 12:22:44 +0000</pubDate>
		<dc:creator>thailehuy</dc:creator>
				<category><![CDATA[Ruby]]></category>
		<category><![CDATA[console]]></category>
		<category><![CDATA[irb]]></category>
		<category><![CDATA[reload]]></category>
		<category><![CDATA[sketches]]></category>

		<guid isPermaLink="false">http://thailehuy.wordpress.com/?p=238</guid>
		<description><![CDATA[Source: http://sketches.rubyforge.org/ Description Sketches allows you to create and edit Ruby code from the comfort of your editor, while having it safely reloaded in IRB whenever changes to the code are saved. Features Spawn an editor of your choosing from IRB. Automatically reload your code when it changes. Use a custom editor command. Use a [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=thailehuy.wordpress.com&amp;blog=6260931&amp;post=238&amp;subd=thailehuy&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Source: <a href="http://sketches.rubyforge.org/" target="_blank">http://sketches.rubyforge.org/</a></p>
<h1><a id="description" href="http://sketches.rubyforge.org/#description">Description</a></h1>
<p>Sketches allows you to create and edit Ruby code from the comfort of your editor, while having it safely reloaded in IRB whenever changes to the code are saved.</p>
<h2><a id="features" href="http://sketches.rubyforge.org/#features">Features</a></h2>
<div>
<ul>
<li>Spawn an editor of your choosing from IRB.</li>
<li>Automatically reload your code when it changes.</li>
<li>Use a custom editor command.</li>
<li>Use a custom temp directory to store sketches in.</li>
</ul>
</div>
<h2><a id="install" href="http://sketches.rubyforge.org/#install">Install</a></h2>
<div>
<p>Download it <a href="http://rubyforge.org/frs/?group_id=8412">here</a> or run:</p>
<pre>$ sudo gem install sketches</pre>
<p>Then require sketches in your <kbd>.irbrc</kbd> file:</p>
<pre>require 'sketches'</pre>
<p>Sketches can be configured to use a custom editor command:</p>
<pre>Sketches.config :editor =&gt; 'gvim'

Sketches.config :editor =&gt; lambda { |path|
  "xterm -fg gray -bg black -e vim #{path} &amp;"
}</pre>
</div>
<h2><a id="examples" href="http://sketches.rubyforge.org/#examples">Examples</a></h2>
<div>
<ul>
<li> Open a new sketch:
<pre>sketch</pre>
</li>
<li> Open a new named sketch:
<pre>sketch :foo</pre>
</li>
<li> Open a sketch from an existing file:
<pre>sketch_from 'path/to/bar.rb'</pre>
</li>
<li> Reopen an existing sketch:
<pre>sketch 2</pre>
<pre>sketch :foo</pre>
</li>
<li> List all sketches:
<pre>sketches</pre>
</li>
<li> Name a sketch:
<pre>name_sketch 2, :foo</pre>
</li>
<li> Save a sketch to an alternant location:
<pre>save_sketch :foo, 'path/to/foo.rb'</pre>
</li>
</ul>
</div>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/thailehuy.wordpress.com/238/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/thailehuy.wordpress.com/238/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/thailehuy.wordpress.com/238/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/thailehuy.wordpress.com/238/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/thailehuy.wordpress.com/238/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/thailehuy.wordpress.com/238/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/thailehuy.wordpress.com/238/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/thailehuy.wordpress.com/238/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/thailehuy.wordpress.com/238/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/thailehuy.wordpress.com/238/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/thailehuy.wordpress.com/238/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/thailehuy.wordpress.com/238/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/thailehuy.wordpress.com/238/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/thailehuy.wordpress.com/238/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=thailehuy.wordpress.com&amp;blog=6260931&amp;post=238&amp;subd=thailehuy&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://thailehuy.wordpress.com/2009/06/13/sketches-reload-you-code-in-console/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/89f133b618e8530d78bf1dc59c5f7dd3?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">thailehuy</media:title>
		</media:content>
	</item>
		<item>
		<title>How to Add Simple Permissions into Your Simple App</title>
		<link>http://thailehuy.wordpress.com/2009/05/20/how-to-add-simple-permissions-into-your-simple-app/</link>
		<comments>http://thailehuy.wordpress.com/2009/05/20/how-to-add-simple-permissions-into-your-simple-app/#comments</comments>
		<pubDate>Wed, 20 May 2009 04:55:08 +0000</pubDate>
		<dc:creator>thailehuy</dc:creator>
				<category><![CDATA[Ruby]]></category>
		<category><![CDATA[clearance]]></category>
		<category><![CDATA[factory_girl]]></category>
		<category><![CDATA[paperclip]]></category>
		<category><![CDATA[permission]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[shoulda]]></category>
		<category><![CDATA[thoughtbot]]></category>

		<guid isPermaLink="false">http://thailehuy.wordpress.com/?p=236</guid>
		<description><![CDATA[Source: http://railstips.org/2009/4/20/how-to-add-simple-permissions-into-your-simple-app-also-thoughtbot-rules Last week, in a few hours, I whipped together flightcontrolled.com for Flight Control, a super fun iPhone game. The site allows users to upload screenshots of their high scores. I thought I would provide a few details here as some may find it interesting. It is a pretty straightforward and simple site, but [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=thailehuy.wordpress.com&amp;blog=6260931&amp;post=236&amp;subd=thailehuy&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Source: <a href="http://railstips.org/2009/4/20/how-to-add-simple-permissions-into-your-simple-app-also-thoughtbot-rules" target="_blank">http://railstips.org/2009/4/20/how-to-add-simple-permissions-into-your-simple-app-also-thoughtbot-rules</a></p>
<div class="body">
<p>Last week, in a few hours, I whipped together <a href="http://flightcontrolled.com/">flightcontrolled.com</a> for Flight Control, a super fun iPhone game. The site allows users to upload screenshots of their high scores. I thought I would provide a few details here as some may find it interesting.</p>
<p>It is a pretty straightforward and simple site, but it did need a few permissions. I wanted users to be able to update their own profile, scores and photos, but not anyone else’s. On top of that, I, as an admin, should be able to update anything on the site. I’m sure there is a better way, but this is what I did and it is working just fine.</p>
<h2>Add admin to users</h2>
<p>I added an admin boolean to the users table. You may or may not know this, but Active Record adds handy boolean methods for all your columns. For example, if the user model has an email column and an admin column, you can do the following.</p>
<pre><code class="ruby">user = User.new
user.email? <span class="comment"># =&gt; false</span>
user.email = <span class="string">'foobar@foobar.com'</span>
user.email? <span class="comment"># =&gt; true</span>

user.admin? <span class="comment"># =&gt; false</span>
user.admin = true
user.admin? <span class="comment"># =&gt; true</span></code></pre>
<h2>Simple permissions module</h2>
<p>Next up, I created a module called permissions, that looks something like this:</p>
<pre><code class="ruby"><span class="keywords">module</span> Permissions
  <span class="keywords">def</span> changeable_by?<span class="brackets">(</span>other_user<span class="brackets">)</span>
    return false <span class="keywords">if</span> other_user.nil?
    user == other_user || other_user.admin?
  <span class="keywords">end</span>
<span class="keywords">end</span></code></pre>
<p>I put this in app/concerns/ and added that directory to the load path, but it will work just fine in lib/.</p>
<h2>Mixin the permission module</h2>
<p>Then in the user, score and photo models, I just include that permission module.</p>
<pre><code class="ruby"><span class="keywords">class</span> Score &lt; ActiveRecord::Base
  include Permissions
<span class="keywords">end</span>

<span class="keywords">class</span> Photo &lt; ActiveRecord::Base
  include Permissions
<span class="keywords">end</span>

<span class="keywords">class</span> User &lt; ActiveRecord::Base
  include Permissions
<span class="keywords">end</span></code></pre>
<h2>Add checks in controllers/views</h2>
<p>Now, in the view I can check if a user has permission before showing the edit and delete links.</p>
<pre><code class="erb"><span class="tag">&lt;%</span>- <span class="keywords">if</span> score<span class="method">.changeable_by?</span><span class="brackets">(</span>current_user<span class="brackets">)</span> -<span class="tag">%&gt;</span>
  &lt;li <span class="keywords">class</span>=<span class="string">"actions"</span>&gt;
    <span class="tag">&lt;%=</span> link_to <span class="string">'Edit'</span>, edit_score_url<span class="brackets">(</span>score<span class="brackets">)</span> <span class="tag">%&gt;</span>
    <span class="tag">&lt;%=</span> link_to <span class="string">'Delete'</span>, score, <span class="symbol">:method</span> =&gt; <span class="symbol">:delete</span> <span class="tag">%&gt;</span>
  &lt;/li&gt;
<span class="tag">&lt;%</span>- <span class="keywords">end</span> -<span class="tag">%&gt;</span></code></pre>
<p>And in the controller, I can do the same.</p>
<pre><code class="ruby"><span class="keywords">class</span> ScoresController &lt; ApplicationController
  before_filter<span class="symbol"> :authorize</span>,<span class="symbol"> :only</span> =&gt; <span class="symbol">[:edit</span>,<span class="symbol"> :update</span>,<span class="symbol"> :destroy</span>]

  private
    <span class="keywords">def</span> authorize
      <span class="keywords">unless</span> @score.changeable_by?<span class="brackets">(</span>current_user<span class="brackets">)</span>
        render<span class="symbol"> :text</span> =&gt; <span class="string">'Unauthorized'</span>,<span class="symbol"> :status</span> =&gt;<span class="symbol"> :unauthorized</span>
      <span class="keywords">end</span>
    <span class="keywords">end</span>
<span class="keywords">end</span></code></pre>
<h2>Macro for model tests</h2>
<p>I didn’t forget about testing either. I created a quick macro for <a href="http://railstips.org/2009/2/21/shoulda-looked-at-it-sooner">shoulda</a> like this (also uses factory girl and <a href="http://railstips.org/2009/3/24/custom-matchers-for-matchy">matchy</a>):</p>
<pre><code class="ruby"><span class="keywords">class</span> ActiveSupport::TestCase
  <span class="keywords">def</span> <span class="keywords">self</span>.should_have_permissions<span class="brackets">(</span>factory<span class="brackets">)</span>
    should <span class="string">"know who has permission to change it"</span> <span class="keywords">do</span>
      object     = Factory<span class="brackets">(</span>factory<span class="brackets">)</span>
      admin      = Factory<span class="brackets">(</span>:admin<span class="brackets">)</span>
      other_user = Factory<span class="brackets">(</span>:user<span class="brackets">)</span>
      object.changeable_by?<span class="brackets">(</span>other_user<span class="brackets">)</span>.should be<span class="brackets">(</span>false<span class="brackets">)</span>
      object.changeable_by?<span class="brackets">(</span>object.user<span class="brackets">)</span>.should be<span class="brackets">(</span>true<span class="brackets">)</span>
      object.changeable_by?<span class="brackets">(</span>admin<span class="brackets">)</span>.should be<span class="brackets">(</span>true<span class="brackets">)</span>
      object.changeable_by?<span class="brackets">(</span>nil<span class="brackets">)</span>.should be<span class="brackets">(</span>false<span class="brackets">)</span>
    <span class="keywords">end</span>
  <span class="keywords">end</span>
<span class="keywords">end</span></code></pre>
<p>Which I can then call from my various model tests:</p>
<pre><code class="ruby"><span class="keywords">class</span> ScoreTest &lt; ActiveSupport::TestCase
  should_have_permissions<span class="symbol"> :score</span>
<span class="keywords">end</span></code></pre>
<p>Looking at it now, I probably could just infer the score factory as I’m in the ScoreTest, but for whatever reason, I didn’t go that far.</p>
<h2>A sprinkle of controller tests</h2>
<p>I also did something like the following to test the controllers:</p>
<pre><code class="ruby"><span class="keywords">class</span> ScoresControllerTest &lt; ActionController::TestCase
  context <span class="string">"A regular user"</span> <span class="keywords">do</span>
    setup <span class="keywords">do</span>
      @user = Factory<span class="brackets">(</span>:email_confirmed_user<span class="brackets">)</span>
      sign_in_as @user
    <span class="keywords">end</span>

    context <span class="string">"on GET to :edit"</span> <span class="keywords">do</span>
      context <span class="string">"for own score"</span> <span class="keywords">do</span>
        setup <span class="keywords">do</span>
          @score = Factory<span class="brackets">(</span>:score,<span class="symbol"> :user</span> =&gt; @user<span class="brackets">)</span>
          get<span class="symbol"> :edit</span>,<span class="symbol"> :id</span> =&gt; @score.id
        <span class="keywords">end</span>

        should_respond_with<span class="symbol"> :success</span>
      <span class="keywords">end</span>

      context <span class="string">"for another user's score"</span> <span class="keywords">do</span>
        setup <span class="keywords">do</span>
          @score = Factory<span class="brackets">(</span>:score<span class="brackets">)</span>
          get<span class="symbol"> :edit</span>,<span class="symbol"> :id</span> =&gt; @score.id
        <span class="keywords">end</span>

        should_respond_with<span class="symbol"> :unauthorized</span>
      <span class="keywords">end</span>
    <span class="keywords">end</span>
  <span class="keywords">end</span>

  context <span class="string">"An admin user"</span> <span class="keywords">do</span>
    setup <span class="keywords">do</span>
      @admin = Factory<span class="brackets">(</span>:admin<span class="brackets">)</span>
      sign_in_as @admin
    <span class="keywords">end</span>

    context <span class="string">"on GET to :edit"</span> <span class="keywords">do</span>
      context <span class="string">"for own score"</span> <span class="keywords">do</span>
        setup <span class="keywords">do</span>
          @score = Factory<span class="brackets">(</span>:score,<span class="symbol"> :user</span> =&gt; @admin<span class="brackets">)</span>
          get<span class="symbol"> :edit</span>,<span class="symbol"> :id</span> =&gt; @score.id
        <span class="keywords">end</span>

        should_respond_with<span class="symbol"> :success</span>
      <span class="keywords">end</span>

      context <span class="string">"for another user's score"</span> <span class="keywords">do</span>
        setup <span class="keywords">do</span>
          @score = Factory<span class="brackets">(</span>:score<span class="brackets">)</span>
          get<span class="symbol"> :edit</span>,<span class="symbol"> :id</span> =&gt; @score.id
        <span class="keywords">end</span>

        should_respond_with<span class="symbol"> :success</span>
      <span class="keywords">end</span>
    <span class="keywords">end</span>
  <span class="keywords">end</span>
<span class="keywords">end</span></code></pre>
<h2>Summary of Tools</h2>
<p>I should call <a href="http://flightcontrolled.com/">flightcontrolled</a>, the <a href="http://www.thoughtbot.com/">thoughtbot</a> project as I used several of their awesome tools. I used <a href="http://giantrobots.thoughtbot.com/2009/2/9/clearance-rails-authentication-for-developers-who-write-tests">clearance</a> for authentication, <a href="http://www.thoughtbot.com/projects/shoulda">shoulda</a> and <a href="http://www.thoughtbot.com/projects/factory_girl">factory girl</a> for testing, and <a href="http://www.thoughtbot.com/projects/paperclip">paperclip</a> for file uploads. This was the first project that I used factory girl on and I really like it. Again, I didn’t get the fuss until I used it, and then I was like “Oooooh! Sweet!”.</p>
<p>One of the cool things about paperclip is you can pass straight up convert options to imagemagick. Flight Control is a game that is played horizontally, so I knew all screenshots would need to be rotated 270 degress. I just added the following convert options (along with strip) to the paperclip call:</p>
<pre><code class="ruby">has_attached_file<span class="symbol"> :image</span>,
 <span class="symbol"> :styles</span> =&gt; <span class="symbol">{:thumb</span> =&gt; <span class="string">'100&gt;'</span>,<span class="symbol"> :full</span> =&gt; <span class="string">'480&gt;'</span>},
 <span class="symbol"> :default_style</span> =&gt;<span class="symbol"> :full</span>,
 <span class="symbol"> :convert_options</span> =&gt; <span class="symbol">{:all</span> =&gt; <span class="string">'-rotate 270 -strip'</span>}</code></pre>
<h2>Conclusion</h2>
<p>You don’t need some fancy plugin or a lot of code to add some basic permissions into your application. A simple module can go a long way. Also, start using Thoughtbot’s projects. I’m really impressed with the developer tools they have created thus far.</p></div>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/thailehuy.wordpress.com/236/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/thailehuy.wordpress.com/236/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/thailehuy.wordpress.com/236/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/thailehuy.wordpress.com/236/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/thailehuy.wordpress.com/236/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/thailehuy.wordpress.com/236/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/thailehuy.wordpress.com/236/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/thailehuy.wordpress.com/236/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/thailehuy.wordpress.com/236/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/thailehuy.wordpress.com/236/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/thailehuy.wordpress.com/236/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/thailehuy.wordpress.com/236/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/thailehuy.wordpress.com/236/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/thailehuy.wordpress.com/236/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=thailehuy.wordpress.com&amp;blog=6260931&amp;post=236&amp;subd=thailehuy&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://thailehuy.wordpress.com/2009/05/20/how-to-add-simple-permissions-into-your-simple-app/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/89f133b618e8530d78bf1dc59c5f7dd3?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">thailehuy</media:title>
		</media:content>
	</item>
		<item>
		<title>Include vs Extend in Ruby</title>
		<link>http://thailehuy.wordpress.com/2009/05/20/include-vs-extend-in-ruby/</link>
		<comments>http://thailehuy.wordpress.com/2009/05/20/include-vs-extend-in-ruby/#comments</comments>
		<pubDate>Wed, 20 May 2009 04:51:36 +0000</pubDate>
		<dc:creator>thailehuy</dc:creator>
				<category><![CDATA[Ruby]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[method]]></category>
		<category><![CDATA[include]]></category>
		<category><![CDATA[extend]]></category>
		<category><![CDATA[instance]]></category>
		<category><![CDATA[class]]></category>

		<guid isPermaLink="false">http://thailehuy.wordpress.com/?p=234</guid>
		<description><![CDATA[Source: http://railstips.org/2009/5/15/include-verse-extend-in-ruby Now that we know the difference between an instance method and a class method, let’s cover the difference between include and extend in regards to modules. Include is for adding methods to an instance of a class and extend is for adding class methods. Let’s take a look at a small example. module [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=thailehuy.wordpress.com&amp;blog=6260931&amp;post=234&amp;subd=thailehuy&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<p>Source: <a href="http://railstips.org/2009/5/15/include-verse-extend-in-ruby" target="_blank">http://railstips.org/2009/5/15/include-verse-extend-in-ruby</a></p>
<div class="body">
<p>Now that we know the difference between <a href="http://railstips.org/2009/5/11/class-and-instance-methods-in-ruby">an instance method and a class method</a>, let’s cover the difference between include and extend in regards to modules. Include is for adding methods to an instance of a class and extend is for adding class methods. Let’s take a look at a small example.</p>
<pre><code class="ruby"><span class="keywords">module</span> Foo
  <span class="keywords">def</span> foo
    puts <span class="string">'heyyyyoooo!'</span>
  <span class="keywords">end</span>
<span class="keywords">end</span>

<span class="keywords">class</span> Bar
  include Foo
<span class="keywords">end</span>

Bar.new.foo <span class="comment"># heyyyyoooo!</span>
Bar.foo <span class="comment"># NoMethodError: undefined method ‘foo’ for Bar:Class</span>

<span class="keywords">class</span> Baz
  extend Foo
<span class="keywords">end</span>

Baz.foo <span class="comment"># heyyyyoooo!</span>
Baz.new.foo <span class="comment"># NoMethodError: undefined method ‘foo’ for #&lt;Baz:0x1e708&gt;</span></code></pre>
<p>As you can see, include makes the foo method available to an instance of a class and extend makes the foo method available to the class itself.</p>
<h2>Include Example</h2>
<p>If you want to see more examples of using include to share methods among models, you can read my article on how I <a href="http://railstips.org/2009/4/20/how-to-add-simple-permissions-into-your-simple-app-also-thoughtbot-rules">added simple permissions</a> to an app. The permissions module in that article is then included in a few models thus sharing the methods in it. That is all I’ll say here, so if you want to see more check out that article.</p>
<h2>Extend Example</h2>
<p>I’ve also got a simple example of using extend that I’ve plucked from the Twitter gem. Basically, Twitter supports two methods for authentication—httpauth and oauth. In order to share the maximum amount of code when using these two different authentication methods, I use a lot of delegation. Basically, the Twitter::Base class takes an instance of a “client”. A client is an instance of either Twitter::HTTPAuth or Twitter::OAuth.</p>
<p>Anytime a request is made from the Twitter::Base object, either get or post is called on the client. The Twitter::HTTPAuth client defines the get and post methods, but the Twitter::OAuth client does not. Twitter::OAuth is just a thin wrapper around the OAuth gem and the OAuth gem actually provides get and post methods on the access token, which automatically handles passing the OAuth information around with each request.</p>
<p>The implementation looks something like this (<a href="http://github.com/jnunemaker/twitter/blob/9b880c4be0a43bf3ea6fd4f5ec1d6c120e6c2b11/lib/twitter/oauth.rb">full file</a> on github):</p>
<pre><code class="ruby"><span class="keywords">module</span> Twitter
  <span class="keywords">class</span> OAuth
    extend Forwardable
    def_delegators<span class="symbol"> :access_token</span>,<span class="symbol"> :get</span>,<span class="symbol"> :post</span>

    <span class="comment"># a bunch of code removed for clarity</span>
  <span class="keywords">end</span>
<span class="keywords">end</span></code></pre>
<p>Rather than define get and post, I simply delegate the get and post instance methods to the access token, which already has them defined. I do that by extending the Forwardable module onto the Twitter::OAuth class and then using the def_delegators class method that it provides. This may not be the most clear example, but it was the first that came to mind so I hope it is understandable.</p>
<h2>A Common Idiom</h2>
<p>Even though include is for adding instance methods, a common idiom you’ll see in Ruby is to use include to append both class and instance methods. The reason for this is that include has a self.included hook you can use to modify the class that is including a module and, to my knowledge, extend does not have a hook. It’s highly debatable, but often used so I figured I would mention it. Let’s look at an example.</p>
<pre><code class="ruby"><span class="keywords">module</span> Foo
  <span class="keywords">def</span> <span class="keywords">self</span>.included<span class="brackets">(</span>base<span class="brackets">)</span>
    base.extend<span class="brackets">(</span>ClassMethods<span class="brackets">)</span>
  <span class="keywords">end</span>

  <span class="keywords">module</span> ClassMethods
    <span class="keywords">def</span> bar
      puts <span class="string">'class method'</span>
    <span class="keywords">end</span>
  <span class="keywords">end</span>

  <span class="keywords">def</span> foo
    puts <span class="string">'instance method'</span>
  <span class="keywords">end</span>
<span class="keywords">end</span>

<span class="keywords">class</span> Baz
  include Foo
<span class="keywords">end</span>

Baz.bar <span class="comment"># class method</span>
Baz.new.foo <span class="comment"># instance method</span>
Baz.foo <span class="comment"># NoMethodError: undefined method ‘foo’ for Baz:Class</span>
Baz.new.bar <span class="comment"># NoMethodError: undefined method ‘bar’ for #&lt;Baz:0x1e3d4&gt;</span></code></pre>
<p>There are a ton of projects that use this idiom, including Rails, DataMapper, <a href="http://railstips.org/2008/7/29/it-s-an-httparty-and-everyone-is-invited">HTTParty</a>, and <a href="http://railstips.org/2008/11/17/happymapper-making-xml-fun-again">HappyMapper</a>. For example, when you use HTTParty, you do something like this.</p>
<pre><code class="ruby"><span class="keywords">class</span> FetchyMcfetcherson
  include HTTParty
<span class="keywords">end</span>

FetchyMcfetcherson.get<span class="brackets">(</span><span class="string">'http://foobar.com'</span><span class="brackets">)</span></code></pre>
<p>When you add the include to your class, HTTParty appends class methods, such as get, post, put, delete, base_uri, default_options and format. I think this idiom is what causes a lot of confusion in the include verse extend understanding. Because you are using include it seems like the HTTParty methods would be added to an instance of the FetchyMcfetcherson class, but they are actually added to the class itself.</p>
<h2>Conclusion</h2>
<p>Hope this helps those struggling with include verse extend. Use include for instance methods and extend for class methods. Also, it is sometimes ok to use include to add both instance and class methods. Both are really handy and allow for a great amount of code reuse. They also allow you to avoid deep inheritance, and instead just modularize code and include it where needed, which is much more the ruby way.</p></div>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/thailehuy.wordpress.com/234/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/thailehuy.wordpress.com/234/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/thailehuy.wordpress.com/234/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/thailehuy.wordpress.com/234/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/thailehuy.wordpress.com/234/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/thailehuy.wordpress.com/234/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/thailehuy.wordpress.com/234/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/thailehuy.wordpress.com/234/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/thailehuy.wordpress.com/234/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/thailehuy.wordpress.com/234/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/thailehuy.wordpress.com/234/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/thailehuy.wordpress.com/234/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/thailehuy.wordpress.com/234/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/thailehuy.wordpress.com/234/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=thailehuy.wordpress.com&amp;blog=6260931&amp;post=234&amp;subd=thailehuy&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://thailehuy.wordpress.com/2009/05/20/include-vs-extend-in-ruby/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/89f133b618e8530d78bf1dc59c5f7dd3?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">thailehuy</media:title>
		</media:content>
	</item>
		<item>
		<title>Fixing The Web With Stylish</title>
		<link>http://thailehuy.wordpress.com/2009/05/20/fixing-the-web-with-stylish/</link>
		<comments>http://thailehuy.wordpress.com/2009/05/20/fixing-the-web-with-stylish/#comments</comments>
		<pubDate>Wed, 20 May 2009 04:49:04 +0000</pubDate>
		<dc:creator>thailehuy</dc:creator>
				<category><![CDATA[Tech tips]]></category>
		<category><![CDATA[design]]></category>
		<category><![CDATA[Firefox]]></category>
		<category><![CDATA[fix]]></category>
		<category><![CDATA[layout]]></category>
		<category><![CDATA[plugins]]></category>
		<category><![CDATA[stylish]]></category>

		<guid isPermaLink="false">http://thailehuy.wordpress.com/2009/05/20/fixing-the-web-with-stylish/</guid>
		<description><![CDATA[Source: http://blog.new-bamboo.co.uk/2009/2/27/fixing-the-web-with-stylish Design becomes beautiful when there’s nothing left to take away. Google understands this; too many other web applications don’t. Many apps grow new features simply to differentiate themselves from the competition, or to increase their target market by supporting multiple conflicting workflows. But every single thing I don’t need detracts from the usability [...]<img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=thailehuy.wordpress.com&amp;blog=6260931&amp;post=233&amp;subd=thailehuy&amp;ref=&amp;feed=1" width="1" height="1" />]]></description>
			<content:encoded><![CDATA[<div class="entrybody">Source: <a href="http://blog.new-bamboo.co.uk/2009/2/27/fixing-the-web-with-stylish" target="_blank">http://blog.new-bamboo.co.uk/2009/2/27/fixing-the-web-with-stylish</a></div>
<div class="entrybody"><img class="picture" src="http://blog.new-bamboo.co.uk/assets/stylish_before_250.png" alt="" />Design becomes beautiful when there’s nothing left to take away. Google understands this; too many other web applications don’t.</p>
<p>Many apps grow new features simply to differentiate themselves from the competition, or to increase their target market by supporting multiple conflicting workflows. But every single thing I don’t need detracts from the usability of the things I do. In the past I’ve had to abandon apps that do more than I need, but not any more.</p>
<p>Now I can fix them.</p>
<p>Stylish is a Firefox plugin that lets you write your own css per-site, overriding the existing css. This means you can set display:none on everything unnecessary.</p>
<p>It’s a <a href="https://addons.mozilla.org/en-US/firefox/addon/2108">one-click install</a>. You’ll also need <a href="https://addons.mozilla.org/en-US/firefox/addon/1843">Firebug</a> to assist in identifying elements you want to block.</p>
<p>Once it’s installed, open firebug and point to the offending feature. Figure out a way to identify it with a CSS selector. In some cases this’ll be easy &#8211; it might have a classname like AnnoyingFeature. In others you’ll have to use CSS3 selectors. For example, you can do:</p>
<div id="gist-71410" class="gist">
<div class="gist-file">
<div class="gist-data gist-syntax">
<div class="gist-highlight">
<pre>
<div id="LC1" class="line"><span class="nt">label</span><span class="o">[</span><span class="nt">id</span><span class="o">*=</span><span class="s2">"requested_by_id"</span><span class="o">]</span> <span class="p">{</span><span class="k">display</span><span class="o">:</span><span class="k">none</span><span class="p">}</span></div>
</pre>
</div>
</div>
<div class="gist-meta"><a href="http://gist.github.com/raw/71410/f3a57a5f33340f146802b9265539b45eddfab6ad/gistfile1.css">view raw</a> <a href="http://gist.github.com/71410">This Gist</a> brought to you by <a href="http://github.com/">GitHub</a>.</div>
</div>
</div>
<p>This matches every Label element with an ‘id’ attribute <em>containing</em> ‘requested_by_id’. This is helpful if the site’s using ids like ‘story_34524_requested_by_id’.</p>
<p>But what about if you had this?</p>
<div id="gist-71411" class="gist">
<div class="gist-file">
<div class="gist-data gist-syntax">
<div class="gist-highlight">
<pre>
<div id="LC1" class="line"><span class="nt">&lt;div&gt;</span></div>
<div id="LC2" class="line">  <span class="nt">&lt;span</span> <span class="na">class=</span><span class="s">'annoying'</span><span class="nt">&gt;</span>annoyance 1<span class="nt">&lt;/span&gt;</span></div>
<div id="LC3" class="line">  <span class="nt">&lt;span&gt;</span>annoyance 2<span class="nt">&lt;/span&gt;</span></div>
<div id="LC4" class="line"><span class="nt">&lt;/div&gt;</span></div>
</pre>
</div>
</div>
<div class="gist-meta"><a href="http://gist.github.com/raw/71411/178b85f5b82980376382e18907456febe3ad2f9a/gistfile1.htm">view raw</a> <a href="http://gist.github.com/71411">This Gist</a> brought to you by <a href="http://github.com/">GitHub</a>.</div>
</div>
</div>
<p>It’d be nice to block the parent div, thereby taking out ‘annoyance 2’, but you can’t; <a href="http://adityamukherjee.com/geekaholic/archives/513">there&#8217;s no &#8220;parent selector&#8221;</a> even in CSS3. Greasemonkey could fix this; Stylish can’t.</p>
<p>While you’re there, you can also fix other usability issues like textboxes being too small &#8211; it’s as simple as ‘textarea {height:300px}’.</p>
<p><img class="picture" src="http://blog.new-bamboo.co.uk/assets/stylish_after_250.png" alt="" />Stylish has a live preview, and it’s only one click to turn off custom styles, so you can afford to be both aggressive and experimental.</p>
<p>You can share custom styles. I’d like to suggest that styles solely designed to remove distractions, rather than ‘re-skin’ sites, include ‘Simplify’ in the name to make them easy to find. Over the next few days I’ll be redesigning some of the web tools I use frequently to make them more of a joy to work with.</p></div>
<br />  <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gocomments/thailehuy.wordpress.com/233/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/comments/thailehuy.wordpress.com/233/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godelicious/thailehuy.wordpress.com/233/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/delicious/thailehuy.wordpress.com/233/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gofacebook/thailehuy.wordpress.com/233/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/facebook/thailehuy.wordpress.com/233/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gotwitter/thailehuy.wordpress.com/233/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/twitter/thailehuy.wordpress.com/233/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/gostumble/thailehuy.wordpress.com/233/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/stumble/thailehuy.wordpress.com/233/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/godigg/thailehuy.wordpress.com/233/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/digg/thailehuy.wordpress.com/233/" /></a> <a rel="nofollow" href="http://feeds.wordpress.com/1.0/goreddit/thailehuy.wordpress.com/233/"><img alt="" border="0" src="http://feeds.wordpress.com/1.0/reddit/thailehuy.wordpress.com/233/" /></a> <img alt="" border="0" src="http://stats.wordpress.com/b.gif?host=thailehuy.wordpress.com&amp;blog=6260931&amp;post=233&amp;subd=thailehuy&amp;ref=&amp;feed=1" width="1" height="1" />]]></content:encoded>
			<wfw:commentRss>http://thailehuy.wordpress.com/2009/05/20/fixing-the-web-with-stylish/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
	
		<media:content url="http://0.gravatar.com/avatar/89f133b618e8530d78bf1dc59c5f7dd3?s=96&#38;d=identicon&#38;r=G" medium="image">
			<media:title type="html">thailehuy</media:title>
		</media:content>

		<media:content url="http://blog.new-bamboo.co.uk/assets/stylish_before_250.png" medium="image" />

		<media:content url="http://blog.new-bamboo.co.uk/assets/stylish_after_250.png" medium="image" />
	</item>
	</channel>
</rss>
