<?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>Scholars&#039; Lab &#187; Wayne Graham</title>
	<atom:link href="http://www.scholarslab.org/author/wsg4w/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.scholarslab.org</link>
	<description>Works in Progress</description>
	<lastBuildDate>Thu, 09 Sep 2010 18:42:31 +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>Code Reviews and the Digital Humanities</title>
		<link>http://www.scholarslab.org/digital-humanities/code-reviews-and-the-digital-humanities/</link>
		<comments>http://www.scholarslab.org/digital-humanities/code-reviews-and-the-digital-humanities/#comments</comments>
		<pubDate>Wed, 18 Aug 2010 12:47:57 +0000</pubDate>
		<dc:creator>Wayne Graham</dc:creator>
				<category><![CDATA[Digital Humanities]]></category>
		<category><![CDATA[SLab Code]]></category>
		<category><![CDATA[Add new tag]]></category>
		<category><![CDATA[best practices]]></category>
		<category><![CDATA[code review]]></category>
		<category><![CDATA[testing]]></category>

		<guid isPermaLink="false">http://www.scholarslab.org/?p=890</guid>
		<description><![CDATA[The following was a response I made in an email exchange with Tom Elliot of the Pleiades Project  and Bethany Nowviskie. Our conversation was prompted by Tom's inquiry on planning, budgeting for, and conducting a code review as part of a grant-funded project. What follows is a slightly modified (and expanded) version of that email conversation.

Testing and code review is something that has been on my mind a lot lately as our shop has been shifting its focus from boutique, one-off projects, to building upon frameworks maintained by other organizations. As these code bases continue to grow, we need to ensure that subtle changes to the core functionality of the underlying systems do not propagate into bugs in our code. We also need a way to handle this situation quickly and efficiently when this does arise. This was especially reinforced by two recent projects our group undertook to migrate nearly decade-old software on to new servers.]]></description>
			<content:encoded><![CDATA[<p>The following was a response I made in an email exchange with Tom Elliot of the <a href="http://pleiades.stoa.org/">Pleiades Project</a> and Bethany Nowviskie. Our conversation was prompted by Tom&#8217;s inquiry on planning, budgeting for, and conducting a code review as part of a grant-funded project. What follows is a slightly modified (and expanded) version of that email conversation.</p>
<p>Testing and code review is something that has been on my mind a lot lately as our shop has been shifting its focus from boutique, one-off projects, to building upon frameworks maintained by other organizations. As these code bases continue to grow, we need to ensure that subtle changes to the core functionality of the underlying systems do not propagate into bugs in our code. We also need a way to handle this situation quickly and efficiently when this does arise. This was especially reinforced by two recent projects our group undertook to migrate nearly decade-old software on to new servers.</p>
<p>If you ask anyone in the office, they will most likely roll their eyes when I start beating the testing drum. <span id="more-890"></span> These are great tools for not only generating pretty green and red bar charts, but also documenting the intention of the programmer in writing the code, and zeroing in on bugs where they occur without weeks of hunting. However, this is only one of the tools in the chest for writing solid code, sans bugs. In fact, there are a lot of sophisticated, freely available, automated tools that help programmers of all skill levels not only write more consistent code, but also zero in on potential performance issues and just plain smelly code (that they obviously wrote just to get running and fully intended to go back and fix later).</p>
<p>Over the years, tools that measure code complexity (like <a href="http://pmd.sourceforge.net/">PMD</a>, <a href="http://phpmd.org/about.html">PHPMD</a>, and <a href="http://ruby.sadi.st/Flog.html">flog</a>), code dependency analyzers (<a href="http://www.clarkware.com/software/JDepend.html">JDepend</a>, <a href="http://pdepend.org/news.html">PHPDepend</a>, and <a href="http://eigenclass.org/hiki.rb?rcov">rcov</a>), copy/paste detection (in PMD, <a href="http://ruby.sadi.st/Flay.html">flay</a>, and <a href="http://github.com/sebastianbergmann/phpcpd">phpcpd</a>), and enforcing coding standards (a la PHPCode Sniffer and rails_best_practices), along with not only unit and integration tests (in whatever style you choose), but a code coverage analysis reports that provides feedback on which lines were executed, go a long way in reducing the number of bugs in code. These tools are really pre-emptive step in writing stronger, more elegant, and ultimately more sustainable code, all before once gets to the point of performing a human code review.</p>
<p>While I don&#8217;t need to be building software per-se, I have started experimenting with the Hudson continuous integration server as a dashboard to get a quick snapshot of how these different metric tests all play together in the code that our team writes. It is no longer good enough to simply have code functioning, we need the code to pass certain thresholds of quality and sustainability before we can release. Where we find issues in the code, like test coverage, high cyclomatic complexity, lots of copy-n-pasted code, or high volatility in dependency scans, we can sit down and perform a rather focused mini code review (resembling the pair-programming idiom) on that section of code to refactor a better solution or approach To this end, we&#8217;re currently working on a set of baseline testing and reporting tools for our projects. Currently, we have Ant scripts for our PHP and Java projects, and a gem bundle for Rails and Sinatra projects.</p>
<p>While we take this approach in the Scholars&#8217; Lab, we were wondering if there were others out there that had opinions or experiences to share about code review during development? If you do, leave a comment, write a post, or tweet at us (@scholarslab, @nowviskie, @wayne_graham) &#8212; and at @paregorios, who started the conversation in the first place. We&#8217;d love to hear about your best practices (and even horror stories) and philosophy on what constitutes good software and useful code reviewing, including whether you think current trends in open source development constitute a good-enough review for DH projects.</p>
<h2>Further Resources</h2>
<p>Java</p>
<ul>
<li><a href="http://pmd.sourceforge.net/">PMD</a></li>
<li><a href="http://www.clarkware.com/software/JDepend.html">JDedend</a></li>
<li><a href="http://www.junit.org/">junit</a></li>
<li><a href="http://www.atlassian.com/software/clover/">Clover</a></li>
<li><a href="http://www.oracle.com/technetwork/java/javase/documentation/index-jsp-135444.html">Javadoc</a></li>
<li><a href="http://checkstyle.sourceforge.net/">Checkstyle</a></li>
</ul>
<p>PHP</p>
<ul>
<li><a href="http://pdepend.org/news.html">PHP Depend</a></li>
<li><a href="http://phpmd.org/about.html">PHPMD</a></li>
<li><a href="http://github.com/sebastianbergmann/phpcpd">phpcpd</a></li>
<li><a href="http://pear.php.net/package/PHP_CodeSniffer/redirected">PHP Codesniffer</a></li>
<li><a href="http://www.phpdoc.org/">phpDocumentor</a></li>
</ul>
<p>Ruby</p>
<ul>
<li><a href="http://ruby.sadi.st/Flog.html">Flog</a></li>
<li><a href="http://ruby.sadi.st/Flay.html">Flay</a></li>
<li><a href="http://metric-fu.rubyforge.org/">metric_fu</a></li>
<li><a href="http://rdoc.sourceforge.net/">rdoc</a></li>
</ul>
<p>Javascript</p>
<ul>
<li><a href="http://docs.jquery.com/QUnit">QUnit</a></li>
<li><a href="http://siliconforks.com/jscoverage/">JSCoverage</a></li>
</ul>
<p>Continuous Integration</p>
<ul>
<li><a href="http://hudson-ci.org/">Hudson</a></li>
<li><a href="http://phing.info/trac/">Phing</a></li>
<li><a href="http://cruisecontrol.sourceforge.net/">CruiseControl</a></li>
</ul>
<p>Issue Tracking</p>
<ul>
<li><a href="https://code.google.com/hosting/">Google Code</a></li>
<li><a href="http://github.com/">GitHub</a></li>
<li><a href="http://kenai.com/">Project Kenai</a></li>
<li><a href="http://www.atlassian.com/software/jira/">Jira</a></li>
<li><a href="http://www.redmine.org/">Redmine</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.scholarslab.org/digital-humanities/code-reviews-and-the-digital-humanities/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Why Ruby?</title>
		<link>http://www.scholarslab.org/slab-code/why-ruby/</link>
		<comments>http://www.scholarslab.org/slab-code/why-ruby/#comments</comments>
		<pubDate>Tue, 11 May 2010 19:27:02 +0000</pubDate>
		<dc:creator>Wayne Graham</dc:creator>
				<category><![CDATA[SLab Code]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[web development]]></category>

		<guid isPermaLink="false">http://www.scholarslab.org/?p=741</guid>
		<description><![CDATA[Stemming from a Twitter conversation last month, I thought it would be a good idea to describe &#8212; in more than the 140 character bursts that Twitter allows &#8212; why we at the Scholars&#8217; Lab often promote Ruby, opposed to one of the other 4 or 5 languages we develop with. This isn&#8217;t an attempt [...]]]></description>
			<content:encoded><![CDATA[<p>Stemming from <a href="http://twitter.com/dougreside/status/12881732120">a Twitter conversation</a> last month, I thought it would be a good idea to describe &#8212; in more than the 140 character bursts that Twitter allows &#8212; why we at the <a href="http://lib.virginia.edu/scholarslab/">Scholars&#8217; Lab</a> often promote Ruby, opposed to one of the other 4 or 5 languages we develop with. This isn&#8217;t an attempt to declare one language &#8220;the best,&#8221; but is meant to lay out some of the fundamental reasons why we use Ruby in the context of our digital humanities work and why we think it&#8217;s a nice language to suggest to folks just starting to program.<span id="more-741"></span></p>
<h2>Qualification</h2>
<p>People often think of the Scholars&#8217; Lab as a <a href="http://www.ruby-lang.org/en/">Ruby</a> (and <a href="http://rubyonrails.org/">Rails</a>) shop, which isn&#8217;t quite the case. We code in many different languages.  In the past several moths, we have written <a href="http://metaphors.lib.virginia.edu">The Mind is a Metaphor</a> in Ruby (with Rails). <a href="http://prosody.lib.virginia.edu/">For Better For Verse</a> uses <a href="http://wordpress.org/">WordPress</a> with <a href="http://www.tei-c.org/index.xml">TEI</a> and <a href="http://java.sun.com/products/jsp/">JSP</a>, and some more recent work on a William Faulkner audio archive employs <a href="http://cocoon.apache.org/2.1/">Cocoon</a> with <a href="http://lucene.apache.org/solr/">Solr</a>. In addition to those collaborations with UVa faculty, we&#8217;ve been writing plugins for <a href="http://omeka.org/">Omeka</a> (and dusting off our PHP skills) and have created a <a href="http://gis.lib.virginia.edu/">discovery interface</a> for our GIS infrastructure in Ruby (with <a href="http://www.sinatrarb.com/">Sinatra</a>). If you analyze the technologies we currently deploy, it turns out we use <a href="http://cocoon.apache.org/">Cocoon</a> + <a href="http://lucene.apache.org/solr/">Solr</a> more than anything else, though we&#8217;re starting to move away from that particular stack as our approach for tool development.</p>
<p>The Scholars&#8217; Lab has a lot of experience with all types of languages, and depending on the circumstances, we choose different tools to accomplish any given task. However, after quite a bit of time helping people get started on a programming path, I&#8217;ve come to appreciate some of the features Ruby provides in getting new programmers up to speed.</p>
<h2>Learning Curve</h2>
<p>Every language has a learning curve. However, once you get the hang of some of the basics of computer languages (flow control, data structures, objects, etc.), the biggest differences come from syntax. All web languages make certain programming exercises easy, and once you buy in to the way in which that language handles programming constructs, moving between languages for experienced programmers becomes a simpler exercise in exploring syntax and built-in functionality.</p>
<p>As one of my computer science professors posited, generally the first language you learn governs the way you code until something significantly better comes along. For a lot of folks getting in to programming for the first time, this usually means either taking a class or finding someone to show you the basics. If you&#8217;re in higher education, this has typically meant the tool of choice is PHP. However, having seen the look of bewilderment on the faces of enough graduate students and faculty members as I attempt to explain the difference between sprintf and printf (printf returns the length of the formatted String and sprintf returns the formatted String), I&#8217;ve come to believe that the syntax of a programming language (and it&#8217;s readability) is an exceptionally important part of a language, especially when teaching basics of software construction.</p>
<h3>Method Chaining</h3>
<p>Without getting into how the PHP and Ruby <a href="http://en.wikipedia.org/wiki/Duck_typing">duck-type</a><a href="http://en.wikipedia.org/wiki/Primitive_data_type"> primitive data types</a> and <a href="http://en.wikipedia.org/wiki/Data_structure">data structures</a>, one big difference in syntax between the two is how one combines multiple method calls together. Ruby uses method chaining for objects whereas PHP uses &#8220;bolted-on&#8221; functions (think of these as order-of-operations from your high school Algebra class). Let&#8217;s look at this brief example of addressing and sorting an associative array in PHP and its equivalent in Ruby:</p>
<pre class="brush: php;">
$projects = array(&quot;solr&quot; =&gt; 4, &quot;php&quot; =&gt; 1, &quot;rails&quot; =&gt; 2, &quot;jsp&quot; =&gt; 3);
$keys = array_keys($projects);
sort($keys);
$sorted = array_slice($keys, 0, 3);
</pre>
<p>If you&#8217;re a little more advanced, you might refactor (rewrite) the code to look more like this (methods anonymously &#8220;bolted-on&#8221; to one another):</p>
<pre class="brush: php;">
$projects = array(&quot;solr&quot; =&gt; 4, &quot;php&quot; =&gt;  1, &quot;rails&quot; =&gt; 2, &quot;jsp&quot; =&gt; 3);
$sorted = array_slice(sort(array_keys($projects)), 0, 3);
</pre>
<p>Now, the same examples in Ruby syntax:</p>
<pre class="brush: ruby;">
projects = {&quot;solr&quot; =&gt; 4, &quot;php&quot; =&gt;  1, &quot;rails&quot; =&gt; 2, &quot;jsp&quot; =&gt; 3}
sorted = projects.keys.sort.slice(0,3)
</pre>
<p>Or, even more concisely:</p>
<pre class="brush: ruby;">
projects = {&quot;solr&quot; =&gt; 4, &quot;php&quot; =&gt;  1, &quot;rails&quot; =&gt; 2, &quot;jsp&quot; =&gt; 3}
sorted = projects.keys.sort[0..3]
</pre>
<p>I&#8217;ve found that Ruby&#8217;s method chaining syntax makes more sense to new programmers than the more mathematical &#8220;bolted-on&#8221; syntax.</p>
<h3>Blocks</h3>
<p>Ruby has a neat construct that you use all over the place to create anonymous functions (a technical term for creating specific functionality without defining a new function to define the action). Let&#8217;s take a function to sort an array of projects. First, in PHP:</p>
<pre class="brush: php;">

function sort_projects_by_count($a, $b)
{
    if($a -&gt; counts == $b -&gt; counts)
    {
        return 0;
    }
    return($a -&gt; counts &gt; $b -&gt; counts) ? +1 : -1;
}

usort($projects, &quot;sort_projects_by_count&quot;);
</pre>
<p>And the same thing in Ruby:</p>
<pre class="brush: ruby;">
projects.sort do |a, b|
    a.counts &lt;=&gt; b.counts
end
</pre>
<p>Ok, so this is a bit of an unfair comparison, but here is an analogous version of the Ruby code in PHP:</p>
<pre class="brush: php;">
usort($projects, create_function($a, $b, 'if($a-&gt;counts == $b-&gt;counts){
    return 0;}return ($a-&gt;counts &gt; $b-&gt;counts ? +1 : -1));
</pre>
<p>No matter how you slice it, Ruby syntax just feels more human. Even if you don&#8217;t know exactly what&#8217;s going on, looking up one operator in the Ruby syntax as opposed to following the logic flow and determining what &#8220;? +1 : -1&#8243; means (it&#8217;s shorthand for an if-then statement) makes the act of reading code much easier.</p>
<h2>Monkeypatching</h2>
<p>If you&#8217;re not familiar with the term, &#8220;monkeypatching&#8221; is what programmers call changing or extending a base class (like an array or string object) to add functionality or change the way it works. Let&#8217;s say you really need to be able to test a string to see if it looks like an integer, you could create a monkeypatch along these lines:</p>
<pre class="brush: ruby;">
class String
    def is_int?
        self =~ /^[-+]?[0-9]*$/
    end
end
</pre>
<p>This code snip extends the String class and uses a <a href="http://en.wikipedia.org/wiki/Regular_expression">Regular Expression</a> (regex) to test if a given String is an integer (number) by simply calling &#8220;is_int?&#8221; (notice the question mark at the end of the definition; this is used for methods that return a Boolean value). That&#8217;s a little advanced, but it does show off a very useful piece of functionality of the language that allows you to do a better job dealing with a <a href="http://en.wikipedia.org/wiki/Duck_typing">duck-typed language</a>.</p>
<h2>Frameworks</h2>
<p>Many people when talking about Ruby associate its use in web development with the Rails <a href="http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller">MVC framework</a>. Just as PHP has <a href="http://framework.zend.com/">Zend</a>, <a href="http://codeigniter.com/">CodeIgniter</a>, <a href="http://cakephp.org/">CakePHP</a>, <a href="http://www.symfony-project.org/">symfony</a>, etc., Ruby has <a href="http://rubyonrails.org/">Rails</a>, <a href="http://www.merbivore.com/">Merb</a>, <a href="http://www.sinatrarb.com/">Sinatra</a>, <a href="http://camping.rubyforge.org/">Camping</a>, and many more. Rails is the 900-pound gorilla of Ruby frameworks, and has a lot of nice features to get new applications off the ground quickly and some really great online guides to setting things up (I frequent <a href="http://guides.rubyonrails.org/">RailsGuides</a>). Since we often suggest Rails to our collaborators I&#8217;ll focus on this framework, but there are several other frameworks out there to choose from.</p>
<p>Think of Ruby (or PHP for that matter) as a pile of building materials: you can to build anything you want if you know how to put everything together. Rails, on the other hand, is like a prefab house where workers pour a foundation, set the house up, and then leave you to add the drywall, siding, windows, and roof. If you need a new component for your prefab house, a sales representative is standing by to help immediately ship you what you need.</p>
<h3>Generators</h3>
<p>To extend the previous metaphor, generators are like sales representatives that allow you to place orders for new aspects of your site.  To create all the erb templates (the default templeting language for Rails), controller methods, model, database migrations, routes, and tests for a new project with a single line, you might run something like the following:</p>
<pre class="brush: ruby;">
script/generate scaffold topic title:string description:text
</pre>
<p>I moved away from using scaffolding pretty quickly, but it does provide a nice starting point for new programmers to build interactions with data models.</p>
<h3>Templates</h3>
<p>The default templating engine in Rails is <a href="http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/">erb</a> which provides a convenient method for generating views of  your models. One of erb&#8217;s most important features is the use of &#8220;partials,&#8221; pieces of code that are used in multiple views by calling the render method. I often replicated this behavior in PHP by calling an include somewhere in a view.</p>
<h3>ActiveRecord</h3>
<p>The key to database interactions in Rails is ActiveRecord. As an SQL expert, I have to admit this part of the Rails framework drove me a bit batty at first, but then again I&#8217;ve been writing SQL for over 10 years (my colleagues often roll their eyes when I start writing it with relational algebra nomenclature), so allowing a framework to abstract this particular piece took some getting used to. If you&#8217;re new to programming, though, this means you don&#8217;t have to learn SQL but instead can use Ruby-style syntax to interact with your database without necessarily needing to care what your <a href="http://en.wikipedia.org/wiki/Relational_database_management_system">RDBMS</a> back-end happens to be.</p>
<p>Take this example of looking up a book review where you have both a &#8220;book&#8221; and &#8220;review&#8221; table. In PHP you would do something like this (this snip will only work with a MySQL connection but has some sub-selection stuff going on):</p>
<pre class="brush: php;">
function query($sql)
{
    global $conn;
    return mysql_query($sql, $conn);
}

function recent_reviews($count)
{
    $query = query(sprintf('SELECT b.book_title, r.review_id, r.created_at, r.id, review_counter.review_total
        FROM reviews r, books b,
            (SELECT count(*) AS review_total FROM reviews) AS review_counter
        WHERE r.book_id = b.book_id
        ORDER BY created_at DESC
        LIMIT %d', $count);

    return $query
}

print_r(recent_reviews(5));
</pre>
<p>Now, for the ActiveRecord equivalent:</p>
<pre class="brush: ruby;">
reviews = Review.find(:limit =&gt; 5, <img src='http://www.scholarslab.org/wp-includes/images/smilies/icon_surprised.gif' alt=':o' class='wp-smiley' /> rder =&gt; &quot;created_at DESC&quot;);
puts reviews.inspect
</pre>
<p>Because of the way in which ActiveRecord sets up its model associations, you&#8217;ll have access to the different name scopes to print out the same information, just in far less code. However, if you really want to, you can pass your SQL to get more granular control over the syntax:</p>
<pre class="brush: ruby;">
reviews = Review.find_by_sql(&quot;SELECT b.book_title, r.review_id, r.created_at, r.id, review_counter.review_total
        FROM reviews r, books b,
            (SELECT count(*) AS review_total FROM reviews) AS review_counter
        WHERE r.book_id = b.book_id
        ORDER BY created_at DESC
        LIMIT ?&quot;, count);
</pre>
<p>One of the real beauties of the ActiveRecord methods is that as long as you&#8217;re using the generic ActiveRecord syntax, your data persistence layer can be pretty much any RDBMS and be changed with a couple lines in the configuration file. The trade-off however, is that you lose a few things and can make slightly more work for yourself than you might anticipate. One important caveat is that ActiveRecord doesn&#8217;t create foreign keys when you set up reference fields. This is actually by design as it&#8217;s using an object-oriented idiom (an object should validate the presence of another, without the underlying persistence layer enforcing any type of constraint), but I find myself adding these in to ensure that the RDBMS takes advantage of the pre-calculated indexes to improve overall performance.</p>
<p>I should also mention that I think the ActiveRecord model has some real limitations. As you develop your models, you will most like be tweaking its fields, which in turn requires new migrations, and you may forget which fields are actually in your models. There are plugins that help with this, but you do need to take additional steps to have this information placed somewhere convenient (I use a pre-commit git hook that calls the <a href="http://github.com/ctran/annotate_models">annotate gem</a> to dynamically annotate my model schemas).</p>
<h3>Security</h3>
<p>You&#8217;ll notice in the last examples I was doing some funny stuff in both the PHP and Ruby examples to protect against <a href="http://en.wikipedia.org/wiki/SQL_injection">SQL injection attacks</a>. If you&#8217;re using the ActiveRecord methods of addressing objects, Rails will take care of this for you. If you&#8217;re using PHP, you&#8217;ll either need to do this yourself (sprintf is commonly used) or rely on a framework to parametrize your statements (you don&#8217;t want someone deleting everything in your database).</p>
<p>You also need to protect yourself from <a href="http://en.wikipedia.org/wiki/Cross-site_scripting">Cross-site Scripting Attacks</a> (XSS) by escaping HTML from fields with dynamic content. erb has a helper function html_escape (h is the shorthand) which escapes this data. In Rails 3, this will change slightly and erb will automatically html_escape model output unless you explicitly tell it not to escape the field. One less thing to remember!</p>
<h3>Testing</h3>
<p>Testing is important, and I try to preach its virtues every chance I get. After finishing a project, the typical programmer won&#8217;t touch the code again until the application breaks. Good testing will save you (or the person that inherits the code) a lot of time discovering exactly what broke the application.</p>
<p>Let&#8217;s face it: testing is a pain in PHP, and I rarely see it done well. What I&#8217;ve really enjoyed about Ruby development is the fact that no matter how you code there is an appropriate testing framework available (I use <a href="http://rspec.info/">rspec</a>). There&#8217;s a strong emphasis on not only unit testing, but also integration and acceptance testing. There are also libraries that give you an idea of how well your <a href="http://github.com/relevance/rcov">code is tested</a>, something I sorely missed from my Java coding endeavors. One other huge plus is that every gem on the <a href="http://www.rubygems.org">rubygems.org</a> site includes test-coverage metrics to give you an idea of how well the code you want to install is tested.</p>
<p>PHP also has testing frameworks with <a href="http://www.phpunit.de/">PHPUnit</a> and <a href="http://code.google.com/p/phpspec/">PHPSpec</a> being rather popular. I won&#8217;t say too much about the PHP testing frameworks other than to say that there are analogous frameworks for writing and running tests in PHP and Ruby. However, I&#8217;ve noticed a slightly more concerted effort to think through the inclusion of the tools in Ruby and their integration into the coding workflow than I&#8217;ve experienced with PHP. With the latter language, I&#8217;ve often fell in to the trap of writing the code, getting it to where I want it, and then, really as an afterthought, writing basic unit tests to get rather skimpy code coverage.</p>
<p>As a case in point, a mantra in Rails development is <a href="http://rubyhoedown2008.confreaks.com/05-bryan-liles-lightning-talk-tatft-test-all-the-f-in-time.html">TATFT</a>.</p>
<p><object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="400" height="302" 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://vimeo.com/moogaloop.swf?clip_id=1534976&amp;server=vimeo.com&amp;show_title=1&amp;show_byline=1&amp;show_portrait=0&amp;color=&amp;fullscreen=1" /><embed type="application/x-shockwave-flash" width="400" height="302" src="http://vimeo.com/moogaloop.swf?clip_id=1534976&amp;server=vimeo.com&amp;show_title=1&amp;show_byline=1&amp;show_portrait=0&amp;color=&amp;fullscreen=1" allowscriptaccess="always" allowfullscreen="true"></embed></object></p>
<p><a href="http://vimeo.com/1534976">BryanL on TATFT</a> from <a href="http://vimeo.com/bryanl">Bryan Liles</a> on <a href="http://vimeo.com">Vimeo</a>.</p>
<h2>Deployment</h2>
<p>Two typical objections raised when contemplating Ruby development are that &#8220;Ruby doesn&#8217;t scale&#8221; and that server setup is a real pain. These are valid concerns, but as with many open source projects with a large number of fanatical supporters, the Ruby community has steadily made improvements in these areas. Actually, for the vast majority of our readership, these issues can be filed away in the solved category.</p>
<h3>Scaling</h3>
<p>I&#8217;ve always found objections to scaling a bit troubling. Scaling is one of those over-used terms that means different things to different people, but most of the &#8220;Rails doesn&#8217;t scale&#8221; comes from Twitter&#8217;s experience with the framework. They found, as they scaled horizontally (adding more servers) to handle loads of 11,000+ requests per second, that a bottleneck existed at the data persistence level as Rails doesn&#8217;t, by default, provide a mechanism to to address multiple databases. Twitter has since moved parts of their code base to <a href="http://www.scala-lang.org/">Scala</a> but has retained the majority of their code in Rails and has developed some rather ingenious messaging capabilities to talk to the appropriate abstraction layers that one needs in very large enterprise applications.</p>
<p>While Twitter shows that Rails is capable of scaling (with lots of work), quite honestly the likelihood of any of our applications needing this level of engineering is slim. I will say, however, that there are relatively simple methods of scaling with your infrastructure should you start running into performance issues. We have, for example, an application written in pure Ruby on Rails deployed as a Tomcat application (the details of which are completely outside the scope of this article, but the application gets all the benefits of an Enterprise class Java environment with the ease of Rails development).</p>
<h2>Server Support</h2>
<p>The Ruby language is included in most (if not all) modern Linux package systems and makes installation a snap. The other &#8220;major&#8221; piece of software you&#8217;ll need is RubyGems (a package manager for Ruby libraries), which is also generally available as a managed package.</p>
<blockquote><p><strong>Note</strong>: There is a major change occurring with the development of Rails 3. Rails is moving from a system of system-wide gems to application-level gems with the introduction of <a href="http://gembundler.com/">GemBundler</a>. This approach is a more stable method of deploying application requirements which not only allows you to ensure that application libraries are properly resolved, but also provide better granular control over which libraries are deployed in specific contexts.</p></blockquote>
<p>There was a time where deploying Rails applications was a real bear. Then along came <a href="http://www.modrails.com/">Phusion Passenger</a> (aka mod_rails). This allows you to run Rails (actually any Rack-based application) through Apache and Nginx without any other port management, service process monitoring, file cleanup, etc. As long as Apache is running, so is your Rails app!</p>
<h2>Community of Support</h2>
<p>The Rails community is pretty great in getting folks off the ground. As with any technology there are a fair number of curmudgeons, but leaders in the community as quick to remind people to be nice (see Yahuda Katz&#8217;s <a href="http://yehudakatz.com/2010/02/09/the-blind-men-and-the-elephant-a-story-of-noobs/">The Blind Men and the Elephant: A Story of Noobs</a>). There are several corporations backing Rails development (<a href="http://www.engineyard.com">EngineYard</a> is a big one) and when the Google Summer of Code for Rails program wasn&#8217;t continued, the Rails community was able to raise $100,000 in three days to support a <a href="http://rubysoc.org/">Ruby Summer of Code</a>.</p>
<p>There are a number of really good podcasts (<a href="http://ruby5.envylabs.com/">Ruby5</a> and <a href="http://5by5.tv/rubyshow">The Ruby Show</a> are good), vodcasts (<a href="http://railscasts.com/">Railscasts</a>), tutorial sites (<a href="http://asciicasts.com/">ACSIIcasts</a>), blogs (<a href="http://www.railsdispatch.com/">RailsDispatch</a>, <a href="http://thoughtbot.com/">ThoughtBot</a>, <a href="http://www.engineyard.com/blog/">EngineYard</a>), open source projects (lots on <a href="http://www.github.com">github</a>), open source books (<a href="http://www.railstutorial.org/book">Rails Tutorials</a>), and some really good reference books from <a href="http://pragprog.com/">The Pragmatic Programmers</a>. There&#8217;s even some humor&#8230;</p>
<p><object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="480" height="385" 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://www.youtube.com/v/p5EIrSM8dCA&amp;hl=en_US&amp;fs=1&amp;" /><param name="allowfullscreen" value="true" /><embed type="application/x-shockwave-flash" width="480" height="385" src="http://www.youtube.com/v/p5EIrSM8dCA&amp;hl=en_US&amp;fs=1&amp;" allowscriptaccess="always" allowfullscreen="true"></embed></object></p>
<h2>Summary</h2>
<p>As much as it drives language purists crazy when I say it, there&#8217;s nothing that you can do in Ruby that you can&#8217;t replicate in PHP (and vice-versa). In my experience, getting people set up with a functioning web application is far easier with Ruby than it is with PHP (and less prone to <a href="http://en.wikipedia.org/wiki/Spaghetti_code">spaghetti code</a>), and the deployment options make Ruby (plus a framework) a great starting point for new programmers to get their feet wet with web application development (check out <a href="http://www.heroku.com">Heroku</a>). If you&#8217;re an experienced developer, does it make sense to drop PHP and rewrite your code base? Absolutely not. However, at some point, you will be faced with the prospect of needing to migrate a legacy application where Ruby may make a lot of sense. As someone who has spent quite a bit of time de-tangling spaghetti code from Perl CGI and mixed HTML and PHP pages, I&#8217;m hoping that the people that will eventually be migrating my Ruby code will not need to perform the level of coding archaeology we&#8217;ve needed to perform.</p>
<p>Like most things in life, choosing the correct tool for the job needs some careful consideration and planning. Ruby makes a lot of sense for getting applications off the ground quickly and reinforcing good practices like testing, code separation, and readability that I find important in forming new digital humanities programmers. Web development over the last decade has become exceptionally complex (AJAX, web services, web standards, multiple browsers, etc.) and the real hope is that by using Ruby and Rails as an approach, people will be inspired to continue down a development path to both enrich their own scholarship and impact the larger digital humanities community without becoming frustrated by syntax. This is a bit of an experiment which we and other digital humanities shops are undertaking, and in which we&#8217;re inviting everyone to participate.  No matter the language, we should all be engaged in teaching best practices in project design and management, in software development techniques, in the construction of usable and elegant interfaces, and in the application of these things to humanities scholarship, through which everyone wins!</p>
<h2>Other Resources</h2>
<ul>
<li><a href="http://railsforphp.com/">Rails for PHP Developers</a></li>
<li><a href="http://www.oreillynet.com/ruby/blog/2007/09/7_reasons_i_switched_back_to_p_1.html">7 Reason I switched back to PHP after 2 years on Rails</a></li>
<li><a href="http://guides.rubyonrails.org/">RubyGuides</a></li>
<li><a href="http://www.jruby.org/">JRuby</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.scholarslab.org/slab-code/why-ruby/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Automating Omeka Deployment with Capistrano</title>
		<link>http://www.scholarslab.org/slab-code/automating-omeka-deployment-with-capistrano/</link>
		<comments>http://www.scholarslab.org/slab-code/automating-omeka-deployment-with-capistrano/#comments</comments>
		<pubDate>Tue, 20 Apr 2010 23:53:25 +0000</pubDate>
		<dc:creator>Wayne Graham</dc:creator>
				<category><![CDATA[SLab Code]]></category>
		<category><![CDATA[capistrano]]></category>
		<category><![CDATA[deployment]]></category>
		<category><![CDATA[omeka]]></category>

		<guid isPermaLink="false">http://www.scholarslab.org/?p=660</guid>
		<description><![CDATA[If you&#8217;ve done much web development, you&#8217;ll know that deploying applications can be a real pain. Typically you get some code (like Omeka), FTP it to your server, run the install, then go grab some plugins and themes and FTP them to your server. If you&#8217;re a bit more sophisticated, you may have put this [...]]]></description>
			<content:encoded><![CDATA[<p>If you&#8217;ve done much web development, you&#8217;ll know that deploying applications can be a real pain. Typically you get some code (like Omeka), FTP it to your server, run the install, then go grab some plugins and themes and FTP them to your server. If you&#8217;re a bit more sophisticated, you may have put this in to an source code management (SCM) system like <a href="http://git-scm.com/">git</a>, <a href="http://mercurial.selenic.com/">mercurial</a>, or <a href="http://subversion.apache.org/">subversion</a>, which then changes your workflow to editing on your local machine, committing the changes to your SCM, logging on to the command line interface for your server, running an update on the code, praying nothing breaks; if it does, you then try to roll back to a working version (you remembered to run svn info on the code before updating so you know what number to go back to). Even if everything goes swimmingly, that&#8217;s a lot of steps and way more applications than I like to fool with, and since it&#8217;s essentially doing the same thing over and over again, wouldn&#8217;t it be nice to automate this process?   <span id="more-660"></span></p>
<p>Enter <a href="http://www.capify.org/index.php/Capistrano">Capistrano</a>&#8230;If you&#8217;ve not used this before, essentially this automates the deployment of web applications to your server environment.  It&#8217;s written in Ruby, but allows you to deploy ANY type of web application (we use it for Cocoon, Rails, Java, and PHP applications in the Scholars&#8217; Lab). If you&#8217;ve got a larger shop, you may also take a look at a web interface called <a href="http://labs.peritor.com/webistrano">Webistrano</a> which allows non-programmer types to deploy software through a web interface.</p>
<p>To show off the power of this software, I thought I&#8217;d write up how we use capistrano to deploy Omeka in various environments. The setup can be a little complex, but there are some good tutorials for getting started (see <a href="http://net.tutsplus.com/tutorials/ruby/setting-up-a-rails-server-and-deploying-with-capistrano-on-fedora-from-scratch/">Setting up a Rails Server and Deploying with Capistrano on Fedora from Scratch</a> and the <a href="http://www.capify.org/index.php/Getting_Started">Capistrano Getting Started</a>). The following code snips assume you have successfully installed capistrano and use Subversion as your SCM (if you need SVN hosting, you can start a new project on <a href="http://code.google.com/hosting/createProject">Google Code</a>; you can also use <a href="https://github.com/">Github</a> if you declare the git scm in the code).</p>
<p>The first step in getting your Omeka project automated for capistrano is ensuring both the capistrano and railsless-deploy gems are installed (if you&#8217;re not a ruby-ist, <a href="http://docs.rubygems.org/read/book/1">gem</a> is a package manager for Ruby applications and libraries):</p>
<pre class="brush: bash;">
sudo gem install capistrano railsless-deploy
</pre>
<p>Capistrano installs a new command on your system called &#8220;capify&#8221; which sets up the boilerplate for capistrano. Just execute the capify script in your project directory:</p>
<pre class="brush: bash;">
cd /path/to/project/trunk
capify .
</pre>
<p>This will create two files, Capfile in your root directory and a config/deploy.rb. You&#8217;ll need to edit the Capfile ever so slightly to add the requirement for the railsless-deploy gem. It should read as follows:</p>
<pre class="brush: ruby;">
load 'deploy' if respond_to?(:namespace) # cap2 differentiator
# Dir['vendor/plugins/*/recipes/*.rb'].each { |plugin| load(plugin) }

require 'rubygems'
require 'railsless-deploy'
load    'config/deploy'
</pre>
<p>Now we just need to do some setup in the config/deploy.rb file to tell capistrano about Omeka. This is where you need to know a little about how your server is set up and you may need to slightly change your server set up in order to use capistrano. The way capistrano works is that it creates a releases directory on your path that holds &#8220;deployments&#8221; of you project. The latest version of the project is then symlinked the project directory into the releases. This allows you to very quickly undo a deployment that goes awry.</p>
<p>As an example, we deploy projects to /usr/local/projects, so our omeka project would get deployed to /usr/local/projects/omeka. Capistano will create a few directories in /usr/local/projects/omeka:</p>
<ul>
<li><strong>releases</strong> (timestamped directories of your application)</li>
<li><strong>shared</strong> (for log files, SCM cache, files you don&#8217;t want to be overwritten)</li>
<li><strong>current</strong> (symlink to latest directory in the releases directory)</li>
</ul>
<p>If you&#8217;re setting up Omeka, you will need to redirect the base Directory to the &#8220;current&#8221; symlink. Here&#8217;s the vhost entry we use for Omeka as an example (this is an Ubuntu server; you may need to change the log file path if you are deploying to another operating system).</p>
<pre class="brush: bash;">
&lt;VirtualHost *:80&gt;
ServerName your.server.org
DocumentRoot /usr/local/projects/omeka/current/
&lt;Directory &quot;/usr/local/projects/omeka/current&quot;&gt;
Options FollowSymLinks
AllowOverride All
Order allow,deny
Allow from all
&lt;/Directory&gt;

ErrorLog /var/log/apache2/omeka_error.log
TransferLog /var/log/apache2/omeka_access.log
&lt;/VirtualHost&gt;
</pre>
<p>Now, to edit the config/deploy.rb file to set things up for automated deployments.</p>
<pre class="brush: ruby;">
# You must always specify the application and repository for every recipe. The
# repository must be the URL of the repository you want this recipe to
# correspond to. The deploy_to path must be the path on each machine that will
# form the root of the application path.

set :application, 'omeka'
set :repository, 'http://your.svn.path/repo/trunk'

set :deploy_to, &quot;/usr/local/projects/#{application}&quot;
set :deploy_via, :remote_cache
set :user, 'deployer'
set :runner, user
set :run_method, :run

default_run_options[:pty] = true

ssh_options[:username] = user
ssh_options[:host_key] = 'ssh-dss'
ssh_options[:paranoid] = false

role :app, 'www.coolomekaapp.org'
role :web, 'www.coolomekaapp.org'
role :db, 'www.coolomekaapp.org'

# ===============
# Custom Tasks
# ===============
namespace :deploy do
desc 'Make sure the archives directory has the proper permissions'
task :chmod_archive_dir, :except=&gt;{:no_release =&gt; true} do

run &quot;chmod g+rw #{current_path}/archives&quot;
end
desc 'Sets up the intitial db.ini config'
task :upload_database_config, :except=&gt;{:no_release =&gt; true} do
db_config = &lt;&lt;-INI
[database]
host     = &quot;your.db.host&quot;
username = &quot;db_user&quot;
password = &quot;db_password&quot;
name     = &quot;db_name&quot;
prefix   = &quot;omeka_&quot;
;port     = &quot;&quot;
INI

put db_config, &quot;#{current_path}/db.ini&quot;

end

desc 'Move archives directory so it doesn\'t get overwritten during deployments'
 task :move_archive_dir, :except=&gt;{:no_release =&gt; true} do
 run &quot;mv #{current_path}/archives #{shared_path}&quot;
 end

 desc 'Just svn up the directory'
 task :svn_up, :except =&gt; {:no_release =&gt; true} do
 run &quot;svn up #{current_path}&quot;
 end

 desc 'Link archives folder to shared directory'
 task :link_archives_dir, :except=&gt;{:no_release =&gt; true} do
 run &quot;cd #{current_path} &amp;&amp; ln -snf #{shared_path}/archives archives&quot;
 end

end

# ======================
# Task Event Hooks
# ======================

after 'deploy:symlink', 'deploy:upload_database_config', 'deploy:link_archives_dir'
after 'deploy:cold', 'deploy:chmod_archives_dir', 'deploy:move_archives_dir'
after 'deploy', 'deploy:cleanup'
</pre>
<p>Ok, there&#8217;s a lot going on here. I&#8217;ll briefly explain what&#8217;s going on, but there should be enough here for you to start hacking. But let&#8217;s see what tasks capistrano know about and you can call. If you are still in your project directory, just type cap -T to list all the capistrano tasks. Your output should look similar to this:</p>
<pre class="brush: bash;">
cap deploy                        # Deploys your project.
cap deploy:check                  # Test deployment dependencies.
cap deploy:chmod_archive_dir      # Make sure the archives directory has the ...
cap deploy:cleanup                # Clean up old releases.
cap deploy:cold                   # Deploys and starts a `cold' application.
cap deploy:migrate                # Run the migrate rake task.
cap deploy:migrations             # Deploy and run pending migrations.
cap deploy:pending                # Displays the commits since your last deploy.
cap deploy:pending:diff           # Displays the `diff' since your last deploy.
cap deploy:restart                # Restarts your application.
cap deploy:rollback               # Rolls back to a previous version and rest...
cap deploy:rollback:code          # Rolls back to the previously deployed ver...
cap deploy:setup                  # Prepares one or more servers for deployment.
cap deploy:start                  # Start the application servers.
cap deploy:stop                   # Stop the application servers.
cap deploy:symlink                # Updates the symlink to the most recently ...
cap deploy:update                 # Copies your project and updates the symlink.
cap deploy:update_code            # Copies your project to the remote servers.
cap deploy:upload                 # Copy files to the currently deployed vers...
cap deploy:upload_database_config # Sets up the intitial db.ini config
cap deploy:web:disable            # Present a maintenance page to visitors.
cap deploy:web:enable             # Makes the application web-accessible again.
cap invoke                        # Invoke a single command on the remote ser...
cap log:tail_log                  # Tail the rails log file
cap shell                         # Begin an interactive Capistrano session.

Some tasks were not listed, either because they have no description,
or because they are only used internally by other tasks. To see all
tasks, type `cap -vT'.

Extended help may be available for these tasks.
Type `cap -e taskname' to view it.
</pre>
<p>To use a specific capistrano task, you just type the command listed. But let&#8217;s get back to the actual script and go over that briefly. Part of the installation process of Omeka requires you to reset the permissions of the archives directory. This is handled by the chmod_archive_dir. However, because of the way that capistrano deploys applications, the archives folder would get overwritten in every deployment. To get around this, we move the archives directory to the shared folder, then create a symlink from the current directory to the shared/archives directory.</p>
<p>There&#8217;s a task to upload_database_config that you can have in your cap script (we deploy out of private repos), but if you&#8217;re deploying out of a public repo, you may want to just put the db.ini file on the server in the shared directory and symlink it into the current_path. Lastly, there are times where you just need to do an &#8220;svn up&#8221; (or git pull) to update something small and not need to do a full deployment. This is where the cap deploy:svn_up helps out&#8230;.guess what it does <img src='http://www.scholarslab.org/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>Capistrano also provides task even hooks to execute specific tasks after specific events. In this script, when you do a cold deployment (when you are setting things up for the first time), the script will change the permissions on the archives directory, then move the archives directory to the shared directory. When you do a normal deploy (after the directory has gotten a proper symlink), the script will upload the database config, then symlink the archives directory and run a cleanup (keep the last 5 versions you deployed).</p>
<p>While there&#8217;s still a bit of up-front set up to do on your server, capistrano significantly speeds up your ability to to consistently deploy software, quickly roll-back if (that changes to &#8220;when&#8221;, if you write code long enough) problems occur, and reinforces a development process that involves SCM!</p>
<h2>Resources</h2>
<ul>
<li><a href="http://www.capify.org/index.php/Capistrano">Capistrano</a></li>
<li><a href="http://www.jonmaddox.com/2006/08/16/automated-php-deployment-with-capistrano/">Automated PHP Deployment with Capistrano</a></li>
<li><a href="http://github.com/leehambley/railsless-deploy">Railsless-deploy</a></li>
<li><a href="http://www.capify.org/index.php/Tutorials">Capistrano Tutorials</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.scholarslab.org/slab-code/automating-omeka-deployment-with-capistrano/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Omeka Timeline Plugin</title>
		<link>http://www.scholarslab.org/slab-code/omeka-timeline-plugin/</link>
		<comments>http://www.scholarslab.org/slab-code/omeka-timeline-plugin/#comments</comments>
		<pubDate>Wed, 14 Apr 2010 19:19:44 +0000</pubDate>
		<dc:creator>Wayne Graham</dc:creator>
				<category><![CDATA[SLab Code]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[development]]></category>
		<category><![CDATA[howto]]></category>
		<category><![CDATA[omeka]]></category>
		<category><![CDATA[plugin]]></category>
		<category><![CDATA[timeline]]></category>

		<guid isPermaLink="false">http://www.scholarslab.org/?p=605</guid>
		<description><![CDATA[As part of our ongoing efforts on our Neatline grant, we needed to include a way of displaying temporal information and interacting with other data stored in Omeka. Just about the time we were starting to write this code, CHNM announced their Plugin Rush which pays an honorarium to give folks some incentive to pitch in and develop a plugin or two. Since we were going to develop the plugin anyway, we're donating this back to the Omeka project, but we thought this might be a good opportunity to talk a little more about the development cycle for Omeka plugins, and hopefully inspire others to get involved.]]></description>
			<content:encoded><![CDATA[<p>As part of our ongoing efforts on our <a href="http://www.neatline.org">Neatline</a> grant, we needed to include a way of displaying temporal information and interacting with other data stored in Omeka. Just about the time we were starting to write this code, CHNM announced their <a href="http://omeka.org/blog/2010/02/18/plugin-rush-2010/">Plugin Rush</a> which pays an honorarium to give folks some incentive to pitch in and develop a plugin or two. Since we were going to develop the plugin anyway, we&#8217;re donating this back to the Omeka project, but we thought this might be a good opportunity to talk a little more about the development cycle for Omeka plugins, and hopefully inspire others to get involved.  <span id="more-605"></span></p>
<p>The specifications for the <a href="http://omeka.org/c/index.php/Plugin_Rush_2010#Timeline_.281.1-1.0.29">Timeline plugin</a> are wonderfully documented and explained on the Omeka wiki. This actually illustrates a great practice that is all too often ignored&#8230;explicitly stating what some software should do. Taking the time to think through the &#8220;what&#8221; a piece of software should do will save  you time in the long run as it forces you to think about how everything fits together, alleviating ambiguity and allowing you to focus on the task at hand.</p>
<p>For this specification, there were two requirements:</p>
<ul>
<li>The plugin should create a helper function for creating a <a href="http://www.simile-widgets.org/timeline/">SIMILE  Timeline</a> widget from an array of items.  The helper function should allow you to  specify which metadata elements the time data should come from, as well  as the element that specifies the caption (by default, the Dublin Core  Title element).</li>
<li> The timeline should allow for time intervals (start and end dates) and points in  time (singe date).</li>
</ul>
<p>While there are two requirements, it&#8217;s a good idea to break this us a little more into individual (atomic) tasks. First, we need to take a look at the <a href="http://code.google.com/p/simile-widgets/wiki/Timeline">SIMILE Timeline Widget documentation</a>. Taking a quick look at their <a href="http://code.google.com/p/simile-widgets/wiki/Timeline_GettingStarted">Getting Started</a> guide, this seems pretty straight forward</p>
<ol>
<li> <a href="http://code.google.com/p/simile-widgets/wiki/Timeline_GettingStarted#Step_1._Link_to_the_API">Include the Timeline javascript</a></li>
<li>Create a div container in the HTML view (e.g. &lt;div id=&#8221;timeline&#8221;&gt;&lt;/div&gt;</li>
<li>Format the items from Omeka as <a href="http://code.google.com/p/simile-widgets/wiki/Timeline_GettingStarted#Step_5._Add_Events">Timeline Events</a></li>
<li>Add the Timeline.create() call to the Omeka HTML view</li>
</ol>
<p>The specification states that this needs to be a helper function. If you&#8217;re not familiar with this term, a helper function is a block of code that does some of the computation for another piece of code. In many frameworks, helper functions aren&#8217;t actual objects, but pieces of procedural code that can be accessed from across the application that help add functionality that isn&#8217;t exactly proper to place in a model or controller; essentially these are statically accessible functions (in coding terms) that can be called from properly instantiated objects (in this case a view).</p>
<p>Now that we have good idea of what the code should do, let&#8217;s turn to some actual code. Omeka uses the <a href="http://framework.zend.com/">Zend Framework</a> to keep from doing a lot of repetitive programming, so most of the syntax of what we need to do is driven by how Zend handles PHP. On top of the Zend Framework, Omeka implements its own plugin infrastructure, so there are a few things we need to take in to account in our design.</p>
<p>The Zend Framework is a <a href="http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller">model-view-controller (MVC) framework</a> designed to organize code for maintainability and DRY-ness (<a href="http://en.wikipedia.org/wiki/Don%27t_repeat_yourself">Don&#8217;t Repeat Yourself</a>). One the the hallmarks of most MVC applications is its physical separation of files and functionalities. In the case of Omeka plugins, the hierarchy is generally split into model, view, controller, and tests directory. For the Timeline plugin, since we are developing a helper function, we use a slightly modified directory structure:</p>
<pre class="brush: bash;">
Timeline
|__helpers
|__tests
|__views
</pre>
<p>We also need some mechanism to tell Omeka about a plugin. This metadata is currently provided in a file called plugin.ini. This file is pretty straight forward, but  let&#8217;s go over it briefly:</p>
<pre class="brush: php;">

[info]
name=&quot;Timeline&quot;
author=&quot;Scholars' Lab&quot;
description=&quot;SIMILE Timeline for Omeka&quot;
link=&quot;http://omeka.org/codex/Plugins/Timeline&quot;
omeka_minimum_version=&quot;1.0&quot;
omeka_tested_up_to=&quot;1.2&quot;
version=&quot;1.1&quot;
tags=&quot;Timeline, simile, chronology, time, temporal&quot;
</pre>
<p>This file is what the Omeka admin interface uses to display information about your plugin. Two things that may not be initially obvious are the<strong> omeka_minimimum_version</strong> and <strong>omeka_tested_up_to</strong> lines. One fact you&#8217;ll learn about software development, especially with API development, is as projects mature, the needs of the API grow along with them. You want to be able to mitigate potential issues should the plugin API change by explicitly setting the minimum revision number that your plugin is tested against (you can get older revisions from the SVN tags repo at <a href="https://omeka.org/svn/tags/">https://omeka.org/svn/tags/</a>).</p>
<blockquote><p><strong>Note:</strong> you can run multiple versions of Omeka on your machine for testing by checking out separate versions of the software in you web tree. For instance, you can have <strong>localhost/omeka1.0</strong>, <strong>localhost/omeka1.1</strong>, <strong>localhost/omeka_trunk</strong>. Setting this up is beyond the scope of this post (be sure to set up separate databases), but if you have questions, leave a comment.</p></blockquote>
<p>The next thing we need to do is tell Omeka what to do with our plugin. The top-level plugin.php file contains instructions (<a href="http://omeka.org/codex/Plugin_API/Hooks">hooks</a>) to tell the Admin interface what to do when a user installs or uninstalls the plugin. This is where we let Omeka know that items tagged with &#8220;Timeline&#8221; should use the Timeline Plugin, to set up some logging to help us debug when something goes wrong, and some default <a href="http://omeka.org/codex/Plugin_API/Hook/define_routes">routes</a> to help make &#8220;pretty&#8221; URLs.</p>
<p>Now, with all the preliminary setup taken care of, now we can start developing the helper function. First, let&#8217;s examine the Controller which tells Omeka what to do when an action is requested by the framework. This is actually a fairly straight forward:</p>
<pre class="brush: php;">

&lt;?php
class Timeline_TimelinesController extends Omeka_Controller_Action

{
	private $logger;

	public function init()
	{
		$this-&gt;_modelClass = 'Item';
		$writer = new Zend_Log_Writer_Stream(LOGS_DIR . DIRECTORY_SEPARATOR . &quot;timeline.log&quot;);
		$this-&gt;logger = new Zend_Log($writer);
	}

	public function showAction()
	{
		$this-&gt;view-&gt;item = $this-&gt;findById();
	}

}
</pre>
<p>Let&#8217;s go over briefly what&#8217;s going on here. There are some semantics in the way in which these Controller objects are named which are inherited from the way in which Zend handles Controllers. The <strong>Timeline_TimelinesController</strong> follows the convention of the &#8220;package&#8221; (the plugin) name, underscore, plural controller name (to handle multiple controllers), and finally &#8220;Controller&#8221; (which explicitly tells a programmer what function the Object performs). Because this is a Framework, we also want to be able to inherit a lot of behaviors without needing to code them ourselves, which is handled by the &#8220;extends Omeka_Controller_Action&#8221; (this is the base <a href="http://en.wikipedia.org/wiki/Create,_read,_update_and_delete">CRUD</a> class for Omeka which overrides and extends the <a href="http://framework.zend.com/manual/en/zend.controller.action.html">Zend_Controller_Action</a> object). The &#8220;important&#8221; part of the Controller code is really the showAction function which sets a variable named &#8220;item&#8221; in the view which contains a reference to the ID of an Omeka object. The rest just sets up a logger to keep track of what&#8217;s going on.</p>
<blockquote><p><strong>Note</strong>: If you run in to problems with this plugin, it is most likely related to logging. For more on this, see the documentation on <a href="http://omeka.org/codex/Retrieving_error_messages">Retrieving Error Messages</a>.</p></blockquote>
<p>Now we can get into the guts of the actual helper function. When you boil down the code, most of this is JavaScript with some strategically placed PHP. What we did is create a helper function named &#8220;createTimeline&#8221; which actually does the work for us. This takes two required items, a div reference to associate the Timeline on your page, and an array of Omeka Items with which to populate the Timeline.</p>
<pre class="brush: php;">
function createTimeline($div, $items = array(), $captionElementSet = &quot;Dublin Core&quot;, $captionElement =  &quot;Title&quot;, $dateElementSet = &quot;Dublin Core&quot;, $dateElement =  &quot;Date&quot; ) {
		echo js(&quot;prototype&quot;);
		global $mets;
		$mets = array($captionElementSet, $captionElement, $dateElementSet, $dateElement);
		?&gt;
		&lt;!--  we have to load the script in this funny way because we need to get the tag into the head of the doc
			because of the the funky way Simile Timeline loads its sub-scripts  --&gt;
		&lt;script type=&quot;text/javascript&quot;&gt;
			scripttag = document.createElement(&quot;script&quot;);
			scripttag.src = &quot;http://static.simile.mit.edu/timeline/api-2.3.0/timeline-api.js?bundle=false&quot;;
			scripttag.type = &quot;text/javascript&quot;;
			$$(&quot;head&quot;)[0].insert(scripttag);

			if (typeof(Omeka) == &quot;undefined&quot;) {
				Omeka = new Object();
			}

			if (!Omeka.Timeline) {
				Omeka.Timeline = new Object();
			}

		&lt;/script&gt;

		&lt;script type=&quot;text/javascript&quot; defer=&quot;defer&quot;&gt;
			Omeka.Timeline.timelinediv = $(&quot;&lt;?php echo $div;?&gt;&quot;);

			Omeka.Timeline.events = [
			&lt;?php
				function event_to_json($item) {
					global $mets;
					return &quot;{ 'title' : '&quot; . getMet($item, $mets[0], $mets[1]) . &quot;',
					'start' : '&quot; . getMet($item, $mets[2], $mets[3]) . &quot;',
					'description' : '&quot; . getMet($item, &quot;Dublin Core&quot;, &quot;Description&quot;) . &quot;',
					'durationEvent':false }&quot;;
				}
				echo implode(',',array_map('event_to_json', $items));
				?&gt;
				];

		&lt;/script&gt;
		&lt;?php
	     echo js(&quot;createTimeline&quot;);
		?&gt;
		&lt;script type=&quot;text/javascript&quot;&gt;
			Event.observe(window, 'load', onLoad);
			Event.observe(document.body, 'resize', onResize);
		&lt;/script&gt;

		&lt;?php
}
</pre>
<p>There&#8217;s a lot going on here, and there is a mix of PHP in the JavaScript. The first thing is making sure the prototype.js library is included, then declaring a variable named &#8220;mets&#8221; in the <a href="http://php.net/manual/en/language.variables.scope.php">global scope</a> (to make it available to other variable scopes). After we&#8217;ve declared $mets, get in to the JavaScript to include on the page and introducing a new JavaScript <a href="http://en.wikipedia.org/wiki/Namespace_%28computer_science%29">Namespace</a> (Omeka.Timeline) which allows you to extend this code in other views.</p>
<p>The second script block actually formats Omeka items that you&#8217;ve called as Timeline Events in the <a href="http://www.json.org/">JSON</a> format calling a helper method we also include in the code:</p>
<pre class="brush: php;">
function getMet($item, $elementSet, $element) {
	 $tmp = $item-&gt;getElementTextsByElementNameAndSetName($element, $elementSet);
	 return addslashes( $tmp[0]-&gt;text ) ;
}
</pre>
<p>This function returns the metadata for an Omeka item, which is then used the createTimeline&#8217;s sub-method of event_to_json to properly construct an event for Timeline. After all the JSON strings are created, we &#8220;glue&#8221; all the array elements with a comma with the <a href="http://php.net/manual/en/function.implode.php">implode</a> function.</p>
<p>As you can see, not a lot of code actually needs to be written to add functionality to Omeka. With a little research, and some pointers on syntax, extending Omeka can be done quite quickly and doesn&#8217;t require a degree in computer science. If you&#8217;re interested in getting started on a plugin, I highly recommend the <a href="http://groups.google.com/group/omeka-dev">Omeka dev list</a>; the community is growing and questions are answered quickly (usually by folks on the Omeka development team) and is a great way to learn about the technical issues surrounding developing software using the Omeka platform.</p>
<h2>Resources</h2>
<ul>
<li><a href="http://omeka.org/codex/Plugin_API">Omeka Plugin API</a></li>
<li><a href="http://groups.google.com/group/omeka-dev">Omeka Developer List</a></li>
<li><a href="https://addons.omeka.org/svn/plugins/Timeline/trunk/">Timeline Source Code</a></li>
<li><a href="http://omeka.org/codex/Plugins/Timeline">Timeline Documentation</a></li>
<li><a href="http://framework.zend.com/">Zend Framework</a></li>
<li><a href="http://framework.zend.com/manual/en/">Zend Framework Documentation</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.scholarslab.org/slab-code/omeka-timeline-plugin/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Large Files and Omeka</title>
		<link>http://www.scholarslab.org/slab-code/large-files-and-omeka/</link>
		<comments>http://www.scholarslab.org/slab-code/large-files-and-omeka/#comments</comments>
		<pubDate>Wed, 16 Dec 2009 19:40:05 +0000</pubDate>
		<dc:creator>Wayne Graham</dc:creator>
				<category><![CDATA[SLab Code]]></category>

		<guid isPermaLink="false">http://www.scholarslab.org/?p=376</guid>
		<description><![CDATA[This issue came up for a friend of the Scholars&#8217; Lab today on Twitter, but it&#8217;s hard to answer in 140 characters. It&#8217;s a question about allowing for larger file sizes in Omeka and there are a few ways to handle this. (Because we want our new blog to be a combination of thoughtful essays [...]]]></description>
			<content:encoded><![CDATA[<p>This issue came up for a friend of the Scholars&#8217; Lab today on Twitter, but it&#8217;s hard to answer in 140 characters. It&#8217;s a question about allowing for larger file sizes in Omeka and there are a few ways to handle this.  (Because we want our new blog to be a combination of thoughtful essays on digital scholarship and quick answers to real-world technical problems, I thought I&#8217;d post here.)</p>
<p>Since Omeka runs on PHP, this is actually a PHP configuration issue and not something you can currently tweak in Omeka. Basically, you just need to tell PHP to allow larger files sizes that are larger than the default. A very easy way to do this is to edit the .htaccess file that Omeka ships with along the following lines:</p>
<pre class="brush: bash;">
php_value upload_max_filesize 20971520
php_value post_max_size 20971520
</pre>
<p>I&#8217;ll note here that doing things this way only affects your Omeka project. Another way to go about this is to add the above to the Apache configuration that defines from where Omeka should be served. For example:</p>
<pre class="brush: bash;">

&lt;VirtualHost *:80&gt;

ServerName www.coolomeka.org
DocumentRoot /var/www/omeka

&lt;Directory &quot;/var/www/omeka&quot;&gt;
    Options FollwSymLinks
    AllowOverride All
    Order allow,deny
    Allow from all
&lt;/Directory&gt;

    ErrorLog logs/omeka_error_log
    TransferLog logs/omeka_transfer_log

    php_value upload_max_filesize 20M
    php_value post_max_size 20M
&lt;/VirtualHost&gt;
</pre>
<p>Lastly, you can edit the php.ini file (usually in /etc/php.ini or /etc/php5/apache2/php.ini). Just do a search in the file and change the following settings:</p>
<pre class="brush: bash;">
  memory_limit = 32M
  post_max_size = 20M
  upload_max_size = 20M
</pre>
<p>You typically don&#8217;t need to reload Apache (as long as you did not edit the Apache configuration file) to get these settings to work.</p>
<p>For more info on this, check out these resources</p>
<ul>
<li><a href="http://php.net/manual/en/ini.core.php">Description of core php.ini directives</a></li>
<li><a href="http://www.radinks.com/upload/config.php">PHP Upload Configuration</a></li>
<li><a href="http://www.developershome.com/wap/wapUpload/wap_upload.asp?page=php2">PHP Directives Related to (Large) File Upload</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.scholarslab.org/slab-code/large-files-and-omeka/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
