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

<channel>
	<title>Startup Monkeys</title>
	<atom:link href="http://www.startupmonkeys.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.startupmonkeys.com</link>
	<description>Stuff we find or create</description>
	<lastBuildDate>Thu, 02 Sep 2010 15:03:26 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>Building a Scrabble MMO in 48 hours</title>
		<link>http://www.startupmonkeys.com/2010/09/building-a-scrabble-mmo-in-48-hours/</link>
		<comments>http://www.startupmonkeys.com/2010/09/building-a-scrabble-mmo-in-48-hours/#comments</comments>
		<pubDate>Thu, 02 Sep 2010 08:58:24 +0000</pubDate>
		<dc:creator>jacques</dc:creator>
				<category><![CDATA[Projects]]></category>
		<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://www.startupmonkeys.com/?p=75</guid>
		<description><![CDATA[Last weekend, I was lucky enough to be able to participate in the first Node Knockout. The rules are simple: you have 48 hours to code someone amazing using the Node.js platform. Planning ahead is allowed, however it has to be all on paper (no digital assets, designs, etc) I&#8217;m not sure what it is [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://nodejs.org/" target="_blank"><img class="alignleft size-full wp-image-138" title="Screen shot 2010-09-02 at 1.37.16 AM" src="http://www.startupmonkeys.com/wp-content/uploads/Screen-shot-2010-09-02-at-1.37.16-AM.png" alt="" width="250" height="94" /></a></p>
<p>Last weekend, I was lucky enough to be able to participate in the first <a href="http://nodeknockout.com/">Node Knockout</a>. The rules are simple: you have 48 hours to code someone amazing using the <a href="http://nodejs.org/">Node.js</a> platform. Planning ahead is allowed, however it has to be all on paper (no digital assets, designs, etc)</p>
<p>I&#8217;m not sure what it is about these weekend coding competitions, but they&#8217;ve always fascinated me. I love seeing real life demonstrations of the fact that technology has progressed to the point where you can go from a harebrained idea to deployed and functional product in a few intense dev days. Or maybe it&#8217;s because I remember my previous life as an ASP.NET developer where the first week of a project would barely cover the schema design and data access layer. Either way, it&#8217;s great for the Node.js community. If you want to convince the world that a language/framework is worth using, the best way is use it to build amazing stuff, amazingly fast.</p>
<p>Having also competed as a solo team in <a href="http://railsrumble.com/">Rails Rumble</a> the last few rounds with <a href="http://blog.railsrumble.com/blog/2009/08/30/and-the-winner-is...">decent results</a>, I had a good idea of what to expect. This time I was able to team up with some really talented local developers I met at MongoSeattle a few months ago: <a href="http://twitter.com/ggoodale">Grant Goodale</a>, <a href="http://twitter.com/dacort">Damon Cortesi</a>, and <a href="http://twitter.com/aviel">Aviel Ginzburg</a>. I figured with a team of 4, we could really bite off something ambitious and might have a chance at actually completing it.</p>
<p><span id="more-75"></span></p>
<p><strong>The Final Product</strong></p>
<p><a href="http://www.startupmonkeys.com/wp-content/uploads/Screen-shot-2010-09-01-at-7.13.47-PM.png"><img class="aligncenter size-full wp-image-81" title="Scrabbly board" src="http://www.startupmonkeys.com/wp-content/uploads/Screen-shot-2010-09-01-at-7.13.47-PM.png" alt="" width="578" height="717" /></a></p>
<p>What we were able to build was a Massively Multiplayer Online Scrabble game. Tiles are placed in realtime, and each player competes with everyone else to build the longest chain of words and the highest score. Once you place a tile, it&#8217;s on the board forever and other players have to work around it. You can start your game on anyone&#8217;s tiles, but after you place your first word you will need to stick to your own tiles until you run out of lives (swapping tiles loses a life).</p>
<p>And yes, we know we&#8217;ll be C&amp;D&#8217;ed by Hasbro very shortly <img src='http://www.startupmonkeys.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> . Just as long as they let us go till the competition voting ends, I&#8217;m happy. For some warped reason, we think blatant copyright infringement is quite hilarious. Especially since Hasbro has proven they have no sense of humor about these sorts of things. Worst case some time in jail would allow me to catch up on my reading.  Anyways, we do plan on getting the name and url changed soon after the competition, so Hasbro: save your corporate letterhead and skip the stern warning.</p>
<p><strong>The Tools</strong></p>
<p><a href="http://www.mongodb.org" target="_blank"><img class="alignleft size-full wp-image-125" title="MongoDB" src="http://www.startupmonkeys.com/wp-content/uploads/Screen-shot-2010-09-02-at-1.29.01-AM.png" alt="" width="265" height="115" /></a></p>
<p>As soon as we came up with the idea, we knew exactly the right tool for this application. MongoDB geospacial indexing was a lifesaver here. We are able to store every tile thats placed on the board by its x,y coordinate and query a bounding box whenever someone changes their view. The resulting queries are ridiculously fast, even with a tile collection approaching over 90 thousand. Once we get too big for that, scaling is going to be dead simple given we can just use modular arithmetic to produce a shard key.</p>
<p><a href="http://www.expressjs.com" target="_blank"><img class="alignleft size-full wp-image-129" title="ExpressJS" src="http://www.startupmonkeys.com/wp-content/uploads/Screen-shot-2010-09-02-at-1.30.47-AM.png" alt="" width="265" height="86" /></a></p>
<p>For the web server, we ended up using ExpressJS since it seemed like the most mature out of the NodeJS web frameworks. As a Ruby/Rails developer (no real node/expressjs experience before), I mostly avoided this aspect of the application, and just worked with static html files, javascript, and css. From what I did use of  ExpressJS, it felt very young. Works well if you do everything exactly right and are willing to do some trial and error, but don&#8217;t expect edge cases to work, or any kind of comprehensive documentation. Just the simple act of setting cookies, or performing a redirect took dev effort. Node&#8217;s philosophy seems to favor that you avoid abstracting too much. This was frustrating to begin with but I soon realized that I really should be knowing the internals of these mechanisms anyways. Using node will force you to become an expert in all things HTTP, which is not a bad thing at all.</p>
<p><a href="http://www.jquery.com" target="_blank"><img class="alignleft size-full wp-image-131" title="jQuery" src="http://www.startupmonkeys.com/wp-content/uploads/Screen-shot-2010-09-02-at-1.31.54-AM.png" alt="" width="285" height="96" /></a></p>
<p>For the UI end of things, I figured I&#8217;d just stick to what I know. JQuery and JQuery UI handled the job just fine. I do have some issues with the codebase of JQuery UI, and it&#8217;s not very hackable, but it&#8217;s widely used and well documented. I felt I had to compromise a lot on the usability however and would have loved to rewrite the sortable component (for the Tile Rack) myself in order to make it work exactly as I wanted. But that just wasn&#8217;t possible in the timeframe we had.</p>
<p><a href="http://www.heroku.com" target="_blank"><img class="alignleft size-full wp-image-133" title="Heroku" src="http://www.startupmonkeys.com/wp-content/uploads/Screen-shot-2010-09-02-at-1.33.00-AM.png" alt="" width="207" height="81" /></a></p>
<p><a href="http://www.mongohq.com"  target="_blank"><img src="http://www.startupmonkeys.com/wp-content/uploads/Screen-shot-2010-09-02-at-7.20.41-AM.png" alt="" title="MongoHQ" width="207" height="46" style="clear:left" class="alignleft size-full wp-image-191" /></a></p>
<p>Hosting wise we used Heroku (with pre-alpha node support). MongoHQ handled the mongodb hosting for us. It all worked like a charm. We had no issues whatsoever while in development, and everything deployed smooth as butter. After we started getting hammered with traffic, the Heroku node does tend to go down a few times a day, requiring a restart but I&#8217;m sure they&#8217;ll get that fixed up once they hit their actual release. In the meantime, after the competition we&#8217;ll probably move our servers elsewhere. </p>
<p><strong>The idea meetup (2 weeks before NodeKO) </strong></p>
<p>The first meeting we had was the idea session. I came prepared with my own idea (along with 3 pages of notes, use cases, and monetization strategies) for a variant on real time web chat. The team heard me out, but it promptly got rejected. It was too bland, and they figured everyone else would be doing it. And if we were going to do this for free in our spare time, they wanted to build something fun. I agreed as well, so we started thinking up game ideas.</p>
<p>As you can probably imagine, these were all over the place initially. Started off exploring the idea of building the card game <a href="http://en.wikipedia.org/wiki/Set_(game)">&#8220;Set&#8221;</a> (a real time card game). We decided we should build something everyone understands, and thought about doing an online Battleship. Then Scrabble. Then we started thinking about ways to combine them &#8220;You sank my Scrabbleship!&#8221;. After some additional debate, the idea of Scrabble on an infinite board started to seem plausible. An hour and a couple filled white boards later we had figured out what we wanted to do, along with some initial ideas for game mechanics.</p>
<p>We also came up with the name. We knew it wasn&#8217;t going to last, but Scrabb.ly seemed fun to use for the short term. Until the lawyers at Hasbro got wind of it of course.</p>
<p><strong>The feasibility meetup (1 week before NodeKO)</strong></p>
<p>After getting psyched up about the infinite Scrabble idea, we wanted to test out whether the game mechanics would actually work. What better way than to chop up and glue together 5 Scrabble boards. The result is pictured here:</p>
<p><a href="http://www.startupmonkeys.com/wp-content/uploads/scrabble_boards.jpg"><img class="aligncenter size-full wp-image-78" title="Scrabble Boards" src="http://www.startupmonkeys.com/wp-content/uploads/scrabble_boards.jpg" alt="" width="500" height="373" /></a></p>
<p>Sitting down and playing with 4 people together on one gigantic Scrabble board allowed us to tweak the rules a bit, and test if it would actually be fun to play in practice. We had a blast, which was proof enough to me that this thing was worth building.</p>
<p>Unfortunately we also found out that one of our team members (Damon) had an important client meeting the next weekend so we&#8217;d be down to 3 people on the project. This was a bummer, but we pressed on.</p>
<p><strong>The UI meetup (1 day before NodeKO)</strong></p>
<p>Right before the competition, we met up one more time in order to really nail down the final user interface design before the competiton started. This time we brought some graph paper and colored pencils then started drawing.</p>
<p style="text-align: center;"><a href="http://cl.ly/2DQS/content"><img class="aligncenter size-full wp-image-93" style="border: 1px solid black;" title="Sketch" src="http://www.startupmonkeys.com/wp-content/uploads/10.png" alt="" width="600" height="461" /></a></p>
<p style="text-align: center;"><a href="http://cl.ly/2Dg0/content"><img class="aligncenter size-full wp-image-95" style="border: 1px solid black;" title="8" src="http://www.startupmonkeys.com/wp-content/uploads/8.png" alt="" width="600" height="461" /></a></p>
<p style="text-align: center;"><a href="http://cl.ly/2DFO/content"><img class="aligncenter size-full wp-image-96" style="border: 1px solid black;" title="9" src="http://www.startupmonkeys.com/wp-content/uploads/9.png" alt="" width="600" height="461" /></a></p>
<p style="text-align: center;"><a href="http://cl.ly/2Dux/content"><img class="aligncenter size-full wp-image-97" style="border: 1px solid black;" title="7" src="http://www.startupmonkeys.com/wp-content/uploads/7.png" alt="" width="600" height="461" /></a></p>
<p>We were also able to snag meetings with a couple of the Seattle Techstar teams to walk them through the paper prototypes and get feedback on what made sense and what didn&#8217;t. This was incredibly useful to finalize all our intro copy, button labels, and the general user interaction of the site before the competition started. We now had reasonable confidence that most users would understand and be able to use our app once it was built.</p>
<p>Finally, before we left for the night, we came up with a gameplan for each of us on what tasks we need to do in what order.</p>
<p><a href="http://cl.ly/2DH4/content"><img class="aligncenter size-full wp-image-98" title="1" src="http://www.startupmonkeys.com/wp-content/uploads/1.png" alt="" width="500" height="672" /></a></p>
<p><a href="http://cl.ly/2DOq/content"><img class="aligncenter size-full wp-image-99" title="2" src="http://www.startupmonkeys.com/wp-content/uploads/2.png" alt="" width="500" height="651" /></a></p>
<p>We decided that if we were going to win this thing, we&#8217;d need to have one person (Aviel) fully focused on design for the entire duration of the competition. That turned out to be a great decision as it allowed us to attain a level of design polish and multiple iterations of design ideas that wouldn&#8217;t have been possible without a dedicated designer fully obsessing over every pixel.</p>
<p><strong>The 48 hours of coding</strong></p>
<p>Like all the other teams, we started as soon as the competition went underway (Friday at 5pm PST) and worked well into the night. Our team of 3 was able to work together at the <a href="http://www.founderscoop.com/">Founder Coop</a> offices in Seattle (where TechStars Seattle is located). We took over a conference room and made it our home for the weekend.</p>
<p>I stuck to my gameplan, went line by line, and got each UI feature functional. Grant worked solely on the backend data (mongodb and nodejs), and Aviel was able to focus on designing a gorgeous game board and tiles in Photoshop. Since our interface was just one screen, we were able to spend a lot of time on each little piece of it, and go through a few different design ideas.</p>
<div id="attachment_100" class="wp-caption aligncenter" style="width: 541px"><a href="http://www.startupmonkeys.com/wp-content/uploads/11.png"><img class="size-full wp-image-100 " title="1" src="http://www.startupmonkeys.com/wp-content/uploads/11.png" alt="" width="531" height="537" /></a><p class="wp-caption-text">First Working Infinite Board UI</p></div>
<p>After going all of Friday night into Saturday morning, we felt we made great progress. The infinite board was working, along with the dragging tiles back and forth between the rack and the board. And we had some nice initial tile and board design psds to integrate. After about 5 hours of sleep on Saturday, we started back up.</p>
<div id="attachment_101" class="wp-caption aligncenter" style="width: 533px"><a href="http://www.startupmonkeys.com/wp-content/uploads/21.png"><img class="size-full wp-image-101 " title="2" src="http://www.startupmonkeys.com/wp-content/uploads/21.png" alt="" width="523" height="537" /></a><p class="wp-caption-text">Adding Board Tiles and Basic Rack Panel</p></div>
<div id="attachment_102" class="wp-caption aligncenter" style="width: 618px"><a href="http://www.startupmonkeys.com/wp-content/uploads/3.png"><img class="size-full wp-image-102 " title="3" src="http://www.startupmonkeys.com/wp-content/uploads/3.png" alt="" width="608" height="415" /></a><p class="wp-caption-text">Adding Rack Tiles and Drag/Drop</p></div>
<p>The Saturday night &#8211; Sunday morning was the toughest stretch of the competition by far. Low sleep, and lots of broken functionality definitely took a strain on motivation levels. After working through the night, on Sunday morning I was pretty much resigned to shipping a half broken piece of crap. The basic infinite board, tile placing, and design looked good, but the game didn&#8217;t work at all. We couldn&#8217;t validate placing a word onto the board, and we hadn&#8217;t even started on the push notifications. I toyed with the idea of cutting my losses and just going to sleep.</p>
<div id="attachment_109" class="wp-caption aligncenter" style="width: 624px"><a href="http://www.startupmonkeys.com/wp-content/uploads/51.png"><img class="size-large wp-image-109" title="5" src="http://www.startupmonkeys.com/wp-content/uploads/51-1024x698.png" alt="" width="614" height="419" /></a><p class="wp-caption-text">Adding Compass and Random Tile Generator</p></div>
<p><a href="http://pusherapp.com" target="_blank"><img class="alignleft size-full wp-image-135" title="PusherApp" src="http://www.startupmonkeys.com/wp-content/uploads/Screen-shot-2010-09-02-at-1.34.17-AM.png" alt="" width="175" height="122" /></a></p>
<p>Pressing on, we found small wins here and there, and finally got the word placing to work. Integration with push notifications from the server was handled by a 3rd party web service called <a href="http://pusherapp.com/">PusherApp</a>, and it took all of about 15 minutes to get up and functional. After those 2 big features were in, I had the motivation to actually finish the thing. On Sunday from noon to 3pm, we basically built the entire game system within Scrabb.ly (end turn, score submission, restart game, etc). It was rapid fire. We went though all the missing features, spent 20 &#8211; 30 minutes coding each one, and on to the next. The rapidly approaching deadline meant whatever didn&#8217;t get done in a short window of development had to be cut.</p>
<div id="attachment_105" class="wp-caption aligncenter" style="width: 624px"><a href="http://www.startupmonkeys.com/wp-content/uploads/6.png"><img class="size-large wp-image-105" title="6" src="http://www.startupmonkeys.com/wp-content/uploads/6-1024x944.png" alt="" width="614" height="566" /></a><p class="wp-caption-text">Adding Play Buttons and Logo Panel</p></div>
<div id="attachment_110" class="wp-caption aligncenter" style="width: 624px"><a href="http://www.startupmonkeys.com/wp-content/uploads/82.png"><img class="size-large wp-image-110 " title="8" src="http://www.startupmonkeys.com/wp-content/uploads/82-1024x753.png" alt="" width="614" height="452" /></a><p class="wp-caption-text">Adding the Score Panel, the leaderboard, and the jGrowl Alerts. Lightened up the Board colors.</p></div>
<p>At 3pm on Sunday we were able to focus the next 2 hours purely on bug fixes and polish. Adding an intro screen, putting in a leaderboard, and fixing a ton of bugs with resolving word positions. The frantic code, checkin, deploy, and swearing lasted right up until minutes before the deadline. Unfortunately some bugs caused us to have to strip out the leaderboard as one of our last checkins. But at that point I couldn&#8217;t really get anything working right as I was running on fumes and could barely think straight.</p>
<p>After the 5pm deadline, we took some shots of whisky, drank a bottle of wine, and called it a night.</p>
<p>To get a feel for how frantic the dev activity was, here&#8217;s the full git commit log: <a href="http://gist.github.com/561680">http://gist.github.com/561680</a>. Near the end of the competition I wasn&#8217;t even coherent enough to spell the swear words correctly in the commit messages.</p>
<div id="attachment_111" class="wp-caption aligncenter" style="width: 624px"><a href="http://www.startupmonkeys.com/wp-content/uploads/final1.png"><img class="size-large wp-image-111 " title="final" src="http://www.startupmonkeys.com/wp-content/uploads/final1-1024x931.png" alt="" width="614" height="559" /></a><p class="wp-caption-text">Final released version!</p></div>
<p><strong>Traffic Stats</strong></p>
<p>In the 3 days since we&#8217;ve launched, we&#8217;ve managed to get almost 7 thousands unique visitors. Over 90 thousand individual Scrabble tiles have been placed on the board so far. Feedback has been fairly positive aside from the fact that the board has gotten way too large and it&#8217;s difficult to scroll far enough to find a place to start playing. We plan to iterate with some of these fixes and release a new version on Friday after competition judging ends.</p>
<p><strong>Why we do it</strong></p>
<p>Over 200 teams signed up for Node Knockout, and around 100 produced final judge-able products. The question is, why do they do it?</p>
<p>Anyone doing it to win the the Node Knockout <a href="http://nodeknockout.com/prizes">prizes</a> has their priorities whacked. Someone who can put together a nice functional app in a weekend should easily be able to make much more than any of these prizes consulting, or working on their own startup.</p>
<p>I think what competitions like this give is a structured way to &#8220;prove your chops&#8221; to the world. Having a successful entry, even if it doesn&#8217;t win, gets huge exposure from important people in the tech sector. Having a concrete example of your abilities in a public forum can mean a huge difference between a low hourly rate and a high one, the difference between a dream job with high salary and a a mediocre one. The tangible benefits of competing successfully can easily equate to tens of thousands of dollars over the course of year for a worthy competitor.</p>
<p>Another great benefit is the motivation that comes with working against the best and the brightest in the industry. You know right away you can&#8217;t be slacking off and have a chance. Judging from the results, this seems to help bring out the best in people. The high caliber of the <a href="http://nodeknockout.com/teams">final submitted apps</a> support that.</p>
<p>Thanks for reading! If you liked our entry, please vote for us here: <a href="http://nodeknockout.com/teams/seattle-js">http://nodeknockout.com/teams/seattle-js</a></p>
<p>&#8212;&#8212;&#8212;-</p>
<p><strong>About Author</strong>: Jacques Crocker is a Ruby on Rails developer in Seattle.<br />Follow him on Twitter at <a href="http://twitter.com/railsjedi">@railsjedi</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.startupmonkeys.com/2010/09/building-a-scrabble-mmo-in-48-hours/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Whitelabel Analytics using Google Analytics</title>
		<link>http://www.startupmonkeys.com/2010/07/whitelabel-analytics-using-google-analytics/</link>
		<comments>http://www.startupmonkeys.com/2010/07/whitelabel-analytics-using-google-analytics/#comments</comments>
		<pubDate>Mon, 19 Jul 2010 19:29:08 +0000</pubDate>
		<dc:creator>eric</dc:creator>
				<category><![CDATA[Tips and Tricks]]></category>
		<category><![CDATA[analytics]]></category>

		<guid isPermaLink="false">http://www.startupmonkeys.com/?p=61</guid>
		<description><![CDATA[Whitelabel products can be seen anytime you walk into a grocery store. They&#8217;re becoming even more popular in online business models: Tracking Most whitelabels operate by either providing a back-end API, or in having a DNS CNAME entry pointing to the host.  In the case, for when you&#8217;re powering a whitelabel through a CNAME entry, [...]]]></description>
			<content:encoded><![CDATA[<p><strong><a href="http://en.wikipedia.org/wiki/White-label_product">Whitelabel</a> products can be seen anytime you walk into a grocery store.</strong><a href="http://en.wikipedia.org/wiki/White-label_product"><br />
</a></p>
<div id="attachment_62" class="wp-caption aligncenter" style="width: 233px"><a href="http://en.wikipedia.org/wiki/File:Crispy_hexagons.jpg"><img class="size-medium wp-image-62" title="Crispy_hexagons Whitelable Product" src="http://www.startupmonkeys.com/wp-content/uploads/Crispy_hexagons-223x300.jpg" alt="" width="223" height="300" /></a><p class="wp-caption-text">Image Courtesy of Wikipedia</p></div>
<p><strong>They&#8217;re becoming even more popular in online business models:</strong></p>
<p><a href="http://www.startupmonkeys.com/wp-content/uploads/vast_whitelabel.png"><img class="aligncenter size-medium wp-image-63" title="Vast Whitelabel" src="http://www.startupmonkeys.com/wp-content/uploads/vast_whitelabel-300x114.png" alt="" width="300" height="114" /></a></p>
<h2>Tracking</h2>
<p>Most whitelabels operate by either providing a back-end API, or in having a DNS CNAME entry pointing to the host.  In the case, for when you&#8217;re powering a whitelabel through a CNAME entry, usually the partner wants to have a way to track their stats.  An easy way for them to do this is to include their Google Analytics code and then have them setup a Custom Segment to isolate the traffic to the whitelabel site.  That setup is easily accomplished through the following instructions:</p>
<h3>Creating Custom Reports</h3>
<p>If you are only interested in statistics for an exact Web site,  following the steps above to drill-down to your site becomes a time  consuming process. You can follow the steps below to create a custom  report that is already focused on one or more URLs. In Google Analytics,  you can create an &#8220;advanced segment&#8221; to narrow down a report to  basically any criteria.</p>
<ol>
<li>Log into Google Analytics</li>
<li>Click &#8220;Advanced Segments&#8221;  under &#8220;My Customizations&#8221; on the left menu.</li>
<li>Click &#8220;Create New  Custom Segment&#8221; on the top right of the screen.</li>
<li>In the &#8220;type to  filter&#8221; search box on the left menu type in &#8220;hostname&#8221; as one word.</li>
<li>Drag  the green hostname box over to the center column and on top of the  dotted box labeled &#8220;dimension or metric&#8221;.</li>
<li>Change the condition  to &#8220;contains&#8221; in the drop down box.</li>
<li>In the value box, type in  your full URL of your web site minus any slashes, http://, www or  anything else. Example: autoparts.yourdomain.com</li>
<li>Down at the bottom of the  form you need to give your new segment a name, for example call it  &#8220;Entomology site traffic&#8221;.</li>
<li>Click the button &#8220;create segment&#8221; to  the right of the name box.</li>
</ol>
<p>You have now created a new segment, which will be available at any  point in the future from only your account. This segment can be applied  to your reports by following the instructions below.</p>
<p><a name="section-6"></a></p>
<h3>Viewing Custom Reports (Segments)</h3>
<ol>
<li>Log into Google Analytics</li>
<li>From any report or the  Dashboard click down arrow in the drop down window next to &#8220;advanced  segments&#8221; on the top right of the screen.</li>
<li>By default &#8220;All  Visits&#8221; will be checked, which is the default segment that shows all  traffic and all sites. Under &#8220;Custom Segments&#8221; you will see the segments  you created.</li>
<li>If you wish to only view your segment, check  it in the &#8220;Custom Segments&#8221; box and un-check &#8220;All Visits&#8221;. If you leave  &#8220;All Visits&#8221; checked, it will overlay the reports to allow comparisons.</li>
<li>Once  you have selected your segment and unchecked &#8220;All Visits&#8221; you can click  &#8220;Apply&#8221; below the &#8220;Custom Segments&#8221; menu.</li>
</ol>
<p>You are now viewing all of the reports that are narrowed to to only  this specific segment or Web site. You can verify the segment is  selected by looking above the graph on any report and it should say the  name of the segment you created.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.startupmonkeys.com/2010/07/whitelabel-analytics-using-google-analytics/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Howto create &#8220;standardized&#8221; CSV exports from MySQL</title>
		<link>http://www.startupmonkeys.com/2010/04/howto-create-standardized-csv-exports-from-mysql/</link>
		<comments>http://www.startupmonkeys.com/2010/04/howto-create-standardized-csv-exports-from-mysql/#comments</comments>
		<pubDate>Mon, 26 Apr 2010 09:15:08 +0000</pubDate>
		<dc:creator>eric</dc:creator>
				<category><![CDATA[Databases]]></category>
		<category><![CDATA[Tips and Tricks]]></category>
		<category><![CDATA[csv]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[sql]]></category>
		<category><![CDATA[tsv]]></category>

		<guid isPermaLink="false">http://www.startupmonkeys.com/?p=52</guid>
		<description><![CDATA[When you start querying/analyzing/and wanting to export data from MySQL (especially anything free-form like SEO/SEM Keyword Data Extracts) you&#8217;ll quickly find out you can&#8217;t simply concatenate your columns together and join them with a delimiter to be read later. Sampling some of our keywords illustrates a number of problems: Quotes: ! 1/4&#8243; inlet muffler Newlines: [...]]]></description>
			<content:encoded><![CDATA[<p>When you start querying/analyzing/and wanting to export data from MySQL (especially anything free-form like <a href="http://www.startupmonkeys.com/2010/04/ruby-script-to-extract-google-analytics-keywords-landing-page-medium-entrances-to-csvtsv-format/">SEO/SEM Keyword Data Extracts</a>) you&#8217;ll quickly find out you can&#8217;t simply concatenate your columns together and join them with a delimiter to be read later.<br />
Sampling some of our keywords illustrates a number of problems:</p>
<p><strong>Quotes:</strong> ! 1/4&#8243; inlet muffler<br />
<strong>Newlines:</strong> xtra vision<br />
headlight h4703xv hours<br />
<strong>Control Characters:</strong> ^HToyota+starlet+coolant+temp+sensor, 242°/250° duration @ .050&#8243; 601 lift<br />
<strong>Commas/Tabs/Semi Colans or other typical delimiters:</strong> &#8220;+bosch, +part no., +67647&#8243;, 2500hd<br />
chevy cat back exhaust<br />
<strong>Unicode:</strong> ايسوزو 20011<br />
ב.מ.וו 545i, 宝马m3冷却系统</p>
<p>The Original MySQL attempt to export this was:</p>
<pre class="sql">SELECT
	`medium`,
	`keyword`,
	`landingpage`,
	SUM(`entrances`) as entrances
INTO OUTFILE 'keyword_data.csv'
	FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"'
	LINES TERMINATED BY '\r\n'
FROM
	`daily_medium_keyword_landingpage_visits`
WHERE
	medium = 'organic'
GROUP BY
	`medium`,
	`keyword`,
	`landingpage`;
</pre>
<p>This produced a really great file format, any crazy characters were escaped properly and it looked great on paper:</p>
<pre>"organic","! 1/4\" inlet muffler","/cache.aspx?q=\"1+4\"+\"inlet+muffler\"&amp;d=76309488474199&amp;mkt=en-US&amp;setlang=en-US&amp;w=6d792683,38b10c05",1
"organic","!((^ toyota avalon mechanical lifter repairs","/auto-parts/toyota/engine-mechanical-valve-lifter",1
"organic","!(gl-508 rm) motorcraft","/auto-part/149373-motorcraft-gl508rm-remanufactured-alternator",1
"organic","!947 dodge power wagon","/auto-part/983439-mallory-ignition-wire-set-m11947",1
"organic","!967 ford mustang parts","/auto-parts/ford-mustang",1
"organic","!971 142 volvo specifications","/auto-parts/volvo-142/engine-electrical-distributor",1
"organic","!972 buick skylark heater core","/cars/1972-buick-skylark",1
</pre>
<p>I loaded up <a href="http://rubyforge.org/projects/fastercsv/">FasterCSV</a> to start parsing the file and quickly figured out how naive I was about the CSV file formats.  MySQL&#8217;s default use of \ as an escape character isn&#8217;t &#8220;standard.&#8221;  It turns out there isn&#8217;t an official CSV standard but I did find something close: <a href="http://tools.ietf.org/html/rfc4180#section-2">http://tools.ietf.org/html/rfc4180#section-2</a></p>
<p>Hmm so effectively none of the control characters should be escaped, but fields should be quoted if they have a delimeter in them, and a quote in the field should be replaced with &#8220;&#8221; to be a CSV Standard.</p>
<p>The other wrinkle is the standard calls for \r\n to be the line delimeter, but what happens if there are other \r\n in the columns?</p>
<p><strong>First Step &#8211; Remove Escape Characters: </strong></p>
<pre class="sql">	FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' ESCAPED BY ''
</pre>
<p>By adding the ESCAPED BY &#8221;, none of the characters will be escaped in the output</p>
<p><strong>Step Two &#8211; Replace \r\n with \n: </strong></p>
<pre class="sql">	REPLACE(`keyword`, "\r\n", "\n")
</pre>
<p><strong>Step Three &#8211; Replace &#8221; with &#8220;&#8221; </strong></p>
<pre class="sql">	REPLACE(REPLACE(`keyword`, "\r\n", "\n"), '"', '""')
</pre>
<p><strong>Step Four &#8211; Replace NULL-Byte with a blank </strong></p>
<pre class="sql">	REPLACE(REPLACE(REPLACE(`keyword`, "\r\n", "\n"), '"', '""'), ""), char(0), "")
</pre>
<p><strong>Step Five &#8211; Replace NULL field with a blank</strong></p>
<pre class="sql">	IFNULL(REPLACE(REPLACE(REPLACE(`keyword`, "\r\n", "\n"), '"', '""'), ""), char(0), ""), "")
</pre>
<p><strong>Putting it all together:</strong></p>
<pre class="sql">SELECT
	IFNULL(REPLACE(REPLACE(REPLACE(`medium`, "\r\n", "\n"), '"', '""'), char(0), ""), "") as medium,
	IFNULL(REPLACE(REPLACE(REPLACE(`keyword`, "\r\n", "\n"), '"', '""'), char(0), ""), "") as keyword,
	IFNULL(REPLACE(REPLACE(REPLACE(`landingpage`, "\r\n", "\n"), '"', '""'), char(0), ""), "") as landingpage,
	SUM(IFNULL(`entrances`, 0)) as entrances
INTO OUTFILE 'keyword_data_new.csv'
	FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' ESCAPED BY ''
	LINES TERMINATED BY '\r\n'
FROM `daily_medium_keyword_landingpage_visits`
WHERE medium = 'organic'
GROUP BY
	IFNULL(REPLACE(REPLACE(REPLACE(`medium`, "\r\n", "\n"), '"', '""'), char(0), ""), ""),
	IFNULL(REPLACE(REPLACE(REPLACE(`keyword`, "\r\n", "\n"), '"', '""'), char(0), ""), ""),
	IFNULL(REPLACE(REPLACE(REPLACE(`landingpage`, "\r\n", "\n"), '"', '""'), char(0), ""), "");
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.startupmonkeys.com/2010/04/howto-create-standardized-csv-exports-from-mysql/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Put Google AdSense Into the Corner, Let your page render first using jQuery</title>
		<link>http://www.startupmonkeys.com/2010/04/put-google-adsense-into-the-corner-let-your-page-render-first-using-jquery/</link>
		<comments>http://www.startupmonkeys.com/2010/04/put-google-adsense-into-the-corner-let-your-page-render-first-using-jquery/#comments</comments>
		<pubDate>Fri, 23 Apr 2010 00:00:36 +0000</pubDate>
		<dc:creator>eric</dc:creator>
				<category><![CDATA[Tips and Tricks]]></category>
		<category><![CDATA[AdSense]]></category>
		<category><![CDATA[hacks]]></category>
		<category><![CDATA[jQuery]]></category>

		<guid isPermaLink="false">http://www.startupmonkeys.com/?p=34</guid>
		<description><![CDATA[First off, if you haven&#8217;t installed both Yahoo YSlow! and Google Page Speed then bookmark this page and come back to it later. Chances are you have bigger fish to fry on the rest of your website. Now if you read my post on AB Testing with Google Website Optimizer, you probably caught on that [...]]]></description>
			<content:encoded><![CDATA[<p>First off, if you haven&#8217;t installed both <a href="http://developer.yahoo.com/yslow/">Yahoo YSlow!</a> and <a href="http://code.google.com/speed/page-speed/">Google Page Speed</a> then bookmark this page and come back to it later.  Chances are you have bigger fish to fry on the rest of your website.</p>
<p>Now if you read my post on <a href="http://www.startupmonkeys.com/2010/01/rails-plugin-for-google-website-optimizer-for-ab-tests-dynamic-content-experiments-and-no-flickering/">AB Testing with Google Website Optimizer</a>, you probably caught on that &lt;script type=&#8221;text/javascript&#8221;&gt; tags block rendering of content on your pages.</p>
<p>Now that you&#8217;re using these tools, your noticing some of these AdSense calls are taking 300ms to render &#8211; sometimes multiple SECONDS&#8230;wait but when I combine those two facts that means my content below the ads isn&#8217;t rendering yet&#8230;</p>
<p><strong>WTF Google!</strong></p>
<p>The good news, is, it&#8217;s possible to unblock the rendering of your page&#8217;s content.  The bad news, is, it involves repositioning the ads after the page has been loaded (a little bit of a flicker).</p>
<p><strong>Solution:</strong></p>
<p>The mechanics of the solution involve three pieces:</p>
<ol>
<li>A placeholder &lt;div&gt; where the ad should be displayed</li>
<li>Shoving the actual ad elements as one of the last items in your HTML document (so everything else gets rendered first)</li>
<li>jQuery code to reposition the ad back into its placeholder slot</li>
</ol>
<p><strong>Inline Content:<br />
</strong></p>
<p>The container for your ads should be pretty straightforward, you know what size ad unit you&#8217;re including so block out the section of your page:</p>
<pre class="html">   &lt;div class="detail_ad_bottom"&gt;
      &lt;div id="fmad_placeholder_Detail_Page_Bottom" style="height: 90px; line-height: 90px;"&gt;&amp;nbsp;&lt;/div&gt;
   &lt;/div&gt;</pre>
<p><strong>Bottom Page Content:<br />
</strong></p>
<p>Your moving div that actually calls AdSense/Ad Manager code:</p>
<pre class="html">&lt;div id="fmads_Detail_Page_Bottom" style="display: none; text-align:center;"&gt;
  &lt;script type="text/javascript"&gt;
   //&lt;![CDATA[
     GA_googleFillSlot("Detail_Page_Bottom");
   //]]&gt;
   &lt;/script&gt;
&lt;/div&gt;</pre>
<p>jQuery Magic to reposition your ads back to where they should be:</p>
<pre class="html">&lt;script type="text/javascript"&gt;
//&lt;![CDATA[
  var fmPositionGoogleAdsInit = false;
  function fmPositionGoogleAd(ad_name) {
    var fun = (function () {
      var placeholder = $('#fmad_placeholder_'+ad_name);
      var ad = $('#fmads_'+ad_name);

      if(placeholder &amp;&amp; ad) {
        var pos = placeholder.offset();
        ad.css({
          "display": "block",
          "position": "absolute",
          "left": pos.left+"px",
          "top": pos.top+"px",
          "width": placeholder.width()
        });
      }
    });

    if(!fmPositionGoogleAdsInit) {
      $(window).resize(fun);
      $(document).ready(fun);
    }
    fun();                     

    fmPositionGoogleAdsInit = true;
  };

  function fmPositionGoogleAds() {
    // List all of your ad slots
    fmPositionGoogleAd('Detail_Page_Bottom');
  };

  fmPositionGoogleAds();
//]]&gt;
&lt;/script&gt;</pre>
<p><strong>Gotchas:<br />
</strong></p>
<p>Now, if you have any other dynamic content on the pages, you may have to manually reposition the ads again.  This is simply done by calling:</p>
<pre class="javascript">fmPositionGoogleAds();</pre>
<p>Hope that helps!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.startupmonkeys.com/2010/04/put-google-adsense-into-the-corner-let-your-page-render-first-using-jquery/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Ruby Script to Extract Google Analytics Keywords, Landing Page, Medium Entrances to CSV/TSV format</title>
		<link>http://www.startupmonkeys.com/2010/04/ruby-script-to-extract-google-analytics-keywords-landing-page-medium-entrances-to-csvtsv-format/</link>
		<comments>http://www.startupmonkeys.com/2010/04/ruby-script-to-extract-google-analytics-keywords-landing-page-medium-entrances-to-csvtsv-format/#comments</comments>
		<pubDate>Mon, 19 Apr 2010 20:36:49 +0000</pubDate>
		<dc:creator>eric</dc:creator>
				<category><![CDATA[Tips and Tricks]]></category>
		<category><![CDATA[analytics]]></category>
		<category><![CDATA[extract]]></category>
		<category><![CDATA[google analytics]]></category>
		<category><![CDATA[sql]]></category>

		<guid isPermaLink="false">http://www.startupmonkeys.com/?p=26</guid>
		<description><![CDATA[I&#8217;ve had versions of this script hacked together before.  In fact, if you search for &#8220;Google Analytics Data Extractor&#8221; the #1 result is a google groups posting I made in a former life for a C# program I developed: http://groups.google.com/group/analytics-help-misc/browse_thread/thread/d2ad6ddf3d73e511 There&#8217;s now a couple of ruby gems out there for handling the Google Analytics API [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve had versions of this script hacked together before.  In fact, if you search for &#8220;Google Analytics Data Extractor&#8221; the #1 result is a google groups posting I made in a former life for a C# program I developed: <a href="http://groups.google.com/group/analytics-help-misc/browse_thread/thread/d2ad6ddf3d73e511">http://groups.google.com/group/analytics-help-misc/browse_thread/thread/d2ad6ddf3d73e511</a></p>
<p>There&#8217;s now a <a href="http://code.google.com/apis/analytics/docs/gdata/gdataLibraries.html">couple of ruby gems out there</a> for handling the <a href="http://code.google.com/apis/analytics/docs/">Google Analytics API</a></p>
<p>One of them I&#8217;ve been doing some light-contributing to, including the pagination support (automatically grab all results in 10,000 result chunks)</p>
<p>I pushed a couple of changes up today at: <a href="http://github.com/er1c/gattica">http://github.com/er1c/gattica</a> that hopefully will get merged into the main trunk soon (<a href="http://github.com/activenetwork/gattica">http://github.com/activenetwork/gattica</a>)</p>
<p>One of the more useful uses of the library is to extract your Organic Search Results Keywords from Google Analytics.  We&#8217;re using this at <a href="http://frugalmechanic.com">Frugal Mechanic</a> to see how people search for <a href="http://frugalmechanic.com/auto-parts">Auto Parts</a>.</p>
<pre class="ruby">require 'rubygems'
require 'gattica'
require 'fastercsv'

ga_profile = "" #Enter your Profile Here
start_date = Date.new(2009, 1, 1)
end_date = Date.new(2009, 12, 31)
file_path = "data" # Directory Needs to exist

puts "Google Username: "
u = gets.chomp
raise "bad username" unless !u.empty?
puts "Google Password: "
system "stty -echo"
pw = gets.chomp
system "stty echo"
raise "bad password" unless !pw.empty?

class ExtractKeywords
  def initialize(email,password)
    @gs = Gattica.new({:email =&gt; email, :password =&gt; password})
  end

  def get_accounts
    results = []
    @gs.accounts.each{|account|
      profile = {}
      profile[:site_title] = account.title
      profile[:profile_id] = account.profile_id ## this is the id required for requests to the API
      profile[:account_name] = account.account_name
      profile[:account_id] = account.account_id
      profile[:web_property_id] = account.web_property_id
      results &lt;&lt; profile     }     return results   end   def connect_profile(profile)     @gs.profile_id = profile[:profile_id]   end   def connect_profile_id(profile_id)     @gs.profile_id = profile_id   end   def get_keywords(start_date = nil, end_date = Date.today)     results = []     csv_data = @gs.get({         :start_date =&gt; (start_date || (end_date - 365)).to_s,
        :end_date =&gt; end_date.to_s,
        :dimensions =&gt; ["medium", "keyword", "landingPagePath"],
        :metrics =&gt; "entrances",
        :sort =&gt; "-entrances",
        :page =&gt; true}).to_csv(:long)

    return FasterCSV.parse(csv_data, :headers =&gt; true)
  end

end

gs = ExtractKeywords.new(u, pw)
gs.connect_profile_id(ga_profile)

(start_date .. end_date).each { |date|
  file = "#{file_path}/medium_keyword_landingpage_visits_#{date.strftime('%Y-%m-%d')}.csv"
  next if File::exists?( file )

  FasterCSV.open(file, "w") do |csv|
    csv &lt;&lt; ["medium", "keyword", "landingPagePath", "entrances"]
    keywords = gs.get_keywords(date, date)
    keywords.each{|row|
      csv &lt;&lt; [row["medium"], row["keyword"], row["landingPagePath"], row["entrances"]]
    }

  end
}
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.startupmonkeys.com/2010/04/ruby-script-to-extract-google-analytics-keywords-landing-page-medium-entrances-to-csvtsv-format/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Cassandra @ Frugal Mechanic</title>
		<link>http://www.startupmonkeys.com/2010/03/cassandra-frugal-mechanic/</link>
		<comments>http://www.startupmonkeys.com/2010/03/cassandra-frugal-mechanic/#comments</comments>
		<pubDate>Thu, 11 Mar 2010 16:55:52 +0000</pubDate>
		<dc:creator>eric</dc:creator>
				<category><![CDATA[Databases]]></category>
		<category><![CDATA[cassandra]]></category>
		<category><![CDATA[nosql]]></category>

		<guid isPermaLink="false">http://www.startupmonkeys.com/?p=19</guid>
		<description><![CDATA[Last night I gave at talk at Seattle Tech Startups Meetup on Cassandra and how we use it at Frugal Mechanic (although in a very High-Level and more of a case study on how we came to choose Cassandra for our specific needs.  Thanks for the great turn-out and some great questions. One of the [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://incubator.apache.org/cassandra/"><img class="aligncenter size-medium wp-image-20" title="cassandra_logo" src="http://www.startupmonkeys.com/wp-content/uploads/cassandra_logo-300x60.png" alt="" width="300" height="60" /></a></p>
<p>Last night I gave at talk at <a href="http://seattletechstartups.com/doku.php">Seattle Tech Startups Meetup</a> on Cassandra and how we use it at Frugal Mechanic (although in a very High-Level and more of a case study on how we came to choose Cassandra for our specific needs.  Thanks for the great turn-out and some great questions.</p>
<p>One of the most useful slides is my resource slide.  Here&#8217;s a summary of all of the great talks/Cassandra slide decks I&#8217;ve come across:</p>
<ul>
<li>NoSQL West Intro: <a href="http://cloudera-todd.s3.amazonaws.com/nosql.pdf">http://cloudera-todd.s3.amazonaws.com/nosql.pdf</a> (Video: <a href="http://www.vimeo.com/5145059">http://www.vimeo.com/5145059</a>)</li>
<li>Cassandra Talk (Rackspace): Vid+PPT: <a href="http://www.parleys.com/">http://www.parleys.com/#st=5&amp;id=1866</a></li>
<li>Cassandra Talk (Facebook): PPT:  <a href="http://static.last.fm/johan/nosql-20090611/cassandra_nosql.ppt">http://static.last.fm/johan/nosql-20090611/cassandra_nosql.ppt</a> Video: <a href="http://vimeo.com/5185526">http://vimeo.com/5185526</a></li>
<li>Cassandra Talk (Digg): <a href="http://nosql.mypopescu.com/post/334198583/presentation-cassandra-in-production-digg-arin">http://nosql.mypopescu.com/post/334198583/presentation-cassandra-in-production-digg-arin</a></li>
<li>WTF is a Super Column: <a href="http://arin.s3.amazonaws.com/pub/docs/WTF-is-a-SuperColumn.pdf">http://arin.s3.amazonaws.com/pub/docs/WTF-is-a-SuperColumn.pdf</a></li>
<li>Get Up and Running w/Cassandra: <a href="http://blog.evanweaver.com/articles/2009/07/06/up-and-running-with-cassandra/">http://blog.evanweaver.com/articles/2009/07/06/up-and-running-with-cassandra/</a></li>
<li>Cassandra: BigTable + Dynamo: <a href="http://assets.en.oreilly.com/1/event/27/Cassandra_%20Open%20Source%20Bigtable%20+%20Dynamo%20Presentation.pdf">http://assets.en.oreilly.com/1/event/27/Cassandra_%20Open%20Source%20Bigtable%20+%20Dynamo%20Presentation.pdf</a></li>
</ul>
<div id="__ss_3399002" style="width: 425px; margin-left: auto; margin-right: auto;"><strong style="display: block; margin: 12px 0 4px;"><a title="NoSQL Cassandra Talk for Seattle Tech Startups 3-10-10" href="http://www.slideshare.net/egpeters/nosql-cassandra-talk-for-seattle-tech-startups-31010-3399002">NoSQL Cassandra Talk for Seattle Tech Startups 3-10-10</a></strong><object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="425" height="355" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="allowFullScreen" value="true" /><param name="allowScriptAccess" value="always" /><param name="src" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=cassandranosqlep-100311104819-phpapp01&amp;rel=0&amp;stripped_title=nosql-cassandra-talk-for-seattle-tech-startups-31010-3399002" /><param name="allowfullscreen" value="true" /><embed type="application/x-shockwave-flash" width="425" height="355" src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=cassandranosqlep-100311104819-phpapp01&amp;rel=0&amp;stripped_title=nosql-cassandra-talk-for-seattle-tech-startups-31010-3399002" allowscriptaccess="always" allowfullscreen="true"></embed></object></p>
<div style="padding: 5px 0 12px;">View more <a href="http://www.slideshare.net/">presentations</a> from <a href="http://www.slideshare.net/egpeters">egpeters</a>.</div>
</div>
]]></content:encoded>
			<wfw:commentRss>http://www.startupmonkeys.com/2010/03/cassandra-frugal-mechanic/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Rails Plugin for Google Website Optimizer for AB Tests &#8211; Dynamic Content Experiments and No Flickering!</title>
		<link>http://www.startupmonkeys.com/2010/01/rails-plugin-for-google-website-optimizer-for-ab-tests-dynamic-content-experiments-and-no-flickering/</link>
		<comments>http://www.startupmonkeys.com/2010/01/rails-plugin-for-google-website-optimizer-for-ab-tests-dynamic-content-experiments-and-no-flickering/#comments</comments>
		<pubDate>Tue, 19 Jan 2010 03:29:23 +0000</pubDate>
		<dc:creator>eric</dc:creator>
				<category><![CDATA[Tips and Tricks]]></category>
		<category><![CDATA[ab testing]]></category>
		<category><![CDATA[analytics]]></category>
		<category><![CDATA[multivariate]]></category>
		<category><![CDATA[plugin]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[website optimizer]]></category>

		<guid isPermaLink="false">http://www.startupmonkeys.com/?p=4</guid>
		<description><![CDATA[Note: If you don&#8217;t care how this plugin works and just want to see/get the code, it&#8217;s available at github: http://github.com/tpunder/gwo There&#8217;s a ton of reasons to reasons to invest in conversion rate increases.  If you&#8217;re spending $1 to acquire 10 visits, and you can get 2 instead of 1 to convert its like putting [...]]]></description>
			<content:encoded><![CDATA[<p><em><strong>Note:</strong> If you don&#8217;t care how this plugin works and just want to see/get the code, it&#8217;s available at github: <a href="http://github.com/tpunder/gwo">http://github.com/tpunder/gwo</a></em></p>
<p>There&#8217;s a ton of reasons to reasons to invest in conversion rate increases.  If you&#8217;re spending $1 to acquire 10 visits, and you can get 2 instead of 1 to convert its like putting an extra dollar in your pocket.   A couple days ago Rand Fishkin from SEOMoz even said &#8220;<a href="http://www.seomoz.org/blog/8-predictions-for-seo-in-2010">2010 is the Year of Conversion Rate Optimization</a>&#8220;</p>
<p>Google Website Optimizer&#8217;s Homepage will give you a <a href="http://www.google.com/websiteoptimizer" target="_blank">number of reasons to use their product</a> My biggest reasons is its <strong>FREE!</strong></p>
<p>Last March, we met with <a href="http://gwotricks.com">Eric Vasilik</a> with the Google Website Optimizer Team in the Seattle Google Office about some hang-ups we had trying to A/B Test functionality and integrate Google Website Optimizer:</p>
<ol>
<li><strong>Speed.</strong> The out-of-the-box Google Website Optimizer code does something like this:
<ol>
<li>Download/Initialize GWO Code</li>
<li>Identify which experiment/treatment group the user should see</li>
<li>Download each of the content sections that should change and</li>
<li>Use javascript to dynamically update each of the content sections of the page (FLICKER!)</li>
</ol>
</li>
<li><strong>Static Content.</strong> Since you must store the HTML fragments in GWO for the service to return back the appropriate multivariate combination to show to users, the HTML fragments must be static.</li>
<li><strong>Maintenance.</strong> This ties back to our #2 reason, we would have to upload each of the HTML fragments into the GWO tool so they could be tested.  Since we have to code them locally to make sure they function &amp; display correctly anyhow, it effectively means we need to duplicate the code to GWO.</li>
</ol>
<p>The solution?  Leveraging a few very very clever Javascript/HTML tricks this is what we&#8217;re doing for our <a href="http://frugalmechanic.com">Auto Parts Price Comparison</a> site.</p>
<p><strong>Background:</strong></p>
<p>A vanilla GWO Implementation operates similar to:</p>
<p><a href="http://www.startupmonkeys.com/wp-content/uploads/GWO-Basic1.png"><img class="aligncenter size-medium wp-image-12" title="GWO Basic" src="http://www.startupmonkeys.com/wp-content/uploads/GWO-Basic1-300x111.png" alt="" width="300" height="111" /></a></p>
<p>Tricked out solution looks like:</p>
<p><a href="http://www.startupmonkeys.com/wp-content/uploads/GWO-Advanced.png"><img class="aligncenter size-medium wp-image-8" title="GWO Advanced" src="http://www.startupmonkeys.com/wp-content/uploads/GWO-Advanced-300x119.png" alt="" width="300" height="119" /></a></p>
<p>Why is it clever?  Lets walk through each of the scenarios for the GWO Variations.</p>
<p><strong>Scenario 1: No Javascript Support</strong></p>
<blockquote><p><span style="text-decoration: line-through;">&lt;!&#8211; Section1 &#8211; Default content &#8211;&gt;<br />
&lt;script&gt;<br />
var GWO_Section1 = utmx(&#8220;variation_number&#8221;, &#8220;Section1&#8243;);<br />
if (GWO_Section1 != undefined &amp;&amp; GWO_Section1 != 0) document.write(&#8216;&lt;no&#8217; + &#8216;script&gt;&#8217;);<br />
&lt;/script&gt;<br />
</span>Default content &#8211; shown by default&lt;br&gt;<br />
&lt;/noscript&gt;</p>
</blockquote>
<p>Browser ignores everything inside &lt;script&gt; tags and ignores the &lt;/noscript&gt;</p>
<blockquote><p><span style="text-decoration: line-through;">&lt;!&#8211; Section1 &#8211; Variation 1 &#8211;&gt;<br />
&lt;script&gt;<br />
if (GWO_Section1 == 1) document.write(&#8216;&lt;/noscript a=&#8221;&#8216;);<br />
&lt;/script&gt;&lt;!&#8211;&#8221;&gt;<br />
Alternative content 1&lt;br&gt;<br />
&lt;script&gt;document.write(&#8216;&lt;&#8217;+'!&#8217;+'-&#8217;+'-&#8217;)&lt;/script&gt;&#8211;&gt;</span></p>
<p>&lt;!&#8211; Section1 &#8211; Variation 2 &#8211;&gt;<br />
&lt;script&gt;<br />
if (GWO_Section1 == 2) document.write(&#8216;&lt;/noscript a=&#8221;&#8216;);<br />
&lt;/script&gt;&lt;!&#8211;&#8221;&gt;<br />
Alternative content 2&lt;br&gt;<br />
&lt;script&gt;document.write(&#8216;&lt;&#8217;+'!&#8217;+'-&#8217;+'-&#8217;)&lt;/script&gt;&#8211;&gt;</p>
</blockquote>
<p><strong>Scenario 2: Browser Supports Javascript &#8211; Variation == Control/Default Treatment</strong></p>
<blockquote><p><span style="text-decoration: line-through;">&lt;!&#8211; Section1 &#8211; Default content &#8211;&gt;<br />
&lt;script&gt;<br />
var GWO_Section1 = utmx(&#8220;variation_number&#8221;, &#8220;Section1&#8243;);<br />
if (GWO_Section1 != undefined &amp;&amp; GWO_Section1 != 0) document.write(&#8216;&lt;no&#8217; + &#8216;script&gt;&#8217;);<br />
&lt;/script&gt;<br />
Default content &#8211; shown by default&lt;br&gt;<br />
&lt;/noscript&gt;</span></p>
</blockquote>
<p>Browser evaluates GWO_Section == 0 so the document.write(&lt;noscript&gt;) is NOT executed resulting HTML code is:</p>
<blockquote><p>Default content &#8211; shown by default&lt;br&gt;<br />
&lt;/noscript&gt;</p>
</blockquote>
<p>Browser ignores the &lt;/noscript&gt;</p>
<blockquote><p><span style="text-decoration: line-through;">&lt;!&#8211; Section1 &#8211; Variation 1 &#8211;&gt;<br />
&lt;script&gt;<br />
if (GWO_Section1 == 1) document.write(&#8216;&lt;/noscript a=&#8221;&#8216;);<br />
&lt;/script&gt;&lt;!&#8211;&#8221;&gt;<br />
Alternative content 1&lt;br&gt;<br />
&lt;script&gt;document.write(&#8216;&lt;&#8217;+'!&#8217;+'-&#8217;+'-&#8217;)&lt;/script&gt;&#8211;&gt;</span></p>
</blockquote>
<p>Since GWO_Section == 0, the document.write(&#8216;&lt;/noscript is not executed, but however the 2nd &lt;script&gt;document.write IS still executed resulting in</p>
<blockquote><p>&lt;!&#8211;&#8221;&gt;<br />
Alternative content 1&lt;br&gt;<br />
&lt;!&#8211;&lt;/script&gt;&#8211;&gt;</p>
</blockquote>
<p>The HTML comment starts with the first &lt;!&#8211; and keeps going until &#8211;&gt; hits (the browser doesn&#8217;t even try to interpret the DOM inside the HTML comment &#8211; making this the most efficient way to &#8220;throw away&#8221; code from even being parsed by the HTML engine.  The variation 1 never gets displayed.</p>
<p><strong>Scenario 3: Browser Supports Javascript &#8211; Variation == Experiment Treatment</strong></p>
<blockquote><p><span style="text-decoration: line-through;">&lt;!&#8211; Section1 &#8211; Default content &#8211;&gt;<br />
&lt;script&gt;<br />
var GWO_Section1 = utmx(&#8220;variation_number&#8221;, &#8220;Section1&#8243;);<br />
if (GWO_Section1 != undefined &amp;&amp; GWO_Section1 != 0) document.write(&#8216;&lt;no&#8217; + &#8216;script&gt;&#8217;);<br />
&lt;/script&gt;<br />
Default content &#8211; shown by default&lt;br&gt;<br />
&lt;/noscript&gt;</span></p>
</blockquote>
<p>Browser evaluates GWO_Section1 == 1 so it DOES print the &lt;noscript&gt; resulting in:</p>
<blockquote><p>&lt;noscript&gt;<br />
Default content &#8211; show by default&lt;br&gt;<br />
&lt;/noscript&gt;</p>
</blockquote>
<p>Browser starts at the first &lt;noscript&gt; and ignores everything until the ending &lt;/noscript&gt; &#8211; thereby ignoring the control treatment&#8217;s HTML</p>
<blockquote><p><span style="text-decoration: line-through;">&lt;!&#8211; Section1 &#8211; Variation 1 &#8211;&gt;<br />
&lt;script&gt;<br />
if (GWO_Section1 == 1) document.write(&#8216;&lt;/noscript a=&#8221;&#8216;);<br />
&lt;/script&gt;&lt;!&#8211;&#8221;&gt;<br />
Alternative content 1&lt;br&gt;<br />
&lt;script&gt;document.write(&#8216;&lt;&#8217;+'!&#8217;+'-&#8217;+'-&#8217;)&lt;/script&gt;&#8211;&gt;</span></p>
</blockquote>
<p>GWO_Section1 evaluates to == 1 and the document.write prints out resulting in:</p>
<blockquote><p>&lt;/noscript a=&#8221;<br />
&lt;!&#8211;&#8221;&gt;<br />
Alternative content 1&lt;br&gt;<br />
&lt;!&#8211;&lt;/script&gt;&#8211;&gt;</p>
</blockquote>
<p>There&#8217;s two clever things in this one.  The first is the a=&#8221; starts to create an HTML attribute that &#8220;ingests&#8221; the dangling &lt;!&#8211;.  The second, is the &lt;!&#8211;&lt;/script&gt;&#8211;&gt; is a complete HTML comment, so there isn&#8217;t a dangling &lt;/script&gt; tag floating in the HTML.  Poof the Web Browser is now showing the Treatment HTML.</p>
<p>After we met with Eric he posted a link and an explanation for this trick at <a href="http://www.gwotricks.com/2009/05/server-side-dynamic-section-variations.html" target="_blank">http://www.gwotricks.com/2009/05/server-side-dynamic-section-variations.html</a></p>
<p>We have a Rails Plugin that encapsulates all of this logic pretty well &#8211; so you don&#8217;t have to worry about all of the javascript hacks:</p>
<p><a href="http://github.com/tpunder/gwo" target="_blank">http://github.com/tpunder/gwo</a></p>
<p>Happy A/B + Multivariate Testing!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.startupmonkeys.com/2010/01/rails-plugin-for-google-website-optimizer-for-ab-tests-dynamic-content-experiments-and-no-flickering/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
