<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-1333829102577914020</id><updated>2011-11-27T19:55:55.519-05:00</updated><category term='ruby'/><category term='NoVA'/><category term='MySQL'/><category term='recipes'/><category term='food'/><category term='photography'/><category term='Mac'/><category term='coding'/><title type='text'>Bit Blender</title><subtitle type='html'>Tasty blended bits of information.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://bitblender.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1333829102577914020/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://bitblender.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Gary</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://twist.smugmug.com/photos/32078646-Ti.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>20</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-1333829102577914020.post-6204858621580282341</id><published>2011-02-14T21:07:00.006-05:00</published><updated>2011-04-29T20:25:59.833-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='coding'/><title type='text'>Making an executable Ruby archive distribution</title><content type='html'>&lt;p&gt;As a follow-up to my &lt;a href="/2011/02/distributing-ruby-application-as.html"&gt;previous post&lt;/a&gt;, you can even make a single executable file of your Ruby archive.  It relies on the fact that zip files are pretty resilient to extra garbage data in the beginning.  Let's say I have two files, &lt;code class="prettyprint"&gt;lib/greetings.rb&lt;/code&gt; and &lt;code class="prettyprint"&gt;bin/hello.rb&lt;/code&gt;.  &lt;code class="prettyprint"&gt;greetings.rb&lt;/code&gt; defines a &lt;code class="prettyprint"&gt;make_hello()&lt;/code&gt; function that is used by &lt;code class="prettyprint"&gt;hello.rb&lt;/code&gt;.  Here's &lt;code class="prettyprint"&gt;hello.rb&lt;/code&gt;:&lt;/p&gt;

&lt;pre class="prettyprint"&gt;
require "lib/greetings"

puts "Enter your name:"
name = gets.strip

puts "\n" + make_hello(name)
&lt;/pre&gt;

&lt;p&gt;You can run it like this:&lt;/p&gt;

&lt;pre class="prettyprint"&gt;
$ ruby bin/hello.rb 
Enter your name:
Steve

Hello, Steve!
$
&lt;/pre&gt;

&lt;p&gt;First, we make a zip file that includes all the required source files:&lt;/p&gt;

&lt;pre class="prettyprint"&gt;
$ zip -r hello.zip bin lib
  adding: bin/ (stored 0%)
  adding: bin/hello.rb (deflated 11%)
  adding: lib/ (stored 0%)
  adding: lib/greetings.rb (deflated 7%)
$ unzip -l hello.zip 
Archive:  hello.zip
  Length     Date   Time    Name
 --------    ----   ----    ----
        0  02-14-11 21:01   bin/
       98  02-14-11 21:01   bin/hello.rb
        0  02-14-11 20:59   lib/
       46  02-14-11 20:59   lib/greetings.rb
 --------                   -------
      144                   4 files
&lt;/pre&gt;

&lt;p&gt;Now we need a ruby header for the zip file, which we'll put into &lt;code class="prettyprint"&gt;header.rb&lt;/code&gt;:&lt;/p&gt;

&lt;pre class="prettyprint"&gt;
#!/usr/bin/ruby -rubygems -x

require "zip/ziprequire"
$:.push $0
require "bin/hello.rb"
__END__
&lt;/pre&gt;

&lt;p&gt;It runs the ruby interpreter, which then loads &lt;code class="prettyprint"&gt;zip/ziprequire&lt;/code&gt;, adds itself (which will be the zip file) to the path, and then requires our main file, &lt;code class="prettyprint"&gt;bin/hello.rb&lt;/code&gt;.  We now prepend this header to the zip file using &lt;code class="prettyprint"&gt;cat header.rb hello.zip &amp;gt; hello_tmp.zip&lt;/code&gt;.  If you now try to run &lt;code class="prettyprint"&gt;unzip -l&lt;/code&gt; on this file, you'll get:&lt;/p&gt;

&lt;pre class="prettyprint"&gt;
$ unzip -l hello_tmp.zip 
Archive:  hello_tmp.zip
warning [hello_tmp.zip]:  97 extra bytes at beginning or within zipfile
  (attempting to process anyway)
  Length     Date   Time    Name
 --------    ----   ----    ----
        0  02-14-11 21:01   bin/
       98  02-14-11 21:01   bin/hello.rb
        0  02-14-11 20:59   lib/
       46  02-14-11 20:59   lib/greetings.rb
 --------                   -------
      144                   4 files
&lt;/pre&gt;

&lt;p&gt;We need to fix this zip file to make it valid.  Thankfully &lt;code class="prettyprint"&gt;zip&lt;/code&gt; has an option for this:&lt;/p&gt;

&lt;pre class="prettyprint"&gt;

$ zip --fix hello_tmp.zip --out hellox.zip
Fix archive (-F) - assume mostly intact archive
Zip entry offsets appear off by 97 bytes - correcting...
 copying: bin/
 copying: bin/hello.rb
 copying: lib/
 copying: lib/greetings.rb
&lt;/pre&gt;

&lt;p&gt;We now have a valid zip archive in &lt;code class="prettyprint"&gt;hellox.zip&lt;/code&gt;, we just need to make it executable by running &lt;code class="prettyprint"&gt;chmod +x hellox.zip&lt;/code&gt;.  Now your whole application is a single executable file that you can run:&lt;/p&gt;

&lt;pre class="prettyprint"&gt;

$ ./hellox.zip 
Enter your name:
Mike

Hello, Mike!
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1333829102577914020-6204858621580282341?l=bitblender.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bitblender.blogspot.com/feeds/6204858621580282341/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1333829102577914020&amp;postID=6204858621580282341' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1333829102577914020/posts/default/6204858621580282341'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1333829102577914020/posts/default/6204858621580282341'/><link rel='alternate' type='text/html' href='http://bitblender.blogspot.com/2011/02/making-executable-ruby-archive.html' title='Making an executable Ruby archive distribution'/><author><name>Gary</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://twist.smugmug.com/photos/32078646-Ti.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1333829102577914020.post-6614242276824357789</id><published>2011-02-08T22:15:00.004-05:00</published><updated>2011-02-08T22:33:37.392-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='coding'/><title type='text'>Distributing a Ruby application as an archive</title><content type='html'>&lt;p&gt;Let's say you have a Ruby application you've written, and it consists of multiple files that you &lt;code class="prettyprint"&gt;require&lt;/code&gt; inside your code.  You want to run this application on some remote machines.  To make it easier to deploy this application, you want to distribute it as a single file (archive.)  This is possible with the &lt;code class="prettyprint"&gt;rubyzip&lt;/code&gt; gem.  Let's say your main application file (&lt;code class="prettyprint"&gt;myapp.rb&lt;/code&gt;) looks like this:&lt;/p&gt;

&lt;pre class="prettyprint"&gt;
require "lib/mylib"
require "lib/otherlib"
require "vendorlib/something"

# Do stuff.
&lt;/pre&gt;

&lt;p&gt;Normally you might run your application with &lt;code class="prettyprint"&gt;ruby ./myapp.rb&lt;/code&gt;.  To run it on another machine, we can use the &lt;code class="prettyprint"&gt;zip/ziprequire&lt;/code&gt; library in &lt;code class="prettyprint"&gt;rubyzip&lt;/code&gt;.  First, make a zip file containing all your application files:&lt;/p&gt;

&lt;pre class="prettyprint"&gt;
zip -r myapp.zip myapp.rb lib vendorlib
&lt;/pre&gt;

&lt;p&gt;Copy &lt;code class="prettyprint"&gt;myapp.zip&lt;/code&gt; to the remote machine, and you can run it like this:&lt;/p&gt;

&lt;pre class="prettyprint"&gt;
ruby -rubygems -Imyapp.zip -e 'require "zip/ziprequire"' -e 'require "myapp"'
&lt;/pre&gt;

&lt;p&gt;See &lt;a href="http://rubyzip.sourceforge.net/"&gt;rubyzip documentation&lt;/a&gt;, specifically &lt;a href="http://rubyzip.sourceforge.net/files/lib/zip/ziprequire_rb.html"&gt;ziprequire.rb&lt;/a&gt; for more information.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1333829102577914020-6614242276824357789?l=bitblender.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bitblender.blogspot.com/feeds/6614242276824357789/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1333829102577914020&amp;postID=6614242276824357789' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1333829102577914020/posts/default/6614242276824357789'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1333829102577914020/posts/default/6614242276824357789'/><link rel='alternate' type='text/html' href='http://bitblender.blogspot.com/2011/02/distributing-ruby-application-as.html' title='Distributing a Ruby application as an archive'/><author><name>Gary</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://twist.smugmug.com/photos/32078646-Ti.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1333829102577914020.post-5446250180348041156</id><published>2011-01-11T09:42:00.002-05:00</published><updated>2011-01-11T09:53:01.056-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Mac'/><category scheme='http://www.blogger.com/atom/ns#' term='coding'/><title type='text'>Using FileMerge with Mercurial</title><content type='html'>&lt;p&gt;Mac OS comes with a GUI merge tool called FileMerge.  This can be used for merges in Mercurial - Mercurial will do this if its internal merge fails.  The binary for FileMerge (&lt;code class="prettyprint"&gt;opendiff&lt;/code&gt;) cannot be used as is, so we need to create a small shell script.  You can put this script anywhere in your path, and call it &lt;code class="prettyprint"&gt;hopendiff&lt;/code&gt;:&lt;/p&gt;

