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

<channel>
	<title>John P Wood &#187; ruby</title>
	<atom:link href="http://johnpwood.net/tag/ruby/feed/" rel="self" type="application/rss+xml" />
	<link>http://johnpwood.net</link>
	<description>collection of thoughts...</description>
	<lastBuildDate>Thu, 10 May 2012 16:37:51 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Professionals Act Professional</title>
		<link>http://johnpwood.net/2012/04/12/professionals-act-professional/</link>
		<comments>http://johnpwood.net/2012/04/12/professionals-act-professional/#comments</comments>
		<pubDate>Thu, 12 Apr 2012 20:31:17 +0000</pubDate>
		<dc:creator>John Wood</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[community]]></category>
		<category><![CDATA[professionalism]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://johnpwood.net/?p=1433</guid>
		<description><![CDATA[I&#8217;m sick of it. Every week (at least it feels that way) some new drama rears its ugly head in the ruby community. Petty arguments via twitter, one ranting blog [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m sick of it.</p>
<p>Every week (at least it feels that way) some new drama rears its ugly head in the ruby community.  Petty arguments via twitter, one ranting blog post after another, people mocking ideas they consider less than ideal, and even some personal attacks thrown in the mix.  There&#8217;s so much drama in fact that there is now a site out there that lists it all for the rest of the world to see.</p>
<p>Seriously?  Are we all still in junior high?  </p>
<p>Just think for a minute about all of the time and energy we are wasting here.  Instead of igniting these flame wars, from which nothing productive is ever achieved, we could be growing as a community.  We could be bringing up the next generation of software developers.  We could be positively encouraging others to build better software.  We could be sharing our experiences with others.  We could be leading by example.</p>
<p>For a community of people complaining that they&#8217;re not treated like professionals, we sure don&#8217;t act very professional.  If this is the way we behave, can we honestly expect people to treat us with the respect that they treat doctors, accountants, teachers, and members of other professions?</p>
<p>If you want to be treated like a professional, it&#8217;s best to start acting like one first.</p>
<p>Take the high road for once.  The view is much nicer.</p>
]]></content:encoded>
			<wfw:commentRss>http://johnpwood.net/2012/04/12/professionals-act-professional/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Optional method parameters in Ruby</title>
		<link>http://johnpwood.net/2011/04/11/optional-method-parameters-in-ruby/</link>
		<comments>http://johnpwood.net/2011/04/11/optional-method-parameters-in-ruby/#comments</comments>
		<pubDate>Mon, 11 Apr 2011 15:13:55 +0000</pubDate>
		<dc:creator>John Wood</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://johnpwood.net/?p=1139</guid>
		<description><![CDATA[One of the things I love about Ruby is the flexibility it provides when it comes to method parameters. It&#8217;s dead simple to provide default values for one or more [...]]]></description>
			<content:encoded><![CDATA[<p>One of the things I love about Ruby is the flexibility it provides when it comes to method parameters.  It&#8217;s dead simple to provide default values for one or more parameters, to handle variable length argument lists, and to pass blocks of code into a method.  But perhaps my favorite is the ability to tack hash key/value pairs onto the end of a method call, and have those options combined into a Hash on the other side.</p>
<pre class="brush:ruby">
def some_method(required_1, required_2, options={})
  # Do something awesome!
end

some_method("foo", "bar")
some_method("foo", "bar", :option_1 => false, :option_2 => true)
some_method("foo",
            "bar",
            :option_1 => false,
            :option_2 => true,
            :option_3 => "something",
            :option_4 => "something else")
</pre>
<p>This may not look like much.  However, this feature alone is capable of producing some very readable code, and is used extensively in APIs throughout the Ruby ecosystem.  Consider for a moment what these APIs would look like if Ruby did not have this capability, which isn&#8217;t hard to imagine for those of us with a background in a language like Java.  You would either be forced to require that each parameter be specified:</p>
<pre class="brush:ruby">
# What is this code doing?  What do the nil values,
# or even the true and false values map to?
some_method("foo", "bar", false, nil, true, nil)
</pre>
<p>or accept a hash or a request object that contains all of the necessary parameters:</p>
<pre class="brush:ruby">
# This is much more readable, but requires that the
# options hash be created on its own line.
options = {:option_1 => true, :option_2 => false}
some_method("foo", "bar", options)
</pre>
<p>Providing optional parameters via hash key/value paris at the end of a method call produces code that is incredibly readable.  <strong>You have the names of the attributes right next to their corresponding values!</strong>  There is no ambiguity whatsoever as to which values match up with which parameters.</p>
<p>It is also very flexible.  The order of the attributes in the hash does not matter, like it does for required attributes.  And, it is very easy to add new options, or delete old ones.  </p>
<p>This approach also makes it easy to specify default values for options that were not specified when calling the method:</p>
<pre class="brush:ruby">
def some_method(required_1, required_2, options={})
  defaults = {
    :option_1 => "option 1 default",
    :option_2 => "option 2 default",
    :option_3 => "option 3 default",
    :option_4 => "option 4 default"
  }
  options = defaults.merge(options)

  # Do something awesome!
end
</pre>
<p>There are however a few minor drawbacks to this approach.  The first is documentation.  Methods that take a hash of options as a parameter do not convey any information about the valid options in the method definition alone.  And, it is possible that the method in question simply forwards the options to another method, sending you on a wild goose chase to determine the set of valid options the code supports.</p>
<pre class="brush:ruby">
# Looking for a list of valid option keys...no help here.
def some_method(required_1, required_2, options={})
  do_something_awesome_with_the_options(options)
end
</pre>
<p><strong>This is why it is so important do document your public API if you are using this approach.</strong>  Take a look at the <a href="http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html">ActiveRecord::Associations::ClassMethods documentation</a>.  This page documents, in a very clear and easy to read mannor, all of the supported options for each method.</p>
<p>It is also worth pointing out that while this approach is great for optional parameters, it is ill suited for required parameters.  Required parameters should be specified outside of the options hash, making it clear that values for the required parameters must be provided.  While it&#8217;s true that stuffing all of your parameters inside a hash means you&#8217;ll never have to look at another <code>wrong number of arguments</code> error again, it will make your code difficult to understand, and easy to misuse.</p>
]]></content:encoded>
			<wfw:commentRss>http://johnpwood.net/2011/04/11/optional-method-parameters-in-ruby/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Introducing Tenacity &#8211; An ORM Independent Way to Manage Inter-database Relationships</title>
		<link>http://johnpwood.net/2011/01/05/introducing-tenacity-an-orm-independent-way-to-manage-inter-database-relationships/</link>
		<comments>http://johnpwood.net/2011/01/05/introducing-tenacity-an-orm-independent-way-to-manage-inter-database-relationships/#comments</comments>
		<pubDate>Wed, 05 Jan 2011 15:45:55 +0000</pubDate>
		<dc:creator>John Wood</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[nosql]]></category>
		<category><![CDATA[polyglot persistence]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[tenacity]]></category>

		<guid isPermaLink="false">http://johnpwood.net/?p=1003</guid>
		<description><![CDATA[I&#8217;m a big believer in polyglot persistence. There are so many (very different) production ready databases available today that&#8217;s it is becoming more and more common to find applications using [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m a big believer in <a href="http://www.slideshare.net/jwoodslideshare/polyglot-persistence-two-great-tastes-that-taste-great-together-4625004">polyglot persistence</a>.  There are so many (very different) production ready databases available today that&#8217;s it is becoming more and more common to find <a href="2009/09/29/using-multiple-database-models-in-a-single-application/">applications using more than one database</a>, utilizing the strengths of each.  Using the right tool for the job gives me a warm, fuzzy feeling inside.</p>
<p>However, polyglot persistence comes with its own set of drawbacks.  One of those drawbacks is the loss of foreign keys, which are very important in maintaining data integrity.  Another drawback is that Object/Relational Mapping (ORM) libraries typically focus on a specific database, or type of database.  So, writing code that manages relationships between objects backed by different databases hasn&#8217;t been nearly as easy as writing code to manage relationships between objects in the same database.</p>
<p>Tenacity&#8217;s goal is to address some of these issues.  <a href="https://github.com/jwood/tenacity">Tenacity</a> is a ruby gem that provides an ORM independent way of managing relationships between models backed by different databases.</p>
<p>Tenacity works by <a href="https://github.com/jwood/tenacity/blob/master/EXTEND.rdoc">extending</a> popular Ruby ORM libraries to respond to a set of methods that the tenacity core uses to build and manage relationships between objects.  By extending the ORM libraries to implement this interface, tenacity is able work with the objects in a generic way, without having to know what database is backing the given objects.  This approach also allows you to continue using your favorite ORM libraries.  To use tenacity, you simply need to <code>include Tenacity</code> inside your model.</p>
<p>Tenacity is heavily based on <a href="http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html">ActiveRecord&#8217;s associations</a>, and aims to behave in much the same way, supporting many of the same options.</p>
<p>This initial release of tenacity supports <code>belongs_to</code>, <code>has_one</code>, and <code>has_many</code> associations, and the <a href="http://api.rubyonrails.org/classes/ActiveRecord/Base.html">ActiveRecord</a>, <a href="https://github.com/couchrest/couchrest">CouchRest</a>, and <a href="https://github.com/jnunemaker/mongomapper">MongoMapper</a> ORMs.  However, there is still <a href="https://github.com/jwood/tenacity/issues">plenty of work to be done</a>.  Feedback, <a href="https://github.com/jwood/tenacity/issues">bug reports</a>, and code contributions are always welcome.</p>
<p>Tenacity is free and open source, and can be found on GitHub at <a href="https://github.com/jwood/tenacity">https://github.com/jwood/tenacity</a>.</p>
<h2>Example</h2>
<p><script src="http://gist.github.com/754326.js"></script></p>
<h2>Download</h2>
<p><code>gem install tenacity</code></p>
]]></content:encoded>
			<wfw:commentRss>http://johnpwood.net/2011/01/05/introducing-tenacity-an-orm-independent-way-to-manage-inter-database-relationships/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Trastel Accepted as Official Service Level Testing Tool @ Orbitz</title>
		<link>http://johnpwood.net/2009/01/07/trastel-accepted-as-official-service-level-testing-tool-orbitz/</link>
		<comments>http://johnpwood.net/2009/01/07/trastel-accepted-as-official-service-level-testing-tool-orbitz/#comments</comments>
		<pubDate>Wed, 07 Jan 2009 23:33:00 +0000</pubDate>
		<dc:creator>John Wood</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[dsl]]></category>
		<category><![CDATA[orbitz]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[testing]]></category>

		<guid isPermaLink="false">http://johnpwood.net/?p=258</guid>
		<description><![CDATA[Trastel, the DSL I created to help with the automated testing of our services at Orbitz, has been accepted as the QA team&#8217;s official testing language. Yippee!]]></description>
			<content:encoded><![CDATA[<p><a href="http://johnpwood.net/2008/10/29/falling-in-love-with-dsls/">Trastel</a>, the DSL I created to help with the automated testing of our services at Orbitz, has been accepted as the QA team&#8217;s official testing language.  Yippee!</p>
]]></content:encoded>
			<wfw:commentRss>http://johnpwood.net/2009/01/07/trastel-accepted-as-official-service-level-testing-tool-orbitz/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Addressbook Webapp Has Been Released</title>
		<link>http://johnpwood.net/2008/12/19/addressbook-webapp-has-been-released/</link>
		<comments>http://johnpwood.net/2008/12/19/addressbook-webapp-has-been-released/#comments</comments>
		<pubDate>Sat, 20 Dec 2008 04:32:02 +0000</pubDate>
		<dc:creator>John Wood</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[personal projects]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://johnpwood.net/?p=204</guid>
		<description><![CDATA[Yay for me! I set a personal goal to have the code for all of my Rails apps on this site by the end of the year. Tonight, I can [...]]]></description>
			<content:encoded><![CDATA[<p>Yay for me!  I set a personal goal to have the code for all of my Rails apps on this site by the end of the year.  Tonight, I can check that one off the list.  The code for the <a href="http://johnpwood.net/projects/webapps/addressbook">Addressbook</a> webapp is now available for download.  Addressbook was not only my first Rails app, but also my first experience with Ajax.  And trust me, it shows.  I learned a lot from this project, especially what not to do.  However, I must also say that I use Addressbook more than any other personal project I have ever completed.  So, it can&#8217;t be that bad :)  I love the fact that I can access my contact information from anywhere, and that I can manage groups of addresses and print mailing labels with the click of a button.  Sure, the UI can be a bit unintuitive, but it&#8217;s not that big of an issue for me.  There are a few more things I&#8217;d like to do with this project.  We&#8217;ll see where it goes from here.</p>
]]></content:encoded>
			<wfw:commentRss>http://johnpwood.net/2008/12/19/addressbook-webapp-has-been-released/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Falling in Love with DSLs</title>
		<link>http://johnpwood.net/2008/10/29/falling-in-love-with-dsls/</link>
		<comments>http://johnpwood.net/2008/10/29/falling-in-love-with-dsls/#comments</comments>
		<pubDate>Wed, 29 Oct 2008 12:55:05 +0000</pubDate>
		<dc:creator>John Wood</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[dsl]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[testing]]></category>

		<guid isPermaLink="false">http://johnpwood.net/?p=162</guid>
		<description><![CDATA[We were recently given a free day at work to hack on a project that was outside the realm of our normal responsibilities, yet could still be beneficial to the [...]]]></description>
			<content:encoded><![CDATA[<p>We were recently given a free day at work to hack on a project that was outside the realm of our normal responsibilities, yet could still be beneficial to the company.  We were encouraged to be creative, explore ideas that interested us, and see if we could come up with something to demo at the end of the day.</p>
<p>Service level testing, or functional testing, has been a hot topic at work recently.  It&#8217;s <a href="http://www.sun.com/software/jini/news/Jini_Orbitz_Profile_Final.pdf">no secret</a> that we have a very large SOA at Orbitz, powered by Jini.  Services, calling services, calling services&#8230;you get the picture.  Historically, we have not been the best at automating the testing of these services.  That is beginning to change.  About two years ago, the team I work on developed a test tool that allows us to interact with our services at a very high level, keeping us out of the nitty-gritty details when invoking a service.  It&#8217;s a command line based tool with a very simple, intuitive syntax.  All of the details are accessible if we need them, but more often than not they just get in the way.  In fact, we enjoyed working at this level of abstraction so much, that I wrote a functional test framework (named jwoodunit by my co-workers) that drove service level tests against our services, which were written in this same &#8220;language&#8221;.  It allowed us to pump out service level tests is very short order that were easy to read, and easy to maintain.  It has only just occurred to me that what we had created was really a Domain Specific Language (DSL).</p>
<p>We have a fairly large quality assurance team that is made up mostly non-developers.  Most of the testing that is done is manual, or driven by an automation tool similar to <a href="http://selenium.openqa.org/">Selenium</a>.  The problem is that the manual testing is slow and not reliable, and tools like Selenium tend to be brittle, since they are based on the layout of the HTML page.  It also prevents you from testing any service that is not directly accessible through the web application.  So, for my project, I wanted to see if I could take my team&#8217;s DSL, clean it up even more (it is still very &#8220;programmy&#8221;), and give it to our quality assurance team so that they could test our services in a more reliable fashion. </p>
<p>What I ended up with was a very high level, English like DSL that I call Trastel (Travel Service Testing Language).  Trastel is implemented in Ruby.  In fact, it&#8217;s safe to say that Trastel <strong>is</strong> Ruby.  I didn&#8217;t implement a new language.  I simply took advantage of Ruby&#8217;s fantastic meta programming capabilities to extend the language with functionality that is needed by the tests.  A example test is worth 1000 words:</p>
<pre class="brush:ruby">
search.flights.on("orbitz").where(
   :origin => "ORD",
   :destination => "LAX",
   :departure_date => "2008/12/10",
   :return_date => "2008/12/15"
)

verify_at_least_one_result

foreach_result do
  verify_equal "ORD", origin
end
</pre>
<p>This does exactly what you&#8217;d expect.  It searches flights on Orbitz, flying from Chicago&#8217;s O&#8217;Hare airport to Los Angeles International Airport on 2008/12/10, returning on 2008/12/15.  We then verify at least one result came back, and that the origin of each flight is O&#8217;Hare.  That&#8217;s it.</p>
<p>Implementing this test in Ruby was a breeze.  Since everything is an object in Ruby, dynamically adding methods to the Object class gives us the ability to create pseudo-keywords, like &#8220;search&#8221;, and &#8220;foreach_result&#8221;.  Trastel also sets an attribute named @response on the test, so the code that implements the verify methods can just check that, instead of having to specify that your checking the response of the service call.  foreach_result will iterate over the @response if it is an array, yielding to the specified code block for each item in that array&#8230;giving us an easy way to check each element.  The last bit of magic surrounds the &#8220;origin&#8221; method.  &#8220;origin&#8221; isn&#8217;t a method on object.  It&#8217;s a method on the the type contained in the @response.  Thanks to Ruby&#8217;s method_missing method, I can forward that method call onto the object in the @response for it to tell me what origin means.  Nice and easy.</p>
<p>Another great thing about Trastel is that it sucks in Active Support.  Active Support brings with it a wealth of extensions to the core ruby classes, letting us do something like this:</p>
<pre class="brush:ruby">
search.flights.on("orbitz").where(
   :origin => "ORD",
   :destination => "LAX",
   :departure_date => 4.weeks.from_now,
   :return_date => 6.weeks.from_now
)
</pre>
<p>We still do all of the heavy lifting (creation of the service request, execution of the remote service, etc) in Java, using JRuby to get the Ruby code to play nice with the Java code.  But, the DSL serves as an excellent means to collect the data needed to drive the test, call the Java code to invoke the service with that data, and verify the data in the response matches what was expected.  It successfully keeps the person writing the test from having to know anything about actually invoking the service.  Writing tests at this level of abstraction also shields us from the majority of service API changes that may come our way.</p>
<p>With all of the benefits that I can already see with this still very immature DSL, I&#8217;m kicking myself for not considering DSLs more seriously in the past.  They have been around quite some time, and have had many well know developers singing their praises.  I&#8217;m looking to see what other problems I&#8217;m currently facing that could be solved a little easier by letting me code a little closer to the domain.  Had I know that it would be this simple, maybe I would have tried it a long time ago.</p>
]]></content:encoded>
			<wfw:commentRss>http://johnpwood.net/2008/10/29/falling-in-love-with-dsls/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Diners Club Webapp Has Been Released</title>
		<link>http://johnpwood.net/2008/10/14/diners-club-webapp-has-been-released/</link>
		<comments>http://johnpwood.net/2008/10/14/diners-club-webapp-has-been-released/#comments</comments>
		<pubDate>Tue, 14 Oct 2008 12:09:45 +0000</pubDate>
		<dc:creator>John Wood</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[personal projects]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://johnpwood.net/?p=151</guid>
		<description><![CDATA[I finally got around to cleaning up and publishing the code for the Diners Club web application. Like everything else I release, I&#8217;m not sure who will actually use it. [...]]]></description>
			<content:encoded><![CDATA[<p>I finally got around to cleaning up and publishing the code for the <a href="http://johnpwood.net/projects/webapps/diners-club">Diners Club web application</a>.  Like everything else I release, I&#8217;m not sure who will actually use it.  But, I find that the whole exercise of <a href="http://johnpwood.net/2008/03/26/release-your-code/">releasing your code</a> is beneficial to you and your code.  So, it&#8217;s worth the effort.  It&#8217;s available via the MIT license, so have at it.</p>
<p>Kind of a funny side note about this project.  I recently found out that a couple of co-workers of mine spend some of their spare time working on <a href="http://planyp.us/">Planypus</a>, which is a site for making plans with your friends.  Sounds familiar!  I was talking to them, and it turns out that Planypus started the exact same way that the Diners Club webapp started, as just a way to organize dinner outings with friends.  They however took it to the next level and threw a business plan around it.  I&#8217;m just not that business savvy I guess :)</p>
]]></content:encoded>
			<wfw:commentRss>http://johnpwood.net/2008/10/14/diners-club-webapp-has-been-released/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Bugzilla and SMTP Authentication</title>
		<link>http://johnpwood.net/2008/04/01/bugzilla-and-smtp-authentication/</link>
		<comments>http://johnpwood.net/2008/04/01/bugzilla-and-smtp-authentication/#comments</comments>
		<pubDate>Tue, 01 Apr 2008 23:43:50 +0000</pubDate>
		<dc:creator>John Wood</dc:creator>
				<category><![CDATA[Scripts]]></category>
		<category><![CDATA[bugzilla]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://johnpwood.net/archives/2008/04/01/4.html</guid>
		<description><![CDATA[The last few months I&#8217;ve been helping out in an effort to upgrade Shotokan Karate of America&#8216;s membership management system. They&#8217;re rewriting the system from scratch, as the old system [...]]]></description>
			<content:encoded><![CDATA[<p>The last few months I&#8217;ve been helping out in an effort to upgrade <a href="http://www.ska.org">Shotokan Karate of America</a>&#8216;s membership management system.  They&#8217;re rewriting the system from scratch, as the old system has grown to be unmaintainable.  The code for the new system was about 85% complete when I signed up, so I offered to start testing the system while the sole developer working on the project finished up the code.  There were no unit tests, so I figured that would be a good place to start.  This naturally brought up the issue of how we would communicate problems and fixes, so I offered to install <a href="http://www.bugzilla.org">Bugzilla</a> on my Linux box and suggested we use that.</p>
<p>It wasn&#8217;t until later that I found out that Bugzilla and SMTP Authentication do not get along (although I see that there <a href="http://groups.google.com/group/mozilla.dev.apps.bugzilla/browse_thread/thread/de86ad9d25eb2191/52a0fb37c83fb19b">is a fix</a> in the pipeline).  Bugzilla supports SMTP, but without authentication.  It also supports sendmail, but my ISP isn&#8217;t too keen on people running their own mail servers, making the use of sendmail difficult.  Since my ISP has a SMTP server for me to use, I figured I would use that.  But, authentication is required on that server.</p>
<p>I toyed around with some ideas in an attempt to keep me from hacking the Bugzilla code.  One involved parsing the data/mailer.testfile file (where mail is sent when the &#8220;testfile&#8221; mail option is set) for the To address and the email body, and sending the email via a custom script.  But, that brought up some interesting race conditions if multiple users made a change that triggered an email at approximately the same time.   We would need to periodically read that file to send the email, wiping out the contents afterward to avoid resending the same messages.  If the timing was right, we could potentially wipe out a message that was never sent.</p>
<p>I took a look at the Bugzilla code, and found the module that was responsible for sending the email.  The code was clean, and pretty easy to follow, even though I know very little perl.  I found the point in the module where it calls out to the Mail Transport Agent to deliver the mail.  I figured I could easily insert a call to a script to deliver the mail, using SMTP Authentication.  That is exactly what I did.  I modified BugMail.pm to include the &#8220;system&#8221; call below.</p>
<pre class="brush:perl">
sub MessageToMTA {
    ....

    # --- Begin New Lines ---
    system('send-bugzilla-email.rb', $msg);
    # --- End New Lines ---

    $mailer-&gt;open($headers-&gt;header_hashref);
    print $mailer $body;
    $mailer-&gt;close;
}</pre>
<p>$msg is a variable that contains the SMTP message to send.  That message includes the To address, the subject of the email, and the body of the email.  In other words, everything we would need to send the mail ourselves.  I then wrote a ruby script to parse the message, and send the email.  Here&#8217;s the script.</p>
<pre class="brush:ruby">
#!/usr/bin/ruby

require 'net/smtp'

from_address = 'username@my_isp.com'

# Break the message up into an array of strings
message_array = ARGV.first.split("\n")

# Pull out the To address and the subject
message_array&#91;1&#93; =~ /^To: (.*)$/
to_address = $1

message_array&#91;2&#93; =~ /^Subject: (.*)$/
subject = $1

# Delete some crap that we do not care about
4.times { |i| message_array.delete_at(0) }

# Combine what remains back into the body
body = message_array.join("\n")

message =  "From: Bugzilla &lt;#{from_address}&gt;\n"
message &lt;&lt; "To: #{to_address}\n"
message &lt;&lt; "Subject: #{subject}\n"
message &lt;&lt; body

# Send the email
Net::SMTP.start('smtp.my_isp.com', 25, 'my_isp.com', 'username', 'password', :login) do |smtp|
  smtp.send_message message, from_address, to_address
end

File.open('/var/log/bugzilla_email.log', 'a+') do |file|
  file.puts("\n-----------------------------------------\n")
  file.puts(message)
end</pre>
<p>Although not ideal, it does the job.  And, if I do say so myself, it works quite well.  I&#8217;ll be looking forward to upgrading to the new version of Bugzilla once it supports SMTP authentication.  In the meantime, this will keep things rolling.</p>
]]></content:encoded>
			<wfw:commentRss>http://johnpwood.net/2008/04/01/bugzilla-and-smtp-authentication/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
	</channel>
</rss>

