<?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>John of All Trades &#187; Code</title>
	<atom:link href="http://www.johnofalltrades.name/tag/code/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.johnofalltrades.name</link>
	<description>Yeah. I&#039;d hack that.</description>
	<lastBuildDate>Sun, 05 Sep 2010 04:34:39 +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>Nexus Development Hint: Debug a Missing Plugin Resource</title>
		<link>http://www.johnofalltrades.name/2010/03/18/nexus-development-hint-debug-a-missing-plugin-resource/</link>
		<comments>http://www.johnofalltrades.name/2010/03/18/nexus-development-hint-debug-a-missing-plugin-resource/#comments</comments>
		<pubDate>Thu, 18 Mar 2010 23:34:22 +0000</pubDate>
		<dc:creator>john</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[Hacks]]></category>
		<category><![CDATA[debugging]]></category>
		<category><![CDATA[Eclipse]]></category>
		<category><![CDATA[Nexus]]></category>

		<guid isPermaLink="false">http://www.johnofalltrades.name/?p=172</guid>
		<description><![CDATA[To counter-balance my rant the other day about plugin development (specifically in Nexus), I thought I&#8217;d offer a method I developed for coping with the hidden errors I ran into. NOTE: Before I start, I should mention that most of the pain I&#8217;m about to describe, along the method of finding and working around the [...]]]></description>
			<content:encoded><![CDATA[<p>To counter-balance my <a href="http://www.johnofalltrades.name/2010/03/16/the-pain-of-plugin-development/"><img style="border: 0pt none; width: 0pt; height: 0pt; display: none;" src="http://tokentracker.com/token.gif?id=abIe2dd40" alt="" />rant</a> the other day about plugin development (specifically in Nexus), I thought I&#8217;d offer a method I developed for coping with the hidden errors I ran into.</p>
<p><strong>NOTE:</strong> Before I start, I should mention that most of the pain I&#8217;m about to describe, along the method of finding and working around the problem, will soon be a thing of the past. I&#8217;ve spoken to some of the Nexus team members, and they assure me they&#8217;re preparing to move Nexus off of Plexus, and onto Guice for dependency injection. So, once Nexus 1.6.0 comes out <em>[I hope]</em> it&#8217;ll be time to scrap these instructions.</p>
<h2>First, a little background.</h2>
<p>I&#8217;ve developed two Nexus plugins now, both small, and in each case I&#8217;ve run into strange behavior where my custom REST resources would go missing with nary an error to the console or logs. After specifying a particular resource URI, I&#8217;d build and deploy the plugin to my local Nexus instance, then try to hit it using <code>curl</code> with something like the following:</p>
<blockquote><p><code>curl --include --basic -H 'Accept: application/json' http://admin:admin123@localhost:8081/nexus/service/local/echo</code></p></blockquote>
<p>If all was well, the resource should respond with some result, throw an error, or something. But all I received was a 404 Not Found for my trouble. I attempted to dial up the log-level in <code>sonatype-work/nexus/conf/log4j.properties</code>, and scanned both <code>nexus-webapp-1.5.0/logs/wrapper.log</code> <strong>and</strong> <code>sonatype-work/nexus/logs/nexus.log</code> (the location I told log4j to use via a FileAppender). Nada.</p>
<p>When I developed my first plugin, this was the point where I started to bang my head against the wall in earnest. I couldn&#8217;t see any problems, it appeared from the logs that my plugin <strong>was</strong> loaded (it was listed in the plugin-manager output for my plugin bundle), and yet <code>curl</code> doesn&#8217;t lie. After about ten hours of remote debugging, I found a breakpoint that let me see what was going on. Eventually, I found a way to code around the problem, commented the hack liberally in my own code, filed a JIRA (<a href="https://issues.sonatype.org/browse/NEXUS-3308">NEXUS-3308</a>), and moved on. Unfortunately, when it came time to test the second plugin, I&#8217;d already dumped all the old debug breakpoints from the first go-round. So, this time I&#8217;m going to document the setup I used to find and fix the problem before I forget it again.</p>
<h2>Prepare your Nexus instance.</h2>
<p>Before we even talk about remote debugging from Eclipse, we need to setup Nexus to listen on a JPDA (java debugger) port. To do this, I modified the stock Java Service Wrapper <code>wrapper.conf</code> file that comes with Nexus as follows:</p>
<p><a href="http://www.johnofalltrades.name/wp-content/uploads/2010/03/nexus-plugin-dev-wrapper.png"><img src="http://www.johnofalltrades.name/wp-content/uploads/2010/03/nexus-plugin-dev-wrapper.png" alt="" width="501" height="263" /></a></p>
<p>You&#8217;ll notice that I actually have two variants of this JPDA configuration: one with <code>suspend=y</code>, and one with <code>suspend=n</code>. In most of my previous experience with remote debugging, I&#8217;ve been starting the application with the debugger attached so I can catch boot errors, things like that. But in the case of Nexus, I almost never use the <code>suspend=y</code> approach, since this makes the whole startup process excruciatingly slow. It&#8217;s not Nexus&#8217; fault, it&#8217;s just what happens when your remote debugger is watching Every. Single. Line. If you&#8217;re even halfway paying attention, you should have no problem watching the logs and attaching your remote debugger in plenty of time to catch the breakpoint you&#8217;re interested in. Particularly if it&#8217;s the breakpoint I&#8217;ll discuss below.</p>
<p>Now that you have Nexus rigged for debugging, let&#8217;s talk about the remote debugger before we restart. Once we restart Nexus, we&#8217;ll need to be on the ball to attach the debugger and intercept our breakpoint of choice at boot.</p>
<h2>Setup your remote debugger.</h2>
<p>What follows is described in terms of Eclipse. It&#8217;s the IDE I use, for a number of reasons. I get endless shit about this from the IDEA fanatics I run into here and there. For those people: I understand you&#8217;re probably of above-average intelligence. You will probably have no problem at all translating my instructions into IDEA-ese.</p>
<p>To start, get the project setup so you can actually navigate something meaningful with your debugger. This means attaching the sources for the <code>plexus-container-default</code> dependency in your project. I&#8217;m using <a href="http://m2eclipse.sonatype.org/">m2eclipse</a>, which makes it pretty easy. The following is a view of my Package Explorer, poised to go download and attach the Plexus source code:</p>
<p><a href="http://www.johnofalltrades.name/wp-content/uploads/2010/03/nexus-plugin-dev-src.png"><img src="http://www.johnofalltrades.name/wp-content/uploads/2010/03/nexus-plugin-dev-src.png" alt="" width="371" height="413" /></a></p>
<p>Next, setup a new Debug Configuration for your remote Nexus instance. This is pretty straightforward; the main points to notice is that your plugin project is selected as the main entry point, the host is set to <code>localhost</code> (or, wherever your Nexus instance is running), and the port is set to <code>8000</code> (this must correspond with the <code>wrapper.conf</code> modifications above). Your configuration should look something like this:</p>
<p><a href="http://www.johnofalltrades.name/wp-content/uploads/2010/03/nexus-plugin-dev-remote.png"><img src="http://www.johnofalltrades.name/wp-content/uploads/2010/03/nexus-plugin-dev-remote.png" alt="" width="480" height="384" /></a></p>
<p>Now we&#8217;re ready to configure that critical breakpoint, and setup our debugging environment so we can see the output. To set the breakpoint, I navigated through the <code>plexus-container-default</code> artifact until I found <code>org.codehaus.plexus.component.collections.AbstractComponentCollection</code>, scrolled to <strong>line 159</strong>, and set the breakpoint, like so:</p>
<p><a href="http://www.johnofalltrades.name/wp-content/uploads/2010/03/nexus-plugin-debug-breakpoint.png"><img src="http://www.johnofalltrades.name/wp-content/uploads/2010/03/nexus-plugin-debug-breakpoint.png" alt="" width="683" height="352" /></a></p>
<p><em>(You can set this breakpoint easily by double-clicking in the left gutter on line 159.)</em> Also, notice that the breakpoint shows up in my Breakpoints View, in the upper-right corner. If you read <strong>NEXUS-3308</strong>, you&#8217;ll understand why this particular breakpoint is so critical&#8230;</p>
<p>Once you&#8217;ve set the breakpoint, also setup a watched expression to output the full exception stacktrace. To do this, right-click in the <strong>Watched Expressions View</strong>, and select <strong>Add Watched Expression</strong>. Then, enter the following:</p>
<p><a href="http://www.johnofalltrades.name/wp-content/uploads/2010/03/nexus-plugin-dev-expr2.png"><img src="http://www.johnofalltrades.name/wp-content/uploads/2010/03/nexus-plugin-dev-expr2.png" alt="" width="356" height="208" /></a></p>
<p>Finally, since the result of that watched expression will be quite verbose, it&#8217;s important to extend the maximum buffer for the output pane in the Watched Expression View. To avoid having to do this twice, I&#8217;ve set mine to <code>0</code> <em>(no maximum)</em>. Just select <strong>Max Length&#8230;</strong> from the following menu, and set it appropriately:</p>
<p><a href="http://www.johnofalltrades.name/wp-content/uploads/2010/03/nexus-plugin-dev-expr.png"><img src="http://www.johnofalltrades.name/wp-content/uploads/2010/03/nexus-plugin-dev-expr.png" alt="" width="270" height="283" /></a></p>
<h2>Debug!</h2>
<p>Now, we&#8217;re ready to dig in and find that elusive bug that&#8217;s been making life hell. To do this, you&#8217;re going to be paying attention!</p>
<p>Ready? Okay, first stop Nexus and then re-run it in console mode. This allows you to stop it with CTL-C after you&#8217;re done debugging. The commands look something like this:</p>
<blockquote><p><code><br />
~/apps/nexus/current/bin/jsw/macosx-universal-32/nexus stop<br />
~/apps/nexus/current/bin/jsw/macosx-universal-32/nexus console<br />
</code></p></blockquote>
<p><strong>As soon as you see output like the following</strong>, attach your remote debugger:</p>
<pre>INFO   | jvm 1    | 2010/03/16 17:22:30 | Initializing Nexus (OSS), Version 1.5.0
</pre>
<p>Now, simply wait. If your Nexus component is failing when it initializes, the breakpoint will halt the system and the watched expression above will contain the full stacktrace. If you click on that watched expression, then click on the output pane, you can use CTL-A (CMD-A), CTL-C (CMD-C) to Select All of the stacktrace, then Copy it to your clipboard. From here, I&#8217;ve had a lot of luck opening Any Old Text Editor and pasting the stacktrace there for perusal. You can now detach the debugger (the button that looks like an electrical &#8216;N&#8217;) and let Nexus finish booting, or hit the continue button (looks like a pause/play button on a DVD player) to set a course for the next failing component.</p>
<p><strong>One word of warning:</strong> if you take your sweet time here, you&#8217;ll miss your chance. This is because the default startup timeout for the Java Service Wrapper in Nexus is 90 seconds. If you take longer than about 30-45 seconds poking through running code, Java Service Wrapper will kill the process. Of course, if you really <strong>have</strong> to have extra time, that&#8217;s configurable too.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.johnofalltrades.name/2010/03/18/nexus-development-hint-debug-a-missing-plugin-resource/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>The Pain of Plugin Development</title>
		<link>http://www.johnofalltrades.name/2010/03/16/the-pain-of-plugin-development/</link>
		<comments>http://www.johnofalltrades.name/2010/03/16/the-pain-of-plugin-development/#comments</comments>
		<pubDate>Tue, 16 Mar 2010 20:19:59 +0000</pubDate>
		<dc:creator>john</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[Nexus]]></category>

		<guid isPermaLink="false">http://www.johnofalltrades.name/?p=156</guid>
		<description><![CDATA[You could probably say I&#8217;m getting a dose of my own medicine. After all, I&#8217;m one of the people responsible for Apache Maven 2.x, including its plugin framework which seems to confuse the hell out of Maven newbies. I used to chalk most of that up to a lack of documentation, and I&#8217;m still sure [...]]]></description>
			<content:encoded><![CDATA[<p>You could probably say I&#8217;m getting a dose of my own medicine.</p>
<p>After all, I&#8217;m one of the people responsible<img style="border: 0pt none; width: 0pt; height: 0pt; display: none;" src="http://tokentracker.com/token.gif?id=0fKbaco78" alt="" /> for <a href="http://maven.apache.org/">Apache Maven 2.x</a>, including its plugin framework which seems to confuse the hell out of Maven newbies. I used to chalk most of that up to a lack of documentation, and I&#8217;m still sure that is part of the problem. But now, I&#8217;ve got a somewhat different perspective on plugin development in a dependency-injected context, and I think there&#8217;s probably more making life difficult for third-party plugin developers than simply being too lazy to read a book.</p>
<p>Recently, I&#8217;ve been working on a few custom plugins to work with <a href="http://nexus.sonatype.org/">Sonatype Nexus</a> for Red Hat (whom I joined back in January). It may sound surprising that I&#8217;m having some trouble with this since I used to work at Sonatype, after all. But alas, the closest I ever got to working on Nexus was writing a client API that I could wrap in a Maven plugin, and a few tools and configurations for building Nexus itself. Anyway, while the Nexus documentation for plugin developers is a little scant and shallow, I don&#8217;t really think it&#8217;s the main problem. Rather, it seems like developers have a tendency to include a plugin framework for their application without thinking much about the fact that people &#8211; people who don&#8217;t have a deep understanding of the application&#8217;s architecture &#8211; will actually try to <strong>use</strong> that framework. I haven&#8217;t understood this problem as well as I should with Maven, since I <strong>do</strong> have such a deep architectural understanding of it. Now I see that by providing a plugin framework for your application, you&#8217;re combining the hardest parts of standalone application development with the hardest parts of library development. In building a customer-facing application, you&#8217;re concerned with providing clean UI and an unbreakable user experience. These things require quite a bit of focused testing, and hiding a lot of the debug-level information from the user, so the output (console and UI) is clean and understandable. On the other hand, when building a good library, you need to worry about maintaining a strict respect of the library&#8217;s API contract over time, good developer documentation, the accessibility (and tunability) of debug-level information, and an absolute minimum of <strong>unexpected or magical behavior</strong>. Again, this requires a ton of focused testing, and some deep thinking about how to provide useful feedback to the developer in the event of an error. When the application is deployed, the developer should be able to hide this troubleshooting information (see application concerns, above).</p>
<p>Where many applications with plugin frameworks seem to really fall down is in supporting the plugin developer. The plugin framework itself seems to be a concession to make the lives of internal developers simpler, and this &#8220;simplicity&#8221; is usually passed on to third-party developers as well &#8211; as an afterthought, without considering that third-party developers don&#8217;t work with this stuff all day, every day. The result is an application that taunts the outside developer with the promise of extensibility, only to make life hell for those who succumb to that particular siren song. Finding themselves knee-deep without a shovel, such developers often have no other choice but to hook up a debugger, set some optimistic breakpoints, and continue down the rabbit hole into an endless iteration of debug session after debug session. &#8220;OK, I think I&#8217;ve finally isolated the code I need&#8230;Whoops! Just missed it.&#8221; CTL-C, set another breakpoint (or refine an existing one because unqualified, it seems to be the usage equivalent of <code>Object.hashCode()</code>) and run it again&#8230;and again&#8230;.and again. Welcome back to the world of trial-and-error programming. If you&#8217;re a decent programmer, you haven&#8217;t been this helpless to fix a coding error since you learned to write code. Often, after eight hours of debugging or more, our poor developer finds out that he has an extra <code>%s</code> in his <code>String.format()</code> call, and the resulting <code>RuntimeException</code> is being swallowed up by the plugin framework. Or, he realizes that he&#8217;s been trying to retrieve an attribute named &#8216;repository-id&#8217; from the <code>Context</code> attributes, when he <strong>should have</strong> been retrieving it from the <code>Request</code> attributes. Isn&#8217;t it obvious that the context has a different lifecycle than the request? Well, they&#8217;re both passed in next to one another in the same method signature, so, well&#8230;<strong>No, it&#8217;s not obvious.</strong></p>
<p>And maybe that&#8217;s the real problem with plugin development: the pitfalls, the potential for errors, even the philosophy that went into the application&#8217;s architecture <strong>is not obvious</strong>. Since most third-party developers aren&#8217;t psychic, confusion and torture ensues. And, while most developers these days &#8211; myself included &#8211; have a big ole crush on dependency-injection frameworks like <a href="http://code.google.com/p/google-guice">Guice</a>, in many ways they only exacerbate the confusion by adding a new dimension of <a href="http://en.wikipedia.org/wiki/Quantum_entanglement">spooky action at a distance</a>. Plugins have always been collections of hook implementations, event listeners, and callbacks that tie into an existing application&#8230;that&#8217;s sort of the definition. But with DI frameworks, these listeners and callbacks have never looked more disembodied. Making matters worse, DI frameworks themselves often over-promise and under-deliver. Like the rest, they are pieces of software, and may contain their own quirks or bugs. At times, the application developers may even place strange and seemingly arbitrary restrictions on how the underlying DI system is used (like forcing a component interface and its implementation to come from the same jar) that undermine the promise of a componentized architecture. In the end, the unflinching observer is all too often challenged to say that using a DI container to support a plugin framework saves anyone any pain at all.</p>
<p>To some extent, the fragmentation issues are unavoidable in plugin development. Fundamentally, you&#8217;re adding a small slice of functionality onto carefully selected extension points in an existing application; when you don&#8217;t understand how that application functions internally, finding the right leverage points for your new feature will always be hard. But how much harder is your application actually making the life of your third-party developers? Providing and advertising a plugin framework for your application implies the desire to create an ecosystem around that application. But providing a plugin framework and API that doesn&#8217;t behave as expected, doesn&#8217;t provide much help to third-party developers, isn&#8217;t well documented, and doesn&#8217;t have an comprehensive suite of tests to continually verify the plugin contract&#8230;all of this is counter-productive at best. At worst, it can alienate some of your application&#8217;s biggest fans.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.johnofalltrades.name/2010/03/16/the-pain-of-plugin-development/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>On Moving to WordPress from Pebble</title>
		<link>http://www.johnofalltrades.name/2009/12/01/on-moving-to-wordpress-from-pebble/</link>
		<comments>http://www.johnofalltrades.name/2009/12/01/on-moving-to-wordpress-from-pebble/#comments</comments>
		<pubDate>Wed, 02 Dec 2009 03:30:26 +0000</pubDate>
		<dc:creator>john</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[Hacks]]></category>
		<category><![CDATA[pebble]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[wordpress]]></category>

		<guid isPermaLink="false">http://www.johnofalltrades.name/?p=140</guid>
		<description><![CDATA[So, you may have noticed that I recently migrated this blog from Pebble to WordPress. Actually, I migrated two former Pebble blogs, plus one former WordPress blog, to this new one. The WP install was simple, and so was the transfer of blog entries from one WP instance to another. Migrating from Pebble was a [...]]]></description>
			<content:encoded><![CDATA[<p><img style="border: 0pt none; width: 0pt; height: 0pt; display: none;" src="http://tokentracker.com/token.gif?id=35I178ndf" alt="" />So, you may have noticed that I recently migrated this blog from Pebble to WordPress. Actually, I migrated <i>two</i> former Pebble blogs, plus one former WordPress blog, to this new one. The WP install was simple, and so was the transfer of blog entries from one WP instance to another. Migrating from Pebble was a little more challenging, particularly since I had a lot of posts that I didn&#8217;t want to handle manually.</p>
<p>I started out by looking at the <b><a href="http://code.google.com/p/pebble2wordpress/">Pebble2Wordpress</a></b> importer code, which is written in Ruby. (I first found a reference to this project from <b><a href="http://www.spritle.com/blogs/?p=107#more-107">The Spritle Blog</a></b> blog post, which has some more information about how it&#8217;s meant to be used.) I selected this project in particular because it&#8217;s written in a language I can fine tune as needed.</p>
<p>I&#8217;m not sure whether this project ever really worked as-is out of the source repository, but I can say for sure that it didn&#8217;t work for me using the instructions provided on the blog post. In an attempt to understand where the developer was coming from, and determine whether I could cobble together something to work for my own particular migration, I dug into the source. What I found seemed pretty overwrought and confusing, particularly for something that amounts to a simple migration script to morph XML documents into DB calls (not something that will ever attract a huge user base). After refactoring the code to provide minimal error handling, and to inline all class definitions into one ruby script alongside the code that uses these classes, I thought I had something understandable to start testing.</p>
<p>The general approach of the code is to create ActiveRecord representations for terms (read: categories), term-post relationships, posts, and comments. Once the database is connected, the script iterates through all <b><code>*.xml</code></b> files in the inputXML directory, and parses the Pebble XML format into Post/Comment/Category instances which are then saved into the WP database. During the parsing process, certain things like image URLs are massaged from the old Pebble layout to something more compatible with WP. Since the default WP uploads directory has the date baked into the file path, I opted to simplify things and put all migrated files into the <b><code>uploads/</code></b> base directory. This has an added benefit of giving me one-stop shopping for anything that breaks during the migration process, instead of picking over a nested directory structure.</p>
<p>Before you can start using the code, you&#8217;ll need to install some RubyGems and OS hooks:</p>
<pre class="brush: bash;">
$  sudo apt-get -y install rubygems mysql-dev
...
$  gem install activerecord activerecord-jdbcmysql-adapter
...
</pre>
<p>Next, you need to create a new directory alongside the <b><code>convert.rb</code></b> script, called <b><code>inputXML/</code></b>. Into this you should copy all of the XML files from the pebble data directory. My data directory was in <b><code>/opt/web/sites/ejlife.net/var/pebble/blogs/buildchimp/</code></b>, so I used the following commands to achieve this:</p>
<pre class="brush: bash;">
$ find /opt/web/sites/ejlife.net/var/pebble/blogs/buildchimp/2009 -type f -name '*.xml' -exec cp '{}' inputXML/ \;
...
$ find /opt/web/sites/ejlife.net/var/pebble/blogs/buildchimp/2008 -type f -name '*.xml' -exec cp '{}' inputXML/ \;
...
$ find /opt/web/sites/ejlife.net/var/pebble/blogs/buildchimp/2007 -type f -name '*.xml' -exec cp '{}' inputXML/ \;
...
$ find /opt/web/sites/ejlife.net/var/pebble/blogs/buildchimp/2006 -type f -name '*.xml' -exec cp '{}' inputXML/ \;
</pre>
<p>I know, using some more arcane shell-fu I could have combined the individual commands into one. However, rather than take a chance of fouling something up with an overly-complex command line, I chose to use the Base arrow-up feature to retrieve and modify my previous command. Simple, effective, and nearly fool-proof.</p>
<p>Luckily, in my case I had a bare WP install that I could abuse a bit while I fine tuned the migration code. Along with a decent database client (I <b>love</b> the free version of <b><a href="http://www.minq.se/products/dbvis/">DBVisualizer</a></b> for this), rolling back failed migrations are a breeze. After a failed migration attempt, I simply issued the following SQL commands to get back near enough to the base install:</p>
<pre class="brush: sql;">
DELETE FROM wp_posts;

DELETE FROM wp_comments;

DELETE FROM wp_term_relationships WHERE object_id $  999
</pre>
<p><b>NOTE:</b> I chose to delete term relationships where the object ID is 1000 or greater, since it seems like the auto-increment field for posts starts at around 1000. This may not be the case in all environments.</p>
<p>I&#8217;m not going to pollute this blog post by giving a full listing of the migration script, but you can download it here: <b><a href="/wp-content/uploads/src/pebble2wordpress/convert.rb">convert.rb</a></b>. It has an accompanying database configuration YAML file that looks like this: <b><a href="/wp-content/uploads/src/pebble2wordpress/database.yml">database.yml</a></b>. The script expects the <b><code>inputXML/</code></b> directory and the <b><code>database.yml</code></b> file to be in the same directory as the script itself. Use the script if you want to, enjoy it, but above all, <i>DON&#8217;T BLAME ME IF YOU BLOW SOMETHING UP</i>.</p>
<p>I then tested, rinsed, and repeated until I was pleased with the result. As a final step, I copied the contents of the <b><code>files/</code></b> and <b><code>images/</code></b> directories from the Pebble data directory into my WP install:</p>
<pre class="brush: bash;">
$  cp -rf /opt/web/sites/ejlife.net/var/pebble/blogs/buildchimp/images/* /opt/web/sites/johnofalltrades.name/htdocs/wp-content/uploads
...
$  cp -rf /opt/web/sites/ejlife.net/var/pebble/blogs/buildchimp/files/* /opt/web/sites/johnofalltrades.name/htdocs/wp-content/uploads
...
</pre>
<p>That&#8217;s about all there is to it. Hackish? Maybe. Effective? Definitely. I&#8217;m very happy in my new WordPress digs!</p>
<p><b>UPDATE:</b> I&#8217;ve submitted my changes back to the project in <b><a href="http://code.google.com/p/pebble2wordpress/issues/detail?id=1">this issue</a></b>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.johnofalltrades.name/2009/12/01/on-moving-to-wordpress-from-pebble/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