&lt;pre class="prettyprint"&gt;
#!/bin/sh
`/usr/bin/opendiff "$@"`
&lt;/pre&gt;

&lt;p&gt;Then modify your &lt;code class="prettyprint"&gt;~/.hgrc&lt;/code&gt; and add:&lt;/p&gt;

&lt;pre class="prettyprint"&gt;
[extensions]
hgext.extdiff =

[extdiff]
cmd.opendiff = hopendiff

[merge-tools]
filemerge.executable = hopendiff
filemerge.args = $local $other -ancestor $base -merge $output 
&lt;/pre&gt;

&lt;p&gt;If you already have some of those sections (like &lt;code class="prettyprint"&gt;[extensions]&lt;/code&gt;), then just add the corresponding lines to those sections.&lt;/p&gt;

&lt;p&gt;That's it.  When you do a merge in Mercurial, it'll open FileMerge if the internal merge fails.  You can also use FileMerge for normal diffs by using &lt;code class="prettyprint"&gt;hg opendiff&lt;/code&gt; instead of &lt;code class="prettyprint"&gt;hg diff&lt;/code&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1333829102577914020-5446250180348041156?l=bitblender.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bitblender.blogspot.com/feeds/5446250180348041156/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1333829102577914020&amp;postID=5446250180348041156' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1333829102577914020/posts/default/5446250180348041156'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1333829102577914020/posts/default/5446250180348041156'/><link rel='alternate' type='text/html' href='http://bitblender.blogspot.com/2011/01/using-filemerge-with-mercurial.html' title='Using FileMerge with Mercurial'/><author><name>Gary</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://twist.smugmug.com/photos/32078646-Ti.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1333829102577914020.post-4575269946295725275</id><published>2011-01-06T17:44:00.002-05:00</published><updated>2011-01-06T18:08:47.160-05:00</updated><title type='text'>Binary and character set safe MySQL dump and restore</title><content type='html'>&lt;p&gt;To dump some MySQL databases in a binary-safe way:&lt;/p&gt;
&lt;pre class="prettyprint"&gt;
mysqldump -uroot -p -h&lt;i&gt;dbhost&lt;/i&gt; \ 
  --skip-extended-insert \ 
  --default-character-set=binary \ 
  --databases &lt;i&gt;dbone&lt;/i&gt; &lt;i&gt;dbtwo&lt;/i&gt; &lt;i&gt;dbthree&lt;/i&gt; \ 
  --add-drop-database --master-data=2 | gzip &amp;gt; /tmp/dump.sql.gz
&lt;/pre&gt;

&lt;p&gt;Replace &lt;code class="prettyprint"&gt;--master-data=2&lt;/code&gt; with &lt;code class="prettyprint"&gt;--lock-all-tables&lt;/code&gt; if dumping from a slave, and record the output of &lt;code class="prettyprint"&gt;SHOW SLAVE STATUS&lt;/code&gt; while everything is locked.&lt;/p&gt;

&lt;p&gt;To import this dump:&lt;/p&gt;
&lt;pre class="prettyprint"&gt;
zcat /tmp/dump.sql.gz | mysql -uroot -p -h&lt;i&gt;otherdbhost&lt;/i&gt; \ 
   --unbuffered --batch --default-character-set=binary
&lt;/pre&gt;

&lt;p&gt;To set up replication:&lt;/p&gt;
&lt;p&gt;Use the &lt;code class="prettyprint"&gt;Relay_Master_Log_File&lt;/code&gt;:&lt;code class="prettyprint"&gt;Exec_Master_Log_Pos&lt;/code&gt; values from &lt;code class="prettyprint"&gt;SHOW SLAVE STATUS&lt;/code&gt; that you recorded.  See this page for explanation:&lt;/p&gt;
&lt;p&gt;&lt;a href="http://www.mysqlperformanceblog.com/2008/07/07/how-show-slave-status-relates-to-change-master-to/"&gt;http://www.mysqlperformanceblog.com/2008/07/07/how-show-slave-status-relates-to-change-master-to/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;
&lt;pre class="prettyprint"&gt;
CHANGE MASTER TO MASTER_HOST = 'abc123.dklfjalddkj.com',
  MASTER_USER = 'rep', MASTER_PASSWORD = 'CHANGEME',
  MASTER_LOG_FILE = 'abc123-bin.003292',
  MASTER_LOG_POS = 283072761;
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1333829102577914020-4575269946295725275?l=bitblender.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bitblender.blogspot.com/feeds/4575269946295725275/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1333829102577914020&amp;postID=4575269946295725275' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1333829102577914020/posts/default/4575269946295725275'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1333829102577914020/posts/default/4575269946295725275'/><link rel='alternate' type='text/html' href='http://bitblender.blogspot.com/2011/01/binary-and-character-set-safe-mysql.html' title='Binary and character set safe MySQL dump and restore'/><author><name>Gary</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://twist.smugmug.com/photos/32078646-Ti.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1333829102577914020.post-4597427218527652915</id><published>2010-09-02T13:40:00.014-04:00</published><updated>2011-01-11T09:42:01.773-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MySQL'/><category scheme='http://www.blogger.com/atom/ns#' term='coding'/><title type='text'>Microsoft Dynamics CRM and PHP</title><content type='html'>&lt;p&gt;I needed to use PHP for two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Given a lead's email address, retrieve the name, title, phone and email address of this lead's owner.&lt;/li&gt;
&lt;li&gt;Insert a new lead into the system.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I spent a lot of time searching the web, and saw many posts of people being unsuccessful with using &lt;code&gt;SoapClient&lt;/code&gt;, so I didn't even bother trying.  The first order of business was to figure out how to login using IFD (Internet Facing Deployment.)  I found &lt;a href="http://www.zenithies.org/articles/1/connect-to-microsoft-dynamics-crm-4-0-web-service-from-php-using-ifd-athentication.html"&gt;this page&lt;/a&gt;, which explained how to authenticate.  It didn't work, but eventually we figured out that our IFD was not configured properly.  In the IFD configuration tool, you can't just leave the "local" subnets blank.  At least one IP range must be added, even if you want everything to use IFD.  We added 127.0.0.1-127.0.0.255 as the "local" range.&lt;/p&gt;

&lt;p&gt;I wrote two classes to handle the CRM.  These are in the &lt;code&gt;mscrm.php&lt;/code&gt; file.&lt;p&gt;

&lt;pre class="prettyprint"&gt;
&amp;lt;?
// vim: filetype=php

class MSCRMServiceException extends Exception {}
class MSCRMLoginFailed extends MSCRMServiceException {}

class MSCRMService {
  protected $host, $org, $domain, $user, $password, $curl_handle, $ticket;

  function __construct($crm_host, $organization, $domain, $user, $password) {
    $this-&amp;gt;host = $crm_host;
    $this-&amp;gt;org = $organization;
    $this-&amp;gt;domain = $domain;
    $this-&amp;gt;user = $user;
    $this-&amp;gt;password = $password;
    $this-&amp;gt;init_curl();
    $this-&amp;gt;login();
  }

  function __destruct() {
    curl_close($this-&amp;gt;curl_handle);
  }

  public function domain_user() {
    return $this-&amp;gt;domain . '\\' . $this-&amp;gt;user;
  }

