<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss 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/" version="2.0">

<channel>
	<title>Jb</title>
	
	<link>http://jb55.com</link>
	<description>I code</description>
	<lastBuildDate>Sun, 02 May 2010 17:41:56 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.feedburner.com/jb55" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="jb55" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><item>
		<title>Customizing snipMate to reduce repetitive tasks</title>
		<link>http://jb55.com/204/customizing-snipmate-to-reduce-repetitive-tasks/</link>
		<comments>http://jb55.com/204/customizing-snipmate-to-reduce-repetitive-tasks/#comments</comments>
		<pubDate>Sun, 02 May 2010 17:41:08 +0000</pubDate>
		<dc:creator>jb55</dc:creator>
				<category><![CDATA[vim]]></category>
		<category><![CDATA[snipmate]]></category>

		<guid isPermaLink="false">http://jb55.com/?p=204</guid>
		<description><![CDATA[snipMate is a vim plugin that allows you to paste snippets of code based on a keyword. To see what I mean, I put together a video that demonstrates how it works:

Most of the c++ snippets that were shown in the video were customized to my needs. snipMate has a bunch of basic snippets for [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.vim.org/scripts/script.php?script_id=2540">snipMate</a> is a vim plugin that allows you to paste snippets of code based on a keyword. To see what I mean, I put together a video that demonstrates how it works:</p>
<p><object width="600" height="373"><param name="movie" value="http://www.youtube.com/v/MQy2rVOf-z0&#038;hl=en_US&#038;fs=1&#038;hd=1"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/MQy2rVOf-z0&#038;hl=en_US&#038;fs=1&#038;hd=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="600" height="373"></embed></object></p>
<p>Most of the c++ snippets that were shown in the video were customized to my needs. snipMate has a bunch of basic snippets for c, c++, python, etc; but to get the most power out of snipMate you&#8217;ll want to start creating your own snippets.</p>
<p>Before we start, we&#8217;ll want to know where to put our snippets. Snippets are loaded from </p>
<p><code>~/.vim/snippets/&lt;syntax&gt;.snippets</code> </p>
<p>where &lt;syntax&gt; is the context in which they are loaded. For example if the current document was loaded as a c++ file, it will automatically load the cpp.snippets, other snippet files will not be loaded (with the exception of the globals snippets file and the c.snippets in the case of the c++ file).</p>
<h3>Creating your first snippet, an example</h3>
<p>Open up the snippets file for the language you want to add snippets for, for this example I&#8217;ll using cpp.snippets.</p>
<p>If you&#8217;ve ever written code in C++ before, you may have experienced the pain of iterating over STL containers. Typing the iterator for loop has probably accumulated days of wasted time in my life. Now I can have my revenge:</p>
<p><code class="cpp">for (std::vector&lt;int&gt;::const_iterator i = c.begin(); i != c.end(); ++i);</code><br />
becomes<br/></p>
<pre>
<code class="cpp">
snippet iter
  for (${1:std::vector}&lt;${2:int}&gt;::${3:const_iterator} ${4:i} = ${5:c}.begin(); $4 != $5.end(); ++$4) {
    ${6}
  }
</code>
</pre>
<p>Taking a look at the original code and the snippet, you should already have some idea how these snippets work.</p>
<p><b>Placeholder</b>: a tab location, this is where you set the placeholder value</p>
<pre>
<code class="cpp">${&lt;placeholder&gt;:&lt;default_value&gt;}
example: ${1:default}
</code>
</pre>
<p><b>Placeholder Value</b>: This value gets automatically updated when it&#8217;s changed<br />
<code class="cpp">$&lt;placeholder&gt;<br />
example: $1</code></p>
<p>To test this out, open a c++ file and type:</p>
<p><code>iter&lt;Tab&gt;</code></p>
<p>It should expand the entire for iterator, allowing to you change types and tab between things you might need to change. Another advantage of this approach is that it reduces the amount of syntax errors you make, since there is a restricted set of things to change for a given snippet.</p>
<h3>Using snippets for makefile templates</h3>
<p>When starting new projects, I find myself looking for old projects of mine, copying the makefiles over, and then changing them so it works for my new project. While this works, lets see if we can take advantage of snipMate to remember templates for us! You can apply this idea to anything really.</p>
<p>I use cmake for alot of my projects, so I created a cmake.snippets and added this:</p>
<pre>
<code>snippet base
	CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
	PROJECT(${1:ProjectName})

	FIND_PACKAGE(${2:LIBRARY})

	INCLUDE_DIRECTORIES(
		${$2_INCLUDE_DIR}
	)

	ADD_SUBDIRECTORY(${3:src})

	ADD_EXECUTABLE($1
	)

	TARGET_LINK_LIBRARIES($1
		${$2_LIBRARIES}
	)

snippet glob
	FILE(GLOB ${1:PROJ_SRCS} *.${2:cpp})
</code>
</pre>
<p>Pretty simple, I plan on adding a lot more cmake directives. The best part of snipMate is now I don&#8217;t have to remember every single cmake construct, I just need to remember simple keywords. As I said before, this idea can apply to any language, script, markup, etc.</p>
<h3>More advanced snippets</h3>
<p>To extend snipMate&#8217;s power further, it can execute vim expressions in a snippet using vim&#8217;s &#8216;eval()&#8217;.</p>
<p><b>Date example</b></p>
<pre>
<code>snippet date
	`strftime("%Y-%m-%d")`
</code>
</pre>
<p>It would be interesting to see what other snippets people think of, comment and share!</p>
]]></content:encoded>
			<wfw:commentRss>http://jb55.com/204/customizing-snipmate-to-reduce-repetitive-tasks/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Using MongoDB as a simple message board backend</title>
		<link>http://jb55.com/173/using-mongodb-as-a-simple-message-board-backend/</link>
		<comments>http://jb55.com/173/using-mongodb-as-a-simple-message-board-backend/#comments</comments>
		<pubDate>Fri, 25 Dec 2009 06:26:13 +0000</pubDate>
		<dc:creator>jb55</dc:creator>
				<category><![CDATA[MongoDB]]></category>
		<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://jb55.com/?p=173</guid>
		<description><![CDATA[I&#8217;ve been implemented a wakaba-like message board using MongoDB as a storage backend and it has been a real breeze so far. Most interactions with the database have been one liners in Python and there is little work involved with inserting new threads or replies. I&#8217;d have to say the worse part of the whole [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been implemented a wakaba-like message board using MongoDB as a storage backend and it has been a real breeze so far. Most interactions with the database have been one liners in Python and there is little work involved with inserting new threads or replies. I&#8217;d have to say the worse part of the whole ordeal so far is the lack of integration with existing Python web frameworks. For that reason I decided not to use Django, which seems tightly coupled to sql solutions. I opted for a Werkzeug + Jinja2 + WTForms package called <a href="http://glashammer.org">Glashammer</a> which has worked quite well for me so far. Anyway, on to MongoDB&#8230;</p>
<h3>Database structure</h3>
<p>With my SQL habits my initial plan was to normalize all posts, with all threads and replies as MongoDB objects within a collection. It turns out this isn&#8217;t the best way to go about it. If the thread has a large amount of replies, the referenced lookup for each reply object is inefficient, and isn&#8217;t the MongoDBish way of doing things. What I found works best is a tree structure or a simple embedded array of replies:</p>
<pre><code class="javascript">{
  _id: ObjectId
  name: "jb55",
  topic: "What is this I don't even",
  msg: "I have important things to say",
  replies: [
    { name: "troll", msg: "tl;dr" },
    { name: "commentator", msg: "cool story bro" }
  ]
}</code></pre>
<p>As you can see replies are embedded in the thread object. This makes pulling the thread from the database very quick, since the data will be together on the disk. You&#8217;ll probably want the thread and replies to have a reference to a user object, or a simple email depending on how sophisticated you want to go. For the purposes of my 4chan/wakaba-like forum it made sense not to store a user reference.</p>
<h3>Some common tasks with PyMongo</h3>
<p>All interactions between Python and MongoDB are pretty smooth and it doesn&#8217;t take much work to get up and running with insertions and updates.</p>
<p><strong>Creating a new thread</strong></p>
<pre><code class="javascript">db.threads.insert(
{'name': 'jb55', 'topic': 'My important topic', 'msg': 'blah blah'})</code></pre>
<p><strong>Adding new replies to a thread</strong></p>
<pre><code class="javascript">reply = {'name': 'jb55', 'msg': 'This is my reply'}
db.threads.update({'_id': thread_id}, {'$push': {'replies': reply}})</code></pre>
<p><strong>Getting and displaying a thread</strong></p>
<pre><code class="javascript">thread = db.threads.find({'_id': thread_id})
data["thread"] = thread
return render_to_response('template.htm', **data)</code></pre>
<p>Easy isn&#8217;t it? No schemas, no hastle. In the interest of keeping this article short and sweet I think I&#8217;ll leave it at that for now. Try it out if you haven&#8217;t yet, it&#8217;s fun!</p>
]]></content:encoded>
			<wfw:commentRss>http://jb55.com/173/using-mongodb-as-a-simple-message-board-backend/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Asynchronous Go API idioms</title>
		<link>http://jb55.com/132/asynchronous-go-api-idioms/</link>
		<comments>http://jb55.com/132/asynchronous-go-api-idioms/#comments</comments>
		<pubDate>Sat, 14 Nov 2009 11:24:53 +0000</pubDate>
		<dc:creator>jb55</dc:creator>
				<category><![CDATA[go]]></category>

		<guid isPermaLink="false">http://jb55.com/?p=132</guid>
		<description><![CDATA[After hacking on my Go Twitter API for the past couple of days I&#8217;ve started noticing some very cool ways of taking advantage of Go&#8217;s concurrency using goroutines and channels, and I thought I&#8217;d share them here.
Originally all of my API calls were synchronous. When you called GetStatus(1234) it made an HTTP request to the [...]]]></description>
			<content:encoded><![CDATA[<p>After hacking on my <a href="http://github.com/jb55/go-twitter">Go Twitter API</a> for the past couple of days I&#8217;ve started noticing some very cool ways of taking advantage of Go&#8217;s concurrency using goroutines and channels, and I thought I&#8217;d share them here.</p>
<p>Originally all of my API calls were synchronous. When you called GetStatus(1234) it made an HTTP request to the server and returned when it was finished. This is ok for most applications, but it definitely left much inflexibility in the API. I thought about leaving it like this, arguing that if the client wanted asynchronous calls it could wrap it with its own goroutine. After thinking about it, having the API handle the work of creating a goroutine isn&#8217;t too big of a deal and makes less work for the client.</p>
<h3>Return channels, not objects!</h3>
<p>This inherent property that all potentially blocking API calls be asynchronous has an interesting effect on how you interact with the API:</p>
<pre><code>api := twitter.NewApi();

statusChannel := api.GetStatus(12345); // asynchronous
pubTimelineChannel := api.GetPublicTimeline(); //asynchronous
status := &lt;-api.GetStatus(123456); // synchronous

fmt.Printf("synchronous status: %s, async status: %s\n",
       status.GetText(), (<-statusChannel).GetText()) ;
</code></pre>
<p>Neat! When the client wants regular synchronous calls, all they have to do is prefix the call with the channel receive operator! What the API does is instead of returning the Status object itself, it returns a <em>buffered</em> channel that receives a Status object. Making the channel buffered has the added benefit of not blocking the goroutine when its sending on the channel, allowing it to destroy itself after receiving the data. When a potentially blocking API call is made, the function creates a 1-sized buffered channel of the return type, launches a goroutine that takes the channel as a parameter, and then returns the channel instantly:</p>
<pre><code>func (self *Api) GetStatus(id int64) chan Status {
  response := make(chan Status, 1);
  go self.goGetStatus(id, response);
  return response;
}</code></pre>
<h3>Loosening the grip on the receive channel</h3>
<p>Awesome! We now have a robust way of getting single Twitter status messages both asynchronously and synchronously, but this isn't flexible enough. Here's why: Say we wanted a clean way of getting N status messages asynchronously. With our current setup we would need to call the function N times and manage N separate channels. Now say you wanted to receive and process the status messages the moment they arrived. To do this you would have to construct a select statement with N cases, this becomes unmanageable with large values of N.</p>
<p>The ideal solution is to let the client create and manage an N-sized buffered channel and pass it into our API. Then they could do something like this:</p>
<pre class="c"><code>const nIds = 10;
receiveChannel := make(chan twitter.Status, nIds);
api.SetReceiveChannel(receiveChannel);

var startId int64 = 5641609144;

for i := 0; i &lt; nIds; i++ {
  api.GetStatus(startId);
  startId++;
}

for i := 0; i &lt; nIds; i++ {
  // reads in status messages as they come in
  fmt.Printf("Status #%d: %v\n", i, &lt;-receiveChannel);
}</code></pre>
<h3>Error handling?</h3>
<p>A popular idiom used in the standard Go packages is <strong>_, ok :=</strong>. The basic idea is you return a bool or an os.Error as the second return value of the function to notify of a success or failure. This does not play well with my API, since returning multiple values from a function breaks method chaining and syncronous calls. I currently handle errors with the api function GetLastError which returns a os.Error, and the GetErrorChannel function which returns a channel that receives errors as they occur, which I found useful for tests and logging errors.</p>
<p>That's all for now, let me know what you think.</p>
]]></content:encoded>
			<wfw:commentRss>http://jb55.com/132/asynchronous-go-api-idioms/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Building and installing your first Go package</title>
		<link>http://jb55.com/114/building-and-installing-your-first-go-package/</link>
		<comments>http://jb55.com/114/building-and-installing-your-first-go-package/#comments</comments>
		<pubDate>Thu, 12 Nov 2009 04:59:45 +0000</pubDate>
		<dc:creator>jb55</dc:creator>
				<category><![CDATA[go]]></category>

		<guid isPermaLink="false">http://jb55.com/?p=114</guid>
		<description><![CDATA[Go packages are just a collection of .go files which share the same package directive at the top of each file. You can think of it as a namespace if your from the C++ crowd.
Go provides a couple of makefiles to make it easy to build and install your own Go packages.
Assuming you&#8217;ve followed the [...]]]></description>
			<content:encoded><![CDATA[<p>Go packages are just a collection of .go files which share the same package directive at the top of each file. You can think of it as a namespace if your from the C++ crowd.</p>
<p>Go provides a couple of makefiles to make it easy to build and install your own Go packages.</p>
<p>Assuming you&#8217;ve followed the instructions on their website and have your environment set up correctly, put this makefile in your package directory:</p>
<pre>
<code>include $(GOROOT)/src/Make.$(GOARCH)

TARG=mypackagename
GOFILES=\
        packagemodule.go\
        anothermodule.go\

include $(GOROOT)/src/Make.pkg
</code>
</pre>
<p>Type<br />
<code>make &#038;&#038; make install</code></p>
<p>and you&#8217;re done! You should now have a fully working package that can be imported into other Go projects!</p>
<p><em>Please note: Go was released yesterday at the time of this posting, it&#8217;s likely this process will change sometime in the future.</em></p>
]]></content:encoded>
			<wfw:commentRss>http://jb55.com/114/building-and-installing-your-first-go-package/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Learn you a Linux for great good!</title>
		<link>http://jb55.com/103/learn-you-a-linux-for-great-good/</link>
		<comments>http://jb55.com/103/learn-you-a-linux-for-great-good/#comments</comments>
		<pubDate>Fri, 23 Oct 2009 16:15:32 +0000</pubDate>
		<dc:creator>jb55</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://jb55.com/?p=103</guid>
		<description><![CDATA[One thing I&#8217;ve noticed in the past couple of years during my time at school and on my software engineering co-op is this: a large percentage of software engineering students simply do not know Linux! This may seem odd to programmers who couldn&#8217;t have it any other way, but if you&#8217;ve never used Linux before [...]]]></description>
			<content:encoded><![CDATA[<p>One thing I&#8217;ve noticed in the past couple of years during my time at school and on my software engineering co-op is this: a large percentage of software engineering students simply do not know Linux! This may seem odd to programmers who couldn&#8217;t have it any other way, but if you&#8217;ve never used Linux before you may be thinking: &#8220;What&#8217;s the big deal&#8221;?</p>
<h3>It&#8217;s fun!</h3>
<p>No, really. Humor me for a second if you&#8217;re skeptical. If you&#8217;re a person who enjoys problem solving and learning new things, Linux is a great environment to experiment and play. It also serves as a practical solution to many problems that you may have not been aware of, which I will talk about in the following sections.</p>
<h3>The setup</h3>
<p>One thing I&#8217;ve always recommended to fellow students and friends is to <strong>set up a dedicated linux server</strong> in your house. Chances are you&#8217;ve accumulated a few old PC&#8217;s (if you haven&#8217;t thrown them out yet) over the years. Any will do, you don&#8217;t need a beefy system to run Linux. For example, jgblue.com runs on 256MB of ram, thanks most in part to how lightweight many linux server distros tend to be. If you don&#8217;t have a spare computer lying around you can always pick up a cheap one from some bargain PC shop. As an alternative you can try installing <a href="http://www.vmware.com/">VMware</a> and running linux in that. I&#8217;m going to assume you have a dedicated linux box for the following sections though.</p>
<p>So what flavor of Linux should you get? <strong>Ubuntu</strong>. You can get it <a href="http://www.ubuntu.com/getubuntu/download-server">here</a> if this is your first time running a server this is your best choice.</p>
<p>I do not plan on making a comprehensive guide to installing and finding your way around Linux. There are plenty of guides on the internet which cover pretty much anything you might want to know. So comprehensive in fact I recommend following a simple rule of thumb: If you find yourself stuck on a problem, and you&#8217;re not sure where to go from there, Google it. Immediately. The minute you find in a situation where you cannot intuitively determine the answer within 10-20 seconds, just Google it. Google <em>knows</em>. That&#8217;s a generally a good rule of thumb for anything you find yourself hacking on.</p>
<h3>So what can I do now?</h3>
<p>Here are a few things which I recommend setting up to get the most out of your new Linux server:</p>
<p><strong>Register a domain name to access your server from anywhere</strong><br />
You may of heard of dynamic dns services such as dyndns.org which provides you with a free subdomain. I prefer a service called <a href="http://freedns.afraid.org/">FreeDNS</a>. FreeDNS allows you to do the same thing, except with your own domain instead. For example I use higgr.com as the domain name for my Linux server at my home.</p>
<p><strong>Set up web server for low traffic personal web pages</strong><br />
By installing a webserver such as Apache, you can easily host web pages for sharing files with your friends or hosting a personal blog. I wouldn&#8217;t recommend hosting high traffic websites though, since it will be running off your internet connection.</p>
<p><strong>Using it as an SSH proxy server for getting around firewalls and encrypting your browsing in public places</strong><br />
Using Putty on windows or SSH in Linux, you can use a SOCKS5 proxy to get your home internet connection to fetch websites and <a href="http://www.google.com/search?q=ssh+tunneling+putty">tunnel</a> them to your Putty or SSH client. This effectively beats most workplace or school firewalls, while encrypting all traffic to prevent snooping. </p>
<p><strong>Using it as a firewall and backdoor into your network</strong><br />
Your Linux box can sit between the internet and your network. By only allowing a few ports such as SSH, you can hide your entire network while giving you the ability to access your network publicly. For example, you can set up an SSH Tunnel to Remote Desktop into your desktop computer at home. I do this to add torrents to my desktop computer at home from work. Having remote desktop access to your computer at home from anywhere in the world by tunneling through higgr.com is very cool :). There&#8217;s even a remote desktop client with SSH capabilities for your iPhone, so you can access your computer from anywhere you have cell reception.</p>
<p>These are just a few of the practical applications I&#8217;ve employed since setting up my Linux server. The rest is up for you to discover. Enjoy!</p>
]]></content:encoded>
			<wfw:commentRss>http://jb55.com/103/learn-you-a-linux-for-great-good/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Static O3D Dependencies</title>
		<link>http://jb55.com/17/static-o3d-dependencies/</link>
		<comments>http://jb55.com/17/static-o3d-dependencies/#comments</comments>
		<pubDate>Thu, 08 Oct 2009 14:47:51 +0000</pubDate>
		<dc:creator>jb55</dc:creator>
				<category><![CDATA[javascript]]></category>
		<category><![CDATA[o3d]]></category>

		<guid isPermaLink="false">http://jb55.com/?p=17</guid>
		<description><![CDATA[One thing you might notice when writing your first O3D javascript application are the dependencies which are included at the top of your page:
o3djs.require('o3djs.util');
o3djs.require('o3djs.math');
o3djs.require('o3djs.rendergraph');
o3djs.require('o3djs.primitives');
o3djs.require('o3djs.effect');
o3djs.require('o3djs.io');
o3djs.require('o3djs.arcball');
o3djs.require('o3djs.material');
o3djs.require('o3djs.quaternion');
O3D will use these calls to grab the corresponding script using an XHR request, then write them to the DOM if they haven&#8217;t been written yet. This dynamic resolution of dependencies is [...]]]></description>
			<content:encoded><![CDATA[<p>One thing you might notice when writing your first O3D javascript application are the dependencies which are included at the top of your page:</p>
<p><code class="javascript">o3djs.require('o3djs.util');<br />
o3djs.require('o3djs.math');<br />
o3djs.require('o3djs.rendergraph');<br />
o3djs.require('o3djs.primitives');<br />
o3djs.require('o3djs.effect');<br />
o3djs.require('o3djs.io');<br />
o3djs.require('o3djs.arcball');<br />
o3djs.require('o3djs.material');<br />
o3djs.require('o3djs.quaternion');</code></p>
<p>O3D will use these calls to grab the corresponding script using an XHR request, then write them to the DOM if they haven&#8217;t been written yet. This dynamic resolution of dependencies is nice for development, since you can add and remove &#8216;headers&#8217; on the fly without having to worry about dependencies or modifying your html. The main problem with this method is that if you use o3d alot, the extra requests to your server may seem a bit unnecessary.</p>
<p>My solution was as follows:</p>
<ol>
<li>Determine which scripts are written to the DOM and in what order</li>
<li>Using a makefile, concatenate the files</li>
<li>Remove all require() calls using sed</li>
<li>Compress the javascript using Yahoo&#8217;s YUI compressor</li>
</ol>
<h3>Determining dependencies</h3>
<p>This step is pretty straightforward. Load your o3d application as usual and then use a DOM inspector such as Firebug, or the built in Safari/Chrome Web Inspector, and take a look in your &lt;head&gt; tag. You should see the injected o3d javascript (under /o3djs/):</p>
<div class="wp-caption aligncenter" style="width: 461px"><img title="o3d dependencies" src="http://static.jgblue.com/img/blog/o3d_depends.png" alt="Injected o3d dependencies" width="451" height="391" /><p class="wp-caption-text">Injected o3d dependencies</p></div>
<p>Keep note of these, as they will be used in the next step</p>
<h3>The makefile</h3>
<p>Using a makefile made the process of concatenating and compressing the javascript extremely simple and streamlined. Here&#8217;s the makefile I used to pull this off:</p>
<pre># This is just the root of my static content directory
# you can remove this if you don't need it
STATIC=../../static/ 

# The folder where the the final compressed
# javascript is installed to after 'make install'
PUBDIR=$(STATIC)js/ 

# A temporary folder used to store
# generated javascript (the concatenated/compressed file)
OUTDIR=./build/ 

# Path to the YUI compressor
COMPRESSOR=../yui/build/yuicompressor-2.4.2.jar

# YUI compressor arguments
COMPRESSOR_ARGS=
VPATH=$(OUTDIR)

# Files to copy to the PUBDIR directory on 'make install'
INST_TARGETS=$(OUTDIR)jgblue3d.js

# The location of your o3djs folder
O3D=$(STATIC)j3d/o3djs/

O3D_TARGETS=$(O3D)base.js $(O3D)util.js $(O3D)event.js \
    $(O3D)error.js $(O3D)math.js $(O3D)rendergraph.js $(O3D)primitives.js \
    $(O3D)effect.js $(O3D)io.js $(O3D)arcball.js $(O3D)quaternions.js \
    $(O3D)material.js $(O3D)jgblue-3d.js

jgblue3d.js: combined3d.js
    java -jar $(COMPRESSOR) $(COMPRESSOR_ARGS) $(OUTDIR)$&lt; -o $(OUTDIR)$@

combined3d.js: $(O3D_TARGETS)
    cat $^ &gt; $(OUTDIR)$@
    sed -e 's/^o3djs\.require\(.*\);$$//g' $(OUTDIR)$@ &gt; $(OUTDIR)$@.clean
    mv $(OUTDIR)$@.clean $(OUTDIR)$@

.PHONY: install clean all

all: jgblue3d.js

install:
    cp $(INST_TARGETS) $(PUBDIR)

clean:
    rm $(OUTDIR)*.js</pre>
<p>To get this working, you&#8217;ll need to change some of the variables to fit your project. The most important thing that you will need to change is the <span style="font-family: monospace">O3D_TARGETS</span> variable. This variable holds <span style="font-family: monospace">base.js</span>, the path to all of the o3d dependencies (gathered in step 1), and finally your o3d code.</p>
<p>Now all you have to do is type:<br />
<code>make &#038;&#038; make install</code></p>
<h3>You&#8217;re done!</h3>
<p>You can now remove the base.js from your html and add the newly generated javascript <a href="http://static.jgblue.com/js/jgblue3d.js">file</a>! It should work exactly the same as the original**. </p>
<p><small>** No promises ;)</small></p>
]]></content:encoded>
			<wfw:commentRss>http://jb55.com/17/static-o3d-dependencies/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>JGBlue – A Jumpgate Evolution Database</title>
		<link>http://jb55.com/3/jgblue-a-jumpgate-evolution-database/</link>
		<comments>http://jb55.com/3/jgblue-a-jumpgate-evolution-database/#comments</comments>
		<pubDate>Wed, 07 Oct 2009 16:50:58 +0000</pubDate>
		<dc:creator>jb55</dc:creator>
				<category><![CDATA[jgblue]]></category>

		<guid isPermaLink="false">http://jb55.com/?p=3</guid>
		<description><![CDATA[Recently I&#8217;ve been working on a new project called JGBlue. JGBlue is a database website written in the Python web framework Django, for the upcoming space combat MMO Jumpgate Evolution. I initially started the project to teach myself Django and Javascript, and to apply my knowledge of data formats and reverse engineering I gained from [...]]]></description>
			<content:encoded><![CDATA[<div class="wp-caption alignleft" style="width: 206px"><a href="http://jgblue.com/items"><img title="JGBlue" src="http://static.jgblue.com/img/jgblue_logo.png" alt="JGBlue - A Jumpgate Evolution Database" width="196" height="45" /></a><p class="wp-caption-text">JGBlue - A Jumpgate Evolution Database</p></div>
<p>Recently I&#8217;ve been working on a new project called JGBlue. JGBlue is a database website written in the Python web framework <a title="Django" href="http://www.djangoproject.com/" target="_blank">Django</a>, for the upcoming space combat MMO <a title="Jumpgate Evolution" href="http://us.jumpgateevolution.com/" target="_blank">Jumpgate Evolution</a>. I initially started the project to teach myself Django and Javascript, and to apply my knowledge of <a href="http://www.sourcepeek.com/wiki/ItemCache.wdb" target="_blank">data formats</a> and reverse engineering I gained from tinkering with World of Warcraft.</p>
<p>JGBlue, pronounced &#8220;Jumpgate Blue&#8221;, follows close to current popular MMO databases such as wowhead.com and aionarmory.com. I wanted it to have the same look and feel of these sites to make it familiar and easy to use.</p>
<p style="text-align: center;"><img class=" aligncenter" title="A look at a typical item page" src="http://static.jgblue.com/img/jgblue_preview.png" alt="A look at a typical item page" /></p>
<p>One thing you&#8217;ll notice in the above image is the prototype model viewer (I call it&#8230; The Jb Cube)! To me JGBlue has always been about playing with new technology that was unfamiliar to me, which is why I had a lot of fun implementing a model viewer in Google&#8217;s new 3d plugin and javascript api called <a href="http://code.google.com/apis/o3d/" target="_blank">O3D</a>. I also plan on making some future blog posts about my experience with the api so far and some techniques to get more performance out of it.</p>
<p>Django has been amazing, from the initial prototypes, to what&#8217;s running on the production server right now. It has provided me with everything I&#8217;ve needed so far, and their documentation is not far from perfection. I have Django running as fastcgi behind nginx, I&#8217;ve done a few benchmarks and I feel confident it&#8217;ll be able to handle whatever the Jumpgate community has to throw at it.</p>
<p>The release is still far away, with Jumpgate Evolution looking at a release sometime in 2010. Should give a perfectionist like me plenty of time :)</p>
<p><small>Looking forward to JGE? Leave a message and let us know what you&#8217;re most looking forward to!</small></p>
]]></content:encoded>
			<wfw:commentRss>http://jb55.com/3/jgblue-a-jumpgate-evolution-database/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
