<?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>Mission Data Blog &#187; darrend</title>
	<atom:link href="http://www.missiondata.com/blog/author/darrend/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.missiondata.com/blog</link>
	<description>Louisville-based Web Development &#38; Software Engineering</description>
	<lastBuildDate>Tue, 24 Jan 2012 14:58:58 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.1</generator>
		<item>
		<title>Treetop: Grammar&#8217;s Cool</title>
		<link>http://www.missiondata.com/blog/web-development/85/treetop-grammars-cool/</link>
		<comments>http://www.missiondata.com/blog/web-development/85/treetop-grammars-cool/#comments</comments>
		<pubDate>Wed, 13 Feb 2008 01:27:39 +0000</pubDate>
		<dc:creator>darrend</dc:creator>
				<category><![CDATA[Web Development]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[Treetop]]></category>

		<guid isPermaLink="false">http://www.missiondata.com/blog/uncategorized/85/treetop-grammars-cool/</guid>
		<description><![CDATA[Treetop was one of the more exciting projects I saw at last year&#8217;s RubyConf. Nathan Sobo&#8217;s Treetop talk is available online and I urge you to watch it. Nathan did a great job of explaining the basics of syntactic analysis, and then got into the specifics of using Treetop&#8217;s implementation of parsing expression grammars to [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://treetop.rubyforge.org/">Treetop</a> was one of the more exciting projects I saw at last year&#8217;s RubyConf. <a href="http://rubyconf2007.confreaks.com/d1t1p5_treetop.html">Nathan Sobo&#8217;s Treetop talk</a> is available online and I urge you to watch it. Nathan did a great job of explaining the basics of syntactic analysis, and then got into the specifics of using Treetop&#8217;s implementation of <a href="http://en.wikipedia.org/wiki/Parsing_expression_grammar">parsing expression grammars</a> to put the concepts to work.</p>
<p>Treetop appeared to gather all the concepts together into an understandable domain specific language. All of the tokenization and node structure can go into a single file, and the interactive nature of Ruby makes for the perfect sand box. I felt like I could get somewhere if I invested just an hour into this. I was happy to find that my impressions were correct.</p>
<p>After a short time I had caught on enough to start writing my own code. Once over the hump the rest was easy. I was able to write and test a Treetop grammar for parsing CSV files within a few hours. I chose CSV parsing because I was already familiar with the format, and I could compare my implementation to not just one but two existing Ruby libraries.<br />
<span id="more-85"></span></p>
<pre><code>commaseparatedfile.treetop</code></pre>
<pre><code>grammar CommaSeparatedFile
  rule lines
    line (newline line)*
    {
      def values
        vals = []
        vals &amp;lt;&amp;lt; line.values
        more.elements.each { |additional| vals &amp;lt;&amp;lt; additional.line.values }
        vals
      end
    }
  end

  rule line
    valueline / emptyline
  end

  rule emptyline
    '' {
      def values
        []
      end
    }
  end

  rule valueline
    leading:(value separator)* trailing:value
    {
      def values
        list = []
        list += leading.elements.collect { |lead| lead.value.text_value } if leading
        list &amp;lt;&amp;lt; trailing.text_value if trailing
        list
      end
    }
  end

  rule value
    quotedvalue / nakedvalue
  end

  rule quotedvalue
    '"' wrapped:( !'"' . / '""' )* '"' {
      def text_value
        wrapped.text_value.gsub(/""/,'"')
      end
    }
  end

  rule nakedvalue
    (!(separator / newline ) .)*
  end

  rule separator
    ','
  end

  rule newline
    [\n]
  end
end</code></pre>
<p>One concept that became clear to me is that the tokens that are defined in the grammar are objects in the resulting tree returned after input is parsed. Ruby code embedded in the Treetop&#8217;s node definitions are added as methods to the object declared by that node.</p>
<p>In the simple harness below, the <code>lines</code> object returned from the parse call below is the object representation of the <code>rule lines</code> node above, defined as the root of the parse tree. The <code>values</code> method I call below is defined above in the definition of the <code>lines</code> node.</p>
<pre><code>require 'rubygems'
require 'treetop'
require 'commaseparatedfile' 

parser = CommaSeparatedFileParser.new
lines = parser.parse("this,that,these,those")
puts lines.values.inspect #-&amp;gt; [["this", "that", "these", "those"]]</code></pre>
<p>I think it was much simpler for me to write this parsing as a grammar instead of trying to combine regular expressions with Ruby code. The grammar above handles multiple lines and quote-wrapped values, and escaped quotes.</p>
<p>It was actually fun to write as well, and I&#8217;m still learning a lot. Notice that my <code>lines</code> node and my <code>valueline</code> node both approach a similar problem (a list of 0 or more items) differently. For the lines the grammar grabs one line followed by 0 or more separators followed by lines. For the valueline I grab 0 ore more values followed by separators and terminate on the last value. Is one approach better than the other? Are they both junk? I can&#8217;t wait to figure that out.</p>
<p>My poor parser is left in the dust by both Ruby&#8217;s <a href="http://www.ruby-doc.org/stdlib/libdoc/csv/rdoc/index.html">stdlib csv library</a>, and the <a href="http://fastercsv.rubyforge.org/">fastercsv gem</a>. I ran these tests under the 1.8.5 version of Ruby. The <a href="http://www.missiondata.com/blog/wp-content/uploads/2008/02/kentuckycsv.gz">dataset I used</a> is 44k in total and has 961 records.</p>
<pre>                       user     system      total        real
stdlib_csv:        0.210000   0.000000   0.210000 (  0.217275)
faster_csv:        0.040000   0.000000   0.040000 (  0.060788)
treetop_grammar:   2.910000   0.010000   2.920000 (  3.126020)</pre>
<p>I don&#8217;t know what sort of speed I should reasonably expect, actually. I&#8217;d hoped that my compiler would be competitive with the standard library&#8217;s csv parsing. It&#8217;s possible that my grammar&#8217;s definition could be innefficient. If anyone has any ideas let me know.</p>
<p>After playing around with Treetop I&#8217;ll definitely tinker some more. There are many other features I haven&#8217;t explored yet, like extending a base grammar to create a new grammar. PEGs have implementations in other languages so this isn&#8217;t a Ruby-only facility, either.</p>
<p>On a side note, I used stdlib&#8217;s benchmark to generate the numbers above. Pretty swank stuff.</p>
<pre><code>require 'rubygems'
require 'treetop'
require 'commaseparatedfile'
require 'fastercsv'
require 'csv'
require 'benchmark'

def with_stdlib_csv(csv)
  parsed = CSV.parse(csv)
  parsed.size
end

def with_fastercsv(csv)
  rows = FasterCSV.parse(csv)
  rows.size
end

def with_treetop_parser(csv)
  parser = CommaSeparatedFileParser.new
  parsed = parser.parse(csv)
  parsed.values.size
end

csv = File.read('../kentucky.csv')

Benchmark.bmbm("treetop_parser:".length) do |x|
  x.report("stdlib_csv:") { with_stdlib_csv(csv) }
  x.report("faster_csv:") { with_fastercsv(csv) }
  x.report("treetop_grammar:") { with_treetop_parser(csv) }
end</code></pre>
]]></content:encoded>
			<wfw:commentRss>http://www.missiondata.com/blog/web-development/85/treetop-grammars-cool/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>&#8216;cvs update: move away foo.bar; it is in the way&#8217;</title>
		<link>http://www.missiondata.com/blog/system-administration/81/cvs-update-move-away-foobar-it-is-in-the-way/</link>
		<comments>http://www.missiondata.com/blog/system-administration/81/cvs-update-move-away-foobar-it-is-in-the-way/#comments</comments>
		<pubDate>Thu, 26 Apr 2007 21:00:40 +0000</pubDate>
		<dc:creator>darrend</dc:creator>
				<category><![CDATA[System Administration]]></category>
		<category><![CDATA[CVS]]></category>
		<category><![CDATA[Utilities]]></category>

		<guid isPermaLink="false">http://www.missiondata.com/blog/utilities/81/cvs-update-move-away-foobar-it-is-in-the-way/</guid>
		<description><![CDATA[A client site still uses cvs, the ever trusty version control system. After what seemed a run of the mill merge I noticed this: C lib/jt400_3_3.jar cvs update: move away lib/jt400_3_3.jar; it is in the way Very peculiar. That file hadn&#8217;t changed on the branch. I googled around and an explanation started to come together. [...]]]></description>
			<content:encoded><![CDATA[<p>A client site still uses cvs, the ever trusty version control system. After what seemed a run of the mill merge I noticed this:</p>
<pre><code class="console">C lib/jt400_3_3.jar
cvs update: move away lib/jt400_3_3.jar; it is in the way</code></pre>
<p>Very peculiar. That file hadn&#8217;t changed on the branch. I <a href="http://www.google.com/search?q=cvs+update+move+away">googled around</a> and an explanation started to come together.</p>
<p>First, from the always excellent <a href="http://mindprod.com/jgloss/cvs.html">Roedy Green’s Java &amp; Internet Glossary on Mindprod</a>:</p>
<blockquote><p>CVS is disturbed by the appearance of repository files it did not put there. Your best bet is simply to delete the entire directory containing the offending file by hand, and re checkout or reupdate the directory to build the necessary entries. Then you can add the files safely.</p></blockquote>
<p>Then, I found this <a href="http://mail.lon-capa.org/pipermail/lon-capa-admin/Week-of-Mon-20030915/000367.html">mailing list post</a>:</p>
<blockquote><p>This means the file that CVS wants to checkout exists on the local machine but CVS never created the file in the past. This it isn&#8217;t CVS&#8217;s file and it complains rather than blindly overwriting.</p></blockquote>
<p>The solution everywhere was the same: just blow away the directory and check it out again. Worked for me. Now I need to puzzle out how it happened in the first place&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.missiondata.com/blog/system-administration/81/cvs-update-move-away-foobar-it-is-in-the-way/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Rails fixtures with models using set_table_name</title>
		<link>http://www.missiondata.com/blog/systems-integration/80/rails-fixtures-with-models-using-set_table_name/</link>
		<comments>http://www.missiondata.com/blog/systems-integration/80/rails-fixtures-with-models-using-set_table_name/#comments</comments>
		<pubDate>Mon, 23 Apr 2007 03:13:47 +0000</pubDate>
		<dc:creator>darrend</dc:creator>
				<category><![CDATA[Systems Integration]]></category>
		<category><![CDATA[Tips]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://www.missiondata.com/blog/uncategorized/80/rails-fixtures-with-models-using-set_table_name/</guid>
		<description><![CDATA[If you have a model that uses set_table_name you may hit a snag when trying to use fixtures and unit tests. The solution is twofold: name the fixture file using the legacy table name, and use the set_fixture_class method in your unit test. I coudn&#8217;t find this method mentioned in Agile Web Development with Rails, [...]]]></description>
			<content:encoded><![CDATA[<p>If you have a model that uses <tt>set_table_name</tt> you may hit a snag when trying to use fixtures and unit tests. The solution is twofold: name the fixture file using the legacy table name, and use the <tt>set_fixture_class</tt> method in your unit test. I coudn&#8217;t find this method mentioned in <em>Agile Web Development with Rails</em>, and there&#8217;s only a brief mention on the <a href="http://wiki.rubyonrails.com/rails/pages/HowToUseLegacySchemas">rubyonrails.com site</a>. Still, some web searches turned up the answers.</p>
<p>Here is a simple example:</p>
<p><span id="more-80"></span></p>
<p>We&#8217;ll start with a table named <code>libris_book</code> that contains book entries. The name <em>libris_book</em> won&#8217;t really fly with the Rails pluralized table name approach. It&#8217;s easy, however, to fix that disconnect in the model:</p>
<pre><code>class Book &amp;lt; ActiveRecord::Base
  set_table_name 'libris_book'
end</code></pre>
<p>Let&#8217;s take a look at our data:</p>
<pre><code class="console">~/src/libris$ script/console
Loading development environment.
&amp;gt;&amp;gt; Book.count
=&amp;gt; 1230</code></pre>
<p>Good enough. Now, we&#8217;ll be good little coders and write some unit tests using fixtures. We&#8217;ll create a fixture file and our test class:</p>
<p><tt>books.yml</tt></p>
<pre><code>test_book:
  title: Life in Lawrenceburg
  author: Darren Day</code></pre>
<p><tt>book_test.rb</tt></p>
<pre><code>require File.dirname(__FILE__) + '/../test_helper'

class BookTest &amp;lt; Test::Unit::TestCase
  fixtures :books

  def test_fixtures
    assert_equal(2,Book.count)
    test_book = books(:test_book)
  end
end</code></pre>
<p>Looks good, but it won&#8217;t work!</p>
<pre><code class="console">~/src/libris$ rake test
(in /home/darrend/src/libris)
/usr/local/bin/ruby -Ilib:test "/usr/local/lib/ruby/gems/1.8/gems/rake-0.7.2/lib/rake/rake_test_loader.rb" "test/unit/book_test.rb"
Loaded suite /usr/local/lib/ruby/gems/1.8/gems/rake-0.7.2/lib/rake/rake_test_loader
Started
E
Finished in 0.01133 seconds.

  1) Error:
test_fixtures(BookTest):
ActiveRecord::StatementInvalid: Mysql::Error: Table 'libris_test.books' doesn't exist: DELETE FROM books
...</code></pre>
<p>Ugh. That doesn&#8217;t look pretty. After some <a href="http://www.google.com/search?q=set_table_name+fixtures">googling around</a> the following comes together</p>
<ul>
<li>Rename <tt>books.yml</tt> to <tt>libris_book.yml</tt>. The fixture yaml file has to share the same name as the database table name.</li>
<li>Change the <tt>fixtures</tt> declaration to <code>fixtures :libris_book</code>.</li>
<li> Use <tt>set_fixture_class</tt> to connect the fixture to the model class.</li>
</ul>
<p>Here is our new and improved <tt>book_test.rb</tt> file:</p>
<pre><code>require File.dirname(__FILE__) + '/../test_helper'

class BookTest &amp;lt; Test::Unit::TestCase
  set_fixture_class :libris_book =&amp;gt; Book
  fixtures :libris_book

  def test_fixtures
    assert_equal(2,Book.count)
    test_book = libris_book(:test_book)
  end
end</code></pre>
]]></content:encoded>
			<wfw:commentRss>http://www.missiondata.com/blog/systems-integration/80/rails-fixtures-with-models-using-set_table_name/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Hibernate and your Getters and Setters</title>
		<link>http://www.missiondata.com/blog/software-development/67/hibernate-and-your-getters-and-setters/</link>
		<comments>http://www.missiondata.com/blog/software-development/67/hibernate-and-your-getters-and-setters/#comments</comments>
		<pubDate>Sun, 11 Mar 2007 18:33:25 +0000</pubDate>
		<dc:creator>darrend</dc:creator>
				<category><![CDATA[Software Development]]></category>
		<category><![CDATA[hibernate]]></category>
		<category><![CDATA[java]]></category>

		<guid isPermaLink="false">http://www.missiondata.com/blog/uncategorized/67/hibernate-and-your-getters-and-setters/</guid>
		<description><![CDATA[When you&#8217;re using Hibernate and are mapping to properties, keep your getters and setters as simple and self-contained as possible. The receiver being initialized may not have any other properties set, and the value being passed may not be fully initialized yet, either. If you don&#8217;t respect these two possibilities, then you will get bit [...]]]></description>
			<content:encoded><![CDATA[<p>When you&#8217;re using Hibernate and are mapping to properties, keep your getters and setters as simple and self-contained as possible. The receiver being initialized may not have any other properties set, and the value being passed may not be fully initialized yet, either. </p>
<p>If you don&#8217;t respect these two possibilities, then you will get bit in the butt alot, when you least expect it. To be fair, these situations can happen whether you&#8217;re using Hibernate or not, but when we first started using the framework we made lots of assumptions.</p>
<p>Here is a completely ridiculous example that violates the above restrictions:<br />
<span id="more-67"></span></p>
<pre><code>public class User
{
  private boolean isSurnameLast;
  private Name name;  

  private String fullName;
  private String initials;

  public boolean setSurnameLast(boolean surnameLast)
  {
    this.isSurnameLast=surnameLast;
  }

  public void setName(Name name)
  {
    this.name = name;
    String lastName;
    String firstName;
    initials = new StringBuffer(name.getSurName().substring(0,1)).append(".").append(name.getGivenName().substring(0,1)).toString();
    if(isSurnameLast)
    {
      lastName = name.getSurName();
      firstName = name.getGivenName();
    }
    else
    {
      lastName = name.getGivenName();
      firstName = name.getSurName();
    }
    fullName = new StringBuffer(firstName).append(" ").append(lastName).toString();
  }
  // more getters and setters blah blah blah
}</code></pre>
<p>(Ignore the lame &#8220;business logic&#8221; here&#8230;)</p>
<p>So you look in your database and you see that all the Users database entries have names and properly set surnameLast values, and all the Names database entries have a surName and a givenName. Sure the code is sloppy, but this is gonna work fine! </p>
<p>Nope, this <code>setName</code> is loaded with problems.</p>
<p>First, you&#8217;ll notice as your app runs that somehow <code>setName</code> is being called and passed a null. What?! Why! Durn that Hibernate, it is trying to trick you! No matter, we&#8217;ll catch it with a null check. You&#8217;ll shield your eyes as to when and why Hibernate or cglib or whatever is calling setters on an instance that isn&#8217;t even in the database, but at least it won&#8217;t blow up. This is the first bad smell you&#8217;ve blown through, you sloppy panicky codemonkey:</p>
<pre><code>  public void setName(Name name)
  {
    if(name!=null) // hahah
   {
       this.name = name;
      ....</code></pre>
<p>Then you&#8217;ll start realizing that no matter what the database says, sometimes you are getting the surname first in the full name! &#8220;Smith Joan!&#8221;, you&#8217;ll cry, but &#8220;it&#8217;s true for Joan Smith in the database! Why?&#8221; Well, sometimes Hibernate is calling <code>setName</code> before it calls <code>setSurnameLast</code>. It doesn&#8217;t matter which you have mapped first in the <tt>User.hbm.xml</tt> file, or which field you have first in the code. You can&#8217;t depend on other properties of User being initialized in the Hibernate setters.</p>
<p>Now, you&#8217;re also going to get NullPointerExceptions thrown out of this code, because sometimes that Name instance you&#8217;re being passed hasn&#8217;t had <em>its</em> properties initialized by Hibernate yet. Talk about something that seems sneaky and evil but is totally above board: Hibernate has created the new Name instance and has passed that reference to other instances, but hasn&#8217;t fully populated the Name instance yet.</p>
<p>That means this line will blow sky-high sometimes:</p>
<pre><code>initials = new StringBuffer(name.getSurName().substring(0,1)).append(".").append(name.getGivenName().substring(0,1)).toString();</code></pre>
<p>The sane thing to do here?</p>
<pre><code>  public void setName(Name name)
  {
    this.name = name;
  }</code></pre>
<p>So if you&#8217;re using Hibernate, go take a look at your classes and mappings! You may very well be doing something dangerous.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.missiondata.com/blog/software-development/67/hibernate-and-your-getters-and-setters/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Slides for &#8220;You&#8217;ll Be Seeing Ruby&#8221;</title>
		<link>http://www.missiondata.com/blog/software-development/56/slides-for-youll-be-seeing-ruby/</link>
		<comments>http://www.missiondata.com/blog/software-development/56/slides-for-youll-be-seeing-ruby/#comments</comments>
		<pubDate>Thu, 18 May 2006 01:07:59 +0000</pubDate>
		<dc:creator>darrend</dc:creator>
				<category><![CDATA[Software Development]]></category>
		<category><![CDATA[presentation]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://blogs.missiondata.com/?p=56</guid>
		<description><![CDATA[The slides for the presentation are finally available, along with a zipfile, on the Presentation: You’ll Be Seeing Ruby page. It&#8217;s been a month now since our last performance, but it all comes back every time I look at the slides.]]></description>
			<content:encoded><![CDATA[<p>The <a href="http://blogs.missiondata.com/wp-content/uploads/manual/seeing_ruby_20060517/slides/1-index.html">slides for the presentation are finally available</a>, along with a zipfile, on the <a href="http://blogs.missiondata.com/?page_id=30">Presentation: You’ll Be Seeing Ruby</a> page.</p>
<p>It&#8217;s been a month now since our last performance, but it all comes back every time I look at the slides.  </p>
]]></content:encoded>
			<wfw:commentRss>http://www.missiondata.com/blog/software-development/56/slides-for-youll-be-seeing-ruby/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>managing disk space with logrotate</title>
		<link>http://www.missiondata.com/blog/system-administration/48/managing-disk-space-with-logrotate/</link>
		<comments>http://www.missiondata.com/blog/system-administration/48/managing-disk-space-with-logrotate/#comments</comments>
		<pubDate>Thu, 27 Apr 2006 02:28:13 +0000</pubDate>
		<dc:creator>darrend</dc:creator>
				<category><![CDATA[System Administration]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[logrotate]]></category>
		<category><![CDATA[Utilities]]></category>

		<guid isPermaLink="false">http://blogs.missiondata.com/?p=48</guid>
		<description><![CDATA[At a customer site, the test and production linux servers for some intranet applications were slowly running out of disk space. The apps themselves were running fine, it was the logfiles generated by the apps that were the issue; logging that came in useful just in case something went wrong, but quickly aged. In one [...]]]></description>
			<content:encoded><![CDATA[<p>At a customer site, the test and production linux servers for some intranet applications were slowly running out of disk space. The apps themselves were running fine, it was the logfiles generated by the apps that were the issue; logging that came in useful just in case something went wrong, but quickly aged. In one particular case, a catalina.out file was several years old and was 11 gigabytes; 90% of it wasn&#8217;t really relevant any longer.</p>
<p>The solution to the problem of wrangling logfiles is probably already installed on your server: it&#8217;s <a href="http://iain.cx/src/logrotate/">logrotate</a>. Chances are logrotate is already configured in your system crontab as a daily task, and chances are it is configured to obey any configuration files found in the <tt>/etc/logrotate.d</tt> directory. If you find that directory, you are probably good to go.</p>
<p><span id="more-48"></span><br />
In <tt>/etc/logrotate.d</tt> you&#8217;ll find several short configuration files. Each configuration deals with 1 or 2 related logfiles that should be checked and possibly administered on a regular basis. Logrotate can do alot of things with a logfile: clear the target log, create a copy of it, compress the copy, keep <em>N</em> backup copies, and it can be configured to rotate the target logfile on a daily, weekly, or monthly basis.</p>
<p>For example, here is how my client handles a logfile generated by a log4j-enabled java rmi server:</p>
<pre>
<code> /appdir/log/java_rmi_log.txt {
    daily
    rotate 10
    copytruncate
    compress
    notifempty
    missingok
 }</code>
</pre>
<p>This configuration means<br />
<blockquote>On a <em>daily</em> basis <em>rotate</em>, <em>compress</em>, and retain up to <em>10</em> days worth of logs using the <em>copytruncate</em> method. But <em>notifempty</em>; if the log is empty don&#8217;t do anything. Finally, <em>missingok</em>, so no need to log an error anywhere if the target logfile isn&#8217;t found.
</p></blockquote>
<p>This is nigh on a <a href="http://en.wikipedia.org/wiki/Domain_Specific_Language">domain specific language</a> for logfile rotation, and like most DSLs I&#8217;ve come across it&#8217;s much easier to read than it is to remember when writing. Don&#8217;t worry, the <a href="http://www.die.net/doc/linux/man/man8/logrotate.8.html">man page for logrotate</a> lists all the details.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.missiondata.com/blog/system-administration/48/managing-disk-space-with-logrotate/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>accessing as400 databases with Ruby, Java, and the RubyJavaBridge</title>
		<link>http://www.missiondata.com/blog/system-administration/46/accessing-as400-databases-with-ruby-java-and-the-rubyjavabridge/</link>
		<comments>http://www.missiondata.com/blog/system-administration/46/accessing-as400-databases-with-ruby-java-and-the-rubyjavabridge/#comments</comments>
		<pubDate>Tue, 25 Apr 2006 04:13:27 +0000</pubDate>
		<dc:creator>darrend</dc:creator>
				<category><![CDATA[System Administration]]></category>
		<category><![CDATA[as400]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[Systems Integration]]></category>

		<guid isPermaLink="false">http://blogs.missiondata.com/?p=46</guid>
		<description><![CDATA[iSeries systems and Ruby are separate universes right now; I know of no native Ruby way to connect to an AS400 system. However, with the help of Java we can bridge the gap. Probably the simplest example is connecting to an iSeries system using JDBC to select from some tables, and with the right libraries [...]]]></description>
			<content:encoded><![CDATA[<p>iSeries systems and Ruby are separate universes right now; I know of no native Ruby way to connect to an AS400 system. However, with the help of Java we can bridge the gap.</p>
<p>Probably the simplest example is connecting to an iSeries system using JDBC to select from some tables, and with the right libraries it is straightforward. It&#8217;s also fun to be doing some nifty quick Ruby but with some preexisting and proven Java libraries.</p>
<p>Also, if you use Java and Ruby, definitely put arton&#8217;s <a href="http://arton.no-ip.info/collabo/backyard/?RubyJavaBridge">RubyJavaBridge</a> in your pocket; you&#8217;ll end up using it more than once.</p>
<p>What you&#8217;ll need:</p>
<ul>
<li>Ruby (well yea)
<li>Java (ok)
<li>access to an AS400 system (that&#8217;s the whole point here&#8230;)<br />For testing, I&#8217;ve been using a free account available from <a href="http://as400.holgerscherer.de/indexeng.html">Holger Scherer Software und Beratung</a>.
<li><tt>jt400.jar</tt> jdbc drivers from <a href="http://jt400.sourceforge.net/">JTOpen</a>, a great open-source Java library for AS400 access.
<li><a href="http://arton.no-ip.info/collabo/backyard/?RubyJavaBridge">RubyJavaBridge</a> is the keystone for this.
</ul>
<p>Installing the software is the hardest part of this exercise, but it&#8217;s more tedium than anything. Once you&#8217;ve got it all downloaded and running the fun can begin.</p>
<p><span id="more-46"></span></p>
<pre>
<code>require 'rjb'
Rjb::load('jtopen_5_0/lib/jt400.jar',['-Djdbc.drivers=com.ibm.as400.access.AS400JDBCDriver'])

DriverManager = Rjb::import('java.sql.DriverManager')

begin
  connection = DriverManager.getConnection('jdbc:as400://as400.holgerscherer.de','YOUR_USERNAME','YOUR_PASSWORD')
  statement = connection.prepareStatement("SELECT count(*) FROM SYSIBM.TABLES")
  result_set = statement.executeQuery

  while(result_set.next())
    puts result_set.getObject(1).toString
  end
ensure
  result_set.close if defined?(result_set) &amp;&amp; !statement.nil?
  statement.close if defined?(statement) &amp;&amp; !statement.nil?
  connection.close if defined?(connection) &amp;&amp; !connection.nil?
end</code>
</pre>
<pre>
<code class="console">$ ruby as400_rjb.rb
19138</code>
</pre>
<p>Very readable, I think. Java and Ruby actually play well together given half the chance and <a href="http://arton.no-ip.info/collabo/backyard/?RubyJavaBridge">arton&#8217;s fantastic bridge</a>.</p>
<p>Compare this with the pure Java implementation.</p>
<pre>
<code>import java.sql.*;

public class As400Jdbc
{
  public static void main(String args[])
  {
    Connection connection = null;
    try
    {
      connection = DriverManager.getConnection("jdbc:as400://as400.holgerscherer.de","USER","PASS");
      PreparedStatement statement = connection.prepareStatement("SELECT COUNT(*) FROM SYSIBM.TABLES");

      ResultSet resultSet = statement.executeQuery();

      while(resultSet.next())
      {
        System.out.println(resultSet.getObject(1));
      }
    }
    catch(SQLException e)
    {
      throw new RuntimeException(e);
    }
    finally
    {
      try{ if(connection!=null) connection.close(); }
      catch(SQLException e)
      {
        throw new RuntimeException(e);
      }
    }

  }
}</code>
</pre>
<pre>
<code class="console">$ javac As400Jdbc.java
$ java -cp jtopen_5_0/lib/jt400.jar:. -Djdbc.drivers=com.ibm.as400.access.AS400JDBCDriver As400Jdbc
19138</code>
</pre>
<p>The Ruby code and the Java code are nearly one-for-one so far, and that&#8217;s fine. Still, let&#8217;s see if we can&#8217;t pull the Java code more into Ruby&#8217;s world. The <code>begin...end</code> stuff gets tiresome; it would be nice if these Java classes could handle the resource allocation and disposal drudgery for us, using blocks.</p>
<pre>
<code>require 'rjb'
Rjb::load('/home/darren/dload/jtopen_5_0/lib/jt400.jar',['-Djdbc.drivers=com.ibm.as400.access.AS400JDBCDriver'])

DriverManager = Rjb::import('java.sql.DriverManager')
class &lt;&lt;DriverManager
  def with_connection(*args)
    begin
      connection = DriverManager.getConnection(*args)
      yield connection
    ensure
      connection.close
    end
  end
end

DriverManager.with_connection('jdbc:as400://as400.holgerscherer.de','USER_NAME','USER_PASS') do |conn|
  statement = conn.prepareStatement("SELECT count(*) FROM SYSIBM.TABLES")
  result_set = statement.executeQuery()
  result_set.next()
  puts result_set.getObject(1).toString
end</code>
</pre>
<p>I know I&#8217;m a geek because stuff like this makes me giggle. So many worlds are colliding here&#8211;Ruby, Java, AS400, DB2, the client system (Linux in this case)&#8211;and it all comes together so nicely. </p>
<p>I&#8217;m not saying I&#8217;d build an app out of this, but it will more than suffice for ad-hoc queries and quick scripts.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.missiondata.com/blog/system-administration/46/accessing-as400-databases-with-ruby-java-and-the-rubyjavabridge/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Seeing Ruby</title>
		<link>http://www.missiondata.com/blog/software-development/39/seeing-ruby/</link>
		<comments>http://www.missiondata.com/blog/software-development/39/seeing-ruby/#comments</comments>
		<pubDate>Thu, 06 Apr 2006 02:01:58 +0000</pubDate>
		<dc:creator>darrend</dc:creator>
				<category><![CDATA[Software Development]]></category>
		<category><![CDATA[meta]]></category>
		<category><![CDATA[presentation]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://blogs.missiondata.com/?p=39</guid>
		<description><![CDATA[This past Friday I participated in my first presentation, Seeing Ruby: An Introduction to the Ruby Programming Language at the iTRC in Louisville, Kentucky. Chuck Fouts, Steven Yelton, and I did our level best to introduce our audience to one of our favorite development tools. Our audience was fantastic, and had lots of questions. Everyone [...]]]></description>
			<content:encoded><![CDATA[<p>This past Friday I participated in my first presentation, <a href="http://blogs.missiondata.com/?page_id=30">Seeing Ruby: An Introduction to the Ruby Programming Language</a> at the <a href="http://www.theitrc.com/">iTRC</a> in Louisville, Kentucky. Chuck Fouts, <a href="http://blogs.missiondata.com/?author=2">Steven Yelton</a>, and <a href="http://blogs.missiondata.com/?author=3">I</a> did our level best to introduce our audience to one of our favorite development tools. Our audience was fantastic, and had lots of questions. Everyone stayed a good 15 minutes after the official end to finish Steven&#8217;s awesome Rails demo.</p>
<p>Honestly, Ruby sells itself though. People tend to sit up and notice blocks and open classes.</p>
<p>We&#8217;ll be posting the presentation slides very soon to the <a href="http://blogs.missiondata.com/?page_id=30">permanent page</a>. And as we take the show to other venues we&#8217;ll be tweaking and refining the content. Initial feedback is to beef up the handout with a cheatsheet of language features; our whirlwind tour of the language goes by quickly and some sort of reference will help keep everything together.</p>
<p>Our audience expressed great interest in <a href="http://www.rubyonrails.org/">Ruby on Rails</a> and <a href="http://en.wikipedia.org/wiki/Ajax_%28programming%29">AJAX</a>, even a few requests for some Seeing More Ruby classes. We&#8217;re debating on whether &#8220;You&#8217;ll Be Seeing AJAX&#8221; or &#8220;You&#8217;ll Be Seeing Rails&#8221; next. :) Let us know which you&#8217;d rather see.</p>
<p>Incidentally, we&#8217;re also taking this presentation to a company for a private show, and we&#8217;d be willing to come talk with you and your colleagues as well. Just <a href="http://www.missiondata.com/contact.shtml">contact us</a>!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.missiondata.com/blog/software-development/39/seeing-ruby/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>cvs history</title>
		<link>http://www.missiondata.com/blog/system-administration/16/cvs-history/</link>
		<comments>http://www.missiondata.com/blog/system-administration/16/cvs-history/#comments</comments>
		<pubDate>Wed, 22 Feb 2006 15:45:52 +0000</pubDate>
		<dc:creator>darrend</dc:creator>
				<category><![CDATA[System Administration]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[Utilities]]></category>

		<guid isPermaLink="false">http://blogs.missiondata.com/?p=16</guid>
		<description><![CDATA[For alot of people, CVS is history; they&#8217;ve switched to SVN and are loving life. For those of us continuing the use this venerable tool, I present the following cobbled together bit of utility. It&#8217;s a bash function. and a nice way to keep an eye on what&#8217;s happening on a cvs repository. I work [...]]]></description>
			<content:encoded><![CDATA[<p>For alot of people, CVS <strong>is</strong> history; they&#8217;ve switched to SVN and are loving life. For those of us continuing the use this venerable tool, I present the following cobbled together bit of utility.</p>
<p>It&#8217;s a bash function. and a nice way to keep an eye on what&#8217;s happening on a cvs repository. I work with a team of around 7 guys, and I like to keep an eye on what&#8217;s changed and who changed it.</p>
<p>It works like this, summarizing what I consider to be the highlights from the <code>cvs history</code> command, and defaults to showing checkins for the current date:</p>
<pre><code class="console">$ cvshist
M 2006-02-22 10:34 EDT jsmith  1.3  InternalOperation.java         APRJ7A/src/com/ppc/oepica/model/internal
M 2006-02-22 10:53 EDT jsmith  1.37 FooBarCopier.java              APRJ7A/src/com/ppc/oepica/model/external
M 2006-02-22 11:26 EDT darrend 1.23 CommonJobBase.java             APRJ7A/src/com/ppc/oepica/model/common</code></pre>
<p>I use the <code>date</code> command, which gives me some flexibility when specifying a date</p>
<pre><code class="console">$ cvshist yesterday
$ cvshist 2 days ago
$ cvshist january 10
$ cvshist 2006-01-11</code></pre>
<p>Here&#8217;s the source. I&#8217;ve used this on linux and cygwin; you&#8217;ll need bash, gnu date, and cvs.</p>
<pre><code class="console">$ declare -f
cvshist ()
{
  export CVSROOT=:pserver:darrend@shlnxtest1:/home/cvs;
  TARGET_DATE=$(date -d "$*" +%Y-%m-%d);
  cvs history -a -z EDT -c -D $TARGET_DATE | sed "s/\s*==.*$//" | grep "$TARGET_DATE"
}</code></pre>
]]></content:encoded>
			<wfw:commentRss>http://www.missiondata.com/blog/system-administration/16/cvs-history/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>quick and dirty ruby</title>
		<link>http://www.missiondata.com/blog/web-development/15/quick-and-dirty-ruby/</link>
		<comments>http://www.missiondata.com/blog/web-development/15/quick-and-dirty-ruby/#comments</comments>
		<pubDate>Thu, 16 Feb 2006 16:24:39 +0000</pubDate>
		<dc:creator>darrend</dc:creator>
				<category><![CDATA[Web Development]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://blogs.missiondata.com/?p=15</guid>
		<description><![CDATA[At work I received an email from someone that listed libraries I should use when connecting to their AS400 system. I wanted a quick way to convert their whitespace table into a nice comma-separated list. Below is the first 6 lines from about 50 or so. Sequence number Library 10 QTEMP 20 DDMFLIB2 .... If [...]]]></description>
			<content:encoded><![CDATA[<p>At work I received an email from someone that listed libraries I should use when connecting to their AS400 system. I wanted a quick way to convert their whitespace table into a nice comma-separated list. Below is the first 6 lines from about 50 or so.</p>
<pre><code>Sequence
 number   Library
    10    QTEMP            

    20    DDMFLIB2
....</code></pre>
<p>If it&#8217;s computable but a one-off, I immediately go to Ruby nowadays. 10 years ago it would have been Perl, 15 or 20 years ago the choice would have been probably a meld of sed|awk.</p>
<p>I always go with <code>__END__</code> in these situations; Ruby stops processing at the <code>__END__</code> line, but you are given an enumerable access to the contents that follow with the magical <code>DATA</code> object.</p>
<pre><code>matches = DATA.collect { |line| line.match(/\\s+\\d+\\s+(\\w+)/) }
matches.compact!
puts matches.collect { |match| match[1] }.join(', ')

__END__
Initial library list:                            

Sequence
 number   Library
    10    QTEMP            

    20    DDMFLIB2
    30    APPSDEV
    50    FILES2                                                       

   110    QRPG                                                              </code></pre>
<p>Let&#8217;s try it out (running Ruby/Windows in a Cygwin terminal):</p>
<pre><code class="console">dday@xpmachine21 ~
$ ruby liblist.rb
QTEMP, DDMFLIB2, APPSDEV, FILES2, QRPG</code></pre>
<p>Success!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.missiondata.com/blog/web-development/15/quick-and-dirty-ruby/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>