  protected function init_curl() {
    $this-&amp;gt;curl_handle = curl_init();
    curl_setopt($this-&amp;gt;curl_handle, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($this-&amp;gt;curl_handle, CURLOPT_CONNECTTIMEOUT, 10);
    curl_setopt($this-&amp;gt;curl_handle, CURLOPT_TIMEOUT, 30);
    curl_setopt($this-&amp;gt;curl_handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
  }

  protected function login() {
    $request = '&amp;lt;?xml version="1.0" encoding="utf-8"?' . '&amp;gt;
      &amp;lt;soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"&amp;gt;
        &amp;lt;soap:Body&amp;gt;
          &amp;lt;Execute xmlns="http://schemas.microsoft.com/crm/2007/CrmDiscoveryService"&amp;gt;
            &amp;lt;Request xsi:type="RetrieveCrmTicketRequest"&amp;gt;
              &amp;lt;OrganizationName&amp;gt;' . $this-&amp;gt;org . '&amp;lt;/OrganizationName&amp;gt;
              &amp;lt;UserId&amp;gt;' . $this-&amp;gt;domain_user() . '&amp;lt;/UserId&amp;gt;
              &amp;lt;Password&amp;gt;' . $this-&amp;gt;password . '&amp;lt;/Password&amp;gt;
            &amp;lt;/Request&amp;gt;
          &amp;lt;/Execute&amp;gt;
        &amp;lt;/soap:Body&amp;gt;
      &amp;lt;/soap:Envelope&amp;gt;';
    $headers = array(
      'Host: ' . $this-&amp;gt;host,
      'Connection: Keep-Alive',
      'SOAPAction: "http://schemas.microsoft.com/crm/2007/CrmDiscoveryService/Execute"',
      'Content-type: text/xml; charset="utf-8"',
      'Content-length: ' . strlen($request)
    );
    curl_setopt($this-&amp;gt;curl_handle, CURLOPT_URL, 'http://' . $this-&amp;gt;host . '/MSCRMServices/2007/SPLA/CrmDiscoveryService.asmx');
    curl_setopt($this-&amp;gt;curl_handle, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($this-&amp;gt;curl_handle, CURLOPT_POST, true);
    curl_setopt($this-&amp;gt;curl_handle, CURLOPT_POSTFIELDS, $request);

    $response = curl_exec($this-&amp;gt;curl_handle);
    $matches = array();
    if(preg_match('/&amp;lt;CrmTicket&amp;gt;([^&amp;lt;]*)&amp;lt;\/CrmTicket&amp;gt;/', $response, $matches)) {
      $this-&amp;gt;ticket = $matches[1];
    } else {
      throw new MSCRMLoginFailed;
    }
  }

  public function xml_request($xml, $service, $action) {
    $request = '&amp;lt;?xml version="1.0" encoding="utf-8"?' . '&amp;gt;
      &amp;lt;soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"&amp;gt;
        &amp;lt;soap:Header&amp;gt;
          &amp;lt;CrmAuthenticationToken xmlns="http://schemas.microsoft.com/crm/2007/WebServices"&amp;gt;
            &amp;lt;AuthenticationType xmlns="http://schemas.microsoft.com/crm/2007/CoreTypes"&amp;gt;2&amp;lt;/AuthenticationType&amp;gt;
            &amp;lt;OrganizationName xmlns="http://schemas.microsoft.com/crm/2007/CoreTypes"&amp;gt;' . $this-&amp;gt;org . '&amp;lt;/OrganizationName&amp;gt;
          &amp;lt;/CrmAuthenticationToken&amp;gt;
        &amp;lt;/soap:Header&amp;gt;
        &amp;lt;soap:Body&amp;gt;
          &amp;lt;' . $action . ' xmlns="http://schemas.microsoft.com/crm/2007/WebServices"&amp;gt;
            ' . $xml . '
          &amp;lt;/' . $action . '&amp;gt;
        &amp;lt;/soap:Body&amp;gt;
      &amp;lt;/soap:Envelope&amp;gt;';
    $headers = array(
      'Host: ' . $this-&amp;gt;host,
      'Connection: Keep-Alive',
      'SOAPAction: "http://schemas.microsoft.com/crm/2007/WebServices/' . $action . '"',
      'Content-type: text/xml; charset="utf-8"',
      'Content-length: ' . strlen($request)
    );
    curl_setopt($this-&amp;gt;curl_handle, CURLOPT_URL, 'http://' . $this-&amp;gt;host . '/MSCrmServices/2007/' . $service . '.asmx ');
    curl_setopt($this-&amp;gt;curl_handle, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($this-&amp;gt;curl_handle, CURLOPT_POST, true);
    curl_setopt($this-&amp;gt;curl_handle, CURLOPT_POSTFIELDS, $request);
    // Ticket goes into a cookie.
    curl_setopt($this-&amp;gt;curl_handle, CURLOPT_COOKIE, 'MSCRMSession=ticket=' . $this-&amp;gt;ticket);
    $response = curl_exec($this-&amp;gt;curl_handle);
    return $response;
  }

  // Retrieves given $attributes for given $entity where $condition_attribute equals $condition_string.
  public function retrieve_multiple($entity, $attributes, $condition_attribute, $condition_string) {
    $attributes_xml = "";
    foreach($attributes as $attribute) {
      $attributes_xml .= "&amp;lt;q1:Attribute&amp;gt;" . htmlspecialchars($attribute) . "&amp;lt;/q1:Attribute&amp;gt;\n";
    }
    $xml = '
      &amp;lt;query xmlns:q1="http://schemas.microsoft.com/crm/2006/Query" xsi:type="q1:QueryExpression"&amp;gt;
        &amp;lt;q1:EntityName&amp;gt;' . htmlspecialchars($entity) . '&amp;lt;/q1:EntityName&amp;gt;
        &amp;lt;q1:ColumnSet xsi:type="q1:ColumnSet"&amp;gt;
          &amp;lt;q1:Attributes&amp;gt;
            ' . $attributes_xml . '
          &amp;lt;/q1:Attributes&amp;gt;
        &amp;lt;/q1:ColumnSet&amp;gt;
        &amp;lt;q1:Distinct&amp;gt;false&amp;lt;/q1:Distinct&amp;gt;
        &amp;lt;q1:Criteria&amp;gt;
          &amp;lt;q1:FilterOperator&amp;gt;And&amp;lt;/q1:FilterOperator&amp;gt;
          &amp;lt;q1:Conditions&amp;gt;
            &amp;lt;q1:Condition&amp;gt;
              &amp;lt;q1:AttributeName&amp;gt;' . htmlspecialchars($condition_attribute) . '&amp;lt;/q1:AttributeName&amp;gt;
              &amp;lt;q1:Operator&amp;gt;Equal&amp;lt;/q1:Operator&amp;gt;
              &amp;lt;q1:Values&amp;gt;
                &amp;lt;q1:Value xsi:type="xsd:string"&amp;gt;' . htmlspecialchars($condition_string) . '&amp;lt;/q1:Value&amp;gt;
              &amp;lt;/q1:Values&amp;gt;
            &amp;lt;/q1:Condition&amp;gt;
          &amp;lt;/q1:Conditions&amp;gt;
        &amp;lt;/q1:Criteria&amp;gt;
      &amp;lt;/query&amp;gt;
    ';
    $response_xml = $this-&amp;gt;xml_request($xml, "CrmService", "RetrieveMultiple");
    $response = array();
    foreach($attributes as $attribute) {
      $matches = array();
      if(preg_match('/&amp;lt;q1:' . $attribute . '[^&amp;gt;]*&amp;gt;([^&amp;lt;]*)&amp;lt;\/q1:' . $attribute . '&amp;gt;/', $response_xml, $matches)) {
        $response[$attribute] = $matches[1];
      }
    }
    return $response;
  }

  // Create an entity.  $attributes should have attributes as keys and their values as values.
  public function create_entity($entity, $attributes) {
    $xml = "&amp;lt;entity xsi:type=\"$entity\"&amp;gt;\n";
    foreach($attributes as $k =&amp;gt; $v) {
      $xml .= "&amp;lt;$k&amp;gt;" . htmlspecialchars($v) . "&amp;lt;/$k&amp;gt;\n";
    }
    $xml .= "&amp;lt;/entity&amp;gt;\n";
    return $this-&amp;gt;xml_request($xml, "CrmService", "Create");
  }

  // Usage: $options = retrieve_attribute("lead", "leadsourcecode")
  public function retrieve_attribute($entity, $attribute) {
    $xml = '
      &amp;lt;Request xsi:type="RetrieveAttributeRequest"&amp;gt;
        &amp;lt;EntityLogicalName&amp;gt;' . $entity . '&amp;lt;/EntityLogicalName&amp;gt;
        &amp;lt;LogicalName&amp;gt;' . $attribute . '&amp;lt;/LogicalName&amp;gt;
      &amp;lt;/Request&amp;gt;
    ';
    $xml = $this-&amp;gt;xml_request($xml, "MetadataService", "Execute");
    // Get a list of &amp;lt;Option&amp;gt; tags.
    $matches = array();
    if(!preg_match_all('/&amp;lt;Option&amp;gt;(.*?)&amp;lt;\/Option&amp;gt;/', $xml, $matches)) {
      return null;
    }
    $options_xml = $matches[1];
    $options = array();
    foreach($options_xml as $option_xml) {
      $matches = array();
      if(!preg_match('/&amp;lt;Value[^&amp;gt;]*&amp;gt;([^&amp;lt;]*)&amp;lt;\/Value&amp;gt;/', $option_xml, $matches)) {
        continue;
      }
      $code = $matches[1];
      if(!preg_match('/&amp;lt;Label&amp;gt;([^&amp;lt;]*)&amp;lt;\/Label&amp;gt;/', $option_xml, $matches)) {
        continue;
      }
      $name = $matches[1];
      $options[] = array("code" =&amp;gt; $code, "name" =&amp;gt; $name);
    }
    return $options;
  }

  public function find_code_by_name($entity, $attribute, $name) {
    $codes = $this-&amp;gt;retrieve_attribute($entity, $attribute);
    foreach($codes as $code) {
      if($code["name"] == $name) {
        return $code["code"];
      }
    }
    return null;
  }
}

class MSCRMLead {
  protected $service;

  function __construct($service) {
    $this-&amp;gt;service = $service;
  }

  public function find_user($guid) {
    $retrieve_fields = array("firstname", "lastname", "title", "internalemailaddress", "address1_telephone1");
    return $this-&amp;gt;service-&amp;gt;retrieve_multiple("systemuser", $retrieve_fields, "systemuserid", $guid);
  }

  public function find_lead_by_email($email) {
    $retrieve_fields = array("leadsourcecode", "firstname", "lastname",
                             "jobtitle", "companyname", "industrycode", "websiteurl", "address1_telephone1", "emailaddress1",
                             "address1_city", "address1_stateorprovince", "address1_country", "ownerid");
    return $this-&amp;gt;service-&amp;gt;retrieve_multiple("lead", $retrieve_fields, "emailaddress1", $email);
  }

  public function find_owner_by_email($email) {
    $lead_info = $this-&amp;gt;find_lead_by_email($email);
    if(!$lead_info["ownerid"]) {
      return null;
    }
    $owner = $this-&amp;gt;find_user($lead_info["ownerid"]);
    return $owner;
  }

  public function find_leadsourcecode($leadsource) {
    return $this-&amp;gt;service-&amp;gt;find_code_by_name("lead", "leadsourcecode", $leadsource);
  }

  public function find_industrycode($industry) {
    return $this-&amp;gt;service-&amp;gt;find_code_by_name("lead", "industrycode", $industry);
  }

  public function create($lead_info) {
    return $this-&amp;gt;service-&amp;gt;create_entity("lead", $lead_info);
  }
}

?&amp;gt;
&lt;/pre&gt;

&lt;p&gt;You can now retrieve a lead's owner information like this:&lt;/p&gt;

&lt;pre class="prettyprint"&gt;
require_once("mscrm.php");

$mscrm_service = new MSCRMService($host, $organization, $domain, $user, $password);
$mscrm_lead = new MSCRMLead($mscrm_service);
$owner = $mscrm_lead-&gt;find_owner_by_email($lead_email);
&lt;/pre&gt;

&lt;p&gt;Creating a new lead is a little more involved, since you need to look up codes for picklists, like &lt;code&gt;leadsourcecode&lt;/code&gt;.  You can do something like this:&lt;/p&gt;


&lt;pre class="prettyprint"&gt;
require_once("mscrm.php");

$mscrm_service = new MSCRMService($host, $organization, $domain, $user, $password);
$mscrm_lead = new MSCRMLead($mscrm_service);

$lead_info = array();

$leadsourcecode = $mscrm_lead-&gt;find_leadsourcecode($lead_source);
if($leadsourcecode) {
  $lead_info["leadsourcecode"] = $leadsourcecode;
}

$lead_info["firstname"] = $firstname;
$lead_info["lastname"] = $lastname;
$lead_info["jobtitle"] = $jobtitle;
$lead_info["companyname"] = $companyname;

$industrycode = $mscrm_lead-&gt;find_industrycode($industry);
if($industrycode) {
  $lead_info["industrycode"] = $industrycode;
}

$lead_info["websiteurl"] = $websiteurl;
$lead_info["address1_telephone1"] = $phone;
$lead_info["emailaddress1"] = $email;
$lead_info["address1_city"] = $city;
$lead_info["address1_stateorprovince"] = $state;
$lead_info["address1_country"] = $country;

$mscrm_lead-&gt;create($lead_info);
&lt;/pre&gt;

Hopefully this will save someone days of headaches.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1333829102577914020-4597427218527652915?l=bitblender.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bitblender.blogspot.com/feeds/4597427218527652915/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1333829102577914020&amp;postID=4597427218527652915' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1333829102577914020/posts/default/4597427218527652915'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1333829102577914020/posts/default/4597427218527652915'/><link rel='alternate' type='text/html' href='http://bitblender.blogspot.com/2010/09/microsoft-dynamics-crm-and-php.html' title='Microsoft Dynamics CRM and PHP'/><author><name>Gary</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://twist.smugmug.com/photos/32078646-Ti.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1333829102577914020.post-8249723200874816307</id><published>2010-01-06T01:39:00.007-05:00</published><updated>2010-09-02T13:06:44.734-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='coding'/><title type='text'>Transparent TCP proxy in ruby (jruby)</title><content type='html'>This is a transparent TCP proxy.  I only tested it in jruby, but I see no reason why it wouldn't work in ruby as well.  The proxy is multithreaded, it starts a new thread to handle every connection, up to a limit.  The client and the server can talk at the same time, and &lt;code&gt;IO.select&lt;/code&gt; is used to figure out who has data to send.

&lt;pre class="prettyprint"&gt;
require "socket"

remote_host = "www.google.com"
remote_port = 80
listen_port = 5000
max_threads = 5
threads = []

puts "starting server"
server = TCPServer.new(nil, listen_port)
while true
  # Start a new thread for every client connection.
  puts "waiting for connections"
  threads &amp;lt;&amp;lt; Thread.new(server.accept) do |client_socket|
    begin
      puts "#{Thread.current}: got a client connection"
      begin
        server_socket = TCPSocket.new(remote_host, remote_port)
      rescue Errno::ECONNREFUSED
        client_socket.close
        raise
      end
      puts "#{Thread.current}: connected to server at #{remote_host}:#{remote_port}"

      while true
        # Wait for data to be available on either socket.
        (ready_sockets, dummy, dummy) = IO.select([client_socket, server_socket])
        begin
          ready_sockets.each do |socket|
            data = socket.readpartial(4096)
            if socket == client_socket
              # Read from client, write to server.
              puts "#{Thread.current}: client-&amp;gt;server #{data.inspect}"
              server_socket.write data
              server_socket.flush
            else
              # Read from server, write to client.
              puts "#{Thread.current}: server-&amp;gt;client #{data.inspect}"
              client_socket.write data
              client_socket.flush
            end
          end
        rescue EOFError
          break
        end
      end
    rescue StandardError =&amp;gt; e
      puts "Thread #{Thread.current} got exception #{e.inspect}"
    end
    puts "#{Thread.current}: closing the connections"
    client_socket.close rescue StandardError
    server_socket.close rescue StandardError
  end

  # Clean up the dead threads, and wait until we have available threads.
  puts "#{threads.size} threads running"
  threads = threads.select { |t| t.alive? ? true : (t.join; false) }
  while threads.size &amp;gt;= max_threads
    sleep 1
    threads = threads.select { |t| t.alive? ? true : (t.join; false) }
  end
end
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1333829102577914020-8249723200874816307?l=bitblender.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bitblender.blogspot.com/feeds/8249723200874816307/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1333829102577914020&amp;postID=8249723200874816307' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1333829102577914020/posts/default/8249723200874816307'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1333829102577914020/posts/default/8249723200874816307'/><link rel='alternate' type='text/html' href='http://bitblender.blogspot.com/2010/01/transparent-tcp-proxy-in-ruby-jruby.html' title='Transparent TCP proxy in ruby (jruby)'/><author><name>Gary</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://twist.smugmug.com/photos/32078646-Ti.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1333829102577914020.post-3207367018668202755</id><published>2008-10-26T00:11:00.018-04:00</published><updated>2010-09-02T13:13:15.101-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='coding'/><title type='text'>Connecting to Multiple Databases in Ruby on Rails</title><content type='html'>&lt;p&gt;If you keep different tables in different databases, you've probably been looking for a way to allow Rails to connect to more than one database.  You've probably found that you can add another configuration (besides &lt;code&gt;development&lt;/code&gt;, &lt;code&gt;test&lt;/code&gt; and &lt;code&gt;production&lt;/code&gt;) to &lt;code&gt;config/database.yml&lt;/code&gt;.  There's a problem with that though.  If you added a configuration called &lt;code&gt;foo&lt;/code&gt;, you are now always connecting to &lt;code&gt;foo&lt;/code&gt;, in production mode, test mode and development mode.  This is especially bad in test mode, since the database would get wiped, but you probably want a different database for development and production too.&lt;/p&gt;

&lt;p&gt;Here's how to do it.  Suppose you have already set up your regular development/test/production databases in &lt;code&gt;config/database.yml&lt;/code&gt;:&lt;/p&gt;

&lt;pre class="prettyprint lang-yaml"&gt;
development:
  adapter: jdbc
  driver: com.mysql.jdbc.Driver
  url: jdbc:mysql://devdb.yourdomain.com:3306/mstore
  username: cart
  password: secret

# Warning: The database defined as 'test' will be erased and
# re-generated from your development database when you run 'rake'.
# Do not set this db to the same as development or production.
test:
  adapter: jdbc
  driver: com.mysql.jdbc.Driver
  url: jdbc:mysql://localhost:3306/mstore
  username: cart
  password: secret

production:
  adapter: jdbc
  driver: com.mysql.jdbc.Driver
  url: jdbc:mysql://proddb.yourdomain.com:3306/mstore
  username: cart
  password: secret
&lt;/pre&gt;

&lt;p&gt;Now you decided to store all the user account information in a different database.  You still want a separate development, test and production databases for your account information.  You'll need to add three more entries to the database configuration then, one for each of the account databases.  You'll need to name them with an appropriate suffix to specify development/test/production:&lt;/p&gt;

&lt;pre class="prettyprint lang-yaml"&gt;
account_development:
  adapter: jdbc
  driver: com.mysql.jdbc.Driver
  url: jdbc:mysql://devdb.yourdomain.com:3306/account
  username: cart
  password: secret

# Warning: The database defined as 'test' will be erased and
# re-generated from your development database when you run 'rake'.
# Do not set this db to the same as development or production.
account_test:
  adapter: jdbc
  driver: com.mysql.jdbc.Driver
  url: jdbc:mysql://localhost:3306/account
  username: cart
  password: secret

account_production:
  adapter: jdbc
  driver: com.mysql.jdbc.Driver
  url: jdbc:mysql://accountdb.yourdomain.com:3306/account
  username: cart
  password: secret
&lt;/pre&gt;

&lt;p&gt;Note that these account databases could be on the same hosts or on different hosts from the main databases.  Now on to the models.  You need to create a base class model for all your account tables, so that only one connection to the account database gets created.  If you just call &lt;code&gt;establish_connection&lt;/code&gt; in every model, it'll open that many database connections, one for each model.  Here's a base model for the account tables:&lt;/p&gt;

&lt;pre class="prettyprint"&gt;
class AccountBase &amp;lt; ActiveRecord::Base
  # No corresponding table in the DB.
  self.abstract_class = true

  # Open a connection to the appropriate database depending
  # on what RAILS_ENV is set to.
  establish_connection("account_#{RAILS_ENV}")
end
&lt;/pre&gt;

&lt;p&gt;Now your account models need to inherit from &lt;code&gt;AccountBase&lt;/code&gt; instead of &lt;code&gt;ActiveRecord::Base&lt;/code&gt;, like this:&lt;/p&gt;

&lt;pre class="prettyprint"&gt;
class Login &amp;lt; AccountBase
end
&lt;/pre&gt;

&lt;p&gt;Because it inherits from &lt;code&gt;AccountBase&lt;/code&gt;, it'll use the database connection already established by &lt;code&gt;AccountBase&lt;/code&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1333829102577914020-3207367018668202755?l=bitblender.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bitblender.blogspot.com/feeds/3207367018668202755/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1333829102577914020&amp;postID=3207367018668202755' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1333829102577914020/posts/default/3207367018668202755'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1333829102577914020/posts/default/3207367018668202755'/><link rel='alternate' type='text/html' href='http://bitblender.blogspot.com/2008/10/connecting-to-multiple-database-in-ruby.html' title='Connecting to Multiple Databases in Ruby on Rails'/><author><name>Gary</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://twist.smugmug.com/photos/32078646-Ti.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1333829102577914020.post-7017256964627131971</id><published>2008-10-20T20:58:00.012-04:00</published><updated>2010-09-02T13:22:34.884-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Mac'/><title type='text'>/etc/hosts and Mac OS X 10.5 Leopard</title><content type='html'>Instead of using /etc/hosts on Mac OS Leopard, the proper way to add hosts is using the &lt;code&gt;dscl&lt;/code&gt; utility.  First, let's list the hosts already there:

&lt;pre&gt;
$ &lt;b&gt;dscl localhost -list /Local/Default/Hosts&lt;/b&gt;
$
&lt;/pre&gt;

Nothing.  Let's add one.  I want to be able to access &lt;code&gt;localhost&lt;/code&gt; as &lt;code&gt;me.blogger.com&lt;/code&gt;.  Right now that doesn't work:

&lt;pre&gt;
$ &lt;b&gt;ping -q -c 1 me.blogger.com&lt;/b&gt;
ping: cannot resolve me.blogger.com: Unknown host
$
&lt;/pre&gt;

Create an entry for it and try again:

&lt;pre&gt;
$ &lt;b&gt;sudo dscl localhost -create /Local/Default/Hosts/me.blogger.com \&lt;/b&gt;
  &lt;b&gt;IPAddress 127.0.0.1&lt;/b&gt;
$ &lt;b&gt;ping -q -c 1 me.blogger.com&lt;/b&gt;
PING me.blogger.com (127.0.0.1): 56 data bytes

--- me.blogger.com ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max/stddev = 0.077/0.077/0.077/0.000 ms
&lt;/pre&gt;

You can list the hosts you have set up, where they point, and you can delete them:

&lt;pre&gt;
$ &lt;b&gt;dscl localhost -list /Local/Default/Hosts&lt;/b&gt;
me.blogger.com
$
$ &lt;b&gt;dscl localhost -read /Local/Default/Hosts/me.blogger.com&lt;/b&gt;
AppleMetaNodeLocation: /Local/Default
IPAddress: 127.0.0.1
RecordName: me.blogger.com
RecordType: dsRecTypeStandard:Hosts
$
$ &lt;b&gt;sudo dscl localhost -delete /Local/Default/Hosts/me.blogger.com&lt;/b&gt;
$ &lt;b&gt;dscl localhost -list /Local/Default/Hosts&lt;/b&gt;
$
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1333829102577914020-7017256964627131971?l=bitblender.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bitblender.blogspot.com/feeds/7017256964627131971/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1333829102577914020&amp;postID=7017256964627131971' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1333829102577914020/posts/default/7017256964627131971'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1333829102577914020/posts/default/7017256964627131971'/><link rel='alternate' type='text/html' href='http://bitblender.blogspot.com/2008/10/etchosts-and-mac-os-x-105-leopard.html' title='/etc/hosts and Mac OS X 10.5 Leopard'/><author><name>Gary</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://twist.smugmug.com/photos/32078646-Ti.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1333829102577914020.post-732972218101947561</id><published>2008-08-13T22:04:00.007-04:00</published><updated>2010-09-01T22:18:20.083-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='coding'/><title type='text'>CyberSource, SOAP and Ruby</title><content type='html'>&lt;p&gt;CyberSource has a SOAP API.  They don't officially support Ruby, but I don't see why it shouldn't work.  Ruby has built-in support for SOAP.  I knew absolutely nothing about SOAP or WSDL an hour ago, so I'm still trying to figure out how this works.  SOAP is a protocol for calling web services and getting the responses from them.  WSDL files describe the available web services.  It looks like CyberSource publishes a new WSDL file for every API version that it supports.  The latest at the moment seems to be 1.38.  You can see a list of their WSDL files &lt;a href="https://ics2ws.ic3.com/commerce/1.x/transactionProcessor/"&gt;here&lt;/a&gt;.  Let's try something:&lt;/p&gt;

&lt;pre class="prettyprint"&gt;
require "soap/wsdlDriver"

wsdl_file = "https://ics2ws.ic3.com/commerce/1.x/" +
  "transactionProcessor/CyberSourceTransaction_1.38.wsdl"

# Create a driver from the definition provided in wsdl_file.
driver = SOAP::WSDLDriverFactory.new(wsdl_file).create_rpc_driver

# See what's available in this driver.
puts driver.methods.sort.join("\n")
&lt;/pre&gt;

&lt;p&gt;This prints out a long list of method names available on the driver that is created from the WSDL file.  AFAICT, the only method you're supposed to call is &lt;code&gt;runTransaction&lt;/code&gt;.  I don't know how to call it yet.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1333829102577914020-732972218101947561?l=bitblender.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bitblender.blogspot.com/feeds/732972218101947561/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1333829102577914020&amp;postID=732972218101947561' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1333829102577914020/posts/default/732972218101947561'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1333829102577914020/posts/default/732972218101947561'/><link rel='alternate' type='text/html' href='http://bitblender.blogspot.com/2008/08/cybersource-soap-and-ruby.html' title='CyberSource, SOAP and Ruby'/><author><name>Gary</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://twist.smugmug.com/photos/32078646-Ti.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1333829102577914020.post-5770623036951611573</id><published>2008-08-10T10:45:00.011-04:00</published><updated>2008-08-10T16:52:46.449-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Mac'/><category scheme='http://www.blogger.com/atom/ns#' term='coding'/><title type='text'>MySQL On Mac OS X Using MacPorts</title><content type='html'>&lt;p&gt;If you only want to use the MySQL client, all you have to do is install it using the port command:&lt;/p&gt;

&lt;pre&gt;
sudo port install mysql5
&lt;/pre&gt;

&lt;p&gt;You can now connect to remote servers with something like this:&lt;/p&gt;

&lt;pre&gt;
mysql5 -usomeuser -psomepassword -hsomehost.somedomain.com
&lt;/pre&gt;

&lt;p&gt;However, if you want to run a MySQL server on your local machine, there are few more things that you have to do.  First, you need to initialize MySQL system tables:&lt;/p&gt;

&lt;pre&gt;
sudo mysql_install_db5 --user=mysql
&lt;/pre&gt;

&lt;p&gt;This initializes MySQL databases under &lt;code&gt;/opt/local/var/db/mysql5/&lt;/code&gt; .  We also need to create a directory under MacPorts that is identical to &lt;code&gt;/var/run&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;
$ &lt;b&gt;ls -ld /var/run&lt;/b&gt;
drwxrwxr-x  27 root  daemon  918 Aug 10 09:07 /var/run
$ &lt;b&gt;sudo mkdir /opt/local/var/run&lt;/b&gt;
$ &lt;b&gt;sudo chgrp daemon /opt/local/var/run&lt;/b&gt;
$ &lt;b&gt;sudo chmod g+w /opt/local/var/run&lt;/b&gt;
$ &lt;b&gt;ls -ld /opt/local/var/run&lt;/b&gt;
drwxrwxr-x  2 root  daemon  68 Aug 10 15:31 /opt/local/var/run
&lt;/pre&gt;

&lt;p&gt;You can now start the MySQL server, but it is NOT SECURE yet.  Make it only listen for local connections using the &lt;code&gt;--bind-address&lt;/code&gt; option.  This makes it impossible to connect to MySQL from the outside.  If you intend this MySQL installation to be used for development only, you should always start it like this.  If you want to use this in production and connect to it from other machines, you'll need to remove the &lt;code&gt;--bind-address&lt;/code&gt; option, but only AFTER SECURING THE INSTALLATION.&lt;/p&gt;

&lt;pre&gt;
sudo mysqld_safe5 --bind-address 127.0.0.1 &gt; /dev/null 2&gt;&amp;1 &amp;
&lt;/pre&gt;

&lt;p&gt;MySQL comes with a nice script to secure it, called &lt;code&gt;mysql_secure_installation&lt;/code&gt;.  Unfortunately, if you installed MySQL using the &lt;code&gt;sudo port install mysql5&lt;/code&gt; command, the script will fail, because it's looking for the &lt;code&gt;mysql&lt;/code&gt; command to be in your path, but the command is really called &lt;code&gt;mysql5&lt;/code&gt;.  You can make a symlink to fix it, though I'm not sure if this will clash with future MacPorts MySQL installations.  You can remove the symlink after you're done if you wish.&lt;/p&gt;

&lt;pre&gt;
sudo ln -s mysql5 /opt/local/bin/mysql
&lt;/pre&gt;

&lt;p&gt;Now you can run the secure script.  Be sure to set the root password, remove anonymous users, disable remote root login, remove the test database, and reload privilege tables.  Basically answer Y to everything.&lt;/p&gt;

&lt;pre&gt;
sudo mysql_secure_installation5
&lt;/pre&gt;

&lt;p&gt;You should now be able to connect using your new root password (you'll be prompted for it):&lt;/p&gt;

&lt;pre&gt;
mysql5 -uroot -p
&lt;/pre&gt;

&lt;p&gt;To remove the symlink you made:&lt;/p&gt;

&lt;pre&gt;
# Make sure it's really a symlink to mysql5.
$ &lt;b&gt;ls -l /opt/local/bin/mysql&lt;/b&gt;
lrwxr-xr-x  1 root  admin  6 Aug 10 16:11 /opt/local/bin/mysql -&gt; mysql5
$ &lt;b&gt;sudo rm /opt/local/bin/mysql&lt;/b&gt;
&lt;/pre&gt;

&lt;p&gt;To shut down MySQL, you can use the &lt;code&gt;mysqladmin&lt;/code&gt; command.  It will prompt for your MySQL root password:&lt;/p&gt;

&lt;pre&gt;
mysqladmin5 shutdown -uroot -p
&lt;/pre&gt;

&lt;p&gt;If you are planning to use this server in production, you should probably create a &lt;code&gt;my.cnf&lt;/code&gt; with all the needed options, and make MySQL start up when the machine boots.  You should also create some non-root users, even if it's only used for development. But this is a topic for another time.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1333829102577914020-5770623036951611573?l=bitblender.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bitblender.blogspot.com/feeds/5770623036951611573/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1333829102577914020&amp;postID=5770623036951611573' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1333829102577914020/posts/default/5770623036951611573'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1333829102577914020/posts/default/5770623036951611573'/><link rel='alternate' type='text/html' href='http://bitblender.blogspot.com/2008/08/mysql-on-mac-os-x-using-macports.html' title='MySQL On Mac OS X Using MacPorts'/><author><name>Gary</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://twist.smugmug.com/photos/32078646-Ti.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1333829102577914020.post-3796788548295257053</id><published>2008-08-06T23:27:00.005-04:00</published><updated>2008-08-06T23:54:52.976-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='Mac'/><category scheme='http://www.blogger.com/atom/ns#' term='coding'/><title type='text'>Ruby On Rails on Mac OS X</title><content type='html'>&lt;p&gt;My laptop already had ruby installed (part of the standard OS install), but I decided to get the latest versions and recompile them.  Thankfully this is trivial with &lt;a href="http://www.macports.org/"&gt;MacPorts&lt;/a&gt;.  To install MacPorts, you'll need to have the developer's toolkit installed (gcc, make, etc) - this on the second CD that comes with the laptop.&lt;/p&gt;

&lt;p&gt;Once you have MacPorts installed, make sure it's in your path, put this in your .bash_profile :&lt;/p&gt;

&lt;pre&gt;
# MacPorts
PATH=/opt/local/bin:/opt/local/sbin:$PATH; export PATH
MANPATH=/opt/local/share/man:$MANPATH; export MANPATH
&lt;/pre&gt;

&lt;p&gt;Now install ruby and rubygems:&lt;/p&gt;

&lt;pre&gt;
sudo port install ruby rb-rubygems
&lt;/pre&gt;

&lt;p&gt;Use gem to install rails:&lt;/p&gt;

&lt;pre&gt;
sudo gem install rails
&lt;/pre&gt;

&lt;p&gt;Mongrel is much better than WEBrick, so install it:&lt;/p&gt;

&lt;pre&gt;
sudo gem install mongrel
&lt;/pre&gt;

You'll need a database to start using Rails.  I'll probably install a local MySQL for that.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1333829102577914020-3796788548295257053?l=bitblender.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bitblender.blogspot.com/feeds/3796788548295257053/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1333829102577914020&amp;postID=3796788548295257053' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1333829102577914020/posts/default/3796788548295257053'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1333829102577914020/posts/default/3796788548295257053'/><link rel='alternate' type='text/html' href='http://bitblender.blogspot.com/2008/08/ruby-on-rails-on-mac-os-x.html' title='Ruby On Rails on Mac OS X'/><author><name>Gary</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://twist.smugmug.com/photos/32078646-Ti.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1333829102577914020.post-388944166141310079</id><published>2008-07-29T11:45:00.001-04:00</published><updated>2008-07-29T11:46:47.501-04:00</updated><title type='text'>Converting from Subversion to Mercurial</title><content type='html'>&lt;p&gt;Nice post about converting your Subversion (svn) code repository to Mercurial (hg):&lt;/p&gt;

&lt;a href="http://ww2.samhart.com/node/49"&gt;SNH&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1333829102577914020-388944166141310079?l=bitblender.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bitblender.blogspot.com/feeds/388944166141310079/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1333829102577914020&amp;postID=388944166141310079' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1333829102577914020/posts/default/388944166141310079'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1333829102577914020/posts/default/388944166141310079'/><link rel='alternate' type='text/html' href='http://bitblender.blogspot.com/2008/07/converting-from-subversion-to-mercurial.html' title='Converting from Subversion to Mercurial'/><author><name>Gary</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://twist.smugmug.com/photos/32078646-Ti.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1333829102577914020.post-7853776486996251391</id><published>2008-07-22T10:07:00.003-04:00</published><updated>2008-07-22T10:20:36.114-04:00</updated><title type='text'>Towing Features</title><content type='html'>&lt;table&gt;
&lt;tr&gt;
  &lt;td&gt;&amp;nbsp;&lt;/td&gt;
  &lt;td&gt;Wheelbase&lt;/td&gt;
  &lt;td&gt;Payload&lt;/td&gt;
  &lt;td&gt;GVWR&lt;/td&gt;
  &lt;td&gt;Towing capacity&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Jeep Wrangler Unlimited Rubicon&lt;/td&gt;
  &lt;td&gt;116&lt;/td&gt;
  &lt;td&gt;1150&lt;/td&gt;
  &lt;td&gt;5465&lt;/td&gt;
  &lt;td&gt;3500&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Toyota FJ Cruiser&lt;/td&gt;
  &lt;td&gt;105.9&lt;/td&gt;
  &lt;td&gt;1277&lt;/td&gt;
  &lt;td&gt;5567&lt;/td&gt;
  &lt;td&gt;5000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
  &lt;td&gt;Toyota Tacoma Access Cab&lt;/td&gt;
  &lt;td&gt;127.8&lt;/td&gt;
  &lt;td&gt;1395&lt;/td&gt;
  &lt;td&gt;5360&lt;/td&gt;
  &lt;td&gt;6500&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1333829102577914020-7853776486996251391?l=bitblender.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bitblender.blogspot.com/feeds/7853776486996251391/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1333829102577914020&amp;postID=7853776486996251391' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1333829102577914020/posts/default/7853776486996251391'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1333829102577914020/posts/default/7853776486996251391'/><link rel='alternate' type='text/html' href='http://bitblender.blogspot.com/2008/07/towing-features.html' title='Towing Features'/><author><name>Gary</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://twist.smugmug.com/photos/32078646-Ti.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1333829102577914020.post-6599045300177986146</id><published>2008-07-14T00:46:00.004-04:00</published><updated>2008-07-14T00:49:14.725-04:00</updated><title type='text'>MacBook Air Ordered</title><content type='html'>&lt;p&gt;Just put in an order for a refurbished Apple MacBook Air.  It's $1500, which is $300 less than a new one with the same specifications (1.6GHz, 80GB disk).  I think that's a smoking deal, for something that still has a 1 year warranty.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1333829102577914020-6599045300177986146?l=bitblender.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bitblender.blogspot.com/feeds/6599045300177986146/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1333829102577914020&amp;postID=6599045300177986146' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1333829102577914020/posts/default/6599045300177986146'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1333829102577914020/posts/default/6599045300177986146'/><link rel='alternate' type='text/html' href='http://bitblender.blogspot.com/2008/07/macbook-air-ordered.html' title='MacBook Air Ordered'/><author><name>Gary</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://twist.smugmug.com/photos/32078646-Ti.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1333829102577914020.post-1845446070282309372</id><published>2008-06-23T22:46:00.006-04:00</published><updated>2008-06-23T23:13:34.882-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='NoVA'/><title type='text'>NoVA Traffic Capacity</title><content type='html'>&lt;p&gt;The Northern Virginia road system is severely under capacity.  I don't know if it was always under capacity, or just became insufficient as the amount of traffic grew, but it definitely cannot handle the current traffic levels.  Peak hours already see miles-long traffic jams, even without any accidents or road work.  And accidents or any other capacity-reducing events jam the system even more.
&lt;/p&gt;

&lt;p&gt;I'm thinking about it from a network and computer systems design perspective.  When designing a system, the aim is to create an N + 2 or greater capacity, where N is the peak traffic level.  Let's say you need 5 machines to handle peak load.  You need at least 7 machines in the pool.  That way you can take out one machine for maintenance at any time, and still be able to handle a failure of another machine, without any effect on serving traffic.
&lt;/p&gt;

&lt;p&gt;The NoVA road system is more like N - 1.  It can't even handle peak load, let alone be able to take a failure or scheduled maintenance and sustain the same traffic levels.  There's only so much expansion that can be done, as there's simply not enough space to build more roads or bridges.  So the solution must be to reduce traffic levels.  Metro really needs to expand.  Lane sharing should be allowed for motorcycles and scooters - that will encourage more people to ride, and reduce traffic levels, since motorcycles take up a lot less space on the road than cars do.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1333829102577914020-1845446070282309372?l=bitblender.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bitblender.blogspot.com/feeds/1845446070282309372/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1333829102577914020&amp;postID=1845446070282309372' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1333829102577914020/posts/default/1845446070282309372'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1333829102577914020/posts/default/1845446070282309372'/><link rel='alternate' type='text/html' href='http://bitblender.blogspot.com/2008/06/nova-traffic-capacity.html' title='NoVA Traffic Capacity'/><author><name>Gary</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://twist.smugmug.com/photos/32078646-Ti.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1333829102577914020.post-7208416827711982209</id><published>2008-06-21T21:13:00.010-04:00</published><updated>2008-07-02T21:52:14.440-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='recipes'/><category scheme='http://www.blogger.com/atom/ns#' term='food'/><title type='text'>Chicken Kebab</title><content type='html'>&lt;p&gt;&lt;img src="http://twist.smugmug.com/photos/324393604_wmcCg-S.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;You'll need:
&lt;ul&gt;
&lt;li&gt;Marinade ingredients below&lt;/li&gt;
&lt;li&gt;2 lbs of chicken breast&lt;/li&gt;
&lt;li&gt;grape tomatoes&lt;/li&gt;
&lt;li&gt;1 cup basmati rice&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;

&lt;p&gt;Marinade - mix ingredients well with a fork:
&lt;ul&gt;
&lt;li&gt;1 cup lemon juice&lt;/li&gt;
&lt;li&gt;2 tablespoons olive oil&lt;/li&gt;
&lt;li&gt;1/4 cup hot water&lt;/li&gt;
&lt;li&gt;1 thinly sliced large onion&lt;/li&gt;
&lt;li&gt;2 crushed garlic cloves&lt;/li&gt;
&lt;li&gt;2 teaspoons salt&lt;/li&gt;
&lt;li&gt;2 teaspoons ground black pepper&lt;/li&gt;
&lt;li&gt;2 teaspoons paprika&lt;/li&gt;
&lt;/ul&gt;
&lt;/p&gt;

&lt;p&gt;Chicken - 2 lbs of chicken breast, cut into 1 inch pieces.  Mix the chicken with the marinade, toss well.  Cover and marinate in the refrigerator overnight (at least 8 hours), preferably 12 to 24 hours.  Mix up the chicken and the marinade again halfway through this time.&lt;/p&gt;

&lt;p&gt;Put 1 cup of basmati rice and 2 cups of water in a dutch oven.  Let soak for 20 minutes.  Bring water to a boil, then reduce heat to low and cover tightly, simmer for 15 minutes.  Turn off heat (do not open the oven) and let stand for another 20 minutes.
&lt;/p&gt;

&lt;p&gt;Preheat the oven broiler.  Skewer the chicken alternately with grape tomatoes.  Put the skewers on an oven tray and put the tray under the broiler for 7 minutes on each side.
&lt;/p&gt;

&lt;p&gt;Serve chicken on skewers over a bed of rice.
&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1333829102577914020-7208416827711982209?l=bitblender.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bitblender.blogspot.com/feeds/7208416827711982209/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1333829102577914020&amp;postID=7208416827711982209' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1333829102577914020/posts/default/7208416827711982209'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1333829102577914020/posts/default/7208416827711982209'/><link rel='alternate' type='text/html' href='http://bitblender.blogspot.com/2008/06/chicken-kebab.html' title='Chicken Kebab'/><author><name>Gary</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://twist.smugmug.com/photos/32078646-Ti.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1333829102577914020.post-5661310429851017858</id><published>2008-06-05T10:17:00.010-04:00</published><updated>2008-06-05T23:30:32.597-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='coding'/><title type='text'>Timezone conversion in Ruby</title><content type='html'>The time in the feed is supplied with 2 fields - date and time.  It is in UTC.  The date looks like &lt;code&gt;20080605&lt;/code&gt;, and the time looks like &lt;code&gt;142115&lt;/code&gt;.  I need to display the current local date and time.  I could've used a regular expression to parse the date/time into components, but I wanted to use &lt;code&gt;strptime&lt;/code&gt;. The &lt;code&gt;Time&lt;/code&gt; module does not have &lt;code&gt;strptime&lt;/code&gt;, but the &lt;code&gt;DateTime&lt;/code&gt; module does.  Once parsed, the &lt;code&gt;DateTime&lt;/code&gt; object is in UTC.  To get local time, use the &lt;code&gt;new_offset&lt;/code&gt; method.

&lt;pre&gt;
require "Date"

# time_str will look like "20080605 142115 UTC".
time_str = feed_date + " " + feed_time + " UTC"

# Parse time_str into a DateTime object.
dt = DateTime.strptime(time_str, "%Y%m%d %H%M%S %Z")

# local_dt will contain the date and time in the local timezone.
local_dt = dt.new_offset(DateTime.now.offset)

display_dt_str = local_dt.strftime("%Y-%m-%d %H:%M")
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1333829102577914020-5661310429851017858?l=bitblender.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bitblender.blogspot.com/feeds/5661310429851017858/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1333829102577914020&amp;postID=5661310429851017858' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1333829102577914020/posts/default/5661310429851017858'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1333829102577914020/posts/default/5661310429851017858'/><link rel='alternate' type='text/html' href='http://bitblender.blogspot.com/2008/06/timezone-conversion-in-ruby.html' title='Timezone conversion in Ruby'/><author><name>Gary</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://twist.smugmug.com/photos/32078646-Ti.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1333829102577914020.post-4128855987385602361</id><published>2008-05-24T10:34:00.005-04:00</published><updated>2008-05-24T11:41:21.784-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='photography'/><title type='text'>Camera system selection</title><content type='html'>What I'm looking for when selecting an SLR camera system:&lt;br /&gt;&lt;br /&gt;

For travel, I need a lightweight body, a wide to telephoto zoom, and an even wider prime.  Ideally, I would want a 15mm prime, and a 24-120 zoom.  Unfortunately, nobody makes a system that has these.&lt;br /&gt;&lt;br /&gt;

For non-travel use, the body can be heavier, and I'd want the same lenses as above, plus a 50mm prime, plus a much longer zoom: 70-200 or longer.  I want image stabilization in everything.&lt;br /&gt;&lt;br /&gt;

In the Canon world, the closest I can come to this system with a lightweight body is:
&lt;ul&gt;
&lt;li&gt;Digital Rebel body (1.6x multiplier for lenses)&lt;/li&gt;
&lt;li&gt;Canon EF-S 17-55 f/2.8 IS USM (27-88 equivalent), excellent lens, but not as wide or as long as I would like&lt;/li&gt;
&lt;li&gt;Canon EF-S 10-22 f/3.5-4.5 USM (16-35 equivalent), slow, no IS, but the only lens this wide available&lt;/li&gt;
&lt;li&gt;Canon EF 14mm f/2.8L USM (22mm equivalent), an alternative to the 10-22 above, but not nearly as wide as I would like&lt;/li&gt;
&lt;li&gt;Sigma 30/1.4 EX DC HSM (48mm equivalent), excellent standard lens&lt;/li&gt;
&lt;li&gt;Canon EF 70-200mm f/2.8L IS USM (112-320 equivalent), excellent long zoom lens&lt;/li&gt;
&lt;/ul&gt;

If I drop the lightweight requirement, I can get a much heavier and more expensive Canon EOS-5D, which has a full-frame sensor.  It's still the lightest full-frame camera available, but weighs almost twice as much as the Rebel.
&lt;ul&gt;
&lt;li&gt;Canon EOS-5D (full-frame sensor)&lt;/li&gt;
&lt;li&gt;Canon EF 24-70mm f/2.8L USM, no IS&lt;/li&gt;
&lt;li&gt;Canon EF 14mm f/2.8L USM, no IS&lt;/li&gt;
&lt;li&gt;Canon EF 16-35mm f/2.8L USM, an alternative to the 24-70 and the 14mm above, would also require the 50mm prime below&lt;/li&gt;
&lt;li&gt;Canon EF 50mm f/1.4 USM, standard prime&lt;/li&gt;
&lt;li&gt;Canon EF 70-200mm f/2.8L IS USM&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1333829102577914020-4128855987385602361?l=bitblender.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bitblender.blogspot.com/feeds/4128855987385602361/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1333829102577914020&amp;postID=4128855987385602361' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1333829102577914020/posts/default/4128855987385602361'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1333829102577914020/posts/default/4128855987385602361'/><link rel='alternate' type='text/html' href='http://bitblender.blogspot.com/2008/05/camera-system-selection.html' title='Camera system selection'/><author><name>Gary</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://twist.smugmug.com/photos/32078646-Ti.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1333829102577914020.post-1124221461212220794</id><published>2008-05-23T09:14:00.020-04:00</published><updated>2008-06-05T23:16:04.190-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='coding'/><title type='text'>Dynamically adding methods to objects</title><content type='html'>Let's say you have a class named &lt;code&gt;Color&lt;/code&gt;, which defines instance methods &lt;code&gt;red&lt;/code&gt;, &lt;code&gt;green&lt;/code&gt; and &lt;code&gt;blue&lt;/code&gt;, which return the corresponding color component (0 to 255).  You have an instance of &lt;code&gt;Color&lt;/code&gt; called &lt;code&gt;santorin&lt;/code&gt;, among other instances.  You just calculated a transparency component for this color and want to add that in.  Problem is, the &lt;code&gt;Color&lt;/code&gt; class doesn't have a transparency attribute.  We don't want transparency in any other instance of this class anyway.  The proper way to do this would probably be to subclass &lt;code&gt;Color&lt;/code&gt; as &lt;code&gt;TransparentColor&lt;/code&gt; and add a transparency attribute there.  But that's more code than we want for this simple task.  You could just define an extra method for the &lt;code&gt;santorin&lt;/code&gt; instance:

&lt;pre&gt;
def santorin.transparency
  5
end
&lt;/pre&gt;

This works, and will return &lt;code&gt;5&lt;/code&gt; when you call it.  But our calculated transparency is in a variable called &lt;code&gt;trans&lt;/code&gt;, so we need to do this:

&lt;pre&gt;
def santorin.transparency
  trans
end
&lt;/pre&gt;

Now that doesn't work, because when you try to call this method, you'll get this:

&lt;pre&gt;
NameError: undefined local variable or method `trans' for ...
  ...
&lt;/pre&gt;

The problem is that &lt;code&gt;trans&lt;/code&gt; is evaluated when you call the method, not when you define it.  The quickest solution here is to use &lt;code&gt;eval&lt;/code&gt; to force evaluation of the variable at definition time:

&lt;pre&gt;
eval "def santorin.transparency() #{trans}; end"
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1333829102577914020-1124221461212220794?l=bitblender.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bitblender.blogspot.com/feeds/1124221461212220794/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1333829102577914020&amp;postID=1124221461212220794' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1333829102577914020/posts/default/1124221461212220794'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1333829102577914020/posts/default/1124221461212220794'/><link rel='alternate' type='text/html' href='http://bitblender.blogspot.com/2008/05/dynamically-adding-methods-to-objects.html' title='Dynamically adding methods to objects'/><author><name>Gary</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://twist.smugmug.com/photos/32078646-Ti.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-1333829102577914020.post-5162842686615756300</id><published>2008-05-21T21:42:00.006-04:00</published><updated>2008-05-21T22:53:16.809-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='photography'/><title type='text'>Mid-size Travel Camera</title><content type='html'>For traveling, it is useful to have a mid-size camera.  Something with a good zoom lens, but smaller than a big DSLR.  This is in addition to a small pocket camera (or cameraphone) that you just keep in your pocket.  I have a Sony DSC-H5, which I like, except that it takes AA batteries - I want a lithium ion battery instead, so I can recharge whenever I want.  I'm also considering the smallest DSLR for this purpose - the Olympus E-420.  Camera specifications are from &lt;a href="http://www.dpreview.com/"&gt;dpreview.com&lt;/a&gt;.

&lt;table&gt; &lt;tr&gt;&lt;td&gt; &lt;/td&gt;&lt;td&gt;&lt;b&gt;Sony DSC-H5&lt;/b&gt;&lt;/td&gt;&lt;td&gt;&lt;b&gt;Nikon Coolpix P80&lt;/b&gt;&lt;/td&gt;&lt;td&gt;&lt;b&gt;Olympus E-420 w/Zuiko 12-60 lens&lt;/b&gt;&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt;&lt;td&gt;Megapixels&lt;/td&gt;&lt;td&gt;7.1&lt;/td&gt;&lt;td&gt;10.1&lt;/td&gt;&lt;td&gt;10&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt;&lt;td&gt;Sensor Size&lt;/td&gt;&lt;td&gt;1/2.5"&lt;/td&gt;&lt;td&gt;1/2.33"&lt;/td&gt;&lt;td&gt;4/3"&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt;&lt;td&gt;Zoom (35mm eq)&lt;/td&gt;&lt;td&gt;36 - 432 (11.4x)&lt;/td&gt;&lt;td&gt;28 - 486 (12.8x)&lt;/td&gt;&lt;td&gt;24 - 120 (5x)&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt;&lt;td&gt;Image Stabilization&lt;/td&gt;&lt;td&gt;Lens&lt;/td&gt;&lt;td&gt;Sensor&lt;/td&gt;&lt;td&gt;Digital?&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt;&lt;td&gt;Max Aperture&lt;/td&gt;&lt;td&gt;f/2.8 - f/3.7&lt;/td&gt;&lt;td&gt;f/2.8 - f/4.0&lt;/td&gt;&lt;td&gt;f/2.8 - f/4.0&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt;&lt;td&gt;Shutter&lt;/td&gt;&lt;td&gt;30 sec - 1/1000 sec&lt;/td&gt;&lt;td&gt;Unknown&lt;/td&gt;&lt;td&gt;60 sec (+ Bulb) - 1/4000 sec&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt;&lt;td&gt;Built-in Flash&lt;/td&gt;&lt;td&gt;pop-up&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;pop-up&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt;&lt;td&gt;Aperture Priority&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt;&lt;td&gt;Shutter Priority&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt;&lt;td&gt;Movie Clips&lt;/td&gt;&lt;td&gt;VGA 30 fps&lt;/td&gt;&lt;td&gt;640x480 30 fps&lt;/td&gt;&lt;td&gt;No&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt;&lt;td&gt;Self-timer&lt;/td&gt;&lt;td&gt;10 sec&lt;/td&gt;&lt;td&gt;3 sec or 10 sec&lt;/td&gt;&lt;td&gt;2 sec or 12 sec&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt;&lt;td&gt;Memory Card&lt;/td&gt;&lt;td&gt;MS Duo / Pro Duo&lt;/td&gt;&lt;td&gt;SD/MMC/SDHC&lt;/td&gt;&lt;td&gt;CF&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt;&lt;td&gt;RAW&lt;/td&gt;&lt;td&gt;No&lt;/td&gt;&lt;td&gt;No&lt;/td&gt;&lt;td&gt;Yes&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt;&lt;td&gt;LCD&lt;/td&gt;&lt;td&gt;3.0", 230,000 pixels&lt;/td&gt;&lt;td&gt;2.7", 230,000 pixels&lt;/td&gt;&lt;td&gt;2.7", 230,000 pixels&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt;&lt;td&gt;Battery&lt;/td&gt;&lt;td&gt;2xAA&lt;/td&gt;&lt;td&gt;Lithium Ion&lt;/td&gt;&lt;td&gt;Lithium Ion&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt;&lt;td&gt;Weight&lt;/td&gt;&lt;td&gt;490g&lt;/td&gt;&lt;td&gt;405g&lt;/td&gt;&lt;td&gt;440g + 575g = 1015g&lt;/td&gt;&lt;/tr&gt; &lt;tr&gt;&lt;td&gt;Dimensions&lt;/td&gt;&lt;td&gt;108 x 81 x 92&lt;/td&gt;&lt;td&gt;110 x 79 x 78&lt;/td&gt;&lt;td&gt;130 x 91 x ~145&lt;/td&gt;&lt;/tr&gt; &lt;/table&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/1333829102577914020-5162842686615756300?l=bitblender.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://bitblender.blogspot.com/feeds/5162842686615756300/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1333829102577914020&amp;postID=5162842686615756300' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1333829102577914020/posts/default/5162842686615756300'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1333829102577914020/posts/default/5162842686615756300'/><link rel='alternate' type='text/html' href='http://bitblender.blogspot.com/2008/05/mid-size-travel-camera.html' title='Mid-size Travel Camera'/><author><name>Gary</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='24' src='http://twist.smugmug.com/photos/32078646-Ti.jpg'/></author><thr:total>0</thr:total></entry></feed>
