<?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-284600789737488130</id><updated>2012-01-23T08:38:03.310-05:00</updated><category term='rubyconf'/><category term='ruby'/><category term='modi'/><category term='powerpoint'/><category term='win32ole'/><category term='wmp'/><category term='tools'/><category term='books'/><category term='sqlite'/><category term='faq'/><category term='word'/><category term='api'/><category term='rubygarden'/><category term='commentary'/><category term='book'/><category term='gui'/><category term='ie'/><category term='wxruby'/><category term='vb'/><category term='site'/><category term='ado'/><category term='outlook'/><category term='sqlserver'/><category term='excel'/><category term='shell'/><category term='windows'/><category term='access'/><category term='ocr'/><category term='watir'/><category term='jruby'/><category term='dotnet'/><category term='wmi'/><category term='msproject'/><category term='itunes'/><title type='text'>Ruby on Windows</title><subtitle type='html'>On using the Ruby programming language on the Microsoft Windows platform</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default?start-index=101&amp;max-results=100'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>111</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-4380391068956869759</id><published>2011-09-02T19:27:00.005-05:00</published><updated>2011-09-02T19:50:36.494-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='outlook'/><category scheme='http://www.blogger.com/atom/ns#' term='win32ole'/><title type='text'>Automating Outlook with Ruby: Saving Mail Messages To Files</title><content type='html'>I've talked in the past about &lt;a href="http://rubyonwindows.blogspot.com/2007/08/automating-outlook-with-ruby-inbox.html"&gt;managing mail messages in your Inbox&lt;/a&gt;, and about &lt;a href="http://rubyonwindows.blogspot.com/2007/12/automating-outlook-with-ruby-saving.html"&gt;saving email attachments to your hard drive&lt;/a&gt;. A reader recently asked about saving a mail message as a file on your hard drive.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This can be done by calling the MailItem's &lt;b&gt;&lt;i&gt;SaveAs()&lt;/i&gt;&lt;/b&gt; method. This method takes two arguments. The first argument is a string indicating the full path and filename to which you want to save the message. The second argument is an integer that indicates the file type.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Let's look at a brief example that saves an Inbox message as an HTML file:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;blockquote&gt;&lt;div&gt;&lt;div&gt;require 'win32ole'&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;olHTML = 5&lt;/div&gt;&lt;div&gt;outlook = WIN32OLE.connect("Outlook.Application")&lt;/div&gt;&lt;div&gt;mapi = outlook.GetNameSpace("MAPI")&lt;/div&gt;&lt;div&gt;inbox  = mapi.GetDefaultFolder(6)&lt;/div&gt;&lt;div&gt;msg = inbox.Items(2)&lt;/div&gt;&lt;div&gt;msg.SaveAs('c:\temp\message.htm', olHTML)&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/blockquote&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Further details on the &lt;i&gt;SaveAs()&lt;/i&gt; method can be found &lt;a href="http://msdn.microsoft.com/en-us/library/aa210279(v=office.11).aspx"&gt;here&lt;/a&gt;.&lt;/div&gt;&lt;div&gt;A full list of valid file type values can be found &lt;a href="http://msdn.microsoft.com/en-us/library/bb208132(v=office.12).aspx"&gt;here&lt;/a&gt;.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;That's all for now. Questions? Comments? Suggestions? Post a comment or send me an email.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Thanks for stopping by!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-4380391068956869759?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/4380391068956869759/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=4380391068956869759' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/4380391068956869759'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/4380391068956869759'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2011/09/automating-outlook-with-ruby-saving.html' title='Automating Outlook with Ruby: Saving Mail Messages To Files'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-4063553665855177187</id><published>2011-03-16T07:01:00.010-05:00</published><updated>2011-03-17T07:00:56.411-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='jruby'/><category scheme='http://www.blogger.com/atom/ns#' term='win32ole'/><title type='text'>JRuby 1.6: Now With Win32OLE Goodness</title><content type='html'>JRuby 1.6 has just been released and, among the many new features, the JRuby installer for Windows now includes the win32ole library. &lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;This is good news for those of us that frequently use Ruby's win32ole module to  automate &lt;a href="http://rubyonwindows.blogspot.com/search/label/excel"&gt;Excel&lt;/a&gt;, &lt;a href="http://rubyonwindows.blogspot.com/search/label/word"&gt;Word&lt;/a&gt;, and a variety of other Windows applications and processes. Now you have the option of running your code on the JVM.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;And running on the JVM opens up a world of possibilities. You could, for example, whip up a Swing GUI via &lt;a href="http://www.monkeybars.org/"&gt;Monkeybars&lt;/a&gt;, and use &lt;a href="https://github.com/rawr/rawr"&gt;Rawr&lt;/a&gt; to package it up as an EXE file for ease of distribution.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Interested? Download JRuby 1.6 at &lt;a href="http://www.jruby.org/"&gt;jruby.org&lt;/a&gt;, take the win32ole module for a drive around the block, and let me know if you hit any potholes.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-4063553665855177187?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/4063553665855177187/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=4063553665855177187' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/4063553665855177187'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/4063553665855177187'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2011/03/jruby-16-now-with-win32ole-goodness.html' title='JRuby 1.6: Now With Win32OLE Goodness'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-7200025808742269936</id><published>2011-02-07T08:49:00.005-05:00</published><updated>2011-02-07T09:01:39.922-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='tools'/><title type='text'>Ruby on NetBeans: Here Comes the Cavalry</title><content type='html'>As &lt;a href="http://rubyonwindows.blogspot.com/2011/01/netbeans-drops-support-for-ruby-and.html"&gt;recently reported&lt;/a&gt;, the NetBeans IDE team has decided to drop support for Ruby (and Rails) from NetBeans 7.0.&lt;br /&gt;&lt;br /&gt;I have received many good suggestions from readers, with RedCar and RubyMine getting a lot of love.&lt;br /&gt;&lt;br /&gt;But wait! What's that? It sounds like trumpets off in the distance... and hoofbeats.&lt;br /&gt;&lt;br /&gt;Indeed, Tom Enebo is leading the cavalry to save the day, as he &lt;a href="http://www.ruby-forum.com/topic/999516"&gt;reported in the JRuby forum&lt;/a&gt; recently:&lt;br /&gt;&lt;blockquote&gt;I have been talking to Netbeans team about us adopting the project and this is a done deal.  Putting together some details and you should see a blog post about this in the next few days.  So, if you like using Netbeans dont worry, it will still be an available option in the plugins catalog (not sure about the full Ruby product as an independent download from netbeans site yet).&lt;/blockquote&gt;Good news for all those that would like to continue to use NetBeans with Ruby.&lt;br /&gt;&lt;br /&gt;Thanks, Tom!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-7200025808742269936?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/7200025808742269936/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=7200025808742269936' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/7200025808742269936'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/7200025808742269936'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2011/02/ruby-on-netbeans-here-comes-cavalry.html' title='Ruby on NetBeans: Here Comes the Cavalry'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-8132987099027460927</id><published>2011-01-27T08:27:00.004-05:00</published><updated>2011-01-27T08:43:26.746-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tools'/><title type='text'>NetBeans Drops Support for Ruby and Rails</title><content type='html'>The NetBeans team has, unfortunately, decided to &lt;a href="http://netbeans.org/community/news/show/1507.html"&gt;remove Ruby and Rails support&lt;/a&gt; from the NetBeans IDE:&lt;div&gt;&lt;blockquote&gt;After thorough consideration, we have taken the difficult step to discontinue support for Ruby on Rails in the NetBeans IDE.&lt;/blockquote&gt;&lt;blockquote&gt;As of January 27, the Ruby on Rails module will be gone from development builds of NetBeans IDE 7.0. &lt;/blockquote&gt;&lt;/div&gt;&lt;div&gt;NetBeans has served me well for larger projects over the years, and NetBeans 6.9.1 retains its existing support for Ruby and Rails. But perhaps it's time to start considering Ruby IDE alternatives, and I'm open to suggestions.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-8132987099027460927?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/8132987099027460927/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=8132987099027460927' title='13 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/8132987099027460927'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/8132987099027460927'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2011/01/netbeans-drops-support-for-ruby-and.html' title='NetBeans Drops Support for Ruby and Rails'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>13</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-6264382848717731112</id><published>2010-09-26T17:48:00.004-05:00</published><updated>2010-09-26T18:04:04.109-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='jruby'/><category scheme='http://www.blogger.com/atom/ns#' term='win32ole'/><title type='text'>Coming Soon: win32ole for JRuby</title><content type='html'>FYI: In a &lt;a href="http://www.ruby-forum.com/topic/217489"&gt;recent Ruby Forum thread&lt;/a&gt;, Charles Nutter wrote:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;FWIW, a win32ole library is in development and should be in JRuby for 1.6.&lt;/blockquote&gt;&lt;br /&gt;This is good news for those of us who do a lot of work with the standard Ruby win32ole library.&lt;br /&gt;&lt;br /&gt;My sincere thanks to all who are helping to make this happen.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-6264382848717731112?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/6264382848717731112/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=6264382848717731112' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/6264382848717731112'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/6264382848717731112'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2010/09/coming-soon-win32ole-for-jruby.html' title='Coming Soon: win32ole for JRuby'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-7897806512118306482</id><published>2010-08-28T14:03:00.003-05:00</published><updated>2010-08-28T14:21:59.134-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='tools'/><title type='text'>New Ruby IDE Discussion Group</title><content type='html'>FYI, Ed Howland has launched a new &lt;a href="http://groups.google.com/group/ruby-ide"&gt;Ruby IDE discussion group&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;I set this group up to focus discussions about a community developed IDE written in Ruby (but not necessarily limited to writing code in just that language. The intent is to take general discussion specific off-line from the main ruby-talk group  Hopefully, this will be a high signal-noise ratio discussion. &lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;So if you've got some constructive thoughts on what would be the ideal Ruby IDE, make yourself heard here:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;a href="http://groups.google.com/group/ruby-ide"&gt;http://groups.google.com/group/ruby-ide&lt;/a&gt;&lt;/blockquote&gt;&lt;br /&gt;David&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-7897806512118306482?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/7897806512118306482/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=7897806512118306482' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/7897806512118306482'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/7897806512118306482'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2010/08/new-ruby-ide-discussion-group.html' title='New Ruby IDE Discussion Group'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-4914404293017560544</id><published>2010-02-10T07:59:00.011-05:00</published><updated>2010-02-11T09:45:14.014-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='powerpoint'/><category scheme='http://www.blogger.com/atom/ns#' term='win32ole'/><title type='text'>Ruby &amp; PowerPoint: Inserting HyperLinks</title><content type='html'>I've &lt;a href="http://rubyonwindows.blogspot.com/2008/08/automating-powerpoint-with-ruby.html"&gt;written previously&lt;/a&gt; about automating &lt;a href="http://rubyonwindows.blogspot.com/search/label/powerpoint"&gt;Microsoft PowerPoint&lt;/a&gt; with Ruby.  Someone recently asked how to use Ruby code to insert hyperlinks into a PowerPoint slide.  Let's take a look now at how this can be done.&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;&lt;br /&gt;Setting the Scene&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Let's quickly review the code that will launch PowerPoint...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;ppt = WIN32OLE.new('PowerPoint.Application')&lt;br /&gt;ppt.Visible = true&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...create a new presentation...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;doc = ppt.Presentations.Add()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...add a new slide...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;slide = doc.Slides.Add(1, 2)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...and insert text into each of the two textboxes...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;slide.Shapes(1).TextFrame.TextRange.Text = "Ruby on Windows"&lt;br /&gt;slide.Shapes(2).TextFrame.TextRange.Text = "Ruby on Windows: PowerPoint"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To fully understand the above code, you may want to read &lt;a href="http://rubyonwindows.blogspot.com/2008/08/automating-powerpoint-with-ruby.html"&gt;this article&lt;/a&gt;, if you haven't already.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;ActionSettings and HyperLinks&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;OK, now we have a PowerPoint presentation with a slide containing two textboxes, and we're ready to make the text of the second textbox a hyperlink.  Let's grab a reference to the TextRange object that holds the text contained in the second textbox (a Shape object) on the slide:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;text_range = slide.Shapes(2).TextFrame.TextRange &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To define the action that is to be taken when a TextRange object is clicked, we need to work with the first item in the TextRange's &lt;span style="font-style:italic;"&gt;ActionSettings &lt;/span&gt;collection:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;action = text_range.ActionSettings(1)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This returns the &lt;span style="font-style:italic;"&gt;ActionSetting &lt;/span&gt;object that represents a MouseClick action. This ActionSetting object includes a &lt;span style="font-style:italic;"&gt;HyperLink &lt;/span&gt;object...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;link = action.Hyperlink&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...and it is the properties of this HyperLink object that we will modify to result in a link to our website:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;link.Address = "http://rubyonwindows.blogspot.com/search/label/powerpoint"&lt;br /&gt;link.ScreenTip = "Click to go to website"&lt;br /&gt;link.TextToDisplay = "Ruby on Windows: PowerPoint"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Our complete code looks like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;&lt;br /&gt;ppt = WIN32OLE.new('PowerPoint.Application')&lt;br /&gt;ppt.Visible = true&lt;br /&gt;doc = ppt.Presentations.Add()&lt;br /&gt;&lt;br /&gt;slide = doc.Slides.Add(1, 2)&lt;br /&gt;slide.Shapes(1).TextFrame.TextRange.Text = "Ruby on Windows"&lt;br /&gt;slide.Shapes(2).TextFrame.TextRange.Text = "Ruby on Windows: PowerPoint"&lt;br /&gt;&lt;br /&gt;text_range = slide.Shapes(2).TextFrame.TextRange &lt;br /&gt;action = text_range.ActionSettings(1)&lt;br /&gt;link = action.Hyperlink&lt;br /&gt;&lt;br /&gt;link.Address = "http://rubyonwindows.blogspot.com/search/label/powerpoint"&lt;br /&gt;link.ScreenTip = "Click to go to website"&lt;br /&gt;link.TextToDisplay = "Ruby on Windows: PowerPoint"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That's all for now, but feel free to post a comment here or send me email with questions, comments, or suggested topics.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-4914404293017560544?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/4914404293017560544/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=4914404293017560544' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/4914404293017560544'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/4914404293017560544'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2010/02/ruby-powerpoint-inserting-hyperlinks.html' title='Ruby &amp; PowerPoint: Inserting HyperLinks'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-3029044052381771110</id><published>2010-01-24T07:55:00.001-05:00</published><updated>2010-01-24T07:56:04.535-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='word'/><category scheme='http://www.blogger.com/atom/ns#' term='powerpoint'/><category scheme='http://www.blogger.com/atom/ns#' term='win32ole'/><category scheme='http://www.blogger.com/atom/ns#' term='excel'/><title type='text'>Saving Microsoft Office Documents as PDFs</title><content type='html'>A recent discussion in the Ruby Forum reminded me that it is possible with Microsoft Office 2007 applications to save a document in Adobe PDF format.&lt;br /&gt;&lt;br /&gt;In the &lt;a href="http://rubyonwindows.blogspot.com/search/label/word"&gt;Microsoft Word&lt;/a&gt; object model, you can call the Document object's &lt;span style="font-style:italic;"&gt;SaveAs()&lt;/span&gt; method, passing it a filename, and the document will be saved in the default format.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;document.SaveAs('c:\temp\MyDocument.doc')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;But the &lt;span style="font-style:italic;"&gt;SaveAs()&lt;/span&gt; method accepts an optional second parameter, an integer that specifies the file format.  In the Word object model, the PDF format is represented by the value &lt;span style="font-style:italic;"&gt;17&lt;/span&gt;.  So, where &lt;span style="font-style:italic;"&gt;document &lt;/span&gt;represents your Document object, you can do the following:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;document.SaveAs('c:\temp\MyDocument.pdf', 17)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;In &lt;a href="http://rubyonwindows.blogspot.com/search/label/excel"&gt;Microsoft Excel&lt;/a&gt;, the Workbook object's &lt;span style="font-style:italic;"&gt;SaveAs()&lt;/span&gt; method accepts the value &lt;span style="font-style:italic;"&gt;57&lt;/span&gt; to specify PDF format:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;workbook.SaveAs('c:\temp\MyWorkbook.pdf', 57)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And in &lt;a href="http://rubyonwindows.blogspot.com/search/label/powerpoint"&gt;Microsoft PowerPoint&lt;/a&gt;, the magic number is 32:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;presentation.SaveAs('c:\temp\MyPresentation.pdf', 32)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You can find enumerations for file types over at MSDN:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/bb238158.aspx"&gt;Microsoft Word&lt;/a&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/bb241279.aspx"&gt;Microsoft Excel&lt;/a&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/bb251061.aspx"&gt;Microsoft PowerPoint&lt;/a&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;A tip of the hat to Joe Peck for &lt;a href="http://www.ruby-forum.com/topic/202700"&gt;mentioning this&lt;/a&gt; in the Ruby Forum.&lt;br /&gt;&lt;br /&gt;Let me know if you have any questions or comments, and thanks for stopping by!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-3029044052381771110?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/3029044052381771110/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=3029044052381771110' title='10 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/3029044052381771110'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/3029044052381771110'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2010/01/saving-microsoft-office-documents-as.html' title='Saving Microsoft Office Documents as PDFs'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-2662284314841581355</id><published>2010-01-20T07:57:00.000-05:00</published><updated>2010-01-20T08:26:46.156-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='word'/><category scheme='http://www.blogger.com/atom/ns#' term='win32ole'/><category scheme='http://www.blogger.com/atom/ns#' term='excel'/><title type='text'>Connecting to One of Many Open Documents</title><content type='html'>If you've hung around here for a little while, then you probably already know that you can use the &lt;span style="font-style:italic;"&gt;WIN32OLE.connect()&lt;/span&gt; method to connect to a running instance of applications like &lt;a href="http://rubyonwindows.blogspot.com/search/label/word"&gt;Microsoft Word&lt;/a&gt;.  Just pass the method the ProgID of the Application object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;word = WIN32OLE.connect('Word.Application')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That works great if you have just one instance of the application running, with one or multiple documents open.  But suppose you have multiple instances of the application running?  How can you be sure that you connect to the instance with Document B, and not the instance with Document A?&lt;br /&gt;&lt;br /&gt;Well, assuming that Document B has been saved and you know the full filename, then you can connect to the &lt;span style="font-style:italic;"&gt;Document &lt;/span&gt;object, rather than to the &lt;span style="font-style:italic;"&gt;Application &lt;/span&gt;object.  And you do this by passing the &lt;span style="font-style:italic;"&gt;WIN32OLE.connect()&lt;/span&gt; method the full filename:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;document = WIN32OLE.connect('C:\Path To File\DocumentB.doc')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This returns an instance of the &lt;span style="font-style:italic;"&gt;Word.Document&lt;/span&gt; object. You can then grab &lt;br /&gt;an instance of that Document's &lt;span style="font-style:italic;"&gt;Application &lt;/span&gt;object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;word = document.Application&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This works with other multiple-instance applications, such as &lt;a href="http://rubyonwindows.blogspot.com/search/label/excel"&gt;Excel&lt;/a&gt;.  The following code connects to a specific instance of an &lt;span style="font-style:italic;"&gt;Excel.Workbook&lt;/span&gt; object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;workbook = WIN32OLE.connect('C:\Path To File\WorkbookB.xls')&lt;br /&gt;xl = workbook.Application&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;By the way, some applications, such as &lt;a href="http://rubyonwindows.blogspot.com/search/label/powerpoint"&gt;PowerPoint&lt;/a&gt;, are single-instance applications, so there will never be multiple instances of the application running, avoiding the need to use the process outlined above.&lt;br /&gt;&lt;br /&gt;There you have it.  Please let me know if you have specific questions or suggestions for future articles.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-2662284314841581355?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/2662284314841581355/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=2662284314841581355' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/2662284314841581355'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/2662284314841581355'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2009/12/connecting-to-one-of-many-open.html' title='Connecting to One of Many Open Documents'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-4143908943123542982</id><published>2010-01-14T08:11:00.014-05:00</published><updated>2010-01-14T09:01:05.021-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='win32ole'/><category scheme='http://www.blogger.com/atom/ns#' term='excel'/><category scheme='http://www.blogger.com/atom/ns#' term='itunes'/><title type='text'>Creating an iTunes Song Inventory in Excel</title><content type='html'>I've talked in the past about automating &lt;a href="http://rubyonwindows.blogspot.com/search/label/itunes"&gt;iTunes&lt;/a&gt;, and about automating &lt;a href="http://rubyonwindows.blogspot.com/search/label/excel"&gt;Excel&lt;/a&gt;. Let's now look at how to use Ruby to produce an iTunes report in Excel. Our finished product will be a sorted worksheet containing Artist, Year, Album, and Song Name.&lt;br /&gt;&lt;br /&gt;As usual, we'll be working with the &lt;span style="font-style:italic;"&gt;win32ole &lt;/span&gt;library, so include the following at the top of your code:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Next, we want to launch the iTunes application using the &lt;span style="font-style:italic;"&gt;WIN32OLE.new()&lt;/span&gt; method:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;itunes = WIN32OLE.new('iTunes.Application')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We'll create an array to hold the iTunes Library data:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;data = []&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The following code iterates over the iTunes &lt;span style="font-style:italic;"&gt;LibraryPlaylist.Tracks &lt;/span&gt;collection. For each track in the library that is not a podcast, our code adds a row to the &lt;span style="font-style:italic;"&gt;data &lt;/span&gt;array containing Artist, Year, Album, and Year, all of which are properties of the Track object.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;itunes.LibraryPlaylist.Tracks.each do |track|&lt;br /&gt;  if not track.Podcast&lt;br /&gt;    data &lt;&lt; [track.Artist, track.Year, track.Album, track.Name]&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To include podcasts in your report, simply remove the &lt;span style="font-style:italic;"&gt;Podcast &lt;/span&gt;conditional:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;itunes.LibraryPlaylist.Tracks.each do |track|&lt;br /&gt;  data &lt;&lt; [track.Artist, track.Year, track.Album, track.Name]&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now that we have our data array, let's sort it...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;data.sort!&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...and then insert a row of field names as the first row:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;data.insert(0, ['ARTIST', 'YEAR', 'ALBUM', 'NAME'])&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Next, we'll launch a new instance of Excel, assigning it to the &lt;span style="font-style:italic;"&gt;xl &lt;/span&gt;variable...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;xl = WIN32OLE.new('Excel.Application')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...and make the application window visible:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;xl.Visible = true&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We create a new workbook by calling the Application object's &lt;span style="font-style:italic;"&gt;Workbooks.Add()&lt;/span&gt; method...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;wb = xl.Workbooks.Add()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...and we get a reference to the first worksheet in the Workbook object's &lt;span style="font-style:italic;"&gt;Worksheets &lt;/span&gt;collection:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ws = wb.Worksheets(1)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now we're ready to &lt;a href="http://rubyonwindows.blogspot.com/2007/04/automating-excel-with-ruby-defining.html"&gt;insert our data into a range of cells&lt;/a&gt;. We'll define a Range of cells that begins at cell A1. We'll call the &lt;span style="font-style:italic;"&gt;Range.Resize()&lt;/span&gt; method to resize the Range to fit the number of rows (data.size) and the number of columns (data.first.size) in our data array.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;rng = ws.Range('A1').Resize(data.size, data.first.size)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Then we insert our data:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;rng.Value = data&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Finally, we'll do a wee bit of formatting, making the first row bold...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ws.Rows(1).Font.Bold = true&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...and adding AutoFilter drop-down lists to the column headers...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ws.Rows(1).AutoFilter()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;There's a variety of other formatting you could apply, as discussed &lt;a href="http://rubyonwindows.blogspot.com/2007/06/automating-excel-with-ruby-formatting.html"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;That's all for now, but feel free to post a comment here or send me email with questions, comments, or suggested topics.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-4143908943123542982?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/4143908943123542982/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=4143908943123542982' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/4143908943123542982'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/4143908943123542982'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2010/01/creating-itunes-song-inventory-in-excel.html' title='Creating an iTunes Song Inventory in Excel'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-5544902475478065216</id><published>2009-08-24T06:45:00.007-05:00</published><updated>2009-08-24T08:37:35.453-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='word'/><category scheme='http://www.blogger.com/atom/ns#' term='win32ole'/><title type='text'>Ruby &amp; Word: Inserting Pictures Into Documents</title><content type='html'>In &lt;a href="http://rubyonwindows.blogspot.com/2009/08/ruby-excel-inserting-pictures-into.html"&gt;an earlier article&lt;/a&gt;, I explained how to insert an image into a range of cells in an Excel worksheet. Today we'll look at how to insert an image into a Word document.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Setting the Scene&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Feel free to play along at home: Imagine that you have a Word document already open...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;word = WIN32OLE.connect('Word.Application')&lt;br /&gt;doc = word.ActiveDocument&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...and you want to insert an image from your PC...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;image = 'C:\Pictures\Picture1.jpg'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...into a given Range object in the document:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;range = doc.Sentences(2)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;The InlineShapes Collection&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The &lt;span style="font-style:italic;"&gt;InlineShapes &lt;/span&gt;object represents a collection of pictures, OLE objects, and ActiveX controls contained within a given &lt;a href="http://rubyonwindows.blogspot.com/2007/04/automating-word-with-ruby-document.html"&gt;Document&lt;/a&gt; or &lt;a href="http://rubyonwindows.blogspot.com/2007/04/automating-word-with-ruby-range-object.html"&gt;Range&lt;/a&gt; object.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;The AddPicture() Method&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;To insert an image into a document or range, we call the &lt;span style="font-style:italic;"&gt;AddPicture()&lt;/span&gt; method on the &lt;span style="font-style:italic;"&gt;InlineShapes &lt;/span&gt;collection. This method accepts up to four parameters:&lt;br /&gt;&lt;br /&gt;* &lt;span style="font-style:italic;"&gt;&lt;span style="font-weight:bold;"&gt;FileName &lt;/span&gt;&lt;/span&gt;(required) - The full path and filename of the image to insert.&lt;br /&gt;&lt;br /&gt;* &lt;span style="font-style:italic;"&gt;&lt;span style="font-weight:bold;"&gt;LinkToFile &lt;/span&gt;&lt;/span&gt;(optional) - If &lt;span style="font-style:italic;"&gt;true &lt;/span&gt;the inserted picture will be linked to the file from which it was created. You'll usually set this to &lt;span style="font-style:italic;"&gt;false &lt;/span&gt;to make the picture an independent copy of the file. The default value is &lt;span style="font-style:italic;"&gt;false&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;* &lt;span style="font-style:italic;"&gt;&lt;span style="font-weight:bold;"&gt;SaveWithDocument &lt;/span&gt;&lt;/span&gt;(optional) - Set to &lt;span style="font-style:italic;"&gt;true &lt;/span&gt;to save the linked picture with the document. The default is &lt;span style="font-style:italic;"&gt;false&lt;/span&gt;. This value will be ignored unless &lt;span style="font-style:italic;"&gt;LinkToFile &lt;/span&gt;is set to &lt;span style="font-style:italic;"&gt;true&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;* &lt;span style="font-style:italic;"&gt;&lt;span style="font-weight:bold;"&gt;Range &lt;/span&gt;&lt;/span&gt;(optional) - A Range object representing the position in which to insert the picture.&lt;br /&gt;&lt;br /&gt;The &lt;span style="font-style:italic;"&gt;AddPicture()&lt;/span&gt; method returns a reference to the newly created InlineShape object that is your picture.&lt;br /&gt;&lt;br /&gt;So, to insert an image at the start of the range that we defined earlier, we could call the method on our &lt;span style="font-style:italic;"&gt;range &lt;/span&gt;object...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;range = doc.Sentences(2)&lt;br /&gt;pic = range.InlineShapes.AddPicture( { 'FileName' =&gt; image } )&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...or we could call it on the Document object and pass the Range object as a parameter:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;range = doc.Sentences(2)&lt;br /&gt;pic = doc.InlineShapes.AddPicture( { 'FileName' =&gt; image, &lt;br /&gt;                                     'Range' =&gt; range } )&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;When providing all four parameters, the syntax looks like this:&lt;br /&gt;&lt;pre&gt;    &lt;br /&gt;pic = doc.InlineShapes.AddPicture( { 'FileName' =&gt; image, &lt;br /&gt;                                     'LinkToFile' =&gt; false, &lt;br /&gt;                                     'SaveWithDocument' =&gt; false, &lt;br /&gt;                                     'Range' =&gt; range } )&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If your only parameter is the FileName, you could bypass the hash format and simply pass the string to the method, like this...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;pic = range.InlineShapes.AddPicture( 'C:\Pictures\Picture1.jpg' )&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To insert the image at the currently selected position, use the &lt;span style="font-style:italic;"&gt;word.Selection.Range&lt;/span&gt; object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;pic = word.Selection.Range.InlineShapes.AddPicture( image )&lt;br /&gt;pic = doc.InlineShapes.AddPicture( { 'FileName' =&gt; image, &lt;br /&gt;                                     'Range' =&gt; word.Selection.Range } )&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And that's our show for today. Thanks for stopping by!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-5544902475478065216?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/5544902475478065216/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=5544902475478065216' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/5544902475478065216'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/5544902475478065216'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2009/08/ruby-word-inserting-pictures-into.html' title='Ruby &amp; Word: Inserting Pictures Into Documents'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-7252018167599528782</id><published>2009-08-20T06:38:00.004-05:00</published><updated>2009-08-20T07:50:57.277-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='book'/><title type='text'>About the Book</title><content type='html'>A few readers have asked me for details on the book-in-progress.&lt;br /&gt;&lt;br /&gt;The working title is "Automating Windows Applications with Ruby". As the title implies, the focus is on automating applications, usually via Win32OLE/COM.&lt;br /&gt;&lt;br /&gt;This is not an "Introduction to Ruby" book: I make the assumption that the reader already has a working knowledge of the language and now wants to put it to good use driving applications and processes on Windows.  Some of the topics covered include:&lt;br /&gt;&lt;br /&gt;* Microsoft Excel&lt;br /&gt;* Microsoft Word&lt;br /&gt;* Microsoft Outlook&lt;br /&gt;* Microsoft PowerPoint&lt;br /&gt;* Database Access with ADO&lt;br /&gt;* Microsoft Access&lt;br /&gt;* Microsoft SQL Server&lt;br /&gt;* Microsoft Internet Explorer&lt;br /&gt;* Microsoft Office Document Imaging (OCR)&lt;br /&gt;* iTunes&lt;br /&gt;* Windows Media Player&lt;br /&gt;* Text-to-Speech&lt;br /&gt;* Microsoft Agent&lt;br /&gt;* Hummingbird HostExplorer&lt;br /&gt;* Attachmate Extra&lt;br /&gt;* Windows Management Instrumentation&lt;br /&gt;&lt;br /&gt;Each chapter will include step-by-step tutorials, and many chapters will also include a reference section covering commonly-used classes, properties, and methods.  Some chapters are extensive, while others may be only a few pages in length.&lt;br /&gt;&lt;br /&gt;This isn't intended to be an "Everything You'd Ever Want To Know About..." resource, but it is my intention to provide you with a book that will serve as both a quick-start guide and a go-to reference for automating a wide variety of applications.&lt;br /&gt;&lt;br /&gt;Stay tuned...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-7252018167599528782?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/7252018167599528782/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=7252018167599528782' title='12 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/7252018167599528782'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/7252018167599528782'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2009/08/about-book.html' title='About the Book'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>12</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-6432464328814526112</id><published>2009-08-05T07:26:00.004-05:00</published><updated>2009-08-05T07:59:24.966-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='win32ole'/><category scheme='http://www.blogger.com/atom/ns#' term='excel'/><title type='text'>Ruby &amp; Excel: Inserting Pictures Into Cells (New and Improved!)</title><content type='html'>In &lt;a href="http://rubyonwindows.blogspot.com/2009/07/ruby-excel-inserting-pictures-into.html"&gt;a previous article&lt;/a&gt;, I discussed a method for inserting images into an Excel worksheet. It seems that the &lt;span style="font-style:italic;"&gt;Worksheet.Pictures.Insert()&lt;/span&gt; method that I demonstrated in that article, though frequently used, is not actually officially documented in the Excel Object Model Reference. An astute reader has called my attention to this fact and, in reply, I hereby present the officially documented---and probably preferred---method for adding an image to a worksheet.&lt;br /&gt;&lt;br /&gt;The Worksheet object's &lt;span style="font-style:italic;"&gt;Shapes &lt;/span&gt;collection includes an &lt;span style="font-style:italic;"&gt;AddPicture()&lt;/span&gt; method that creates a picture from an existing file and returns a Shape object that represents the new picture. The syntax is:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;.AddPicture(Filename, LinkToFile, SaveWithDocument, Left, Top, Width, Height)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;All seven arguments are required, but this allows you to specify the position and size of the picture in the method call.&lt;br /&gt;&lt;br /&gt;The following code inserts an image into the range of cells from C3 to F5 in the active worksheet:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;&lt;br /&gt;xl = WIN32OLE.connect('Excel.Application')&lt;br /&gt;ws = xl.ActiveSheet&lt;br /&gt;&lt;br /&gt;range = ws.Range('C3:F5')&lt;br /&gt;&lt;br /&gt;pic = ws.Shapes.AddPicture( { &lt;br /&gt;    'FileName' =&gt; 'C:\Pictures\Image1.jpg', &lt;br /&gt;    'LinkToFile' =&gt; false, &lt;br /&gt;    'SaveWithDocument' =&gt; true, &lt;br /&gt;    'Left' =&gt; range.Left, &lt;br /&gt;    'Top' =&gt; range.Top, &lt;br /&gt;    'Width' =&gt; range.Width, &lt;br /&gt;    'Height' =&gt; range.Height &lt;br /&gt;} )&lt;br /&gt;    &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You can find further details on the AddPicture() method on &lt;a href="http://msdn.microsoft.com/en-us/library/bb209605.aspx"&gt;MSDN&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;My thanks to Charles Roper for his inquiry, prompting me to dig a little deeper.&lt;br /&gt;&lt;br /&gt;And my thanks to you for stopping by!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-6432464328814526112?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/6432464328814526112/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=6432464328814526112' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/6432464328814526112'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/6432464328814526112'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2009/08/ruby-excel-inserting-pictures-into.html' title='Ruby &amp; Excel: Inserting Pictures Into Cells (New and Improved!)'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-2136933897701255202</id><published>2009-08-02T15:33:00.003-05:00</published><updated>2009-08-02T15:38:27.428-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='win32ole'/><category scheme='http://www.blogger.com/atom/ns#' term='ocr'/><category scheme='http://www.blogger.com/atom/ns#' term='modi'/><title type='text'>OCR: Converting Images to Text with MODI</title><content type='html'>Joe Schmoe from Kokomo has a scanned image of a 300-page contract.  Joe wishes he could search this file for certain rates and terms, but it's an image, not a text file.  &lt;span style="font-weight:bold;"&gt;OCR &lt;span style="font-style:italic;"&gt;&lt;/span&gt;&lt;/span&gt;might be just what the doctor ordered.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Optical character recognition, usually abbreviated to OCR, is the mechanical or electronic translation of images of handwritten, typewritten or printed text (usually captured by a scanner) into machine-editable text.&lt;/span&gt; --&lt;a href="http://en.wikipedia.org/wiki/Optical_character_recognition"&gt;Wikipedia&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;One such OCR solution that you may already have available to you is Microsoft Office Document Imaging (MODI), part of the Microsoft Office suite.  Let's look at how you can use Ruby and the MODI API to automate the conversion of a scanned document into text.&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;&lt;br /&gt;Installing MODI&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;MODI might not have been installed when you installed Microsoft Office, so your first step may be to install it from the Office install disks.  If installed, you will probably find an icon for "Microsoft Office Document Imaging" located on your Windows Start/Programs menus under "Microsoft Office Tools".  If it's not there, go to your Add/Remove Software control panel, select your Microsoft Office installation, and select the option to add features.  Then follow the necessary steps, which may vary depending on your version of Windows and Office.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Accessing the MODI API&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;To begin with, we'll use the &lt;span style="font-style:italic;"&gt;win32ole&lt;/span&gt; module to create a new instance of the &lt;span style="font-style:italic;"&gt;MODI.Document&lt;/span&gt; object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;doc = WIN32OLE.new('MODI.Document')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Loading the Image&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The next step is to call the document object's &lt;span style="font-style:italic;"&gt;Create()&lt;/span&gt; method, passing it the name of the .TIF file to load:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;doc.Create('C:\images\image1.tif')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;NOTE: MODI only works with TIFF files.  If your image is in another format (.JPG or .PNG, for example), you can use an image editor (such as Paint.NET or Photoshop) or code library (such as RMagick) to convert it to TIFF format.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Performing the OCR&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The &lt;span style="font-style:italic;"&gt;OCR()&lt;/span&gt; method performs the optical character recognition on the document.  The mthod can be called without parameters...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;doc.OCR()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...or with any of three optional parameters:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;doc.OCR( { 'LangId' =&gt; 9, &lt;br /&gt;           'OCROrientImage' =&gt; true, &lt;br /&gt;           'OCRStraightenImage' =&gt; true } )&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;LangId&lt;/span&gt;: An integer representing the language of the document.  English = 9, French = 12, German = 7, Italian = 16, Spanish = 10.  This value defaults to the user's regional settings.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;OCROrientImage&lt;/span&gt;: This boolean value specifies whether the OCR engine attempts to determine the orientation (portrait versus landscape) of the page.  The value defaults to &lt;span style="font-style:italic;"&gt;true&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;OCRStraightenImage&lt;/span&gt;: This boolean value specifies whether the OCR engine attempts to "deskew" the image to correct minor misalignments.  The value defaults to &lt;span style="font-style:italic;"&gt;true&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;You may find that tweaking these parameters from their default values produces better results, depending on the individual image(s) you're working with.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Getting the Text&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Naturally, you'll want to get your hands on the text produced by the OCR process.  Each page of the Document is represented by an Image object.  The Image object contains a Layout object; and that Layout object's Text property represents the text for that image/page.  So the hierarchy looks like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Document&lt;br /&gt;    =&gt;Images&lt;br /&gt;        =&gt;Image&lt;br /&gt;            =&gt;Layout&lt;br /&gt;                =&gt;Text&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To accrue the entire text, simply iterate over the Document.Images collection and grab the Layout.Text values.  For example:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;File.open('my_text.txt', 'w') do |f|&lt;br /&gt;    for image in doc.Images&lt;br /&gt;        f.puts("\n" + image.Layout.Text + "\n")&lt;br /&gt;    end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Text, But Not Formatting&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;No OCR process can guarantee 100% accuracy, but I've found that MODI does a pretty good job recognizing text.  Results will vary, of course, depending on the quality of the TIFF image.  Note, however, that it cannot preserve formatting of tabular data.  So while the text in a series of columns may be produced with a high degree of accuracy, that text will probably be produced with one value per line.  So...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;apple    orange    pear&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...comes out as...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;apple&lt;br /&gt;orange&lt;br /&gt;pear&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Paragraphs of text have, in my experience, been produced with the proper line feeds.  Play around with it and see if it meets your needs.&lt;br /&gt;&lt;br /&gt;That concludes our show for today.  Thanks for tuning in!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-2136933897701255202?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/2136933897701255202/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=2136933897701255202' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/2136933897701255202'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/2136933897701255202'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2009/08/ocr-converting-images-to-text-with-modi.html' title='OCR: Converting Images to Text with MODI'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-1910133545778414753</id><published>2009-07-12T18:01:00.005-05:00</published><updated>2009-08-05T08:07:33.098-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='win32ole'/><category scheme='http://www.blogger.com/atom/ns#' term='excel'/><title type='text'>Ruby &amp; Excel: Inserting Pictures Into Cells</title><content type='html'>A reader recently asked how to insert an image from their PC into a cell in an Excel worksheet. So if you have a couple of minutes, I'll demonstrate how to insert an image, specify the exact position, and resize the image.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;UPDATE:&lt;/span&gt; It seems that the Worksheet.Pictures.Insert() method that I demonstrated below, though frequently used, is not actually officially documented in the Excel Object Model Reference. See &lt;a href="http://rubyonwindows.blogspot.com/2009/08/ruby-excel-inserting-pictures-into.html"&gt;this newer article&lt;/a&gt; for the officially documented method.&lt;br /&gt;&lt;br /&gt;Let's start by connecting to an open Excel &lt;a href="http://rubyonwindows.blogspot.com/2007/04/automating-excel-with-ruby-worksheet.html"&gt;worksheet&lt;/a&gt;:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;xl = WIN32OLE.connect('Excel.Application')&lt;br /&gt;ws = xl.ActiveSheet&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Inserting the Image&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;To insert a picture into a worksheet, call the Worksheet object's &lt;span style="font-style:italic;"&gt;Pictures.Insert()&lt;/span&gt; method, passing it the filename of the image to insert:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;pic = ws.Pictures.Insert('C:\Pictures\Image1.jpg')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The &lt;span style="font-style:italic;"&gt;Pictures.Insert()&lt;/span&gt; method inserts a new Picture object into the &lt;span style="font-style:italic;"&gt;Pictures &lt;/span&gt;collection and returns a reference to the new Picture object, which we've assigned to the variable &lt;span style="font-style:italic;"&gt;pic&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Positioning the Image&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;OK, now that you've got the picture inserted into the worksheet, you probably want to specify its exact position, perhaps aligned with a certain range of cells. Let's start by defining that &lt;a href="http://rubyonwindows.blogspot.com/2007/04/automating-excel-with-ruby-rows-columns.html"&gt;range &lt;/a&gt;of cells, from cell C3 to cell F5:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;range = ws.Range('C3:F5')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We want the picture aligned with the top/left corner of our range, so we'll set our Picture object's &lt;span style="font-style:italic;"&gt;Top &lt;/span&gt;and &lt;span style="font-style:italic;"&gt;Left &lt;/span&gt;properties to be the same as our Range object's &lt;span style="font-style:italic;"&gt;Top &lt;/span&gt;and &lt;span style="font-style:italic;"&gt;Left &lt;/span&gt;properties.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;pic.Top = range.Top&lt;br /&gt;pic.Left = range.Left&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Resizing the Image&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Now that we have the top/left position set, let's move on to specifying the width and height of the picture.  The original image has an &lt;span style="font-style:italic;"&gt;&lt;a href="http://en.wikipedia.org/wiki/Aspect_ratio_%28image%29"&gt;aspect ratio&lt;/a&gt;&lt;/span&gt;, which is the image's width divided by its height.  An image that is 800x600 pixels could be said to have an aspect ratio of 4:3. When resizing an image, you may wish to maintain its original aspect ratio, to avoid making the image appear stretched in one direction or the other.&lt;br /&gt;&lt;br /&gt;By default, a Picture object in Excel has its aspect ratio locked, so that when you change &lt;span style="font-style:italic;"&gt;either &lt;/span&gt;the width &lt;span style="font-style:italic;"&gt;or &lt;/span&gt;the height, the other dimension is automatically adjusted to preserve the aspect ratio.&lt;br /&gt;&lt;br /&gt;You adjust the width and/or height of a Picture object by setting its---wait for it---&lt;span style="font-style:italic;"&gt;Width &lt;/span&gt;and &lt;span style="font-style:italic;"&gt;Height &lt;/span&gt;properties.  With the aspect ratio locked, we can set the image to fill the width of the range...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;pic.Width = range.Width&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...or to fill the height of the range...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;pic.Height = range.Height&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...but we cannot successfully apply both settings without first unlocking the aspect ratio.&lt;br /&gt;&lt;br /&gt;To unlock the Picture's aspect ratio, you set the Picture object's &lt;span style="font-style:italic;"&gt;ShapeRange.LockAspectRatio&lt;/span&gt; property to &lt;span style="font-style:italic;"&gt;false&lt;/span&gt;:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;pic.ShapeRange.LockAspectRatio = false&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now that we have unlocked the aspect ratio, we can set the picture's width and height to match the range's width and height:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;pic.Width = range.Width&lt;br /&gt;pic.Height = range.Height&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The &lt;span style="font-style:italic;"&gt;Width &lt;/span&gt;and &lt;span style="font-style:italic;"&gt;Height &lt;/span&gt;properties are just numerical values. So you could instead do something like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;pic.Width = 400&lt;br /&gt;pic.Height = 300&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And that, my friends, is how you insert, position, and resize an image in Excel.&lt;br /&gt;&lt;br /&gt;Let me know if you have any questions or comments, and thanks for stopping by!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-1910133545778414753?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/1910133545778414753/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=1910133545778414753' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/1910133545778414753'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/1910133545778414753'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2009/07/ruby-excel-inserting-pictures-into.html' title='Ruby &amp; Excel: Inserting Pictures Into Cells'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-2679054707872784897</id><published>2009-07-09T07:34:00.001-05:00</published><updated>2009-07-09T07:54:37.593-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='win32ole'/><category scheme='http://www.blogger.com/atom/ns#' term='excel'/><title type='text'>Ruby &amp; Excel: Hiding and Unhiding Columns, Rows, and Worksheets</title><content type='html'>Occasionally, when working with Excel, you may have a need to hide certain columns or rows in a worksheet. As an example, perhaps your worksheet lists revenue for each of 12 months, but your intended recipient only wants to see the columns showing year-to-date totals. Whatever the reason, you'd like to simply hide certain columns (or rows) rather than delete them completely. And since you're &lt;span style="font-style:italic;"&gt;here&lt;/span&gt;, you'd probably prefer to do that &lt;span style="font-style:italic;"&gt;with Ruby code&lt;/span&gt;...&lt;br /&gt;&lt;br /&gt;Let's set the scene by assuming that your code is working with an open workbook and you want to hide columns in the currently selected &lt;a href="http://rubyonwindows.blogspot.com/2007/04/automating-excel-with-ruby-worksheet.html"&gt;worksheet&lt;/a&gt;:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;xl = WIN32OLE.connect('Excel.Application')&lt;br /&gt;wb = xl.ActiveWorkbook&lt;br /&gt;ws = xl.ActiveSheet&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Hiding Columns&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;To hide one or more columns, obtain a reference to the &lt;a href="http://rubyonwindows.blogspot.com/2007/04/automating-excel-with-ruby-rows-columns.html"&gt;column range(s)&lt;/a&gt; and set the &lt;span style="font-style:italic;"&gt;Hidden &lt;/span&gt;property to &lt;span style="font-style:italic;"&gt;true&lt;/span&gt;:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ws.Columns('A').Hidden = true&lt;br /&gt;ws.Columns('D:K').Hidden = true&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Unhiding (displaying) columns works the same way; except, of course, you set the &lt;span style="font-style:italic;"&gt;Hidden &lt;/span&gt;property to &lt;span style="font-style:italic;"&gt;false&lt;/span&gt;:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ws.Columns('A').Hidden = false&lt;br /&gt;ws.Columns('D:K').Hidden = false&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The following code ensures that all columns in a worksheet are visible:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ws.Columns.Hidden = false&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Hiding Rows&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Rows have a &lt;span style="font-style:italic;"&gt;Hidden &lt;/span&gt;property, too, so you can do this to hide rows...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ws.Rows('3').Hidden = true&lt;br /&gt;ws.Rows('7:11').Hidden = true&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...and to unhide them:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ws.Rows('3').Hidden = false&lt;br /&gt;ws.Rows('7:11').Hidden = false&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Hiding Worksheets&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;But what if you want to hide an entire worksheet? The Worksheet object doesn't have a &lt;span style="font-style:italic;"&gt;Hidden &lt;/span&gt;property. Instead, you'll use the &lt;span style="font-style:italic;"&gt;Visible &lt;/span&gt;property. So to hide the 'Great Amish Scientists' worksheet in the active workbook (&lt;span style="font-style:italic;"&gt;wb&lt;/span&gt;), we'd use this code:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;wb.Worksheets('Great Amish Scientists').Visible = false&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And to unhide it, we'd toggle the &lt;span style="font-style:italic;"&gt;Visible &lt;/span&gt;property back to &lt;span style="font-style:italic;"&gt;true&lt;/span&gt;:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;wb.Worksheets('Great Amish Scientists').Visible = true&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The following code ensures that all worksheets in a workbook are visible:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;wb.Worksheets.each do |ws|&lt;br /&gt;    ws.Visible = true&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And that, folks, is pretty much all there is to it. Let me know if you have any questions or comments, and thanks for stopping by!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-2679054707872784897?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/2679054707872784897/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=2679054707872784897' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/2679054707872784897'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/2679054707872784897'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2009/07/ruby-excel-hiding-and-unhiding-columns.html' title='Ruby &amp; Excel: Hiding and Unhiding Columns, Rows, and Worksheets'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-548040704791074903</id><published>2009-06-28T19:15:00.012-05:00</published><updated>2009-06-28T20:17:39.379-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='wxruby'/><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='gui'/><title type='text'>wxRuby: Changing Text Fonts and Colours</title><content type='html'>A reader recently asked me how to change the text font size and color for &lt;a href="http://rubyonwindows.blogspot.com/search/label/wxruby"&gt;wxRuby&lt;/a&gt; controls such as the StaticText control. Let's take a look at how you can do this.&lt;br /&gt;&lt;br /&gt;Imagine, if you will, that we have a StaticText control named &lt;span style="font-style:italic;"&gt;my_control&lt;/span&gt;...&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Changing Fonts&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;We'll begin by creating a new Font object by calling the &lt;span style="font-style:italic;"&gt;Font.new(&lt;/span&gt;) method:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;my_font = Wx::Font.new()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Next, we'll set various attributes of our new Font object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;my_font.set_point_size(20)&lt;br /&gt;my_font.set_family(Wx::FONTFAMILY_MODERN)&lt;br /&gt;my_font.set_style(Wx::FONTSTYLE_ITALIC) &lt;br /&gt;my_font.set_weight(Wx::FONTWEIGHT_BOLD)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Once we have our Font object defined to our satisfaction, we can then pass it to our control's &lt;span style="font-style:italic;"&gt;set_font()&lt;/span&gt; method:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;my_control.set_font(my_font)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Changing Colors&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Text color is &lt;span style="font-style:italic;"&gt;not&lt;/span&gt; an attribute of the Font object, but is rather the foreground color of the control. So, to change the color of the text, we call the control's &lt;span style="font-style:italic;"&gt;set_foreground_colour()&lt;/span&gt; method and pass it a Colour object. We'll make it easy and use one of wxRuby's built-in Colour constants:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;my_control.set_foreground_colour(Wx::RED)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Make a note  of the spelling: &lt;span style="font-style:italic;"&gt;colour&lt;/span&gt;, not &lt;span style="font-style:italic;"&gt;color&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Further Reading&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;A simple working example can be found &lt;a href="http://pastie.org/527541"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Documentation of the Font object can be found &lt;a href="http://wxruby.rubyforge.org/doc/font.html"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Documentation of the &lt;span style="font-style:italic;"&gt;set_foreground_colour()&lt;/span&gt; method can be found &lt;a href="http://wxruby.rubyforge.org/doc/window.html#Window_setforegroundcolour"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Documentation of the Colour object can be found &lt;a href="http://wxruby.rubyforge.org/doc/colour.html"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;And there you have it. Let me know if you'd like to see more on this or other subjects. &lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-548040704791074903?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/548040704791074903/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=548040704791074903' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/548040704791074903'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/548040704791074903'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2009/06/wxruby-changing-text-fonts-and-colours.html' title='wxRuby: Changing Text Fonts and Colours'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-1610032345734731799</id><published>2009-06-23T08:00:00.001-05:00</published><updated>2009-06-23T08:04:45.002-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='word'/><title type='text'>Ruby &amp; Word: Inserting Tables</title><content type='html'>Microsoft Word is great for text documents. Microsoft Excel is great for tables of data. But, sometimes, you need to get your chocolate in your peanut butter. In other words, you may occasionally need to include a table in a Word document. Let's walk through how to do that.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Setting the Stage&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;To borrow an example from my upcoming book, let's say that you want to insert a table that contains a list of all movies starring Spencer Tracy and Katharine Hepburn, including the year each movie was released, the title, and the director. Your 2-dimensional &lt;span style="font-style:italic;"&gt;films&lt;/span&gt; array might look like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;films = []&lt;br /&gt;films &lt;&lt; ["1942", "Woman of the Year", "George Stevens"]&lt;br /&gt;films &lt;&lt; ["1942", "Keeper of the Flame", "George Cukor"]&lt;br /&gt;films &lt;&lt; ["1945", "Without Love", "Harold S. Bucquet"]&lt;br /&gt;films &lt;&lt; ["1947", "The Sea of Grass", "Elia Kazan"]&lt;br /&gt;films &lt;&lt; ["1948", "State of the Union", "Frank Capra"]&lt;br /&gt;films &lt;&lt; ["1949", "Adam's Rib", "George Cukor"]&lt;br /&gt;films &lt;&lt; ["1952", "Pat and Mike", "George Cukor"]&lt;br /&gt;films &lt;&lt; ["1957", "Desk Set", "Walter Lang"]&lt;br /&gt;films &lt;&lt; ["1967", "Guess Who's Coming to Dinner", "Stanley Kramer"]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Let's connect to a running &lt;a href="http://rubyonwindows.blogspot.com/2007/04/automating-word-with-ruby-application.html"&gt;instance of Word&lt;/a&gt; and use the currently selected &lt;a href="http://rubyonwindows.blogspot.com/2007/04/automating-word-with-ruby-document.html"&gt;document&lt;/a&gt;:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;word = WIN32OLE.connect('Word.Application')&lt;br /&gt;doc = word.ActiveDocument&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Moving to the End of the Document&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;First, let's move to the end of the document, where we'll add our new table. We do this by calling the &lt;span style="font-style:italic;"&gt;Selection.EndKey()&lt;/span&gt; method, which moves the selection to the end of a word, line, or document. We'll pass this method a value of &lt;span style="font-style:italic;"&gt;6&lt;/span&gt;, which specifies that we want to move to the end of the document:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;word.Selection.EndKey(6)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Adding a New Table&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The Tables collection in Word's object model represents all the Table objects in a selection, range, or document. To add a new Table to a document, call the Document object's &lt;span style="font-style:italic;"&gt;Tables.Add()&lt;/span&gt; method and pass it three parameters:&lt;br /&gt;&lt;br /&gt;* The range object representing where the table is to be inserted&lt;br /&gt;* The number of rows for the new table&lt;br /&gt;* The number of columns for the new table&lt;br /&gt;&lt;br /&gt;Now let's add a new table with one row and three columns (we'll add more rows later):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;table = doc.Tables.Add(word.Selection.Range, 1, 3)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The &lt;span style="font-style:italic;"&gt;Tables.Add()&lt;/span&gt; method returns a reference to the newly created table, which we have assigned to the cleverly named variable &lt;span style="font-style:italic;"&gt;table&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Inserting Text into Table Cells&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;You can reference a single cell in a table by calling the &lt;span style="font-style:italic;"&gt;Cell()&lt;/span&gt; method and passing it the row and column numbers (NOTE: the first row or column is represented by &lt;span style="font-style:italic;"&gt;1&lt;/span&gt;, not &lt;span style="font-style:italic;"&gt;0&lt;/span&gt;). Once you have your cell, you can set the text via its &lt;span style="font-style:italic;"&gt;Range.Text&lt;/span&gt; property; so we add the header text as follows:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;table.Cell(1, 1).Range.Text = 'Year'&lt;br /&gt;table.Cell(1, 2).Range.Text = 'Film Title'&lt;br /&gt;table.Cell(1, 3).Range.Text = 'Director'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Adding Rows&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;To add a row to your table, simply call the Table object's &lt;span style="font-style:italic;"&gt;Rows.Add()&lt;/span&gt; method. Now that we've added the header text, let's iterate over our &lt;span style="font-style:italic;"&gt;films&lt;/span&gt; array and add a new row to the table for each film and insert the text:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;films.each_with_index do |film, r|&lt;br /&gt;    table.Rows.Add()&lt;br /&gt;    film.each_with_index do |field, c|&lt;br /&gt;        table.Cell(r + 2, c + 1).Range.Text = field&lt;br /&gt;    end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;There you have it. Thanks for stopping by!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-1610032345734731799?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/1610032345734731799/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=1610032345734731799' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/1610032345734731799'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/1610032345734731799'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2009/05/ruby-word-inserting-tables.html' title='Ruby &amp; Word: Inserting Tables'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-2614253677748061348</id><published>2009-06-12T07:06:00.014-05:00</published><updated>2009-06-12T07:35:30.993-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='word'/><title type='text'>Ruby &amp; Word: Counting Words and Pages</title><content type='html'>Someone recently asked how to get a count of the number of words and pages in a Microsoft Word document.  This is done by calling the &lt;span style="font-style:italic;"&gt;ComputeStatistics()&lt;/span&gt; method on a Range or Document object.&lt;br /&gt;&lt;br /&gt;As an example (play along at home), let's imagine that you have a &lt;a href="http://rubyonwindows.blogspot.com/search/label/word"&gt;Word&lt;/a&gt; document open.  Your first step is to use the &lt;span style="font-style:italic;"&gt;win32ole &lt;/span&gt; library's &lt;span style="font-style:italic;"&gt;connect()&lt;/span&gt; method to connect to the existing instance of Word:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;word = WIN32OLE.connect('Word.Application')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You pass the &lt;span style="font-style:italic;"&gt;ComputeStatistics()&lt;/span&gt; method an integer representing the type of statistic that you want to calculate.  In other words, "What do you want to count?"  So let's take a moment to define constants for those values:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;WdStatisticCharacters = 3&lt;br /&gt;WdStatisticCharactersWithSpaces = 5&lt;br /&gt;WdStatisticWords = 0&lt;br /&gt;WdStatisticLines = 1&lt;br /&gt;WdStatisticParagraphs = 4&lt;br /&gt;WdStatisticPages = 2&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You can call the &lt;span style="font-style:italic;"&gt;ComputeStatistics()&lt;/span&gt; method on a &lt;a href="http://rubyonwindows.blogspot.com/2007/04/automating-word-with-ruby-document.html"&gt;Document object&lt;/a&gt;...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;doc = word.ActiveDocument&lt;br /&gt;word_count = doc.ComputeStatistics(WdStatisticWords)&lt;br /&gt;page_count = doc.ComputeStatistics(WdStatisticPages)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...or on a &lt;a href="http://rubyonwindows.blogspot.com/2007/04/automating-word-with-ruby-range-object.html"&gt;Range object&lt;/a&gt;...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;paragraph = doc.Paragraphs(27)&lt;br /&gt;word_count = paragraph.Range.ComputeStatistics(WdStatisticWords)&lt;br /&gt;char_count = paragraph.Range.ComputeStatistics(WdStatisticCharacters)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;When called on a Document object, the method accepts an optional second parameter, &lt;span style="font-style:italic;"&gt;IncludeFootnotesAndEndnotes&lt;/span&gt;, a boolean which (obviously) specifies if the calculation should include footnotes and endnotes:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;word_count = doc.ComputeStatistics(WdStatisticWords, true)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The &lt;span style="font-style:italic;"&gt;IncludeFootnotesAndEndnotes&lt;/span&gt; parameter defaults to &lt;span style="font-style:italic;"&gt;false&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;Official details on the &lt;span style="font-style:italic;"&gt;ComputeStatistics()&lt;/span&gt; method are available from MSDN &lt;a href="http://msdn.microsoft.com/en-us/library/aa171867(office.11).aspx"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;That's all for now.  Thanks for stopping by!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-2614253677748061348?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/2614253677748061348/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=2614253677748061348' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/2614253677748061348'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/2614253677748061348'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2009/06/ruby-word-counting-words-and-pages.html' title='Ruby &amp; Word: Counting Words and Pages'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-7579703581704514797</id><published>2009-06-04T08:00:00.000-05:00</published><updated>2009-06-04T08:18:51.198-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='word'/><title type='text'>Ruby &amp; Word: Creating and Applying Styles</title><content type='html'>Microsoft &lt;a href="http://rubyonwindows.blogspot.com/search/label/word"&gt;Word&lt;/a&gt; uses the Styles model to apply a set of pre-defined formatting to text.  Styles can also serve a second purpose, to tag sections of the document as normal, title, headings and such.  You can then, for example, create a Table of Contents in Word based on the text that is formatted with the Heading styles.&lt;br /&gt;&lt;br /&gt;Naturally, you can do all this with code (otherwise, I wouldn't be wasting your time here).  Over the next few minutes, we'll walk through the process of creating a new Style, setting its properties, and then applying that style to text.&lt;br /&gt;&lt;br /&gt;The Style object represents a single built-in or user-defined Word style.  The Styles collection contains all the Style objects within a document.  To reference the Styles collection, simply call the Styles method on the document:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;doc = word.ActiveDocument&lt;br /&gt;styles = doc.Styles&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To create a new Style, we call the &lt;span style="font-style:italic;"&gt;Add()&lt;/span&gt; method on the Styles object and pass it a hash defining the name and the type of the new Style.  The following code creates a new Paragraph style named 'Code':&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;code_style = doc.Styles.Add({'Name' =&gt; 'Code', 'Type' =&gt; 1})&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The Type property defines what StyleType your new Style is based on.  Possible values are:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;WdStyleTypeParagraph = 1&lt;br /&gt;wdStyleTypeCharacter = 2&lt;br /&gt;WdStyleTypeTable     = 3&lt;br /&gt;WdStyleTypeList      = 4&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The default is WdStyleTypeParagraph.&lt;br /&gt;&lt;br /&gt;The &lt;span style="font-style:italic;"&gt;Add()&lt;/span&gt; method returns a reference to the newly-created Style object.  Now that you have your new Style object, you can customize it through various properties that you can set.  As a starting point, you may want to base your new style on another style by setting the &lt;span style="font-style:italic;"&gt;BaseStyle &lt;/span&gt;property:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;code_style.BaseStyle = 'Normal'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Font properties can be defined...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;code_style.Font.Name = 'Consolas'&lt;br /&gt;code_style.Font.Size = 12&lt;br /&gt;code_style.Font.Bold = false&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...as well as paragraph spacing and background colors:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;code_style.NoSpaceBetweenParagraphsOfSameStyle = true&lt;br /&gt;code_style.Shading.BackgroundPatternColor = 15132390&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Note that not all properties will apply to all style types.&lt;br /&gt;&lt;br /&gt;Now that you've created your own Style, you might want to automatically apply it to some existing text.  The following code iterates over each paragraph in the document (&lt;span style="font-style:italic;"&gt;doc&lt;/span&gt; variable).  For each paragraph that uses the 'Preformatted Text' style, the new 'Code' style is applied instead:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;doc.Paragraphs.each do |paragraph|&lt;br /&gt;    if paragraph.Style.NameLocal == 'Preformatted Text'&lt;br /&gt;        paragraph.Style = 'Code'&lt;br /&gt;    end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;There you have it.  If you'd like to learn more here about Styles, or anything else related to automating Word with Ruby, please let me know.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-7579703581704514797?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/7579703581704514797/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=7579703581704514797' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/7579703581704514797'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/7579703581704514797'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2009/06/ruby-word-creating-and-applying-styles.html' title='Ruby &amp; Word: Creating and Applying Styles'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-1891061465285187623</id><published>2009-05-24T17:00:00.001-05:00</published><updated>2009-05-24T17:05:51.644-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='tools'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>The OCRA Compiler: Tips, Tricks, and Gotchas</title><content type='html'>I've &lt;a href="http://rubyonwindows.blogspot.com/2009/05/ocra-one-click-ruby-application-builder.html"&gt;previously mentioned&lt;/a&gt; Lars Christensen's &lt;a href="http://rubyforge.org/projects/ocra/"&gt;One-Click Ruby Application Builder&lt;/a&gt;, a "compiler" for Ruby scripts that shows a lot of potential (and current value).  There's not much documentation yet, but folks are installing it, using it, and providing feedback.  It works right out of the box for many purposes, but here are a few things to keep in mind as you use it...&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;"Failed to create directory" Error&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;One user reported receiving the error "Failed to create directory" when running the compiled executable.  As a possible workaround, try running ocra.rb from the directory where your script is located.  For example, instead of running...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ocra.rb "C:\code\rubyscripts\application.rbw"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...navigate to the "C:\code\rubyscripts" directory, then run:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ocra.rb application.rbw&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Note that while this resolved the problem on my machine, it didn't help the person who originally reported the problem.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Require RubyGems&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;When you compile your script (which uses one or more gems) with OCRA and then run the executable, you may receive a 'no such file to load' error.  Try adding the line...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'rubygems'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...to the top of your script, above the other &lt;span style="font-style:italic;"&gt;require&lt;span style="font-style:italic;"&gt;&lt;/span&gt;&lt;/span&gt; statements.  I've found that a script that runs pre-compiled without this statement may not execute once compiled.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Compile Without Running&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I mentioned earlier that it would be nice to have an option to avoid fully running the script, similar to RubyScript2Exe's &lt;span style="font-style:italic;"&gt;exit if RUBYSCRIPT2EXE.is_compiling?&lt;/span&gt; idiom.  A tip of the hat goes to reader "BackOrder", who offered the following solution:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;exit if Object.const_defined?(:Ocra)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Put all your &lt;span style="font-style:italic;"&gt;require &lt;/span&gt;statements at the top of your code, followed by this line.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;OCRA and Mechanize: "libxml2.dll not found"&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;There may be a problem compiling a script that requires the mechanize gem.  Running the compiled executable resulted in a "libxml2.dll was not found" error on my machine.&lt;br /&gt;&lt;br /&gt;Note that the above observations relate to version 1.0.2 (current as of this writing) of the ocra gem.&lt;br /&gt;&lt;br /&gt;More information is available in the OCRA &lt;a href="http://rubyforge.org/forum/?group_id=8185"&gt;forums&lt;/a&gt; and &lt;a href="http://rubyforge.org/tracker/?group_id=8185"&gt;bug tracker&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Thanks again to Lars for creating OCRA, and to the users who have installed it, used it, and provided their feedback.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-1891061465285187623?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/1891061465285187623/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=1891061465285187623' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/1891061465285187623'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/1891061465285187623'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2009/05/ocra-compiler-tips-tricks-and-gotchas.html' title='The OCRA Compiler: Tips, Tricks, and Gotchas'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-8860010970366469069</id><published>2009-05-17T11:03:00.003-05:00</published><updated>2009-05-17T11:35:35.685-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='win32ole'/><category scheme='http://www.blogger.com/atom/ns#' term='excel'/><title type='text'>Handling WIN32OLE Events in Excel</title><content type='html'>Someone recently asked how to have Ruby react to events in Excel.  Specifically, they were trying retrieve the contents of a row in a worksheet when it's selected.&lt;br /&gt;&lt;br /&gt;The win32ole module provides a WIN32OLE_EVENT class that will allow you to execute a block of code when a specific event occurs.&lt;br /&gt;&lt;br /&gt;To set the scene, let's use the &lt;span style="font-style:italic;"&gt;&lt;span style="font-weight:bold;"&gt;WIN32OLE.connect()&lt;/span&gt;&lt;/span&gt; method to connect to an existing instance of Microsoft Excel and grab a reference to the currently active workbook:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;xl = WIN32OLE.connect('Excel.Application')&lt;br /&gt;wb = xl.ActiveWorkbook&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Next, we'll call the &lt;span style="font-style:italic;"&gt;&lt;span style="font-weight:bold;"&gt;WIN32OLE_EVENT.new()&lt;/span&gt;&lt;/span&gt; method to create a new OLE event object.  You pass this method an OLE object---our Workbook object---and the name of the event sink.  In this instance, we want to use the &lt;span style="font-style:italic;"&gt;WorkbookEvents&lt;/span&gt; sink:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ev = WIN32OLE_EVENT.new(wb, 'WorkbookEvents')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Once you have your event sink defined, you call its &lt;span style="font-style:italic;"&gt;&lt;span style="font-weight:bold;"&gt;on_event()&lt;/span&gt;&lt;span style="font-weight:bold;"&gt;&lt;/span&gt;&lt;/span&gt; method to hook into a particular event and run a block of code when that event fires.  In our scenario, we want to take action when the &lt;span style="font-style:italic;"&gt;&lt;span style="font-weight:bold;"&gt;SheetSelectionChange &lt;/span&gt;&lt;/span&gt;event fires.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ev.on_event('SheetSelectionChange') do&lt;br /&gt;    range = xl.Selection&lt;br /&gt;    puts(range.Value)&lt;br /&gt;    STDOUT.flush()&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The above block of code will execute when the user selects a range of cells, and will print out the array of values from the selected cells.&lt;br /&gt;&lt;br /&gt;Finally, you need to start the event message loop to begin the event monitoring:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;loop do&lt;br /&gt;    WIN32OLE_EVENT.message_loop&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;In the real world, we need a means to exit the message loop.  Let's catch the &lt;span style="font-style:italic;"&gt;&lt;span style="font-weight:bold;"&gt;BeforeClose &lt;/span&gt;&lt;/span&gt;event, which fires (of course) just prior to the workbook being closed:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ev.on_event('BeforeClose') do&lt;br /&gt;    exit_event_loop&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now, when the &lt;span style="font-style:italic;"&gt;&lt;span style="font-weight:bold;"&gt;BeforeClose &lt;/span&gt;&lt;/span&gt;event fires, we'll have it call a new &lt;span style="font-style:italic;"&gt;&lt;span style="font-weight:bold;"&gt;exit_event_loop()&lt;/span&gt;&lt;/span&gt; method, which sets a &lt;span style="font-style:italic;"&gt;&lt;span style="font-weight:bold;"&gt;$LOOP&lt;/span&gt;&lt;/span&gt; value to &lt;span style="font-style:italic;"&gt;&lt;span style="font-weight:bold;"&gt;false&lt;/span&gt;&lt;/span&gt;:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;$LOOP = true&lt;br /&gt;&lt;br /&gt;def exit_event_loop&lt;br /&gt;    $LOOP = false&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Finally, we'll modify our earlier message loop block, accordingly, and also toss in a brief pause:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;while $LOOP&lt;br /&gt;    WIN32OLE_EVENT.message_loop&lt;br /&gt;    sleep(0.1)&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Our complete code looks something like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;&lt;br /&gt;xl = WIN32OLE.connect('Excel.Application')&lt;br /&gt;wb = xl.ActiveWorkbook&lt;br /&gt;&lt;br /&gt;ev = WIN32OLE_EVENT.new(wb, 'WorkbookEvents')&lt;br /&gt;&lt;br /&gt;ev.on_event('SheetSelectionChange') do&lt;br /&gt;    range = xl.Selection&lt;br /&gt;    puts(range.Value)&lt;br /&gt;    STDOUT.flush()&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;ev.on_event('BeforeClose') do&lt;br /&gt;    puts('Closed');STDOUT.flush&lt;br /&gt;    exit_event_loop&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;$LOOP = true&lt;br /&gt;&lt;br /&gt;def exit_event_loop&lt;br /&gt;    $LOOP = false&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;while $LOOP&lt;br /&gt;    WIN32OLE_EVENT.message_loop&lt;br /&gt;    sleep(0.1)&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And there you have it.  Tweak to suit your individual needs.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-8860010970366469069?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/8860010970366469069/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=8860010970366469069' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/8860010970366469069'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/8860010970366469069'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2009/05/handling-win32ole-events-in-excel.html' title='Handling WIN32OLE Events in Excel'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-1201501479819524668</id><published>2009-05-14T07:12:00.002-05:00</published><updated>2009-05-14T08:00:41.161-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='tools'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>OCRA: One-Click Ruby Application Builder</title><content type='html'>I &lt;a href="http://rubyonwindows.blogspot.com/2009/05/rubyscript2exe-and-rubygems.html"&gt;recently mentioned&lt;/a&gt; the fact that RubyScript2Exe 0.5.3 doesn't play well with recent versions of RubyGems.  At the end of that post I mentioned that there are alternatives emerging for creating executables from your Ruby code, including Lars Christensen's OCRA, the One-Click Ruby Application Builder.&lt;br /&gt;&lt;br /&gt;I've had a chance to take OCRA for a short test drive and it looks promising.  Like Erik Veenstra's &lt;a href="http://rubyonwindows.blogspot.com/2007/09/compiling-your-ruby-app-with.html"&gt;RubyScript2Exe&lt;/a&gt;, OCRA lets you "compile" your ruby code into an EXE file that you can distribute to others, without requiring that the users have Ruby installed on their PCs.  To quote the OCRA page:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;The executable is a self-extracting, self-running executable that contains the Ruby interpreter, your source code and any additionally needed ruby libraries or DLL.&lt;/blockquote&gt;&lt;br /&gt;OCRA can be installed via RubyGems: open a console window and type:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;gem install ocra&lt;/pre&gt;&lt;br /&gt;It's also available from &lt;a href="http://rubyforge.org/projects/ocra/"&gt;the RubyForge page&lt;/a&gt;.  Version 1.0.2 is the latest as of this writing.&lt;br /&gt;&lt;br /&gt;The syntax to compile a script is...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ocra.rb [option] your/script.rb&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...where &lt;span style="font-style:italic;"&gt;option&lt;/span&gt; is one or more of the following:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;--dll dllname    Include additional DLLs from the Ruby bindir.&lt;br /&gt;--no-lzma        Disable LZMA compression of the executable.&lt;br /&gt;--quiet          Suppress output.&lt;br /&gt;--help           Display this information.&lt;br /&gt;--windows        Force Windows application (rubyw.exe)&lt;br /&gt;--console        Force console application (ruby.exe)&lt;br /&gt;--no-autoload    Don't load/include script.rb's autoloads&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Thus far, I have exercised only the &lt;span style="font-style:italic;"&gt;--windows&lt;/span&gt; and &lt;span style="font-style:italic;"&gt;--console&lt;/span&gt; options.&lt;br /&gt;&lt;br /&gt;To create a non-console application, either use the &lt;span style="font-style:italic;"&gt;--windows&lt;/span&gt; option or give your script the &lt;span style="font-style:italic;"&gt;.rbw&lt;/span&gt; filename extension.  Compiling a basic non-console script is as simple as opening a console window, navigating to the folder containing your script, and typing:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ocra.rb myscript.rbw&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;OCRA will then run your script to check for dependencies, gather the necessary files, and create your executable.  An option to avoid fully running the script would be nice, similar to RubyScript2Exe's &lt;span style="font-style:italic;"&gt;exit if RUBYSCRIPT2EXE.is_compiling?&lt;/span&gt; idiom.&lt;br /&gt;&lt;br /&gt;OCRA uses LZMA compression, so the resulting executable is relatively small.  A simple wxRuby app, for example, resulted in a 2.5 Mb executable.  The same app compiled with RubyScript2Exe weighed in at over 9 Mb.  Apps that do not require a GUI toolkit will be even smaller, perhaps 500k.&lt;br /&gt;&lt;br /&gt;I haven't spent a lot of time with OCRA yet, but I think it shows great potential and I want to thank Lars Christensen for his efforts.  If you have a need to distribute Ruby programs to non-technical users, you should &lt;a href="http://rubyforge.org/projects/ocra/"&gt;check it out&lt;/a&gt; and pass your feedback along to Lars.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-1201501479819524668?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/1201501479819524668/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=1201501479819524668' title='14 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/1201501479819524668'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/1201501479819524668'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2009/05/ocra-one-click-ruby-application-builder.html' title='OCRA: One-Click Ruby Application Builder'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>14</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-4102993752494622029</id><published>2009-05-11T07:16:00.001-05:00</published><updated>2009-05-11T07:40:20.535-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='excel'/><title type='text'>Ruby &amp; Excel: Extracting Cell Comments</title><content type='html'>Someone recently asked how to get the comment text from a particular cell in an &lt;a href="http://rubyonwindows.blogspot.com/search/label/excel"&gt;Excel&lt;/a&gt; worksheet.&lt;br /&gt;&lt;br /&gt;You could call the cell's &lt;span style="font-style:italic;"&gt;Comment&lt;/span&gt; property to obtain a reference to the Comment object, then use the Comment object's &lt;span style="font-style:italic;"&gt;Text&lt;/span&gt; property to get---or set---the text of the comment.&lt;br /&gt;&lt;br /&gt;Imagine that you have a workbook open and you want to get the comment text from cell B2 in the currently selected worksheet. The following code should do the trick:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;&lt;br /&gt;xl = WIN32OLE.connect('Excel.Application')&lt;br /&gt;ws = xl.ActiveSheet&lt;br /&gt;cell = ws.Range('B2')&lt;br /&gt;comment = cell.Comment&lt;br /&gt;text = comment.Text&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Do you---or would you like to---use comments in your worksheets? I can go into further detail about the Comment object and the Comments collection, but that's all we have time for today.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-4102993752494622029?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/4102993752494622029/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=4102993752494622029' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/4102993752494622029'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/4102993752494622029'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2009/05/ruby-excel-extracting-cell-comments.html' title='Ruby &amp; Excel: Extracting Cell Comments'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-8720190509985733835</id><published>2009-05-03T15:43:00.005-05:00</published><updated>2009-05-03T16:20:04.393-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='tools'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>RubyScript2Exe and RubyGems</title><content type='html'>I've &lt;a href="http://rubyonwindows.blogspot.com/2007/09/compiling-your-ruby-app-with.html"&gt;written here previously&lt;/a&gt; about Erik Veenstra's &lt;a href="http://www.erikveen.dds.nl/rubyscript2exe"&gt;RubyScript2Exe&lt;/a&gt; "compiler".  I use it frequently to create standalone EXE files on Windows.&lt;br /&gt;&lt;br /&gt;If you use RubyScript2Exe, you should note that the current version (0.5.3) doesn't appear to play well with the new version (1.3.2) of &lt;a href="http://rubyforge.org/projects/rubygems/"&gt;Rubygems&lt;/a&gt; released a few weeks ago.  Attempting to compile a script results in the error:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    undefined method `list' for Gem::Specification:Class&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Some additional discussion can be found in this &lt;a href="http://www.ruby-forum.com/topic/184395"&gt;Ruby Forum thread&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Perhaps Erik will update RubyScript2Exe to utilize the &lt;span style="font-style:italic;"&gt;Gem.loaded_specs&lt;/span&gt; method, as suggested by Eric Hodel.&lt;br /&gt;&lt;br /&gt;In the meantime, RubyGems 1.3.1 continues to work well for me with RubyScript2Exe. Be aware, however, that Daniel Berger points out in the above thread that version 1.3.2 addresses a couple issues related to the win32-file library.&lt;br /&gt;&lt;br /&gt;There are alternatives emerging for creating executables from your Ruby code, including &lt;a href="http://rubyforge.org/projects/ocra/"&gt;ocra&lt;/a&gt; and &lt;a href="http://rubyforge.org/projects/copiousfreetime/"&gt;crate&lt;/a&gt;. I haven't had the opportunity to try either of these yet, but plan to in the near future. If you have experience with these or similar tools, feel free to share your comments here. Thanks!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-8720190509985733835?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/8720190509985733835/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=8720190509985733835' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/8720190509985733835'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/8720190509985733835'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2009/05/rubyscript2exe-and-rubygems.html' title='RubyScript2Exe and RubyGems'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-397907589812533028</id><published>2009-02-28T15:00:00.001-05:00</published><updated>2009-02-28T15:44:30.198-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='win32ole'/><title type='text'>WIN32OLE Objects: Class Names and Methods</title><content type='html'>Over at Stack Overflow, &lt;a href="http://stackoverflow.com/questions/595003/ruby-win32ole-how-to-determine-the-ole-class-type-whether-the-ole-class-suppor" target="blank"&gt;a member asked a couple questions&lt;/a&gt; regarding working with WIN32OLE objects.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;&lt;span style="font-weight:bold;"&gt;Q1:  How do I determine the class name of an OLE object instance?&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The code:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;object&lt;/span&gt;.ole_obj_help.name&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The explanation:&lt;br /&gt;&lt;br /&gt;Calling the &lt;span style="font-weight:bold;"&gt;ole_obj_help&lt;span style="font-style:italic;"&gt;&lt;/span&gt;&lt;/span&gt; method on a WIN32OLE object returns a WIN32OLE_TYPE object. The WIN32OLE_TYPE object includes a name attribute. Calling this on an Excel application object...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;xl = WIN32OLE.new( 'Excel.Application' )&lt;br /&gt;xl.ole_obj_help.name&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...returns the string '_Application', while calling it on a Worksheet object...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt; xl.ActiveSheet.ole_obj_help.name&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...returns the string '_Worksheet'.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;&lt;span style="font-weight:bold;"&gt;Q2:  How can I tell whether an object instance supports a particular method?&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The code:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;object&lt;/span&gt;.ole_methods.collect!{ |x| x.to_s }.include?( 'MethodName' )&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The explanation:&lt;br /&gt;&lt;br /&gt;As &lt;a href="http://rubyonwindows.blogspot.com/2007/04/ruby-win32ole-inspecting-objects.html" target="blank"&gt;mentioned previously here&lt;/a&gt;, calling the &lt;span style="font-weight:bold;"&gt;ole_methods&lt;span style="font-style:italic;"&gt;&lt;/span&gt;&lt;/span&gt; method on a WIN32OLE object returns an array of WIN32OLE objects that represent methods that can be called on that object. You can convert each object in the array to a string, using the &lt;span style="font-weight:bold;"&gt;collect!&lt;span style="font-style:italic;"&gt;&lt;/span&gt;&lt;/span&gt; method. Then it's a simple matter to call the &lt;span style="font-weight:bold;"&gt;include?&lt;span style="font-style:italic;"&gt;&lt;/span&gt;&lt;/span&gt; method to see if the resulting array of strings contains a certain value.&lt;br /&gt;&lt;br /&gt;And so we can use this to find that the Excel application object includes a &lt;span style="font-weight:bold;"&gt;Quit&lt;span style="font-style:italic;"&gt;&lt;/span&gt;&lt;/span&gt; method...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;xl.ole_methods.collect!{ |x| x.to_s }.include?( 'Quit' )&lt;br /&gt;=&gt; true&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...but does not include a &lt;span style="font-weight:bold;"&gt;Close&lt;span style="font-style:italic;"&gt;&lt;/span&gt;&lt;/span&gt; method:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;xl.ole_methods.collect!{ |x| x.to_s }.include?( 'Close' )&lt;br /&gt;=&gt; false&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;There you have it. Thanks for stopping by!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-397907589812533028?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/397907589812533028/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=397907589812533028' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/397907589812533028'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/397907589812533028'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2009/02/win32ole-objects-class-names-and.html' title='WIN32OLE Objects: Class Names and Methods'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-4911038564856230668</id><published>2009-01-28T08:18:00.005-05:00</published><updated>2009-01-28T09:00:16.260-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='excel'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Automating Excel with Ruby: One Cell, Multiple Fonts</title><content type='html'>As a loyal reader mentions in the comments to &lt;a href="http://rubyonwindows.blogspot.com/2007/06/automating-excel-with-ruby-formatting.html" target="blank"&gt;my article about Formatting Worksheets&lt;/a&gt;, he wanted to apply different formatting to separate words in a single cell. Specifically, he had cells containing a surname, a comma, and then first name, such as:&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;Jeter, Derek&lt;br /&gt;Matsui, Hideki&lt;br /&gt;Teixeira, Mark&lt;/blockquote&gt;&lt;br /&gt;His goal was to format the last name in bold, while leaving the first name in normal weight font:&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Jeter&lt;/span&gt;, Derek&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Matsui&lt;/span&gt;, Hideki&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Teixeira&lt;/span&gt;, Mark&lt;/blockquote&gt;&lt;br /&gt;(BTW, the sample names are mine. How many days until Opening Day? Or at least until pitchers and catchers report? But, alas, I digress...)&lt;br /&gt;&lt;br /&gt;My suggested solution was to use the Characters object to get a reference to a specific subset of characters within the cell, rather than to all of the cell's text.&lt;br /&gt;&lt;br /&gt;Call the &lt;span style="font-weight:bold;"&gt;Characters(&lt;span style="font-style:italic;"&gt;start&lt;/span&gt;, &lt;span style="font-style:italic;"&gt;length&lt;/span&gt;)&lt;/span&gt; method on a Range object (usually a cell) to return a reference to the specific characters. Note that the Characters object's index is 1-based.&lt;br /&gt;&lt;br /&gt;Once you have your subset of characters you can apply Font formatting much like you would with a Cell object, setting the Font object's Name, Size, Bold, ColorIndex, and other properties.&lt;br /&gt;&lt;br /&gt;So, assuming you have a worksheet object, &lt;span style="font-style:italic;"&gt;ws&lt;/span&gt;, and you want to format the values in cells A1 through A3 as described above, try the following code:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ws.Range('A1:A3').Cells.each do |cell|&lt;br /&gt;    # find the index of the first comma:&lt;br /&gt;    comma = cell.Value.to_s.index(',')&lt;br /&gt;    # get the range of characters before the comma&lt;br /&gt;    lastname = cell.Characters(1, comma)&lt;br /&gt;    # get the range of characters after the comma&lt;br /&gt;    firstname = cell.Characters(comma + 2, cell.Value.to_s.size)&lt;br /&gt;    # apply formatting:&lt;br /&gt;    lastname.Font.Bold = true&lt;br /&gt;    firstname.Font.Bold = false&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;There you have it. Let me know if you have questions or comments.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-4911038564856230668?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/4911038564856230668/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=4911038564856230668' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/4911038564856230668'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/4911038564856230668'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2009/01/automating-excel-with-ruby-one-cell.html' title='Automating Excel with Ruby: One Cell, Multiple Fonts'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-4467054225380537280</id><published>2009-01-21T08:32:00.008-05:00</published><updated>2009-01-21T09:12:48.666-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><category scheme='http://www.blogger.com/atom/ns#' term='commentary'/><title type='text'>Amen, Brother</title><content type='html'>In an article entitled "&lt;a href="http://akitaonrails.com/2009/1/13/the-best-environment-for-rails-on-windows" target="_blank"&gt;The Best Environment for Rails on Windows&lt;/a&gt;", Fabio Akita writes:&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;The same goes for Windows: some Railers would say that you need to give up on Windows completely and simply go to Ubuntu or Mac OS X. I know for a fact that there are lots of people simply unable to do just that. And “give up their jobs” – as some kindly suggest – is not an option. Some of the people that are starting Rails in closed-minded companies are exactly the seeds those companies need to start to change their minds and every time we, Railers, tell them to just move to another job, we are killing a precious opportunity to introduce Rails to those companies.&lt;/blockquote&gt;&lt;br /&gt;In a followup comment to a &lt;a href="http://www.railsinside.com/tutorials/210-setting-up-a-rails-development-environment-on-windows.html" target="_blank"&gt;post on Rails Inside&lt;/a&gt;, Fabio adds:&lt;br /&gt;&lt;blockquote&gt;Please keep this in mind: there are several circumstances that keep people locked to Windows. Specially if this person works for a closed minded company that won't allow him to use Ubuntu. The instant you tell this person "give up, Ruby is unable to be used under Windows", you lose the single person that could actually bootstrap Ruby adoption inside this company. People have to start somewhere and sometimes Windows is this way. So let's help them get started and one of three things will happen: 1) he will be super happy and will evangelize Ruby to his peers, leading to mass adoption on this company; 2) he will lose hope on his employer and will try to find another job; 3) he will simply give up and none of us lose nothing.&lt;/blockquote&gt;&lt;br /&gt;You should read Fabio's article and the comments at both links above; even if, like me, you don't use Rails as much as you use Ruby itself.&lt;br /&gt;&lt;br /&gt;It's very easy (and very fashionable -- all the cool kids do it!) for the Mac OS and Linux crowds to take shots at the Windows developers.  And I tip my hat to those that actually changed jobs just so that they could avoid working with Windows.  Good for you.&lt;br /&gt;&lt;br /&gt;But some of us actually &lt;span style="font-style:italic;"&gt;like&lt;/span&gt; our jobs and our companies, despite having to work with -- Gasp! Cover the children's ears, Mother! -- Windows.  Besides, as Marcos Ricardo commented on Fabio's site, "We can never throw way 90% of market share."&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-4467054225380537280?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/4467054225380537280/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=4467054225380537280' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/4467054225380537280'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/4467054225380537280'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2009/01/amen-brother.html' title='Amen, Brother'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-5335857335595501232</id><published>2008-11-30T12:00:00.002-05:00</published><updated>2008-11-30T12:06:44.040-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='word'/><category scheme='http://www.blogger.com/atom/ns#' term='win32ole'/><title type='text'>Automating Word with Ruby: Adding Bookmarks</title><content type='html'>Someone recently asked a question about using Ruby to add bookmarks to a Microsoft Word document.  Here's a brief, but hopefully helpful, explanation...&lt;br /&gt;&lt;br /&gt;Word's &lt;a href="http://rubyonwindows.blogspot.com/2007/04/automating-word-with-ruby-document.html"&gt;Document object&lt;/a&gt; includes a &lt;span style="font-style:italic;"&gt;Bookmarks&lt;/span&gt; collection.  To get a reference to this collection, call the &lt;span style="font-style:italic;"&gt;Bookmarks()&lt;/span&gt; method on the Document object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;word = WIN32OLE.connect('Word.Application')&lt;br /&gt;doc = word.ActiveDocument&lt;br /&gt;bookmarks = doc.Bookmarks()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To create a new bookmark, call the &lt;span style="font-style:italic;"&gt;Add()&lt;/span&gt; method on the Bookmarks collection, passing it (1) a one-word name for the new bookmark, and (2) the range to be bookmarked.  The following line of code adds a new bookmark, cleverly named 'Bookmark1', for the currently selected text:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;bookmarks.Add('Bookmark1', word.Selection)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The Bookmarks collection includes (among others) a &lt;span style="font-style:italic;"&gt;Count()&lt;/span&gt; method for getting the number of items in the collection, and an &lt;span style="font-style:italic;"&gt;Exists()&lt;/span&gt; method for determining if a bookmark with that name already exists.  So the following code prints the number of bookmarks, then adds a bookmark if it doesn't already exist, then prints the number of bookmarks again:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;puts(doc.Bookmarks.Count)&lt;br /&gt;if not doc.Bookmarks.Exists('Bookmark1')&lt;br /&gt;    doc.Bookmarks.Add('Bookmark1', word.Selection)&lt;br /&gt;end&lt;br /&gt;puts(doc.Bookmarks.Count)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To get a reference to an individual Bookmark, call the &lt;span style="font-style:italic;"&gt;Bookmarks()&lt;/span&gt; method and pass it the name of the bookmark:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;bookmark1 = doc.Bookmarks('Bookmark1')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To delete an individual Bookmark, call its &lt;span style="font-style:italic;"&gt;Delete()&lt;/span&gt; method:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;doc.Bookmarks('Bookmark1').Delete&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;There you have it.  Let me know if you have questions or comments.  And, of course, there'll be more on this topic (and many others) in the book I'm currently working on.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-5335857335595501232?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/5335857335595501232/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=5335857335595501232' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/5335857335595501232'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/5335857335595501232'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2008/11/automating-word-with-ruby-adding.html' title='Automating Word with Ruby: Adding Bookmarks'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-5369401288858379331</id><published>2008-09-27T13:18:00.002-05:00</published><updated>2008-09-27T13:35:09.552-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='books'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Scripted GUI Testing with Ruby</title><content type='html'>Anyone who reads this blog knows that I love Ruby, and I love Automation.&lt;br /&gt;&lt;br /&gt;Ian Dees is wise enough to realize that automation is not the Ultimate Solution to Everything, pointing out that "some domains are better suited than others for automation". "So," he asks, "why not let the computers and people each do what they're good at?" To that end, he offers his new book "&lt;a href="http://www.amazon.com/gp/product/1934356182" target="_blank"&gt;Scripted GUI Testing with Ruby&lt;/a&gt;", a book for testers who code and coders who test -- and maybe for others, as well. &lt;br /&gt;&lt;br /&gt;As the title implies, test scripts are written in the Ruby language -- and its Java implementation, JRuby -- and the author assumes that readers will have some experience with Ruby. If you've written and run a few Ruby scripts, you'll be fine. Ian doesn't require you to be a black-belt Rubyist to understand what's going on here, and his humor helps keep it interesting.&lt;br /&gt;&lt;br /&gt;Ian's guinea pig for client-side testing is LockNote, a simple text editor that saves your notes with password-protected encryption. The program is freely available for Windows, and Ian has developed his own cross-platform Java/Swing version, dubbed "JunqueNote". Using these two applications, Dees teaches us how to automate testing of GUI applications on both the Windows and Java platforms. You'll learn how to launch the app and use API calls to find windows, automate keystrokes and mouse-clicks, and more. This can be valuable as either a means to an end, or as the end goal itself; whether you're testing software, or simply looking to automate it via the user interface.&lt;br /&gt;&lt;br /&gt;In one chapter, Dees provides a gentle introduction to the popular RSpec Behaviour Driven Development framework. In another, he shows how we can simplify our test code by separating out the common code from the platform-specific code.&lt;br /&gt;&lt;br /&gt;Because the focus is on software testing, the author devotes a chapter to leveraging randomness to expose bugs that might otherwise be missed. Another chapter focuses on the ZenTest test matrix library. A later chapter delves into testing web-based applications using Selenium and WATIR, and how to combine these with RSpec.&lt;br /&gt;&lt;br /&gt;As a Ruby on Windows advocate, I'm pleased to see a book that devotes more than just a half-dozen pages to Windows-specific task automation. But "Scripted GUI Testing with Ruby" spends a good deal of time discussing Java-based testing, as well.&lt;br /&gt;&lt;br /&gt;This book is targeted at software testers, and they'll certainly be the section of the market that gets the maximum value from it. But it has potential value beyond that niche. There's something useful to be learned by both testers and non-testers, on both Java and Windows platforms.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-5369401288858379331?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/5369401288858379331/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=5369401288858379331' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/5369401288858379331'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/5369401288858379331'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2008/09/scripted-gui-testing-with-ruby.html' title='Scripted GUI Testing with Ruby'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-2105029956717161058</id><published>2008-08-31T08:56:00.001-05:00</published><updated>2008-08-31T08:59:45.896-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='powerpoint'/><category scheme='http://www.blogger.com/atom/ns#' term='win32ole'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Automating PowerPoint with Ruby</title><content type='html'>Here at &lt;span style="font-style:italic;"&gt;Ruby on Windows&lt;/span&gt;, we've looked at how to automate a variety of applications using Ruby. One popular MS Office app that I haven't yet discussed is PowerPoint, Microsoft's ubiquitous presentation software. But, like other Office apps, PowerPoint exposes a full-featured object model for automating, so there's virtually nothing that you can't do with PowerPoint via Ruby code.&lt;br /&gt;&lt;br /&gt;As usual, we'll be leveraging the &lt;span style="font-weight:bold;"&gt;win32ole&lt;/span&gt; library:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We'll start by creating an instance of the PowerPoint Application object, and assigning that object to a variable we'll call &lt;span style="font-weight:bold;"&gt;ppt&lt;/span&gt; :&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ppt = WIN32OLE.new('PowerPoint.Application')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Note that PowerPoint (like Outlook and Project) is a single-instance application. This means that if an instance of PowerPoint is already running, that instance will be returned, even though you called the WIN32OLE.new method. If PowerPoint is not currently running, a new instance will be launched.&lt;br /&gt;&lt;br /&gt;Like most MS Office applications, the PowerPoint application window is not visible by default when launched from code. To show or hide the Application window, set the &lt;span style="font-weight:bold;"&gt;Visible&lt;/span&gt; property...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ppt.Visible = true&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The Presentations collection contains the list of presentation documents currently open. To create a new Presentation, we'll call the &lt;span style="font-weight:bold;"&gt;Presentations.Add()&lt;/span&gt; method...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;doc = ppt.Presentations.Add()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To open an existing presentation, we'll call the &lt;span style="font-weight:bold;"&gt;Presentations.Open(&lt;/span&gt;) method and pass it the path and name of the PPT file...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;doc = ppt.Presentations.Open('c:\docs\mully1.ppt')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The Presentations.Add() and Presentations.Open() methods add the new Presentation object to the Presentations collection, and return a reference to the Presentation object. As you can see above, we're assigning that object to a variable we've called &lt;span style="font-weight:bold;"&gt;doc&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;The Presentation object contains a collection of Slide objects. To add a new slide to your presentation, call the &lt;span style="font-weight:bold;"&gt;Slides.Add()&lt;/span&gt; method and pass it two integers as arguments: the position at which to insert the new slide, and the layout to use for the new slide...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ppLayoutText = 2&lt;br /&gt;slide = doc.Slides.Add(1, ppLayoutText)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The position argument must be between 1 and the current number of slides plus 1.&lt;br /&gt;&lt;br /&gt;The Slides.Add() method adds the new slide to the Slides collection and returns a reference to the new slide, which we've assigned to the variable &lt;span style="font-weight:bold;"&gt;slide&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;To get the current count of slides, call the &lt;span style="font-weight:bold;"&gt;Slides.Count()&lt;/span&gt; method.&lt;br /&gt;&lt;br /&gt;So, we could use this code to add a new blank slide to the end of the collection:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;slide = doc.Slides.Add(doc.Slides.Count + 1, ppLayoutBlank)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You can find a complete list of Slide Layout constants in &lt;a href="http://msdn.microsoft.com/en-us/library/aa211582(office.11).aspx" target="_blank"&gt;this Microsoft document&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Each slide object contains a Shapes collection of all objects on the slide, such as textboxes, pictures, and OLE objects.&lt;br /&gt;&lt;br /&gt;In the words of Microsoft, a shape object's TextFrame "contains the text in the text frame and the properties and methods that control the alignment and anchoring of the text frame". The TextFrame object's TextRange property "returns a TextRange object that represents the text in the specified text frame". You can get/set the text via the TextRange object's Text property. Got all that? There'll be a quiz later.&lt;br /&gt;&lt;br /&gt;Putting this all together, we can insert or change the text of the first textbox on our slide using a line of code like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;slide.Shapes(1).TextFrame.TextRange.Text = "Hello, World!"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To save your newly-created presentation, call the &lt;span style="font-weight:bold;"&gt;SaveAs()&lt;/span&gt; method and pass it the path and filename:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;doc.SaveAs('c:\docs\presentation1.ppt')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To save changes to a previously-saved presentation, call the &lt;span style="font-weight:bold;"&gt;Save()&lt;/span&gt; method:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;doc.Save()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Call the presentation object's &lt;span style="font-weight:bold;"&gt;Close()&lt;/span&gt; method to close the presentation, and call the application object's &lt;span style="font-weight:bold;"&gt;Quit()&lt;/span&gt; method to exit the PowerPoint application:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;doc.Close()&lt;br /&gt;ppt.Quit()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Well, that's our show for today. There's much more to be covered on this topic. Please let me know if you have specific questions or suggestions for future articles.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-2105029956717161058?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/2105029956717161058/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=2105029956717161058' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/2105029956717161058'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/2105029956717161058'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2008/08/automating-powerpoint-with-ruby.html' title='Automating PowerPoint with Ruby'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-1661201160190579596</id><published>2008-07-06T10:00:00.003-05:00</published><updated>2008-07-06T10:18:29.324-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='win32ole'/><category scheme='http://www.blogger.com/atom/ns#' term='excel'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Working with Win32OLE Constants</title><content type='html'>Excel (and Word, Outlook, etc.) has hundreds of built-in constants that represent numeric values. When reviewing code written in Visual Basic, you may see these constants passed when calling methods or setting property values:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;mychart.ChartType = &lt;span style="font-style:italic;"&gt;xlColumnClustered&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;But the above line of code won't work on its own in Ruby, as &lt;span style="font-style:italic;"&gt;xlColumnClustered&lt;/span&gt; won't be recognized as a constant. So, when translating this code to Ruby, how do you get it to work?&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Do-It-Yourself&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;In my code examples here, I usually either provide the actual value...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;mychart.ChartType = 51&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...or explicitly assign the value to a constant or variable myself:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;xlColumnClustered = 51&lt;br /&gt;mychart.ChartType = xlColumnClustered&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;In Ruby, constants must begin with an upper-case letter, so '&lt;span style="font-style:italic;"&gt;xlColumnClustered&lt;/span&gt;' is really a variable in the last example above. To make it a constant, I should actually name it something like '&lt;span style="font-style:italic;"&gt;XlColumnClustered&lt;/span&gt;', with an upper-case X.&lt;br /&gt;&lt;br /&gt;How did I know that the Excel constant &lt;span style="font-style:italic;"&gt;xlColumnClustered &lt;/span&gt;equals 51? Well, I simply googled '&lt;span style="font-style:italic;"&gt;Const xlColumnClustered&lt;/span&gt;' and quickly found examples where the constant was being explicitly declared in VB/VBA code ("Const xlColumnClustered = 51"). Googling for '&lt;span style="font-style:italic;"&gt;Excel Constants&lt;/span&gt;' will return numerous pages that list all the the Excel constants and their corresponding values. Microsoft provides their own listing &lt;a href="http://msdn.microsoft.com/en-us/library/aa221100(office.11).aspx" target="_blank"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Loading the Win32OLE Constants&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;But you don't have to do it yourself. Ruby's win32ole library allows you to load an object's built-in constants into a class or module. To do so, first create an empty class or module:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class ExcelConst&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Then call the &lt;span style="font-weight:bold;"&gt;WIN32OLE.const_load&lt;/span&gt; method. Pass this method your previously-defined &lt;a href="http://rubyonwindows.blogspot.com/2007/03/automating-excel-with-ruby-application.html" target="_blank"&gt;Excel application object&lt;/a&gt; and your new ExcelConst class:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;WIN32OLE.const_load(excel, ExcelConst)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This loads the Excel application object's built-in constants into your ExcelConst class, but &lt;span style="font-style:italic;"&gt;each constant will now begin with an upper-case letter&lt;/span&gt;, as required in Ruby. Now you can call Excel's built-in constants from your new ExcelConst class. So our original example...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;mychart.ChartType = xlColumnClustered&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...works with only a slight modification, inserting the name of our ExcelConst class and capitalizing the first letter of the constant:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;mychart.ChartType = ExcelConst::XlColumnClustered&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;There you have it. This same method works for loading constants from other win32 application objects, such as Word or Outlook.&lt;br /&gt;&lt;br /&gt;By the way, you can review the docs for the WIN32OLE library, including the const_load method, &lt;a href="http://www.ruby-doc.org/core/classes/WIN32OLE.html" target="_blank"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I hope you found this useful. Feel free to post a comment here or email me if you have questions, comments, or suggestions for future articles (or &lt;a href="http://rubyonwindows.blogspot.com/2008/06/ruby-on-windows-book.html"&gt;the book&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-1661201160190579596?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/1661201160190579596/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=1661201160190579596' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/1661201160190579596'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/1661201160190579596'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2008/07/working-with-win32ole-constants.html' title='Working with Win32OLE Constants'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-2150259143347856139</id><published>2008-06-30T07:19:00.002-05:00</published><updated>2008-06-30T07:57:15.512-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><category scheme='http://www.blogger.com/atom/ns#' term='book'/><title type='text'>Ruby on Windows: The Book</title><content type='html'>A reader recently commented:&lt;br /&gt;&lt;br /&gt;"Have you written a book on Ruby + Windows? I'd love to see the material expanded and put down on paper. I suppose I can print it out myself, but it's just not the same."&lt;br /&gt;&lt;br /&gt;I've been asked about this before. While I currently have no such "Ruby on Windows" book to offer, I can now say that I have finally started on it.&lt;br /&gt;&lt;br /&gt;I welcome any comments, suggestions, or advice you may have -- as readers and/or writers.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-2150259143347856139?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/2150259143347856139/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=2150259143347856139' title='12 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/2150259143347856139'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/2150259143347856139'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2008/06/ruby-on-windows-book.html' title='Ruby on Windows: The Book'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>12</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-6309254094213519604</id><published>2008-06-22T08:00:00.003-05:00</published><updated>2008-06-22T08:40:23.162-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='win32ole'/><category scheme='http://www.blogger.com/atom/ns#' term='excel'/><title type='text'>Automating Excel: Creating Charts</title><content type='html'>They say a picture is worth a thousand words, and sometimes a chart can make your point more effectively than the raw data behind it can. This is especially true when comparing relative values such as monthly revenue data or team statistics.&lt;br /&gt;&lt;br /&gt;In our &lt;a href="http://rubyonwindows.blogspot.com/2008/06/automating-excel-chart-axis-and-legend.html"&gt;last episode&lt;/a&gt;, we looked at how to define titles in existing Excel charts. Sticking with the Excel Charts theme, let's now investigate how to create new charts in Excel.&lt;br /&gt;&lt;br /&gt;A chart is a visualization of data, and for this example, the data represents Runs Scored and Runs Allowed for American League Baseball teams, as reported on the &lt;a href="http://mlb.mlb.com/mlb/standings/index.jsp" target="_blank"&gt;mlb.com&lt;/a&gt; website. Our Excel worksheet contains a row for each team, with columns for Runs Scored and Runs Allowed:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_9LH5hSdA01Y/SF2N-g-dIUI/AAAAAAAAADE/lLxEOQIS7QM/s1600-h/ChartData.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_9LH5hSdA01Y/SF2N-g-dIUI/AAAAAAAAADE/lLxEOQIS7QM/s320/ChartData.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5214480048708198722" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;We'll use the &lt;span style="font-weight: bold;"&gt;win32ole&lt;/span&gt; library for automating Excel, and we'll connect to a running instance of Excel and use the already open 'mlb_stats.xls' workbook:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;xl = WIN32OLE.connect('Excel.Application')&lt;br /&gt;wb = xl.Workbooks('mlb_stats.xls')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Let's define some parameter variables that we'll use later:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;xlColumns = 2&lt;br /&gt;xlColumnClustered = 51&lt;br /&gt;xlWhite = 2&lt;br /&gt;xlRed = 3&lt;br /&gt;xlBlue = 5&lt;br /&gt;xlGray = 15&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To add a new Chart object to the workbook's Charts collection, call the &lt;span style="font-weight:bold;"&gt;Charts.Add&lt;/span&gt; method:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;mychart = wb.Charts.Add&lt;br /&gt;mychart.Name = "MLB Scoring"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The &lt;span style="font-weight:bold;"&gt;Charts.Add&lt;/span&gt; method returns a reference to the newly-created Chart object, which we've assigned to the variable &lt;span style="font-style:italic;"&gt;mychart&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;To delete an existing Chart object, call the Charts(&lt;span style="font-style:italic;"&gt;chart&lt;/span&gt;).Delete method, where &lt;span style="font-style:italic;"&gt;chart&lt;/span&gt; is the name or (1-based) index of the chart to delete:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;wb.Charts("MLB Scoring").Delete&lt;br /&gt;wb.Charts(1).Delete&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Naturally, we can't produce a chart without data, and we use the &lt;span style="font-weight:bold;"&gt;SetSourceData&lt;/span&gt;  method to define the source of the data that the chart will represent. This method takes 2 arguments, [1] a range of cells from a worksheet, and [2] a number that indicates whether to plot the graph by rows (1) or by columns (2):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;mychart.SetSourceData wb.Worksheets("Runs Scored and Allowed").Range("A1:C15"), xlColumns&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;There are many different types of charts to choose from, and for our purpose, we'll use a 2-dimensional column [aka vertical bars] chart, setting the &lt;span style="font-weight:bold;"&gt;ChartType&lt;/span&gt; property to 51 (via our previously-defined xlColumnClustered variable):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;mychart.ChartType = xlColumnClustered&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now, you could execute your code at this point and create a new chart. But let's tweak the colors a bit. The SeriesCollection object holds each series of data and its related properties. Our chart has 2 series, one for Runs Scored, and one for Runs Allowed. Let's make the Runs Scored columns blue and the Runs Allowed columns red, via the &lt;span style="font-weight:bold;"&gt;Interior.ColorIndex&lt;/span&gt; property:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;mychart.SeriesCollection(1).Interior.ColorIndex = xlBlue&lt;br /&gt;mychart.SeriesCollection(2).Interior.ColorIndex = xlRed&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We could also dictate the name that is displayed for each series in the legend:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;mychart.SeriesCollection(1).Name = "Runs Scored"&lt;br /&gt;mychart.SeriesCollection(2).Name = "Runs Allowed  "&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If you do not define a name for each series, Excel will try to pull it from your source data worksheet.&lt;br /&gt;&lt;br /&gt;The PlotArea represents the area on which the data (columns, in our case) is plotted. The ChartArea is the area surrounding the PlotArea, on which the title, axes, and legend are placed. For demonstration purposes, let's define specific colors for these objects by setting their &lt;span style="font-weight:bold;"&gt;Interior.ColorIndex&lt;/span&gt; property:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;mychart.ChartArea.Interior.ColorIndex = xlWhite&lt;br /&gt;mychart.ChartArea.Border.ColorIndex = xlBlue&lt;br /&gt;mychart.PlotArea.Interior.ColorIndex = xlGray&lt;br /&gt;mychart.PlotArea.Border.ColorIndex = xlWhite&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Finally, we'll add a title to the top of the chart, and format the text:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;mychart.HasTitle = true&lt;br /&gt;mychart.ChartTitle.Characters.Text = "American League - Runs Scored vs. Runs Allowed"&lt;br /&gt;mychart.ChartTitle.Font.Name = 'Verdana'&lt;br /&gt;mychart.ChartTitle.Font.Size = 16&lt;br /&gt;mychart.ChartTitle.Font.Bold = true&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Note that we first have to set the &lt;span style="font-weight:bold;"&gt;HasTitle&lt;/span&gt; property to true. Without first doing this, trying to define the various &lt;span style="font-weight:bold;"&gt;ChartTitle&lt;/span&gt; properties will raise an error.&lt;br /&gt;&lt;br /&gt;Our complete code looks like &lt;a href="http://pastie.org/219846" target="_blank"&gt;this&lt;/a&gt;, and produces  a chart that looks like this:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_9LH5hSdA01Y/SF0_wAMlFgI/AAAAAAAAAC0/-QW5vC45ruM/s1600-h/Chart.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://1.bp.blogspot.com/_9LH5hSdA01Y/SF0_wAMlFgI/AAAAAAAAAC0/-QW5vC45ruM/s320/Chart.png" alt="" id="BLOGGER_PHOTO_ID_5214394037483935234" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Not a bad start, eh? A quick glance at this chart tells you that Boston and Chicago are doing very well, run-wise, and that it could be a very long season for Kansas City and Seattle. And we see that Texas scores a tremendous amount of runs, but allows even more.&lt;br /&gt;&lt;br /&gt;I encourage you to investigate the vast array of chart methods and properties. Towards that end, you might want to check out &lt;a href="http://peltiertech.com/Excel/ChartsHowTo/" target="_blank"&gt;Jon Peltier's Chart tutorials&lt;/a&gt;, which I have found to be helpful.&lt;br /&gt;&lt;br /&gt;Questions? Comments? Suggestions? Post a comment here or send me an email.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-6309254094213519604?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/6309254094213519604/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=6309254094213519604' title='12 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/6309254094213519604'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/6309254094213519604'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2008/06/automating-excel-creating-charts.html' title='Automating Excel: Creating Charts'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_9LH5hSdA01Y/SF2N-g-dIUI/AAAAAAAAADE/lLxEOQIS7QM/s72-c/ChartData.png' height='72' width='72'/><thr:total>12</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-1880492493025856777</id><published>2008-06-15T03:00:00.002-05:00</published><updated>2008-06-15T09:21:44.870-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='excel'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Automating Excel: Chart, Axis, and Legend Titles</title><content type='html'>A reader writes, "I need to use Ruby to automate Excel... How can I set the chart title, axes title, or legend name, etc in the chart by using Ruby?"  I confess that my knowledge of Excel charts is limited, but let's dive in and see what we can learn...&lt;br /&gt;&lt;br /&gt;As usual, you'll start by using the win32ole library. For our example, we'll connect to a running &lt;a href="http://rubyonwindows.blogspot.com/2007/03/automating-excel-with-ruby-application.html"&gt;instance of Excel&lt;/a&gt;:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;xl = WIN32OLE.connect('Excel.Application')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Referencing a Chart Object&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Let's assume that ws is your &lt;a href="http://rubyonwindows.blogspot.com/2007/04/automating-excel-with-ruby-worksheet.html"&gt;worksheet object&lt;/a&gt;. You can obtain a reference to a Chart object either...&lt;br /&gt;&lt;br /&gt;...via the Charts collection, if it is a chart sheet...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;chart = ws.Charts(1).Chart&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;or via the ChartObjects collection, if it is an embedded chart (not a chart sheet)...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;chart = ws.ChartObjects(1).Chart&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...or via the ActiveChart method if the chart is the currently active object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;chart = xl.ActiveChart&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Setting the Chart Title&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;In order to set the value of a chart's title, you must ensure that the HasTitle property is enabled:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;chart.HasTitle = true&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Then set the value of the ChartTitle.Text property:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;chart.ChartTitle.Text = "2008 Sales"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Setting the Axes Title&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Select one of the  chart's axes by calling the Chart object's Axes method and passing it an integer representing either the category, value, or series axis:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;XL_CATEGORY = 1&lt;br /&gt;XL_VALUE = 2&lt;br /&gt;XL_SERIESAXIS = 3&lt;br /&gt;axis = chart.Axes(XL_CATEGORY)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Much like with the chart title above, in order to set the value of an axis' title, you must ensure that the HasTitle property is enabled:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;axis.HasTitle = true&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Then set the value of the ChartTitle.Text property:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;axis.AxisTitle.Text = "July Sales"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Setting the Legend Names&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;You can reference a legend via the Chart object's SeriesCollection method, passing it the (1-based) index of the item you wish to reference:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;chart.SeriesCollection(1).Name = "Jan"&lt;br /&gt;chart.SeriesCollection(2).Name = "Feb"&lt;br /&gt;chart.SeriesCollection(3).Name = "Mar"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And there you have it. If you found this to be useful, you may want to check out &lt;a href="http://msdn.microsoft.com/en-us/library/aa213725(office.11).aspx"&gt;this Microsoft MSDN Chart object reference&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Got a question, comment, or suggestion? Post your comment here or send me an email.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-1880492493025856777?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/1880492493025856777/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=1880492493025856777' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/1880492493025856777'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/1880492493025856777'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2008/06/automating-excel-chart-axis-and-legend.html' title='Automating Excel: Chart, Axis, and Legend Titles'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-6592104922488958059</id><published>2008-06-03T19:23:00.000-05:00</published><updated>2008-06-03T19:27:05.395-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><title type='text'>Running In A Console Window?</title><content type='html'>Someone recently asked me how to determine if your code is running in a console window  or not. Perhaps you have a program that provides both GUI and command line interfaces and you need to know which interface is being used. You may wish to output error messages to the console, if it exists, but to a file if the console doesn't exist.&lt;br /&gt;&lt;br /&gt;UPDATE:  Perhaps the preferred means for this is the &lt;span style="font-weight:bold;"&gt;STDIN.isatty&lt;/span&gt; method, which returns true if running in a console window and false otherwise...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;if STDIN.isatty&lt;br /&gt;    # do some command-line stuff&lt;br /&gt;else&lt;br /&gt;    # do some GUI stuff&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Thanks to Brent and Dan for their comments.&lt;br /&gt;&lt;br /&gt;Alternatively, Nobu Nakada mentioned in &lt;a href="http://www.ruby-forum.com/topic/152599"&gt;a recent Ruby Forum post&lt;/a&gt; that you cannot open the CONIN$ (console input) device file unless you are running in a console window. So the following method (adapted from Nobu) would return true if running in a console window and false otherwise...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def in_console?&lt;br /&gt;    begin&lt;br /&gt;        open("CONIN$") {}&lt;br /&gt;        console = true&lt;br /&gt;    rescue SystemCallError&lt;br /&gt;        console = false&lt;br /&gt;    end&lt;br /&gt;    return console&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Usage example:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;if in_console?&lt;br /&gt;    # do some command-line stuff&lt;br /&gt;else&lt;br /&gt;    # do some GUI stuff&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;There you have it. Got a question or suggestion? Post a comment or send me email.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-6592104922488958059?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/6592104922488958059/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=6592104922488958059' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/6592104922488958059'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/6592104922488958059'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2008/06/running-in-console-window.html' title='Running In A Console Window?'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-3124088567636616779</id><published>2008-04-21T22:30:00.000-05:00</published><updated>2008-04-21T21:32:27.242-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Shutting Down Windows from Ruby Code</title><content type='html'>Someone recently asked how to shut down or restart Windows from their Ruby code. Windows provides an executable, "shutdown.exe", to accomplish this. Execute this command from your script with the appropriate parameters and you can log off, shutdown, or restart the OS.&lt;br /&gt;&lt;br /&gt;Parameters accepted include, but are not limited to:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;-l        Log off (cannot be used with -m option)&lt;br /&gt;-s        Shutdown the computer&lt;br /&gt;-r        Shutdown and restart the computer&lt;br /&gt;-t xx     Set timeout for shutdown to xx seconds&lt;br /&gt;-f        Forces running applications to close without warning&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So, the following line of code will close all applications without a warning and instantly shut down and restart your PC:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;system('shutdown.exe -r -f -t 0')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To get the full list of parameters, open a console window and enter&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;shutdown.exe /?&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Further details can be found in &lt;a href="http://technet.microsoft.com/en-us/library/bb491003.aspx"&gt;this Microsoft article&lt;/a&gt;. And Gordon Thiesfeld offers WMI and win32ole alternatives in &lt;a href="http://www.ruby-forum.com/topic/150301"&gt;this Ruby Forum thread&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-3124088567636616779?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/3124088567636616779/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=3124088567636616779' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/3124088567636616779'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/3124088567636616779'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2008/04/shutting-down-windows-from-ruby-code.html' title='Shutting Down Windows from Ruby Code'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-1985435176799323563</id><published>2008-04-02T18:15:00.002-05:00</published><updated>2008-04-03T07:14:17.455-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='jruby'/><category scheme='http://www.blogger.com/atom/ns#' term='win32ole'/><title type='text'>Win32OLE Library for JRuby?</title><content type='html'>Charles O. Nutter of the JRuby project is &lt;a href="http://wiki.jruby.org/wiki/Google_Summer_of_Code_2008"&gt;seeking proposals for Google's Summer of Code&lt;/a&gt; (GSoC).&lt;br /&gt;&lt;br /&gt;Among the suggested projects is this one of possible interest to many [J]Ruby on Windows users:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Win32OLE Library for JRuby - Implement the win32ole library for JRuby. This would also be an excellent contribution, since there's already libraries like Jacob to take some of the pain out of it, and it would be great to have it working on JRuby. (suggested by sgwong)&lt;/blockquote&gt;&lt;br /&gt;Update from Charlie:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;BTW, if you know anyone that might be interested in doing this through GSoC, please spread the word quickly. The &lt;span style="font-weight:bold;"&gt;deadline for submissions is Monday, April 7&lt;/span&gt;, so there's not a lot of time left.&lt;br /&gt;&lt;br /&gt;And to make it more attractive, there's already a very nice Java library that provides COM/OLE support (http://danadler.com/jacob/) so the task is more about wiring that library into JRuby than mucking about with any low-level native nasties.&lt;/blockquote&gt;&lt;br /&gt;I'd love to see this project undertaken, as it could be a big boost for those of us who currently do COM automation work with Ruby. I haven't done much with JRuby yet (a wee bit of Jython in the past), but I like the possibilities for having an easily-distributable app (JAR file?) that includes a GUI (e.g., Swing) and Win32OLE functionality.&lt;br /&gt;&lt;br /&gt;What do you think? Would a Win32OLE library increase the likelihood of you using JRuby for some future projects?&lt;br /&gt;&lt;br /&gt;&lt;a href="http://digg.com/submit?phase=2&amp;url=http://rubyonwindows.blogspot.com/2008/04/win32ole-library-for-jruby.html"&amp;target="_blank"&gt;&lt;br /&gt;&lt;img border="0" alt="Digg my article" src="http://digg.com/img/badges/91x17-digg-button.gif"/&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-1985435176799323563?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/1985435176799323563/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=1985435176799323563' title='12 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/1985435176799323563'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/1985435176799323563'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2008/04/win32ole-library-for-jruby.html' title='Win32OLE Library for JRuby?'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>12</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-4845950475693500215</id><published>2008-03-29T20:51:00.013-05:00</published><updated>2008-03-30T08:11:23.502-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><category scheme='http://www.blogger.com/atom/ns#' term='commentary'/><title type='text'>Windows: Ruby's Red-Headed Stepchild</title><content type='html'>In case you missed it, there has been increased online discussion these past few days regarding the viability of Windows as a development platform for Ruby. This was triggered by at least two blog posts...&lt;br /&gt;&lt;br /&gt;Luis Lavena: &lt;br /&gt;&lt;br /&gt;&lt;a href="http://blog.mmediasys.com/2008/03/06/is-windows-a-supported-platform-for-ruby-i-guess-not/"&gt;"Is Windows a supported platform for Ruby? I guess not"&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Peter Cooper:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.rubyinside.com/is-windows-a-first-class-platform-for-ruby-823.html"&gt;"Is Windows a first class platform for Ruby, or not?"&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;"All the cool kids are doing it."&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I've been disappointed by some of the readers' comments to these posts. As in so many discussions within the open source community, there's a significant anti-Microsoft sentiment. There are many reasons to dislike Microsoft and Windows. But those who wonder aloud if anyone still uses Windows, or that comment that "real programmers don't use Windows" are either tragically ignorant or blinded by their passion for Linux and/or MacOS. Yes, I know, we're wandering back towards the &lt;a href="http://www.ruby-forum.com/topic/102776"&gt;Ruby in the Enterprise&lt;/a&gt; debate.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;“The race is not always to the swift, nor the battle to the strong, but that's the way to bet.” -Damon Runyon&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Fortunately, there are voices of reason in this discussion, including Peter Cooper, who, while not a Windows user, says "a poor Windows Ruby ecosystem could harm the growth and spread of the language". Whether you like it or not, Windows is far and away the most popular desktop OS. Many desktop developers in the [insert Darth Vader/Evil Empire theme here] Corporate World are required to develop for Windows because &lt;span style="font-style:italic;"&gt;that's where the users are.&lt;/span&gt; We might be able to "sneak Ruby through the system", but expecting a company to switch hundreds or thousands of users from Windows to Linux or Mac is childish.&lt;br /&gt;&lt;br /&gt;If you love Ruby, and care about the future of The Language, then you &lt;span style="font-style:italic;"&gt;want&lt;/span&gt; Windows to be a "first-class platform for Ruby". It's not about developer preferences. It's about market forces.&lt;br /&gt;&lt;br /&gt;Ruby is more than just Rails.&lt;br /&gt;&lt;br /&gt;Software development is more than just websites.&lt;br /&gt;&lt;br /&gt;Ruby needs Windows more than Windows needs Ruby.&lt;br /&gt;&lt;br /&gt;Think about it.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://digg.com/submit?phase=2&amp;url=http://rubyonwindows.blogspot.com/2008/03/windows-rubys-red-headed-stepchild.html"&amp;target="_blank"&gt;&lt;br /&gt;&lt;img border="0" alt="Digg my article" src="http://digg.com/img/badges/91x17-digg-button.gif"/&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-4845950475693500215?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/4845950475693500215/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=4845950475693500215' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/4845950475693500215'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/4845950475693500215'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2008/03/windows-rubys-red-headed-stepchild.html' title='Windows: Ruby&apos;s Red-Headed Stepchild'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-2283873733770139226</id><published>2008-01-27T11:05:00.000-05:00</published><updated>2008-01-31T08:40:59.472-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='rubygarden'/><category scheme='http://www.blogger.com/atom/ns#' term='excel'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>RubyGarden Archives: Scripting Excel</title><content type='html'>&lt;span style="font-style:italic;"&gt;&lt;span style="font-weight:bold;"&gt;Editor's Note: &lt;/span&gt;Once upon a time, there was a website named RubyGarden.org, which contained many helpful links and articles. The website has recently dropped off the face of the earth. The following "Scripting Excel" article was salvaged &lt;a href="http://www.google.com/search?q=cache:q7fKHFEtxjwJ:rubygarden.org/ruby/page/show/ScriptingExcel+scriptingexcel+site:rubygarden.org&amp;hl=en&amp;ct=clnk&amp;cd=1&amp;gl=us"&gt;from   the Google cache&lt;/a&gt; and is provided here in its entirety.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;hr&gt;&lt;br /&gt;Although the Pickaxe book has some examples of scripting Excel, I still had quite a lot of digging to do for some of the scripts I needed. I got some by looking at the M$ docs (poor as they are**), and some by looking at Perl examples. So this page is for collecting examples of code that others might adapt (and add to).&lt;br /&gt;&lt;br /&gt;See also: ScriptingOutlook, ScriptingAccess&lt;br /&gt;&lt;br /&gt;          o You can download the Office XP Excel Object Reference help file as part of an expanded help bundle here [1]. It's large [19MB], but it includes a bunch of VBA help files that I couldn't track down otherwise. The file you need for Excel items is VBAXL10.CHM. It appears to have complete docs on all the Excel objects. -- ChrisMorris &lt;br /&gt;&lt;br /&gt;          o A great resource for general Excel and programming related questions (well with VBA) are Chip Pearsons [Excel Pages]. I learned a lot from his article [Cell References In A Range] for example. -- BernhardLeicher &lt;br /&gt;&lt;br /&gt;First of all:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Opening spreadsheets, accessing workbooks and worksheets&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;excel = WIN32OLE::new('excel.Application')&lt;br /&gt;workbook = excel.Workbooks.Open('c:\examples\spreadsheet.xls')&lt;br /&gt;worksheet = workbook.Worksheets(1) #get hold of the first worksheet&lt;br /&gt;worksheet.Select  #bring it to the front -need sometimes to run macros, &lt;br /&gt;#    not for working with a worksheet from ruby&lt;br /&gt;excel['Visible'] = true #make visible, set to false to make invisible &lt;br /&gt;#    again. Don't need it to be visible for script to work&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;reading data from spreadsheet&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;worksheet.Range('a12')['Value']  #get value of single cell&lt;br /&gt;data = worksheet.Range('a1:c12')['Value'] #read into 2D array&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;finding the first empty row (using empty column A)&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;line = '1'&lt;br /&gt;while worksheet.Range("a#{line}")['Value']&lt;br /&gt;   line.succ!&lt;br /&gt;end #line now holds row number of first empty row&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;or to read as you go&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;line = '1'&lt;br /&gt;data = []&lt;br /&gt;while worksheet.Range("a#{line}")['Value']&lt;br /&gt;   data &lt;&lt; worksheet.Range("a#{line}:d#{line}")['Value']&lt;br /&gt;   line.succ!&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;writing data into spreadsheet, example&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;worksheet.Range('e2')['Value'] = Time.now.strftime &lt;br /&gt;    '%d/%m/%Y' #single value&lt;br /&gt;worksheet.Range('a5:c5')['Value'] = ['Test', '25', 'result']&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;loading all Excel constants into a class&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class ExcelConst&lt;br /&gt;end&lt;br /&gt;WIN32OLE.const_load(excel, ExcelConst)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now the constant xlDown is accessible as&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ExcelConst::XlDown&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To find out what constants to use you can use this script. You run it by passing in a string which is matched against the constant names.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;&lt;br /&gt;module ExcelConsts&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;excel = WIN32OLE.new("Excel.Application")&lt;br /&gt;WIN32OLE.const_load(excel, ExcelConsts)&lt;br /&gt;excel.quit()&lt;br /&gt;&lt;br /&gt;puts 'Matches for: ' + ARGV[0]&lt;br /&gt;ExcelConsts.constants.each {|const|&lt;br /&gt;    match = const.match(/#{ARGV[0]}/)&lt;br /&gt;    value = eval("ExcelConsts::#{const}")&lt;br /&gt;    puts ' '*4 + const + ' =&gt; ' + value.to_s unless match.nil?&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;An example would be looking for the constant to center text. I ran&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ruby search_excel_consts.rb Center &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;and the following results came up:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;XlCenterAcrossSelection =&gt; 7&lt;br /&gt;XlVAlignCenter =&gt; -4108&lt;br /&gt;XlCenter =&gt; -4108&lt;br /&gt;XlLabelPositionCenter =&gt; -4108&lt;br /&gt;XlPhoneticAlignCenter =&gt; 2&lt;br /&gt;XlHAlignCetner =&gt; -4108&lt;br /&gt;XlHAlignCenterAcrossSelection =&gt; 7&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;calling macros&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;excel.Run('SortByNumber')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Setting background colour&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;worksheet.Range('a3:f5').Interior['ColorIndex'] = 36 #pale yellow&lt;br /&gt;# Set background color back to uncoloured (rnicz)&lt;br /&gt;worksheet.Range('a3:f5').Interior['ColorIndex'] = -4142 # XlColorIndexNone constant&lt;br /&gt;# or use Excel constant to set background color back to uncoloured&lt;br /&gt;worksheet.Range('a3:f5').Interior['ColorIndex'] = ExcelConst::XlColorIndexNone&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Adding Formulae&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;emptyRow = 15&lt;br /&gt;worksheet.Range("t#{emptyRow}")['Formula'] = "=(Q#{emptyRow}+L#{emptyRow}+I#{emptyRow}+S#{emptyRow})"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;saving changes&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;workbook.Close(1)&lt;br /&gt;# or&lt;br /&gt;workbook.SaveAs 'myfile.xls'&lt;br /&gt;# default path is the system defined My Documents folder&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;ending session&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;excel.Quit&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If you're experimenting from within irb and are having problems with processes hanging around after you've called excel.Quit - try deleting the reference to excel and invoking the garbage collector.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;excel.Quit&lt;br /&gt;excel = nil&lt;br /&gt;GC.start&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Hopefully this is of some use. Please add anything else you have discovered.&lt;br /&gt;&lt;br /&gt;Some further stuff that I learned so far...&lt;br /&gt;It partly overlaps with what ChrisMorris already wrote, maybe we can merge it later on. -- BernhardLeicher&lt;br /&gt;&lt;br /&gt;Start Excel, create new workbook and save it:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;excel = WIN32OLE.new("excel.application")&lt;br /&gt;excel.visible = true     # in case you want to see what happens &lt;br /&gt;workbook = excel.workbooks.add&lt;br /&gt;workbook.saveas('c:\examples\spreadsheet1.xls')&lt;br /&gt;workbook.close&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Or, suppose Excel is already started and a few Excel files are opened (=workbooks in Excel jargon): Connect to the running instance of Excel, activate one of the workbooks and write something.&lt;br /&gt;&lt;br /&gt;This also shows that Excel collections can be iterated very handy with "each", and that collections can sometimes be indexed by number or by name:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;excel = WIN32OLE.connect("excel.application")&lt;br /&gt;excel.workbooks.each{|wb|puts wb.name}  # loop through workbooks and display names&lt;br /&gt;excel.workbooks(1).activate             # activate by number&lt;br /&gt;excel.workbooks("Mappe1").activate      # or by name     &lt;br /&gt;excel.range("b5").value="soso"          # write something to cell B5&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Connecting to Excel is particularly good fun when done interactively from irb: You instantly see what happens!&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;irb(main):001:0&gt; require 'win32ole'&lt;br /&gt;true&lt;br /&gt;irb(main):002:0&gt; excel=WIN32OLE.connect('excel.application')&lt;br /&gt;#&lt;WIN32OLE:0xa0b3528&gt;&lt;br /&gt;irb(main):003:0&gt; excel.workbooks.each{|wb|puts wb.name}&lt;br /&gt;PERSONL.XLS&lt;br /&gt;Mappe1&lt;br /&gt;Mappe2&lt;br /&gt;Mappe3&lt;br /&gt;nil&lt;br /&gt;irb(main):004:0&gt; excel.workbooks(1).name&lt;br /&gt;"PERSONL.XLS"&lt;br /&gt;irb(main):005:0&gt; excel.workbooks("Mappe1").activate&lt;br /&gt;true&lt;br /&gt;irb(main):006:0&gt; excel.range("b5").value="soso"&lt;br /&gt;nil&lt;br /&gt;&lt;br /&gt;Excel =&gt; workbook =&gt; worksheet =&gt; range(cell)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;What always bugged me when browsing through examples were the various ways of referring to a particular cell on a particular worksheet in a particular workbook. When Excel is started and "Mappe1" is the currently active workbook and "Tabelle1" is the currently active worksheet, all following statements do the same thing:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;excel.workbooks("Mappe1").worksheets("Tabelle1").range("a1").value&lt;br /&gt;excel.worksheets("Tabelle1").range("a1").value&lt;br /&gt;excel.activeworkbook.activesheet.range("a1").value&lt;br /&gt;excel.activesheet.range("a1").value&lt;br /&gt;excel.range("a1").value&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;My confusion was probably caused by the fact that a lot of properties/methods can be called on "excel" directly and then default to the currently active workbook/worksheet. It's more a matter of taste to specify "activesheet" and "activeworkbook" or not.&lt;br /&gt;&lt;br /&gt;And regarding the hierarchy, it seems to be as simple as: When Excel is up and running, it contains 0 (no file opened) or more workbooks (Workbook? Just Excel jargon for an Excel file!), with each workbook/file containing 1 or more worksheets.&lt;br /&gt;&lt;br /&gt;Various methods for addressing a cell or range of cells&lt;br /&gt;&lt;br /&gt;In the Excel object model there isn't something like a cell object, it's all covered by the "Range" object. A range can represent only one cell or a whole bunch of them (a column, a row, a rectangular block of cells, ....).&lt;br /&gt;&lt;br /&gt;Let's assume for the following examples, that "sheet" contains a reference to an Excel worksheet, obtained e.g. by:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;excel = WIN32OLE.connect('excel.application')   # connect to running instance of Excel&lt;br /&gt;sheet = excel.activesheet&lt;br /&gt;&lt;br /&gt;sheet.range(cellname/cell[, cellname/cell])&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;A range can be obtained by using the worksheet's Range property. A range with only one cell:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;sheet.range("a1")&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Or a rectangular block of cells (A1 to C3):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;sheet.range("a1", "c3")&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Same with one argument:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;sheet.range("a1:c3")&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Whole Column A:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;sheet.range("a:a")&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Whole Row 3:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;sheet.range("3:3")&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;A range itself has a range property, thus allowing to write:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;sheet.range("c3").range("a1").address     # &gt;&gt; "$C$3"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Doesn't make much sense, one might note that the second range's address becomes relative to the first range.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;sheet.cells(rowindex, columnindex)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This is all wonderful, but shouldn't there be a way of addressing cells by row and column number? The worksheet's Cells property does that: It gets you a range with one cell by specifying the row and column number. The indices are counted from 1, so Cells(1,1) gives you cell A1:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;sheet.cells(3,1).address                  # &gt;&gt; "$C$3"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Combined with range to get range A1:C3:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;sheet.range(sheet.cells(1,1), sheet.cells(3,3))   &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And when applied to another range, row and column index become relative to the first range:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;sheet.range("b2").cells(2,2).address      # &gt;&gt; "$C$3"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The index can be negative:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;sheet.range("c3").cells(-1,-1).address    # &gt;&gt; "$A$1"&lt;br /&gt;range.offset(rowoffset, columnoffset)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If you have a range, this can be used to return another range that is y rows and x columns away (or offset) from this one. This time the offsets count from 0:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;sheet.range("a1").offset(0,0).address      # &gt;&gt; "$A$1"&lt;br /&gt;sheet.range("b5").offset(1,1).address      # &gt;&gt; "$C$6"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;While offset somehow reminds of the cells function, this might make a difference: If range contains a block of cells, an offset block of cells is returned too:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;sheet.range("a1:c3").offset(0,2).address   # &gt;&gt; "$C$1:$E$3"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Negative offsets can be specified too:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;sheet.range("b2").offset(-1,-1).address    # &gt;&gt; "$A$1"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Getting cell values&lt;br /&gt;&lt;br /&gt;There isn't only one method for obtaining a cell's value, but at least three of them: text, value, value2. So which should one use, what is the difference between them?&lt;br /&gt;&lt;br /&gt;Sidenote:&lt;br /&gt;An Excel cell's content is somewhat relative, what you see isn't necessarily what is actually inside the cell, because a cell's content is displayed according to a specified format. A cell might contain "0.12345", but is displayed as "0.12" or "0.12 DM" or whatever. It might be good to know, that internally a cell's content is either a text or a floating point number. That's it, nothing else.&lt;br /&gt;&lt;br /&gt;Just for curiosity:&lt;br /&gt;Dates are represented internally as floating point values too (more details at Chip Pearson's site: HTTP://www.cpearson.com/excel/datetime.htm):³59³ "Excel stores dates and times as a number representing the number of days since 1900-Jan-0, plus a fractional portion of a 24 hour day: ddddd.tttttt . This is called a serial date, or serial date-time."&lt;br /&gt;So if the content is 37936.0 and its format is "date", it's displayed as "11.11.03" or "Nov 2003".&lt;br /&gt;&lt;br /&gt;For the following examples let's assume a content of:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;A1 =&gt; 10.12345 =&gt; formatted with 2 decimal digits =&gt; 10.12&lt;br /&gt;B1 =&gt; 10.12345 =&gt; formatted as currency =&gt; 10,00 DM&lt;br /&gt;C1 =&gt; 11.11.03 =&gt; date =&gt; 11.11.03 &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;range.text&lt;br /&gt;&lt;br /&gt;Text property retrieves the value as it is displayed, as a string. It's readonly, so only for getting values. Because my country settings are "German", floats are displayed with a comma.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;sheet.range("a1").text      # &gt;&gt; "10,12"&lt;br /&gt;sheet.range("b1").text      # &gt;&gt; "10,12 DM"&lt;br /&gt;sheet.range("c1").text      # &gt;&gt; "11.11.03"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;range.value&lt;br /&gt;&lt;br /&gt;This is the result when retrieved with value:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;sheet.range("a1").value       # &gt;&gt; 10.12345&lt;br /&gt;sheet.range("b1").value       # &gt;&gt; "10,1235"&lt;br /&gt;sheet.range("c1").value       # &gt;&gt; "2003/11/11 00:00:00"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Retrieves the "internal" value (A1 becomes a float), whereby currency and date are still returned as strings albeit somewhat different than before.&lt;br /&gt;&lt;br /&gt;range.value2&lt;br /&gt;&lt;br /&gt;According to the Excel documentation value2 behaves just like value, but additionally retrieves currency and dates as doubles (the actual internal content):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;sheet.range("a1").value2        # &gt;&gt; 10.12345&lt;br /&gt;sheet.range("b1").value2        # &gt;&gt; 10.12345&lt;br /&gt;sheet.range("c1").value2        # &gt;&gt; 37936.0 &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Yes, seems to work as advertised.&lt;br /&gt;&lt;br /&gt;Setting values&lt;br /&gt;&lt;br /&gt;Seems that only "value" is useful here. An integer or float arrives as expected as number in Excel:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;sheet.range("a1").value = 1.2345&lt;br /&gt;sheet.range("a1").value = 2&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;For strings Excel does the same processing that it does when something is interactively entered. It thus depends on how Excel interprets the string:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;sheet.range("a1").value = "10.11.2003"     # becomes a date&lt;br /&gt;sheet.range("a1").value = "1,2345"           # becomes a number (at least with German country settings)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Iterating ranges...&lt;br /&gt;&lt;br /&gt;...with each&lt;br /&gt;&lt;br /&gt;Ranges can be iterated with each:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;sheet.range("a1:a10").each{|cell|puts cell.value}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If the range is a block of cells the iteration goes from left to right, then down one line, and so on:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;sheet.range("a1:b5").each{|cell|puts cell.value}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Iterating block of cells by row and output the first cell of each row:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;sheet.range("b3:c7").rows.each{|r|puts r.cells(1,1).value}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;and by column:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;sheet.range("b3:c7").columns.each{|c|puts c.cells(1,1).value}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Result of row.value is an array within an array:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;sheet.range("b3:c7").rows.each{|r|puts r.value.inspect}  # &gt;&gt; [1.0, 10.0]?&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...with activecell&lt;br /&gt;&lt;br /&gt;Like moving around an Excel sheet with the cursor. Moving down one cell:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;sheet.activecell.offset(1,0).activate&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Walking down from the active cell until an empty cell is encountered:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;sheet.activecell.offset(1,0).activate while excel.activecell.value&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...with an index&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;range = sheet.range("b3:c7")&lt;br /&gt;noofrows = range.rows.count&lt;br /&gt;(1..noofrows).each{|i|puts range.cells(i,1).value}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Named Ranges&lt;br /&gt;&lt;br /&gt;Named ranges make Excel spreadsheets more usable for the end user. To create a named range "myRange":&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;sheet.names.Add( { 'Name' =&gt; 'myRange', 'RefersTo' =&gt; 'A2:A216' } )&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;One problem! This doesn't work. Use a Range object for RefersTo?, not a String:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;myRange = sheet.Range( 'A2:A216' )&lt;br /&gt;sheet.names.Add( { 'Name' =&gt; 'myRange', 'RefersTo' =&gt; myRange } )&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;How do you use named ranges in ruby? Named ranges are kept in the worksheet as well as the workbook. You may need to check both locations.&lt;br /&gt;&lt;br /&gt;Something like the following works for named ranges manually defined by the user:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;rangeString = workbook.names( 'Sheet1!myRange' ).Value&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;1. Remove "=" prefix (e.g. "=Sheet1!$A$2:$A$4") &lt;br /&gt;&lt;pre&gt;&lt;br /&gt;rangeString = rangeString.slice( 1, rangeString.length - 1 ) if ( rangeString =~ /^=/ ) worksheet.range( rangeString ).value = 'testing...'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Finding Data Regions...&lt;br /&gt;&lt;br /&gt;Data don't always start in row 1, column A, the number of columns might not be fixed, and the number of rows is most often variable. There are two handy methods that can help to find that "block of data".&lt;br /&gt;&lt;br /&gt;Let's assume, that B3:C7 contain data.&lt;br /&gt;&lt;br /&gt;...with CurrentRegion&lt;br /&gt;&lt;br /&gt;Given any cell inside the "data block", CurrentRegion?() finds the surrounding contiguous data region and returns its range:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;sheet.range("b5").currentregion.address   # &gt;&gt; "$B$3:$C$7"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...by "jumping around"&lt;br /&gt;&lt;br /&gt;There's a shortcut key in Excel "&lt;CTRL&gt;+&lt;DOWN/UP/RIGHT/LEFT-ARROW&gt;", that allows you to jump to the end/start of regions with content. With our example and the cursor in B3, pressing &lt;CTRL&gt;+&lt;DOWN&gt; would jump the cursor to cell B7, pressing that shortcut again would move the cursor to the last line of the spreadsheet to B65536. There's an equivalent method: "End()".&lt;br /&gt;&lt;br /&gt;Finding the last row with data:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;sheet.range("b3").end(-4121).address   # &gt;&gt; "$B$7"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The parameter indicates the direction, the Excel constants are:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;xlDown    = -4121&lt;br /&gt;xlToLeft  = -4159&lt;br /&gt;xlToRight = -4161&lt;br /&gt;xlUp      = -4162&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Saving to CSV (or other formats)&lt;br /&gt;&lt;br /&gt;Note that Excel can show quite a lot of warnings / confirm request. To supress these:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;excel.DisplayAlerts = false&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Then:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;workbook.SaveAs 'myfile.csv', xlCSV&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;where xlCSV = 6.&lt;br /&gt;&lt;br /&gt;Here are some common file formats:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;xlCSV=6&lt;br /&gt;xlCSVMac=22&lt;br /&gt;xlCSVMSDOS=24&lt;br /&gt;xlCSVWindows=23&lt;br /&gt;xlCurrentPlatformText=-4158&lt;br /&gt;xlExcel9795=43&lt;br /&gt;xlTextMSDOS=21&lt;br /&gt;xlTextPrinter=36&lt;br /&gt;xlTextWindows=20&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;See also: ScriptingOutlook, ScriptingAccess&lt;br /&gt;&lt;br /&gt;- How about OpenOffice and Ruby scripting? Anything in this area? The examples all seem to rely on&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;and I believe this will only work on Windows OSes.&lt;br /&gt;&lt;br /&gt;&lt;hr&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-2283873733770139226?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/2283873733770139226/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=2283873733770139226' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/2283873733770139226'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/2283873733770139226'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2008/01/rubygarden-archives-scripting-excel.html' title='RubyGarden Archives: Scripting Excel'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-4023312061507308473</id><published>2008-01-20T11:53:00.000-05:00</published><updated>2008-01-31T08:40:38.031-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='rubygarden'/><category scheme='http://www.blogger.com/atom/ns#' term='access'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>RubyGarden Archives: Scripting Access</title><content type='html'>&lt;span style="font-style:italic;"&gt;&lt;span style="font-weight:bold;"&gt;Editor's Note: &lt;/span&gt;Once upon a time, there was a website named RubyGarden.org, which contained many helpful links and articles. That website has recently disappeared. The following "Scripting Access" article was salvaged &lt;a href="http://www.google.com/search?q=cache:Uy0oqGPuJhcJ:rubygarden.org/Ruby/page/show/ScriptingAccess+scriptingaccess+site:rubygarden.org&amp;hl=en&amp;ct=clnk&amp;cd=1&amp;gl=us"&gt;from the Google cache&lt;/a&gt; and is provided here in its entirety.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;hr&gt;&lt;br /&gt;Here is a quick example on getting data from a Microsoft Access database.&lt;br /&gt;&lt;br /&gt;Recordset with a connection, using JET. This works, but for some reason the memory requirements were large. Using an ODBC source for the same data seems to use almost no memory!&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require "win32ole"&lt;br /&gt;&lt;br /&gt;conn = WIN32OLE.new("ADODB.Connection")&lt;br /&gt;conn["Provider"] = "Microsoft.Jet.OLEDB.4.0"&lt;br /&gt;conn.Open('c:\ruby\dev\calldata.mdb')&lt;br /&gt;&lt;br /&gt;rs = conn.Execute("select date, dialednumber, extension, cost from&lt;br /&gt;    callrecords where call = 2 and date &gt;=#01-jan-2005#")&lt;br /&gt;rs.getrows.each do |row|&lt;br /&gt;    puts row&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;conn.Close&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Recordset without a connection, using JET. This works, but for some reason the memory requirements were large. Using an ODBC source for the same data seems to use almost no memory!&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require "win32ole"&lt;br /&gt;&lt;br /&gt;rs = WIN32OLE.new("ADODB.recordset")&lt;br /&gt;&lt;br /&gt;qry = "select date, dialednumber, extension, cost from callrecords where&lt;br /&gt;    call = 2 and date &gt;=#01-jan-2005#"&lt;br /&gt;constr = 'Provider=Microsoft.Jet.OLEDB.4.0;Data&lt;br /&gt;Source=c:\data\calldata.mdb'&lt;br /&gt;&lt;br /&gt;rs.open(qry,constr)&lt;br /&gt;&lt;br /&gt;rs.getrows.each do |row|&lt;br /&gt;    puts row&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Recordset without connection using an ODBC source. My program took about 28MB mem vs. 39MB for the JET version. The difference is massive when working with a lot of data Requires that you create the ODBC data source!&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require "win32ole"&lt;br /&gt;&lt;br /&gt;rs = WIN32OLE.new("ADODB.recordset")&lt;br /&gt;qry = "select date, dialednumber, extension, cost from callrecords where&lt;br /&gt;    call = 2 and date &gt;=#01-jan-2005#"&lt;br /&gt;rs.open(qry,"DSN=calldata;")&lt;br /&gt;&lt;br /&gt;rs.getrows.each do |row|&lt;br /&gt;    puts row&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Here's a function you can drop at the top of your program that alters the definition of Object. It will work for loading access data into any object class that supports "&lt;&lt;" and "transpose" - such as Array. Transpose is required since the recordset comes back with the rows "rotated".&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;public&lt;br /&gt;def readMDB(qry,dsn)&lt;br /&gt;    rs = WIN32OLE.new("ADODB.recordset")&lt;br /&gt;    rs.open(qry,"DSN=#{dsn};")&lt;br /&gt;    rs.getrows.each do |row|&lt;br /&gt;        self &lt;&lt; row&lt;br /&gt;    end&lt;br /&gt;    self.transpose&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Usage example&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;qry = "select date, dialednumber, extension, cost from callrecords"&lt;br /&gt;dsn = "DSN=calldata;"&lt;br /&gt;&lt;br /&gt;a = []&lt;br /&gt;a.readMDB(qry, dsn)&lt;br /&gt;p a&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Question: Is there a way to use paramters on the query like the ones from DBI? E.g.: (note the '?')&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;select * from callrecords where call = ?&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;hr&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-4023312061507308473?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/4023312061507308473/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=4023312061507308473' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/4023312061507308473'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/4023312061507308473'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2008/01/rubygarden-archives-scripting-access.html' title='RubyGarden Archives: Scripting Access'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-1807503088540316332</id><published>2008-01-13T10:50:00.000-05:00</published><updated>2008-01-31T08:38:38.688-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='outlook'/><category scheme='http://www.blogger.com/atom/ns#' term='rubygarden'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>RubyGarden Archives: Scripting Outlook</title><content type='html'>&lt;span style="font-style:italic;"&gt;&lt;span style="font-weight:bold;"&gt;Editor's Note: &lt;/span&gt;Once upon a time, there was a website named RubyGarden.org, which contained many helpful links and articles. The website has recently dropped off the face of the earth. The following "Scripting Outlook" article was salvaged &lt;a href="http://www.google.com/search?q=cache:mLr7PP6dna0J:rubygarden.org/Ruby/page/show/ScriptingOutlook+scriptingoutlook+site:rubygarden.org&amp;hl=en&amp;ct=clnk&amp;cd=1&amp;gl=us"&gt;from the Google cache&lt;/a&gt; and is provided here in its entirety.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;hr&gt;&lt;br /&gt;With some help of the the ScriptingExcel page and the Office helpfiles mentioned there a short script to list your Outlook messages. The script should be pretty easy to expand. One method that was really helpfull in figuring out what is possible is:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;OLEObject.ole_methods&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;MarkJanssen&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;&lt;br /&gt;myApp = WIN32OLE::new("outlook.Application")&lt;br /&gt;&lt;br /&gt;# load Outlook OLE constants&lt;br /&gt;&lt;br /&gt;class OutlookConst&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;WIN32OLE.const_load(myApp, OutlookConst)&lt;br /&gt;&lt;br /&gt;p "OlMailItem = #{OutlookConst::OlMailItem}"&lt;br /&gt;&lt;br /&gt;ns = myApp.GetNameSpace("MAPI")&lt;br /&gt;#ns.Logon # uncomment for online usage&lt;br /&gt;folders = ns.Folders&lt;br /&gt;&lt;br /&gt;new_messages = 0&lt;br /&gt;&lt;br /&gt;folders.each { &lt;br /&gt;    | folder |&lt;br /&gt;     puts "+" + folder.Name&lt;br /&gt;&lt;br /&gt;    begin&lt;br /&gt;        folder.Folders.each {&lt;br /&gt;            | folder |&lt;br /&gt;            # puts " " + folder.Name&lt;br /&gt;            if ["Inbox","Sent Items"].member? folder.Name&lt;br /&gt;                folder.Items.each { &lt;br /&gt;                    | msg |&lt;br /&gt;                    if msg['UnRead']&lt;br /&gt;                        new_messages += 1&lt;br /&gt;                    end&lt;br /&gt;                    puts "  From: " + msg['SenderName']&lt;br /&gt;                    puts "  Subject: " + msg['Subject']&lt;br /&gt;                }&lt;br /&gt;            end&lt;br /&gt;        }&lt;br /&gt;    rescue&lt;br /&gt;        &lt;br /&gt;         puts " Unable to open"&lt;br /&gt;    end&lt;br /&gt;}&lt;br /&gt;puts "You have #{new_messages} new message(s)"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Glauber 2003-10-09:&lt;br /&gt;&lt;br /&gt;    * Here's a quick tip that would have saved me some time: When iterating over a list of Outlook items (e.g.: MailItems) in order to move or delete some of them, you must do so backwards (GetLast... GetPrevious... GetPrevious...). In other words, don't attempt to access an item that's after the one you deleted or moved.&lt;br /&gt;    * Here's another tip: there's no good way to find the From Internet email address of a message. Most times, creating a reply and extracting the first recipient for it will work, but sometimes not (it may give you an Exchange X.400 address instead of what you're looking for). Don't send the reply, discard it.&lt;br /&gt;&lt;br /&gt;&lt;hr&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-1807503088540316332?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/1807503088540316332/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=1807503088540316332' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/1807503088540316332'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/1807503088540316332'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2008/01/rubygarden-archives-scripting-outlook.html' title='RubyGarden Archives: Scripting Outlook'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-4590999204934395719</id><published>2008-01-03T08:25:00.000-05:00</published><updated>2008-01-03T08:44:06.563-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><category scheme='http://www.blogger.com/atom/ns#' term='dotnet'/><title type='text'>IronRuby, Ruby.NET, and the Differences</title><content type='html'>Following on the heels of &lt;a href="http://on-ruby.blogspot.com/2007/12/rubynetironruby-interview-with-m-david.html"&gt;Pat Eyler's interview of M. David Peterson&lt;/a&gt;, Mr. Peterson has posted an article entitled &lt;span style="font-style:italic;"&gt;&lt;a href="http://www.oreillynet.com/windows/blog/2008/01/rubynet_vs_ironruby_whats_the.html"&gt;Ruby.NET vs. IronRuby: What's The Difference?&lt;/a&gt;&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;Microsoft's John Lam has posted a follow-up &lt;a href="http://www.iunknown.com/2008/01/ironruby-vs-rub.html"&gt;on his blog&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;It's good to see increased public discussion of these two important projects.&lt;br /&gt;&lt;a href="http://digg.com/submit?phase=2&amp;url=http://rubyonwindows.blogspot.com/2008/01/ironruby-rubynet-and-differences.html"&amp;target="_blank"&gt;&lt;br /&gt;&lt;img border="0" alt="Digg my article" src="http://digg.com/img/badges/91x17-digg-button.gif"/&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-4590999204934395719?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/4590999204934395719/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=4590999204934395719' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/4590999204934395719'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/4590999204934395719'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2008/01/ironruby-rubynet-and-differences.html' title='IronRuby, Ruby.NET, and the Differences'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-8776493168859603736</id><published>2008-01-02T19:37:00.000-05:00</published><updated>2008-01-03T08:08:23.013-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='excel'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Parsing Spreadsheets with the Roo Gem</title><content type='html'>I've &lt;a href="http://rubyonwindows.blogspot.com/search/label/excel"&gt;talked at length&lt;/a&gt; about using the &lt;span style="font-weight:bold;"&gt;win32ole&lt;/span&gt; library to automate Microsoft Excel. But there are alternatives for accessing data in Excel spreadsheets -- some of which don't even require Excel to be installed. One of these is the &lt;a href="http://roo.rubyforge.org/"&gt;roo&lt;/a&gt; gem, which allows you to extract data from Excel, OpenOffice and Google spreadsheets. Roo provides read-only access to Excel and OpenOffice spreadsheets, but both read and write access to Google spreadsheets.&lt;br /&gt;&lt;br /&gt;To install the roo gem, including its dependencies, open a console window and enter:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;gem install roo -y&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Require the roo library in your script:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'roo'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To parse an Excel worksheet, we first create an instance of the Excel workbook object by calling the &lt;span style="font-weight: bold;"&gt;Excel.new&lt;/span&gt; method and passing it the path and filename:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;xl = Excel.new('C:\my_workbook.xls')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The next step is to define which worksheet in the workbook we will be working with. We do this by setting the &lt;span style="font-weight: bold;"&gt;Excel.default_sheet&lt;/span&gt; value to one of the worksheets in the &lt;span style="font-weight: bold;"&gt;Excel.sheets&lt;/span&gt; array:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;xl.default_sheet = xl.sheets[0]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To extract the value from a particular cell, call the &lt;span style="font-weight:bold;"&gt;cell&lt;/span&gt; method, passing it the row number and either the column number or column letter. Examples:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;val = xl.cell(3, 5)&lt;br /&gt;val = xl.cell(3, 'E')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The &lt;span style="font-weight:bold;"&gt;row&lt;/span&gt; method returns an array of values from the specified row number, so...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;values = xl.row(3)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...returns the values from the third row.&lt;br /&gt;&lt;br /&gt;Similarly, the &lt;span style="font-weight:bold;"&gt;column&lt;/span&gt; method returns an array of values from a column:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;values = xl.column(5)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Get the full details from the &lt;a href="http://roo.rubyforge.org/"&gt;roo homepage&lt;/a&gt;, and the &lt;a href="http://roo.rubyforge.org/rdoc/index.html"&gt;roo Rdoc page&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;That's all for now. As always, feel free to post a comment here or email me with questions, comments, or suggestions.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;br /&gt;&lt;a href="http://digg.com/submit?phase=2&amp;url=http://rubyonwindows.blogspot.com/2008/01/parsing-spreadsheets-with-roo-gem.html"&amp;target="_blank"&gt;&lt;br /&gt;&lt;img border="0" alt="Digg my article" src="http://digg.com/img/badges/91x17-digg-button.gif"/&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-8776493168859603736?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/8776493168859603736/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=8776493168859603736' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/8776493168859603736'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/8776493168859603736'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2008/01/parsing-spreadsheets-with-roo-gem.html' title='Parsing Spreadsheets with the Roo Gem'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-3638340087838876816</id><published>2007-12-28T12:06:00.000-05:00</published><updated>2007-12-28T14:27:00.538-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><category scheme='http://www.blogger.com/atom/ns#' term='dotnet'/><title type='text'>Ruby.NET/IronRuby Interview With M. David Peterson</title><content type='html'>Over on the &lt;span style="font-weight:bold;"&gt;On Ruby&lt;/span&gt; blog, &lt;a href="http://on-ruby.blogspot.com/2007/12/rubynetironruby-interview-with-m-david.html"&gt;Pat Eyler interviews M. David Peterson&lt;/a&gt; about the &lt;a href="http://rubydotnet.googlegroups.com/web/Home.htm"&gt;Ruby.NET&lt;/a&gt; project. Peterson comments on the relationships between Ruby.NET, &lt;a href="http://en.wikipedia.org/wiki/IronRuby"&gt;IronRuby&lt;/a&gt;, Microsoft, and the larger Ruby community. Check it out.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-3638340087838876816?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/3638340087838876816/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=3638340087838876816' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/3638340087838876816'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/3638340087838876816'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/12/rubynetironruby-interview-with-m-david.html' title='Ruby.NET/IronRuby Interview With M. David Peterson'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-7932633991979857459</id><published>2007-12-06T08:22:00.000-05:00</published><updated>2007-12-06T08:56:07.184-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='outlook'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Automating Outlook with Ruby: Saving File Attachments</title><content type='html'>A while back, we looked at &lt;a href="http://rubyonwindows.blogspot.com/2007/08/automating-outlook-with-ruby-inbox.html"&gt;accessing the Outlook Inbox and managing messages&lt;/a&gt;. A reader recently asked how to iterate over the messages and save file attachments to their hard drive. Specifically, they wanted to save only those file attachments that exceeded 500k in size.&lt;br /&gt;&lt;br /&gt;In Outlook, a message (MailItem) object's file attachments are accessed via the &lt;span style="font-weight:bold;"&gt;Attachments &lt;/span&gt;collection:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;message.Attachments.each do |attachment|&lt;br /&gt;    # Do something here&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The Attachment object has few properties and methods, but one you will use is the FileName property, which returns the, uh, filename.&lt;br /&gt;&lt;br /&gt;To save the attachment, call its &lt;span style="font-weight:bold;"&gt;SaveAsFile &lt;/span&gt;method, passing it a path and filename:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;attachment.SaveAsFile("c:\\attachments\\#{attachment.FileName}")&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The attachment object does not offer a method/property for determining the file size. If you were looking to save only files of a certain size, you could save the file, then check the saved file's size and delete it if necessary. This is not optimal, but it does the trick.&lt;br /&gt;&lt;br /&gt;So, putting this all together, we'd have something like the following code which iterates over your Inbox messages and saves all attachments of 500,000 bytes or larger:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;&lt;br /&gt;outlook = WIN32OLE.new('Outlook.Application')&lt;br /&gt;mapi = outlook.GetNameSpace('MAPI')&lt;br /&gt;inbox = mapi.GetDefaultFolder(6)&lt;br /&gt;inbox.Items.each do |message|&lt;br /&gt;    message.Attachments.each do |attachment|&lt;br /&gt;        filename = "c:\\attachments\\#{attachment.FileName}"&lt;br /&gt;        attachment.SaveAsFile(filename)           &lt;br /&gt;        File.delete(filename) if File.size(filename) &lt; 500000&lt;br /&gt;    end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That's our show for today. Questions? Comments? Suggestions? Post a comment here or send me an email.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;br /&gt;&lt;a href="http://digg.com/submit?phase=2&amp;url=http://rubyonwindows.blogspot.com/2007/12/automating-outlook-with-ruby-saving.html"&amp;target="_blank"&gt;&lt;br /&gt;&lt;img border="0" alt="Digg my article" src="http://digg.com/img/badges/91x17-digg-button.gif"/&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-7932633991979857459?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/7932633991979857459/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=7932633991979857459' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/7932633991979857459'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/7932633991979857459'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/12/automating-outlook-with-ruby-saving.html' title='Automating Outlook with Ruby: Saving File Attachments'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-3587521183955310938</id><published>2007-12-02T15:42:00.000-05:00</published><updated>2007-12-02T15:51:59.209-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><category scheme='http://www.blogger.com/atom/ns#' term='dotnet'/><title type='text'>The Ruby.NET Compiler: A Little Love</title><content type='html'>Amidst all the &lt;a href="http://en.wikipedia.org/wiki/IronRuby"&gt;IronRuby &lt;/a&gt;hype, you could be forgiven if you didn't hear mention of the &lt;a href="http://rubydotnet.googlegroups.com/web/Home.htm"&gt;Ruby.NET Compiler&lt;/a&gt;. But if you use Ruby on Windows -- and you probably do if you're reading this blog -- you owe it to yourself to at least be aware of the work that Wayne Kelly and friends have been doing.&lt;br /&gt;&lt;br /&gt;To quote the Ruby.NET page:&lt;br /&gt;&lt;blockquote&gt;Ruby.NET is a compiler that translates Ruby source code into .NET intermediate code. This allows components implemented using the Ruby language to:&lt;br /&gt;&lt;br /&gt;    * Natively execute on the .NET platform&lt;br /&gt;    * Be linked with third party components developed using other .NET languages such as C#.&lt;br /&gt;    * Utilize the extensive resources of the .NET platform including visual design and debugging tools, the security framework and an extensive collection of class libraries used, for example, to create Windows forms, web and database applications. &lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;Ruby.NET version 0.9 was released a couple weeks ago and can be installed via a binary installer from &lt;a href="http://rubydotnetcompiler.googlecode.com/files/Ruby.NET-0.9.0.msi"&gt;here&lt;/a&gt;. It requires the Microsoft .Net Framework version 2.0. &lt;br /&gt;&lt;br /&gt;There's an optional Visual Studio integration package, which requires &lt;a href="http://www.microsoft.com/downloads/details.aspx?familyid=51A5C65B-C020-4E08-8AC0-3EB9C06996F4&amp;displaylang=en"&gt;Visual Studio 2005 SDK version 4.0&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I've just started tinkering with this, so I can't provide any meaningful analysis or instruction on it yet. But I like the possibilities that it presents...&lt;br /&gt;&lt;br /&gt;* Compile your code to a .Net executable&lt;br /&gt;* Use the .NET framework's Forms library for your GUI&lt;br /&gt;* Leverage the vast array of .NET framework libraries&lt;br /&gt;* Work is currently underway to port the win32ole library to Ruby.NET&lt;br /&gt;&lt;br /&gt;I'd like to see more documentation and examples, including Windows forms code samples. If you have Ruby.NET examples you would like to share, feel free to email them to me or post in the comments.&lt;br /&gt;&lt;br /&gt;There's also a Ruby.NET Compiler Discussion group &lt;a href="http://groups.google.com/group/RubyDOTNET/topics"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;So check it out and maybe, as Antonio Cangiano recommends, &lt;a href="http://antoniocangiano.com/2007/11/23/give-rubynet-09-some-love/"&gt;give Ruby.NET some love&lt;/a&gt;. I think this is a project that deserves much more attention.&lt;br /&gt;&lt;a href="http://digg.com/submit?phase=2&amp;url=http://rubyonwindows.blogspot.com/2007/12/rubynet-compiler-little-love.html"&amp;target="_blank"&gt;&lt;br /&gt;&lt;img border="0" alt="Digg my article" src="http://digg.com/img/badges/91x17-digg-button.gif"/&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-3587521183955310938?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/3587521183955310938/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=3587521183955310938' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/3587521183955310938'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/3587521183955310938'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/12/rubynet-compiler-little-love.html' title='The Ruby.NET Compiler: A Little Love'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-3663007481825289936</id><published>2007-11-12T19:51:00.000-05:00</published><updated>2007-11-14T22:09:27.384-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='word'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Find &amp; Replace with MS Word</title><content type='html'>While browsing through some &lt;a href="http://snippets.dzone.com/tags/ruby"&gt;DZone Ruby Snippets&lt;/a&gt;, I came across &lt;a href="http://snippets.dzone.com/posts/show/4747"&gt;this nice little example&lt;/a&gt; of using Ruby to automate Find &amp; Replace in Microsoft Word, compliments of &lt;a href="http://snippets.dzone.com/user/timmorgan"&gt;Tim Morgan&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;You could use such a snippet as part of a mail-merge type solution. To use it, create a form letter in a Word document, with bracketed placeholders where the actual values are to be inserted. Example:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Dear [name],&lt;br /&gt;&lt;br /&gt;On [date] account [account_number] was charged a [amount] fee.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Let's take a walk through the code...&lt;br /&gt;&lt;br /&gt;Load the &lt;span style="font-weight:bold;"&gt;win32ole&lt;/span&gt; library:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Launch Microsoft Word:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;word = WIN32OLE.new('Word.Application')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Open the template document:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;doc = word.Documents.Open('c:\file_to_open.doc')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Iterate over a hash containing keys and values. Each key is the placeholder code and the corresponding value is the text to insert:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;{&lt;br /&gt;  'name' =&gt; 'Tim Morgan',&lt;br /&gt;  'date' =&gt; Date.today.strftime('%B %d, %Y'),&lt;br /&gt;  'account_number' =&gt; '123456',&lt;br /&gt;  'amount' =&gt; '$45.76'&lt;br /&gt;}.each do |key, value|&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Start the search at the beginning of the document:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;word.Selection.HomeKey(unit=6)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Grab a reference to the &lt;span style="font-weight:bold;"&gt;Find &lt;/span&gt;object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;find = word.Selection.Find&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Set the Find object's text string (the hash key) to locate, surrounded by brackets:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;find.Text = "[#{key}]"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;For each occurrence of the string found (via the Find object's &lt;span style="font-weight:bold;"&gt;Execute &lt;/span&gt;method), insert the replacement text (the hash value):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;while word.Selection.Find.Execute&lt;br /&gt;  word.Selection.TypeText(text=value)&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Save the document with a new name:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;doc.SaveAs('c:\output_file.doc')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Close the document:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;doc.Close&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The example above shows the hash hard-coded in the script, but you could build on this to load an array of hashes from a text or YAML file, Excel worksheet, or database table.&lt;br /&gt;&lt;br /&gt;My thanks to Tim Morgan, who posted the original snippet on Dzone, and to you, for stopping by to read it!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-3663007481825289936?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/3663007481825289936/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=3663007481825289936' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/3663007481825289936'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/3663007481825289936'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/11/find-replace-with-ms-word.html' title='Find &amp; Replace with MS Word'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-7822790394707472270</id><published>2007-11-11T17:53:00.001-05:00</published><updated>2007-11-15T08:26:07.713-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='wxruby'/><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='gui'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Getting Started with the wxRuby GUI Toolkit</title><content type='html'>Many readers have asked "What is the best GUI toolkit?". There is no single answer to this question, and I won't try to lobby for my GUI of choice. Use whatever works best for you and your users.&lt;br /&gt;&lt;br /&gt;I use the wxRuby library. It's cross-platform, provides a native look-and-feel, and is easy to install (via a gem) and distribute with your application (via RubyScript2Exe). Further features can be found in the wxRuby wiki &lt;a href="http://wxruby.rubyforge.org/wiki/wiki.pl?IntroductionToWxRuby"&gt;here&lt;/a&gt;. wxRuby is the Ruby interface to wxWidgets, a very stable and &lt;a href="http://en.wikipedia.org/wiki/WxWidgets#Software_using_wxWidgets"&gt;widely-used&lt;/a&gt; widget toolkit.&lt;br /&gt;&lt;br /&gt;Installing wxRuby is simple using RubyGems:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;gem install wxruby&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Though you can use a GUI designer, creating most forms "by hand" is probably simpler than you suspect. A useful wxRuby user interface can be created in less than 50 lines of code, as we shall see...&lt;br /&gt;&lt;br /&gt;Let's create a simple form with a label, text box, combo box, and button. We start by requiring the wx library and including the Wx namespace:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'wx'&lt;br /&gt;include Wx&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We create a new class which inherits from the &lt;span style="font-weight:bold;"&gt;Wx::Frame&lt;/span&gt; class and includes an &lt;span style="font-weight:bold;"&gt;initialize&lt;/span&gt; method:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class MyFrame &lt; Frame&lt;br /&gt;  def initialize()&lt;br /&gt;    super(nil, -1, 'My Frame Title')&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The Frame class' &lt;span style="font-weight:bold;"&gt;super&lt;/span&gt; constructor method takes the following arguments (all but Parent are optional):&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Parent&lt;/span&gt;: The window parent. This may be NULL. If it is non-NULL, the frame will always be displayed on top of the parent window on Windows.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;ID&lt;/span&gt;: The window identifier. It may take a value of -1 to indicate a default value.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Title&lt;/span&gt;: The caption to be displayed on the frame’s title bar.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Position&lt;/span&gt;: The window position. A value of (-1, -1) indicates a default position, chosen by either the windowing system or Widgets, depending on platform.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Size&lt;/span&gt;: The window size. A value of (-1, -1) indicates a default size, chosen by either the windowing system or Widgets, depending on platform.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Style&lt;/span&gt;: The window style (ie, if the minimize, maximize, and close boxes appear on the Frame).&lt;br /&gt;&lt;br /&gt;Further details on the Frame class can be found in the wxRuby wiki &lt;a href="http://wxruby.rubyforge.org/doc/frame.html"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Next, let's add the code to the &lt;span style="font-weight:bold;"&gt;initialize&lt;/span&gt; method to create a panel, which will contain the other controls:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;@my_panel = Panel.new(self)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The &lt;span style="font-weight:bold;"&gt;self&lt;/span&gt; passed to the &lt;span style="font-weight:bold;"&gt;new&lt;/span&gt; constructor is a reference to the Frame object, which we are passing as the parent of the Panel object.&lt;br /&gt;&lt;br /&gt;Next, we'll create a variety of controls, passing the newly created &lt;span style="font-weight:bold;"&gt;@my_panel&lt;/span&gt; as the parent of each control:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;@my_label = StaticText.new(@my_panel, -1, 'My Label Text', &lt;br /&gt;  DEFAULT_POSITION, DEFAULT_SIZE, ALIGN_CENTER)&lt;br /&gt;@my_textbox = TextCtrl.new(@my_panel, -1, 'Default Textbox Value')&lt;br /&gt;@my_combo = ComboBox.new(@my_panel, -1, 'Default Combo Text', &lt;br /&gt;  DEFAULT_POSITION, DEFAULT_SIZE, ['Item 1', 'Item 2', 'Item 3'])&lt;br /&gt;@my_button = Button.new(@my_panel, -1, 'My Button Text')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I recommend assigning all your form controls to instance variables. This is the reason for the '@' preceding each control variable name.&lt;br /&gt;&lt;br /&gt;We want to bind the button click to a "my_button_click" method which we will add to this class later. We do with the &lt;span style="font-weight:bold;"&gt;evt_button&lt;/span&gt; method:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;evt_button(@my_button.get_id()) { |event| my_button_click(event)}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now we'll proceed to the layout: arranging the controls in the Panel. We'll do this using a &lt;a href="http://wxruby.rubyforge.org/doc/boxsizer.html"&gt;BoxSizer&lt;/a&gt;, which we'll have arrange the controls vertically:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;@my_panel_sizer = BoxSizer.new(VERTICAL)&lt;br /&gt;@my_panel.set_sizer(@my_panel_sizer)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Next we add each control to the panel's sizer by calling the sizer's &lt;a href="http://wxruby.rubyforge.org/doc/sizer.html#Sizer_add"&gt;add&lt;/a&gt; method:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;@my_panel_sizer.add(@my_label, 0, GROW|ALL, 2)&lt;br /&gt;@my_panel_sizer.add(@my_textbox, 0, GROW|ALL, 2)&lt;br /&gt;@my_panel_sizer.add(@my_combo, 0, GROW|ALL, 2)&lt;br /&gt;@my_panel_sizer.add(@my_button, 0, GROW|ALL, 2)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;See the &lt;a href="http://wxruby.rubyforge.org/doc/sizer.html#Sizer_add"&gt;Sizer.Add documentation&lt;/a&gt; for an explanation of the various parameters.&lt;br /&gt;&lt;br /&gt;Our last line of code in the &lt;span style="font-weight:bold;"&gt;initialize&lt;/span&gt; method makes the frame visible:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;show()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Don't forget to add the my_button_click method that is called by the button click:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def my_button_click(event)&lt;br /&gt;    # Your code here&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That concludes our MyFrame class. Now we want to create a MyApp class that will call our MyFrame class:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class MyApp &lt; App&lt;br /&gt;  def on_init&lt;br /&gt;    MyFrame.new&lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Finally, we create a new instance of our MyApp class and call its main_loop method:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;MyApp.new.main_loop()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;View the complete code &lt;a href="http://pastie.caboo.se/116661"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;There you have it. A complete, but simple, wxRuby form. It's not much to look at...&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_9LH5hSdA01Y/RzeQmR7MOjI/AAAAAAAAABc/D06cvFeIZWw/s1600-h/wxForm1.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_9LH5hSdA01Y/RzeQmR7MOjI/AAAAAAAAABc/D06cvFeIZWw/s400/wxForm1.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5131729287733066290" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;...but it demonstrates the basics and we'll enhance it shortly.&lt;br /&gt;&lt;br /&gt;Soon, we'll cover related topics: dressing up our GUI a little, using the wxSugar extensions, using a Forms designer such as &lt;a href="http://www.wxformbuilder.org/"&gt;wxFormBuilder&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Questions? Comments? Suggestions? Post a comment here or send me an email message.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;br /&gt;&lt;a href="http://digg.com/submit?phase=2&amp;url=http://rubyonwindows.blogspot.com/2007/11/getting-started-with-wxruby-gui-toolkit.html"&amp;target="_blank"&gt;&lt;br /&gt;&lt;img border="0" alt="Digg my article" src="http://digg.com/img/badges/91x17-digg-button.gif"/&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-7822790394707472270?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/7822790394707472270/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=7822790394707472270' title='17 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/7822790394707472270'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/7822790394707472270'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/11/getting-started-with-wxruby-gui-toolkit.html' title='Getting Started with the wxRuby GUI Toolkit'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_9LH5hSdA01Y/RzeQmR7MOjI/AAAAAAAAABc/D06cvFeIZWw/s72-c/wxForm1.jpg' height='72' width='72'/><thr:total>17</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-1675333968321804097</id><published>2007-11-01T08:07:00.000-05:00</published><updated>2007-11-01T07:37:55.925-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='rubyconf'/><title type='text'>RubyConf 2007</title><content type='html'>I'll be attending my first RubyConf here in Charlotte this weekend and there's a lot of great stuff on &lt;a href="http://rubyconf.org/agenda.html"&gt;the agenda&lt;/a&gt;. In addition, I learned from Bill Plummer that Microsoft is hosting an event Thursday night with John Lam talking about IronRuby. Unfortunately, I will have to miss it but I hope that John will duplicate the best parts of it in his RubyConf talk Saturday morning. Charlie Nutter follows that with his JRuby presentation. Of course, "Writing Client and Desktop Applications in Ruby" by Bruce Williams also caught my eye, as I am a desktop apps developer.&lt;br /&gt;&lt;br /&gt;There's been a bit of a lull in my blog postings (and replies to readers) of late, as I've been swamped with work, but I plan to post more frequently going forward. I have a few topics suggested by readers, but welcome any comments, questions, or suggestions you may have. I still feel that Windows is Ruby's red-headed stepchild, with far more potential than the attention given to it. That may change (somewhat) in the future as projects such as IronRuby and the Ruby.Net compiler mature.&lt;br /&gt;&lt;br /&gt;And if you're at RubyConf, stop by and say 'Hey!'&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-1675333968321804097?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/1675333968321804097/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=1675333968321804097' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/1675333968321804097'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/1675333968321804097'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/11/rubyconf-2007.html' title='RubyConf 2007'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-1749888690749898683</id><published>2007-10-21T16:35:00.000-05:00</published><updated>2007-10-21T15:41:49.734-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='wxruby'/><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='gui'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Windows XP Visual Style Controls with wxRuby</title><content type='html'>wxRuby uses the native Microsoft Windows common controls when displaying widgets on a Windows OS. You may have noticed, however, that the form controls do not have the Windows XP look and feel. They have the flat and 2-dimensional look of earlier Windows versions...&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_9LH5hSdA01Y/RxuzoqDrdDI/AAAAAAAAABM/iabC79KhVC8/s1600-h/wxForm5.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_9LH5hSdA01Y/RxuzoqDrdDI/AAAAAAAAABM/iabC79KhVC8/s320/wxForm5.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5123886512129930290" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;...rather than having the rounded corners and 3-dimensional look of Windows XP...&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_9LH5hSdA01Y/Rxuzz6DrdEI/AAAAAAAAABU/GSAX5noX3fA/s1600-h/wxForm6.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://4.bp.blogspot.com/_9LH5hSdA01Y/Rxuzz6DrdEI/AAAAAAAAABU/GSAX5noX3fA/s320/wxForm6.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5123886705403458626" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Note, among other things, the "glowing button" effect when the mouse hovered over the "Get Films Data" button.&lt;br /&gt;&lt;br /&gt;This is because Windows XP comes with both version 5 and version 6 of the Common Controls, but unless specifically instructed to use version 6, it will default to using version 5 controls.&lt;br /&gt;&lt;br /&gt;So, how do you instruct Windows to use the version 6 controls for your wxRuby GUI application? By including a simple manifest XML file with your application.&lt;br /&gt;&lt;br /&gt;Copy the following text into a new text file. Save the file as "rubyw.exe.manifest" in your ruby/bin folder, the same folder that contains rubyw.exe. You might also save a copy to the same folder as "ruby.exe.manifest".&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;?xml version="1.0" encoding="UTF-8" standalone="yes"?&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"&amp;gt;&lt;br /&gt;    &amp;lt;assemblyIdentity version="1.8.6.0" processorArchitecture="X86" &lt;br /&gt;        name="Microsoft.Winweb.Ruby" type="win32"/&amp;gt;&lt;br /&gt;        &amp;lt;description&amp;gt;Ruby interpreter&amp;lt;/description&amp;gt;&lt;br /&gt;        &amp;lt;dependency&amp;gt;&lt;br /&gt;         &amp;lt;dependentAssembly&amp;gt;&lt;br /&gt;                &amp;lt;assemblyIdentity &lt;br /&gt;                    type="win32" &lt;br /&gt;                    name="Microsoft.Windows.Common-Controls" &lt;br /&gt;                    version="6.0.0.0" &lt;br /&gt;                    processorArchitecture="X86" &lt;br /&gt;                    publicKeyToken="6595b64144ccf1df" &lt;br /&gt;                    language="*"&lt;br /&gt;                /&amp;gt;&lt;br /&gt;        &amp;lt;/dependentAssembly&amp;gt;&lt;br /&gt;    &amp;lt;/dependency&amp;gt;&lt;br /&gt;&amp;lt;/assembly&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;When the ruby.exe or rubyw.exe interpreter is run, Windows will look for a manifest file matching the program name in the same folder. When it finds our manifest file, it will then use the version 6 controls, as defined in the &lt;span style="font-weight:bold;"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt; section of the manifest.&lt;br /&gt;&lt;br /&gt;We place the file in our c:\ruby\bin directory because that is where ruby.exe and rubyw.exe are located. We name the file ruby.exe.manifest or rubyw.exe.manifest to match the name of the interpreter being run.&lt;br /&gt;&lt;br /&gt;If you are compiling your application with RubyScript2Exe, copy the manifest file into the folder with your script, then embed the manifest file in your compiled executable using the &lt;span style="font-weight:bold;"&gt;RUBYSCRIPT2EXE.bin&lt;/span&gt; statement:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;RUBYSCRIPT2EXE.bin = ["rubyw.exe.manifest"]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;When your compiled executable is run, your manifest file will be extracted to the same temporary "/bin" folder as the rubw.exe (or ruby.exe) interpreter.&lt;br /&gt;&lt;br /&gt;Questions? Comments? Suggestions? Post a comment here or send me email.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;br /&gt;&lt;a href="http://digg.com/submit?phase=2&amp;url=http://rubyonwindows.blogspot.com/2007/10/windows-xp-visual-style-controls-with.html"&amp;target="_blank"&gt;&lt;br /&gt;&lt;img border="0" alt="Digg my article" src="http://digg.com/img/badges/91x17-digg-button.gif"/&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-1749888690749898683?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/1749888690749898683/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=1749888690749898683' title='14 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/1749888690749898683'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/1749888690749898683'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/10/windows-xp-visual-style-controls-with.html' title='Windows XP Visual Style Controls with wxRuby'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_9LH5hSdA01Y/RxuzoqDrdDI/AAAAAAAAABM/iabC79KhVC8/s72-c/wxForm5.jpg' height='72' width='72'/><thr:total>14</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-525031493600314925</id><published>2007-10-07T17:25:00.000-05:00</published><updated>2007-10-07T17:26:04.669-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Hide &amp; Seek: Using NTFS Alternate Data Streams</title><content type='html'>The NTFS file system utilized by Windows (NT and later) includes support for Alternate Data Streams (ADS). ADS was implemented in NTFS to provide compatibility with Apple's Macintosh Hierarchical File System (HFS), which uses resource forks to store icons and other information for a file, but such streams can be used to store any type of data that a normal file would store. You could, for example, use an ADS to store metadata about the file to which the ADS is attached. You could even store binary data inside an ADS. This might be useful for storing backup versions of a file before making changes.&lt;br /&gt;&lt;br /&gt;To work with an alternate data stream for a file, simply append a colon and the stream name to the filename. The following code appends a stream named 'stream1' to file.txt:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;open('file.txt:stream1', 'w') do |f|&lt;br /&gt;    f.puts('Your Text Here')&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If file.txt did not exist, it would have been created and would be 0 bytes in size, since no data was written to the main stream. But you can append streams to an existing file, as well. The reported size of the file would not change, though it now includes alternate data streams of embedded data.  &lt;br /&gt;&lt;br /&gt;Reading from a stream is just as simple:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;stream_text = open('file.txt:stream1').read&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Alternate data streams can also be used for storing binary data, including executables:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;bytes = open('MyApp.exe', 'rb').read&lt;br /&gt;open('file.txt:MyApp.exe', 'wb') do |f|&lt;br /&gt;    f.write(bytes)&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;bytes = open('file.txt:MyApp.exe','rb').read&lt;br /&gt;open('MyApp2.exe', 'wb') do |f|&lt;br /&gt;    f.write(data)&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;A file can contain multiple data streams, so you could, for example, include a data stream for text and another stream for binary data.&lt;br /&gt;&lt;br /&gt;Another possible use for ADS would be to create a backup of the file which could be restored later:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def create_backup(filename)&lt;br /&gt;    open("#{filename}:backup", "wb") do |f|&lt;br /&gt;        f.write(open(filename, "rb").read)&lt;br /&gt;    end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;def restore_backup(filename)&lt;br /&gt;    if not File.exist?("#{filename}:backup")&lt;br /&gt;        puts("Backup stream does not exist!")&lt;br /&gt;        return&lt;br /&gt;    end&lt;br /&gt;    backup = open("#{filename}:backup", "rb").read&lt;br /&gt;    open("#{filename}", "wb") do |f|&lt;br /&gt;        f.write(backup)&lt;br /&gt;    end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Note: &lt;/span&gt;A file's alternate data streams will be preserved on the NTFS disk, but would be stripped off of a file when copied to a non-NTFS disk, such as a flash drive or CD/DVD disk; or when a file is copied via ftp. For this reason you may not want to rely on alternate data streams for storing critical data.&lt;br /&gt;&lt;br /&gt;There you have it. Sound interesting? Further details on Alternate Data Streams can be found &lt;a href="http://www.wikistc.org/wiki/Alternate_data_streams"&gt;here&lt;/a&gt; and &lt;a href="http://www.bleepingcomputer.com/tutorials/tutorial25.html"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;br /&gt;&lt;a href="http://digg.com/submit?phase=2&amp;url=http://rubyonwindows.blogspot.com/2007/10/hide-seek-using-ntfs-alternate-data.html"&amp;target="_blank"&gt;&lt;br /&gt;&lt;img border="0" alt="Digg my article" src="http://digg.com/img/badges/91x17-digg-button.gif"/&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-525031493600314925?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/525031493600314925/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=525031493600314925' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/525031493600314925'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/525031493600314925'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/10/hide-seek-using-ntfs-alternate-data.html' title='Hide &amp; Seek: Using NTFS Alternate Data Streams'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-7122147920212512286</id><published>2007-09-30T11:30:00.000-05:00</published><updated>2007-10-05T07:19:16.863-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><category scheme='http://www.blogger.com/atom/ns#' term='sqlserver'/><title type='text'>Using Ruby &amp; SQL-DMO to Automate SQL Server Tasks</title><content type='html'>If you work with Microsoft SQL Server, you might like to know that you can automate many of your administrative tasks by leveraging Distributed Management Objects (SQL-DMO). SQL Server's Enterprise Manager is based on DMO, so most (if not all) of what you do through the Enterprise Manager interface can be automated through SQL-DMO -- and Ruby. Create, manage, and backup databases, tables, views, jobs, stored procedures, and more. Let's take a look at some of the available objects and Ruby code  that uses them.&lt;br /&gt;&lt;br /&gt;We start by creating an instance of the &lt;span style="font-weight:bold;"&gt;SQLServer &lt;/span&gt;object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;server = WIN32OLE.new('SQLDMO.SQLServer')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To connect to your server using SQL Authentication, call the &lt;span style="font-weight:bold;"&gt;Connect &lt;/span&gt;method providing the server IP address or name, the login, and password:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;server.Connect('127.0.0.1', 'login', 'password')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To connect using Windows Authentication, set the &lt;span style="font-weight:bold;"&gt;LoginSecure &lt;/span&gt;property to true, then call the Connect method with just the server IP address or name:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;server.LoginSecure = true&lt;br /&gt;server.Connect('127.0.0.1')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;When you're done, be sure to close your connection by calling the &lt;span style="font-weight:bold;"&gt;Disconnect &lt;/span&gt;method:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;server.Disconnect&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The &lt;span style="font-weight:bold;"&gt;Databases &lt;/span&gt;method returns a collection of Database objects. You can access a specific Database object by passing the database name to the Databases method:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;database = server.Databases('my database')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The Database object includes dozens of methods and several collections, including Tables, Views, and StoredProcedures. This code, for example, prints the names of all tables:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;for table in database.Tables&lt;br /&gt;    puts table.Name&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Furthermore, many database objects expose a &lt;span style="font-weight:bold;"&gt;Script &lt;/span&gt; method that returns the SQL code for creating that object. This little block of code saves the SQL for creating all your Table objects:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;for table in database.Tables&lt;br /&gt;    File.open("#{table.Name}.sql", "w") do |f|&lt;br /&gt;        f.puts table.Script&lt;br /&gt;    end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The following block of code writes the text of each stored procedure to a text file:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;database.StoredProcedures.each do |sp|&lt;br /&gt;    File.open("#{sp.Name}.txt", "w") do |f|&lt;br /&gt;        f.puts sp.Text&lt;br /&gt;    end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The SQL Server Agent service, which runs and manages jobs, is accessed via the &lt;span style="font-weight:bold;"&gt;JobServer &lt;/span&gt;object. The following code starts and stops this service:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;server.JobServer.Start&lt;br /&gt;server.JobServer.Stop&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The JobServer object's &lt;span style="font-weight:bold;"&gt;Jobs &lt;/span&gt;method returns a collection of all SQLServer Agent jobs:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;for job in server.JobServer.Jobs&lt;br /&gt;    puts "Name: #{job.Name}\nDescription: #{job.Description}\n\n"&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I have a script that iterates over the Jobs collection and creates a "jobs inventory", an Excel workbook listing each job's name, description, frequency (daily, weekly, etc.) and start time.&lt;br /&gt;&lt;br /&gt;Further details on SQL-DMO can be found in &lt;a href="http://msdn.microsoft.com/msdnmag/issues/01/05/sqldmo/"&gt;this MSDN article&lt;/a&gt;, and in &lt;a href="http://www.sqlteam.com/article/introduction-to-sql-dmo"&gt;this SQL Team article&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;That's all for today, but if you use SQL Server, perhaps this has given you some ideas on how to use your Ruby skills to make SQL Server admin tasks a little easier. &lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;UPDATE: &lt;/span&gt;You may also be interested in &lt;a href="http://rubyonwindows.blogspot.com/2007/03/ruby-ado-and-sqlserver.html"&gt;this earlier article&lt;/a&gt; about using Ruby and ADO for your SQL Server queries.&lt;br /&gt;&lt;br /&gt;Feel free to post a comment here or send me an email with questions, comments, or suggestions.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;br /&gt;&lt;a href="http://digg.com/submit?phase=2&amp;url=http://rubyonwindows.blogspot.com/2007/09/using-ruby-sql-dmo-to-automate-sql.html"&amp;target="_blank"&gt;&lt;br /&gt;&lt;img border="0" alt="Digg my article" src="http://digg.com/img/badges/91x17-digg-button.gif"/&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-7122147920212512286?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/7122147920212512286/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=7122147920212512286' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/7122147920212512286'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/7122147920212512286'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/09/using-ruby-sql-dmo-to-automate-sql.html' title='Using Ruby &amp; SQL-DMO to Automate SQL Server Tasks'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-7896911168000156780</id><published>2007-09-26T08:35:00.000-05:00</published><updated>2007-10-07T07:06:00.616-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='tools'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Using Rake to Automate Windows Desktop App Builds</title><content type='html'>We've recently talked about &lt;a href="http://rubyonwindows.blogspot.com/2007/09/compiling-your-ruby-app-with.html"&gt;compiling your Ruby app with RubyScript2Exe&lt;/a&gt;, and &lt;a href="http://rubyonwindows.blogspot.com/2007/09/installing-your-ruby-app-with-inno.html"&gt;creating an install package with Inno Setup&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I was inspired by RoW reader Luis Lebron, who recently shared with me his Rakefile for automating the running of RubyScript2Exe from within the &lt;a href="http://ruby.netbeans.org"&gt;NetBeans Ruby IDE&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;My process for packaging an application for distribution involves, among other things:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Compiling the code with RubyScript2Exe&lt;br /&gt;&lt;li&gt;Replacing the default EXE icon with my own (using &lt;a href="http://www.angusj.com/resourcehacker/"&gt;Resource Hacker&lt;/a&gt;)&lt;br /&gt;&lt;li&gt;Moving the EXE file to my Install folder&lt;br /&gt;&lt;li&gt;Updating the ReadMe file with the new version number&lt;br /&gt;&lt;li&gt;Updating the Inno Setup script with the new version number&lt;br /&gt;&lt;li&gt;Running the Inno Setup script to create a new Setup.exe file&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;This isn't a particularly time-consuming list of manual tasks. But I spend my workdays developing tools to automate my fellow employees' manual tasks. So it was inevitable that I would seek to do the same for myself.&lt;br /&gt;&lt;br /&gt;I had no prior experience with &lt;a href="http://rubyforge.org/projects/rake"&gt;Rake&lt;/a&gt;, a DSL created by Jim Weirich to automate project builds. I'm neither a C programmer nor a web developer, so I wasn't really motivated to investigate what Rake might have to offer. But after receiving Luis' email, I looked into using Rake, and then researched the options for running Resource Hacker and the Inno Setup compiler from the command line. &lt;br /&gt;&lt;br /&gt;I still know almost nothing about Rake, but I learned enough to create a simple Rakefile to automate all of the above tasks. This could, of course, be done in Ruby without the using Rake, but NetBeans' Rake integration and templates make it handy. Now I can right-click on my project icon in NetBeans, select Run Rake Task =&gt; create_setup, and Rake takes care of the rest.&lt;br /&gt;&lt;br /&gt;In case you're interested, here's an example of my Rakefile. It can no doubt be improved upon, but should give you an example to start from (Beware of line-wrap):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'rake'&lt;br /&gt;require 'fileutils'&lt;br /&gt;include FileUtils&lt;br /&gt;&lt;br /&gt;# set constant values:&lt;br /&gt;LIB_FOLDER = File.expand_path('../lib')&lt;br /&gt;INSTALL_FOLDER = File.expand_path('../install')&lt;br /&gt;ISCC = "C:/Program Files/Inno Setup 5/iscc.exe"&lt;br /&gt;RESHACKER = "C:/Program Files/ResHacker/ResHacker.exe"&lt;br /&gt;ISS_FILE = "#{INSTALL_FOLDER}/Setup.iss"&lt;br /&gt;README_FILE = "#{INSTALL_FOLDER}/ReadMe.txt"&lt;br /&gt;&lt;br /&gt;# extract values from main.rb file:&lt;br /&gt;main_rb = open('../lib/main.rb').read&lt;br /&gt;APP_TITLE = main_rb.scan(/APP_TITLE = '(.+)'/)[0][0]&lt;br /&gt;EXE_NAME = main_rb.scan(/EXE_NAME = '(.+)'/)[0][0]&lt;br /&gt;EXE_BASENAME = EXE_NAME.gsub('.exe', '')&lt;br /&gt;APP_VERSION = main_rb.scan(/APP_VERSION = '(.+)'/)[0][0]&lt;br /&gt;&lt;br /&gt;# rake tasks:&lt;br /&gt;task :default =&gt; [:create_setup]&lt;br /&gt;&lt;br /&gt;desc "Create setup.exe"&lt;br /&gt;task :create_setup =&gt; [:move_exe, :modify_icon, :create_iss_script, &lt;br /&gt;    :edit_readme] do&lt;br /&gt;    puts "Creating setup.exe"&lt;br /&gt;    Dir.chdir(INSTALL_FOLDER)&lt;br /&gt;    system(ISCC, ISS_FILE)&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;desc "Create ISS script"&lt;br /&gt;task :create_iss_script =&gt; [:move_exe] do&lt;br /&gt;    puts "Creating ISS script"&lt;br /&gt;    Dir.chdir(INSTALL_FOLDER)&lt;br /&gt;    data = ISS_TEXT.gsub('[APP_TITLE]', APP_TITLE)&lt;br /&gt;    .gsub('[APP_VERSION]', APP_VERSION)&lt;br /&gt;    .gsub('[EXE_NAME]', EXE_NAME)&lt;br /&gt;    .gsub('[EXE_BASENAME]', EXE_BASENAME)&lt;br /&gt;    File.open(ISS_FILE, 'w') do |f|&lt;br /&gt;        f.puts(data)&lt;br /&gt;    end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;desc "Edit ReadMe.txt"&lt;br /&gt;task :edit_readme do&lt;br /&gt;    puts "Updating ReadMe.txt file"&lt;br /&gt;    Dir.chdir(INSTALL_FOLDER)&lt;br /&gt;    txt = nil&lt;br /&gt;    open(README_FILE) do |f|&lt;br /&gt;        txt = f.read&lt;br /&gt;    end&lt;br /&gt;    old_version = txt.scan(/Version (\d\d\.\d\d\.\d\d)/)[0][0]&lt;br /&gt;    txt = txt.gsub(old_version, APP_VERSION)&lt;br /&gt;    File.delete(README_FILE)&lt;br /&gt;    open(README_FILE, 'w') do |f|&lt;br /&gt;        f.puts(txt)&lt;br /&gt;    end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;desc "Modify EXE icon"&lt;br /&gt;task :modify_icon =&gt; [:move_exe] do&lt;br /&gt;    puts "Modifying EXE icon"&lt;br /&gt;    Dir.chdir (INSTALL_FOLDER)&lt;br /&gt;    arg = " -addoverwrite #{EXE_NAME}, #{EXE_NAME}, application.ico, &lt;br /&gt;          icongroup, appicon, 0"&lt;br /&gt;    system(RESHACKER + arg)&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;desc "Move EXE to install folder"&lt;br /&gt;task :move_exe =&gt; [:compile_code] do&lt;br /&gt;    puts "Moving EXE to install folder"&lt;br /&gt;    mv("#{LIB_FOLDER}/main.exe", "#{INSTALL_FOLDER}/#{EXE_NAME}")&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;desc "Compile code into EXE"&lt;br /&gt;task :compile_code do&lt;br /&gt;    puts "Compiling main.rb into EXE"&lt;br /&gt;    system("rubyscript2exe.cmd", "#{LIB_FOLDER}/main.rb")&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;# text of Inno Setup script:&lt;br /&gt;ISS_TEXT =&lt;&lt;-END_OF_ISS&lt;br /&gt;&lt;br /&gt;[Setup]&lt;br /&gt;AppName=[APP_TITLE]&lt;br /&gt;AppVerName=[APP_TITLE] version [APP_VERSION]&lt;br /&gt;AppPublisher=David L. Mullet&lt;br /&gt;AppPublisherURL=http://davidmulletcom&lt;br /&gt;AppContact= david.mullet@gmail.com&lt;br /&gt;AppVersion=[APP_VERSION]&lt;br /&gt;DefaultDirName=C:\\[APP_TITLE]&lt;br /&gt;DefaultGroupName=[APP_TITLE]&lt;br /&gt;UninstallDisplayIcon={app}\\[EXE_NAME]&lt;br /&gt;Compression=lzma&lt;br /&gt;SolidCompression=yes&lt;br /&gt;OutputDir=.&lt;br /&gt;OutputBaseFilename="[EXE_BASENAME]_Setup"&lt;br /&gt;&lt;br /&gt;[Files]&lt;br /&gt;Source: "[EXE_NAME]"; DestDir: "{app}"; Flags: ignoreversion&lt;br /&gt;Source: "Readme.txt"; DestDir: "{app}"; Flags: isreadme ignoreversion&lt;br /&gt;&lt;br /&gt;[Icons]&lt;br /&gt;Name: "{group}\\[APP_TITLE]"; Filename: "{app}\\[EXE_NAME]"; &lt;br /&gt;    WorkingDir: "{app}"&lt;br /&gt;Name: "{group}\\View ReadMe File"; Filename: "{app}\\ReadMe.txt"; &lt;br /&gt;    WorkingDir: "{app}"&lt;br /&gt;&lt;br /&gt;END_OF_ISS&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As always, post a comment here or send me an email with questions, comments, or suggestions.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;br /&gt;&lt;a href="http://digg.com/submit?phase=2&amp;url=http://rubyonwindows.blogspot.com/2007/09/using-rake-to-automate-windows-desktop.html"&amp;target="_blank"&gt;&lt;br /&gt;&lt;img border="0" alt="Digg my article" src="http://digg.com/img/badges/91x17-digg-button.gif"/&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-7896911168000156780?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/7896911168000156780/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=7896911168000156780' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/7896911168000156780'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/7896911168000156780'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/09/using-rake-to-automate-windows-desktop.html' title='Using Rake to Automate Windows Desktop App Builds'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-6426156006412807290</id><published>2007-09-23T08:23:00.000-05:00</published><updated>2007-09-23T10:17:56.338-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='tools'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Installing Your Ruby App with Inno Setup Installer</title><content type='html'>In &lt;a href="http://rubyonwindows.blogspot.com/2007/09/compiling-your-ruby-app-with.html"&gt;my last article&lt;/a&gt;, we looked at 'compiling' your Ruby app into a single portable executable file using RubyScript2Exe.&lt;br /&gt;&lt;br /&gt;As a reader commented, "For windows it becomes so important to make things 'one click'. I guess the next step would be to also wrap this in some kind of simple installer script to store it in the right place and add the appropriate menu/desktop icons."&lt;br /&gt;&lt;br /&gt;Which brings us to the topic of today's discussion: using &lt;a href="http://www.jrsoftware.org/isinfo.php"&gt;Inno Setup&lt;/a&gt; to create an Install program (ie, setup.exe) for your Ruby applications.&lt;br /&gt;&lt;br /&gt;Inno Setup, created, by Jordan Russell, has been around for about a decade, is very well documented, and frequently updated. It's very customizable and extendable, and allows you to create professional installation packages that will allow you to create directories, install files to multiple locations (install folder, Windows system folder, etc.), create Start menu Program groups and icons, and place shortcut icons on the user's Desktop and Quick Launch bar. And it's free of charge.&lt;br /&gt;&lt;br /&gt;Inno Setup creates your Setup.exe file based on parameters that you define in a text-based script. Your ISS script file is divided into sections, with parameter=value pairs, much like a Windows INI file. Starting out, you'll probably use the Inno Setup user interface to create and edit your scripts, and then to compile your installation package into a setup.exe and run it.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_9LH5hSdA01Y/RvZ9UqDrdAI/AAAAAAAAAA0/1yw2CXITMwg/s1600-h/inno_setup1.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://2.bp.blogspot.com/_9LH5hSdA01Y/RvZ9UqDrdAI/AAAAAAAAAA0/1yw2CXITMwg/s400/inno_setup1.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5113412220766352386" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Section names may include &lt;span style="font-weight:bold;"&gt;[Setup]&lt;/span&gt;, &lt;span style="font-weight:bold;"&gt;[Files]&lt;/span&gt;, &lt;span style="font-weight:bold;"&gt;[Icons]&lt;/span&gt;, and &lt;span style="font-weight:bold;"&gt;[Code]&lt;/span&gt;. Inno Setup includes a comprehensive help file detailing all of the options, and there is an extensive FAQ document on the website.&lt;br /&gt;&lt;br /&gt;A typical ISS script might look something like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;[Setup]&lt;br /&gt;AppName=My Ruby Application&lt;br /&gt;AppVerName=My Ruby Application version 09.23.07&lt;br /&gt;AppPublisher=David Mullet&lt;br /&gt;AppPublisherURL=http://davidmullet.com&lt;br /&gt;AppContact=david@davidmullet.com&lt;br /&gt;AppVersion=09.23.07&lt;br /&gt;DefaultDirName=C:\My Ruby Application&lt;br /&gt;DefaultGroupName=My Ruby Application&lt;br /&gt;UninstallDisplayIcon={app}\MyRubyApp.exe&lt;br /&gt;Compression=lzma&lt;br /&gt;SolidCompression=yes&lt;br /&gt;&lt;br /&gt;[Files]&lt;br /&gt;Source: "MyRubyApp.exe"; DestDir: "{app}"; Flags: ignoreversion&lt;br /&gt;Source: "ReadMe.txt"; DestDir: "{app}"; Flags: isreadme ignoreversion&lt;br /&gt;&lt;br /&gt;[Icons]&lt;br /&gt;Name: "{group}\My Ruby Application"; Filename: "{app}\MyRubyApp.exe"; WorkingDir: "{app}"&lt;br /&gt;Name: "{group}\View ReadMe File"; Filename: "{app}\ReadMe.txt"; WorkingDir: "{app}"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Inno Setup is highly customizable. You can, for example, customize the install wizard, or include a [Code] section with Pascal procedures (see the Help file for details and examples). The scripts are straight text, and the compiler can be called from the command line, so you can create and compile your ISS script all within Ruby, if you like, such as in a Rakefile. I'll include just such an example in an upcoming article.&lt;br /&gt;&lt;br /&gt;So, you can now create a single executable file for your Ruby application and use a professional-looking installer to distribute it.&lt;br /&gt;&lt;br /&gt;Questions? Comments? Suggestions? Post a comment here or send me an email message.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;br /&gt;&lt;a href="http://digg.com/submit?phase=2&amp;url=http://rubyonwindows.blogspot.com/2007/09/installing-your-ruby-app-with-inno.html"&amp;target="_blank"&gt;&lt;br /&gt;&lt;img border="0" alt="Digg my article" src="http://digg.com/img/badges/91x17-digg-button.gif"/&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-6426156006412807290?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/6426156006412807290/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=6426156006412807290' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/6426156006412807290'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/6426156006412807290'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/09/installing-your-ruby-app-with-inno.html' title='Installing Your Ruby App with Inno Setup Installer'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_9LH5hSdA01Y/RvZ9UqDrdAI/AAAAAAAAAA0/1yw2CXITMwg/s72-c/inno_setup1.jpg' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-8109921215446771721</id><published>2007-09-18T22:43:00.000-05:00</published><updated>2007-09-23T08:14:38.168-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='tools'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Compiling Your Ruby App with RubyScript2Exe</title><content type='html'>I've mentioned RubyScript2Exe &lt;a href="http://rubyonwindows.blogspot.com/2007/02/distributing-ruby-apps-on-windows.html"&gt;previously&lt;/a&gt;. This tool allows you to 'compile' a script/application into a portable executable file (EXE) that you can easily provide to your users without requiring them to install Ruby and the required libraries. RubyScript2Exe traces and gathers all the necessary files, including the Ruby interpreter, and 'compiles' them into a single EXE file. You can easily embed images and icon files, and DLLs such as SQLite.&lt;br /&gt;&lt;br /&gt;I put the word 'compile' in quotes above because RubyScript2Exe does not transform your code as a C compiler or .Net compiler would. Rather, it collects all the files necessary to run your application and bundles them into a single EXE file. When the user runs that EXE file, that bundle is quickly extracted to a temporary file and your Ruby code is executed.&lt;br /&gt;&lt;br /&gt;Installing RubyScript2Exe is as easy as falling off a log, thanks to RubyGems. Just get to a command prompt and enter:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;gem install rubyscript2exe&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Include the following &lt;b&gt;require&lt;/b&gt; statement at the top of your script:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'rubyscript2exe'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Whenever possible, include all your &lt;b&gt;require&lt;/b&gt; statements at the top of your script. This ensures that RubyScript2Exe successfully traces and includes all the necessary files your application will need.&lt;br /&gt;&lt;br /&gt;If you are compiling a non-console script and therefore want to use the rubyw.exe interpreter, rather than the ruby.exe interpreter, include the following module variable near the top of your script:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;RUBYSCRIPT2EXE.rubyw = true&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;When I use the &lt;a href="http://ruby.netbeans.org"&gt;NetBeans Ruby IDE&lt;/a&gt;, which defaults a new project's main script name to "main.rb", I include the above code to avoid renaming the script with a ".rbw" extension or providing command-line parameters to the RubyScript2Exe compiler. More on that later.&lt;br /&gt;&lt;br /&gt;A Ruby Forum reader and RubyScript2Exe user recently mentioned "I want to be able to wrap the icon file along with the rest of the application." You can embed additional files such as icons or DLLs in the executable like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;RUBYSCRIPT2EXE.bin = ["my_icon.ico", "sqlite3.dll"]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;When you run your compiled executable, RubyScript2Exe extracts all the files from your executable into a temporary directory. But sometimes you need to know the location of the folder the executable was originally run from. Just call the &lt;b&gt;RUBYSCRIPT2EXE.exedir&lt;/b&gt; method:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;APPLICATION_PATH = RUBYSCRIPT2EXE.exedir&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Enough preparation! Let's compile our application. Go to a command prompt and enter:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;rubyscript2exe my_script.rb&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...or...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;rubyscript2exe my_script.rbw&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If your script has a filename extension of &lt;span style="font-weight:bold;"&gt;.rb&lt;/span&gt;, RubyScript2Exe will include the ruby.exe interpreter and a console window. If your script has a filename extension of &lt;span style="font-weight:bold;"&gt;.rbw&lt;/span&gt;, RubyScript2Exe will include the rubyw.exe interpreter and your app will therefore not have a console window; this is the same as if you had included &lt;b&gt;RUBYSCRIPT2EXE.rubyw = true&lt;/b&gt; in your code.&lt;br /&gt;&lt;br /&gt;The size of your compiled executable can vary widely depending on what files are needed to be included. A simple console app may be 1mb in size, a wxRuby 0.6 GUI app may be 3-4mb, and a wxRuby 2 GUI app may be 6-8mb in size. Part of this size is due to a known 'bug' that may cause some files (the 8mb wxRuby2.so file, for example) to be included twice. This affects the size of the EXE file but not the performance.&lt;br /&gt;&lt;br /&gt;There you have it. But this post just scratches the surface. RubyScript2Exe's creator, Erik Veenstra, has done a great job maintaining and documenting this tool, and you should take a few minutes and read the docs &lt;a href="http://www.erikveen.dds.nl/rubyscript2exe/index.html"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Questions? Comments? Suggestions?&lt;br /&gt;&lt;br /&gt;Post a comment here or send me an email.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;br /&gt;&lt;a href="http://digg.com/submit?phase=2&amp;url=http://rubyonwindows.blogspot.com/2007/09/compiling-your-ruby-app-with.html"&amp;target="_blank"&gt;&lt;br /&gt;&lt;img border="0" alt="Digg my article" src="http://digg.com/img/badges/91x17-digg-button.gif"/&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-8109921215446771721?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/8109921215446771721/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=8109921215446771721' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/8109921215446771721'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/8109921215446771721'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/09/compiling-your-ruby-app-with.html' title='Compiling Your Ruby App with RubyScript2Exe'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-7178578894583230642</id><published>2007-09-14T18:21:00.001-05:00</published><updated>2007-09-14T20:23:39.734-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='excel'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Ruby &amp; Excel: 911 Characters Only, Please</title><content type='html'>I was writing some code this week to &lt;a href="http://rubyonwindows.blogspot.com/2007/03/ruby-ado-and-sqlserver.html"&gt;query an SQL Server database&lt;/a&gt; and write the results to an &lt;a href="http://rubyonwindows.blogspot.com/search/label/excel"&gt;Excel&lt;/a&gt; workbook. &lt;br /&gt;&lt;br /&gt;I was &lt;a href="http://rubyonwindows.blogspot.com/2007/04/automating-excel-with-ruby-defining.html"&gt;defining a range in an Excel worksheet&lt;/a&gt; and inserting data using code something like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;worksheet.Range("A2").Resize(data.size, data[0].size).Value = data&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The above code defines a range starting at cell A2 and extending the width and length of my 2-dimensional array, then inserts my array into that range of cells.&lt;br /&gt;&lt;br /&gt;This worked great -- about 95 percent of the time. But occasionally it would bomb. My debugging uncovered the fact that it would always and only bomb if a string exceeding 911 characters was inserted into a cell.&lt;br /&gt;&lt;br /&gt;Some quick googling revealed that this is a known Excel 2003 bug, &lt;a href="http://support.microsoft.com/kb/818808"&gt;acknowledged by Microsoft&lt;/a&gt;. Microsoft's suggested workaround is "don't do that"; don't try to insert more than 911 characters into a cell as part of an array. Gee, thanks.&lt;br /&gt;&lt;br /&gt;I was using the method above because it is significantly faster than inserting data one cell at a time. This isn't a big deal for 100 lines x 10 columns of data, but when you're inserting a million cells of data, there's a huge time difference. I could successfully insert the data one cell at a time, but my users might have to take extended coffee breaks. &lt;br /&gt;&lt;br /&gt;I could write the data to a tab-delimited, comma-delimited, or XML text file; then open it in Excel and tweak it if necessary. But I hoped to avoid that if possible.&lt;br /&gt;&lt;br /&gt;In case you're wondering, I resolved it with a compromise. Rather than insert the data all at once or cell-by-cell, I inserted it row-by-row:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;worksheet.Range("A#{r + 2}").Resize(1, row.size).Value = row&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This will still raise an exception when attempting to insert more than 911 characters into a cell. But we'll handle those exceptions by inserting that row's data cell-by-cell.&lt;br /&gt;&lt;br /&gt;The revised code looks something like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;data.each_with_index do |row, r|&lt;br /&gt;    begin&lt;br /&gt;        # try to insert the entire array into the row&lt;br /&gt;        worksheet.Range("A#{r + 2}").Resize(1, row.size).Value = row&lt;br /&gt;    rescue&lt;br /&gt;        # if exception, then insert each cell individually&lt;br /&gt;        row.each_with_index do |field, c|&lt;br /&gt;            worksheet.Cells(r + 2, c + 1).Value = field&lt;br /&gt;        end&lt;br /&gt;    end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;It's not as fast as an all-at-once insert, but much faster than a cell-by-cell insert.&lt;br /&gt;&lt;br /&gt;By the way, the '+1' offset for column number is because Ruby arrays have zero-based indexes, while Excel's column indexes are 1-based. And the '+2' offset for row number is for the same reason, plus 1 to skip the first row of the worksheet, which contains the field names.&lt;br /&gt;&lt;br /&gt;So, insert data into worksheets as arrays when you can. But don't let the 911-Characters Bug bite.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-7178578894583230642?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/7178578894583230642/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=7178578894583230642' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/7178578894583230642'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/7178578894583230642'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/09/ruby-excel-911-characters-only-please.html' title='Ruby &amp; Excel: 911 Characters Only, Please'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-6606954361436828014</id><published>2007-09-12T18:30:00.000-05:00</published><updated>2007-09-12T19:24:31.324-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='tools'/><title type='text'>NetBeans Ruby IDE</title><content type='html'>If you're looking for a new Ruby editor, I suggest you check out NetBeans. &lt;a href="http://blogs.sun.com/tor/"&gt;Tor Norbye&lt;/a&gt; and the gang have been doing a great job adapting this Java IDE for use with Ruby and Rails.&lt;br /&gt;&lt;br /&gt;I usually use &lt;a href="http://www.scintilla.org/SciTE.html"&gt;SciTE, the Scintilla text editor&lt;/a&gt;, for writing my Ruby code. It's fast, flexible, and lightweight, consuming a fraction of the resources of a full-blown IDE. It's still my editor of choice for small scripts. But I've begun using NetBeans for larger projects and am very pleased so far.&lt;br /&gt;&lt;br /&gt;NetBeans is now available in a Ruby-only version which, presumably, is a little slimmer than the full Java + Ruby IDE. And you can trim a little more fat by deactivating a few plug-ins. On my Windows XP systems, memory usage is in the 80-120Mb range. That's acceptable, even on my more memory-challenged machine, when you consider the potential productivity gains offered by the IDE features. And the fact that it's free is certainly a plus.&lt;br /&gt;&lt;br /&gt;Further details can be found on the &lt;a href="http://ruby.netbeans.org"&gt;NetBeans Ruby Support page&lt;/a&gt;, on the &lt;a href="http://wiki.netbeans.org/wiki/view/Ruby"&gt;NetBeans Ruby wiki&lt;/a&gt;, and in numerous in-depth reviews like &lt;a href="http://www.javalobby.org/java/forums/t100988.html"&gt;this one by Daniel Spiewak&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;New builds are posted every few hours, and the latest stable build can always be found &lt;a href="http://deadlock.netbeans.org/hudson/job/ruby/lastStableBuild"&gt;here&lt;/a&gt;. I usually replace my nbrubyide folder every day or two.&lt;br /&gt;&lt;br /&gt;So if you're looking for a full-featured Ruby IDE, take NetBeans for a test drive and see if it meets your needs. It may be a dumb name, but it's a good Ruby IDE.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-6606954361436828014?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/6606954361436828014/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=6606954361436828014' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/6606954361436828014'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/6606954361436828014'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/09/netbeans-ruby-ide.html' title='NetBeans Ruby IDE'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-7269565287317223638</id><published>2007-09-09T12:52:00.000-05:00</published><updated>2007-09-09T13:15:58.039-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><title type='text'>RubyConf 2007 Comes to Charlotte</title><content type='html'>Somehow, I had missed the fact that RubyConf 2007 will be held here in Charlotte, North Carolina (USA) November 2-4.&lt;br /&gt;&lt;br /&gt;Fortunately, a friend made me aware of this fact in time for me to register to attend.&lt;br /&gt;&lt;br /&gt;Lots of good stuff is on the &lt;a href="http://www.rubyconf.org/agenda.html"&gt;agenda&lt;/a&gt;. Here's a couple talks that caught my eye as a Windows desktop developer:&lt;br /&gt;&lt;br /&gt;John Lam on &lt;i&gt;The State of IronRuby&lt;/i&gt;&lt;br /&gt;Bruce Williams on &lt;i&gt;Writing Client and Desktop Applications in Ruby&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Registration is &lt;a href="http://www.regonline.com/rubyconf2007"&gt;still open&lt;/a&gt;, but I hear that seats are going fast.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-7269565287317223638?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/7269565287317223638/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=7269565287317223638' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/7269565287317223638'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/7269565287317223638'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/09/rubyconf-2007-comes-to-charlotte.html' title='RubyConf 2007 Comes to Charlotte'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-2286098758884985472</id><published>2007-08-28T20:50:00.000-05:00</published><updated>2007-09-07T19:44:51.533-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='outlook'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Automating Outlook with Ruby: Inbox &amp; Messages</title><content type='html'>In response to my &lt;a href="http://rubyonwindows.blogspot.com/search/label/outlook"&gt;series on Automating Outlook with Ruby&lt;/a&gt;, several readers have asked about accessing the Inbox and managing messages.&lt;br /&gt;&lt;br /&gt;We start by using the win32ole library to create a new instance (or connect to a currently running instance) of the Outlook application object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;outlook = WIN32OLE.new('Outlook.Application')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Next we'll get the MAPI namespace:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;mapi = outlook.GetNameSpace('MAPI')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To get a reference to the Inbox folder, call the MAPI object's &lt;b&gt;GetDefaultFolder&lt;/b&gt; method, passing it the integer 6, which represents the Inbox folder:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;inbox = mapi.GetDefaultFolder(6)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To get the Personal Folders object, call the MAPI object's &lt;b&gt;Folders.Item&lt;/b&gt; method, passing it the name of the folder.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;personal_folders = mapi.Folders.Item('Personal Folders')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To reference a subfolder, call the parent folder object's &lt;b&gt;Folders.Item&lt;/b&gt; method, passing it the name of the folder.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;baseball_folder = personal_folders.Folders.Item('Baseball')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You can get a count of a folder's unread items by calling the &lt;b&gt;UnreadItemCount&lt;/b&gt; method:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;puts "#{inbox.UnreadItemCount} unread messages"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;A folder object's &lt;b&gt;Items&lt;/b&gt; method returns a collection of message objects, which you can iterate over:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;inbox.Items.each do |message|&lt;br /&gt;    # Your code here...&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You can also pass the Items method a (1-based) index to retrieve a single message:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;first_message = inbox.Items(1)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Once your code tries to access methods and properties of a Message object, an Outlook security dialog will prompt the user to allow access to Address Book entries. The user must click a checkbox to allow access and select a time limit between 1 and 10 minutes.&lt;br /&gt;&lt;br /&gt;Message objects have dozens of methods/properties, including:&lt;br /&gt;&lt;br /&gt;SenderEmailAddress&lt;br /&gt;SenderName&lt;br /&gt;To&lt;br /&gt;Cc&lt;br /&gt;Subject&lt;br /&gt;Body&lt;br /&gt;&lt;br /&gt;To see a complete list, you could call the &lt;b&gt;ole_methods&lt;/b&gt; method, as explained &lt;a href="http://rubyonwindows.blogspot.com/2007/04/ruby-win32ole-inspecting-objects.html"&gt;here&lt;/a&gt;. So to view a sorted list of the Message object's methods and properties, we could do this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;methods = []&lt;br /&gt;inbox.Items(1).ole_methods.each do |method|&lt;br /&gt;    methods &lt;&lt; method.to_s&lt;br /&gt;end&lt;br /&gt;puts methods.uniq.sort&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To delete a message, call its &lt;b&gt;Delete&lt;/b&gt; method:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;message.Delete&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To move a message to another folder, call its &lt;b&gt;Move&lt;/b&gt; method, passing it the folder object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;baseball_folder = personal_folders.Folders.Item('Baseball')&lt;br /&gt;message.Move(baseball_folder)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So, if we wanted to check our Inbox and move all messages that contain 'Cardinals' in the subject line to our 'Baseball' folder, we could do something like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;inbox.Items.Count.downto(1) do |i|&lt;br /&gt;    message = inbox.Items(i)&lt;br /&gt;    if message.Subject =~ /cardinals/i&lt;br /&gt;        message.Move(baseball_folder)&lt;br /&gt;    end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As reader Ana points out, we should use the &lt;b&gt;Count&lt;/b&gt; and &lt;b&gt;downto&lt;/b&gt; methods to ensure that our inbox.Items index stays in sync, even as we move messages out of the Inbox. Otherwise, we run the risk of a message being skipped when the message above it is moved or deleted.&lt;br /&gt;&lt;br /&gt;That concludes our program for today. Feel free to post a comment here or send me email with questions, comments, or suggestions.&lt;br /&gt;&lt;a href="http://digg.com/submit?phase=2&amp;url=http://rubyonwindows.blogspot.com/2007/08/automating-outlook-with-ruby-inbox.html"&amp;target="_blank"&gt;&lt;br /&gt;&lt;img border="0" alt="Digg my article" src="http://digg.com/img/badges/91x17-digg-button.gif"/&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-2286098758884985472?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/2286098758884985472/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=2286098758884985472' title='13 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/2286098758884985472'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/2286098758884985472'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/08/automating-outlook-with-ruby-inbox.html' title='Automating Outlook with Ruby: Inbox &amp; Messages'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>13</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-3348217251067732422</id><published>2007-08-26T16:52:00.000-05:00</published><updated>2007-08-26T17:36:22.253-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='outlook'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Automating Outlook with Ruby: Address Books</title><content type='html'>We recently discussed &lt;a href="http://rubyonwindows.blogspot.com/2007/08/automating-outlook-with-ruby-contacts.html"&gt;working with Outlook Contacts&lt;/a&gt;. On a related note, a reader asked how to access the Outlook Address Books. So let's dive right in...&lt;br /&gt;&lt;br /&gt;We start by using the win32ole library to create a new instance (or connect to a currently running instance) of the Outlook application object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;outlook = WIN32OLE.new('Outlook.Application')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Next we'll get the MAPI namespace:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;mapi = outlook.GetNameSpace('MAPI')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The MAPI object's &lt;b&gt;Session.AddressLists&lt;/b&gt; method, when called without parameters,  returns a collection of the various Address Books available to Outlook, such as "Contacts", "Personal Address Book", and "Global Address List". The following code iterates over this collection and prints the name of each available Address Book:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;mapi.Session.AddressLists.each do |list|&lt;br /&gt;    puts list.Name&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To work with a particular Address Book, call the MAPI object's &lt;b&gt;Session.AddressLists&lt;/b&gt; method and pass it the name of the Address Book:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;address_list = mapi.Session.AddressLists('Global Address List')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This returns a single AddressList object. Call this object's &lt;b&gt;AddressEntries&lt;/b&gt; method to obtain a list of AddressEntry objects:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;address_entries = address_list.AddressEntries&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Upon executing the above line of code, an Outlook security dialog will prompt the user to allow access to Address Book entries. The user must click a checkbox to allow access and select a time limit between 1 and 10 minutes.&lt;br /&gt;&lt;br /&gt;Once access to the Address Book entries has been granted, you can iterate over the AddressEntries collection and get the Name and Address for each entry, or only for entries that meet certain criteria:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;address_entries.each do |address|&lt;br /&gt;    if address.Name =~ /Machiavelli/&lt;br /&gt;        name = address.Name&lt;br /&gt;        email_address = address.Address&lt;br /&gt;    end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To search for an Address, you could call the AddressEntries collection's &lt;b&gt;Item&lt;/b&gt; method. This accepts an integer as a (1-based) index:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;address_entry = address_entries.Item(27)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...but also accepts a text string:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;address_entry = address_entries.Item("sinatra, f")&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This seems to return only the first AddressEntry object that meets the text string criteria. If no AddressEntry meets the text criteria, it seems to return the closest item following. So don't assume that the object returned meets your search criteria -- check the Name and/or Address properties to be certain.&lt;br /&gt;&lt;br /&gt;There you have it. As always, feel free to post a comment here or send me email with questions, comments, or suggestions.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;br /&gt;&lt;a href="http://digg.com/submit?phase=2&amp;url=http://rubyonwindows.blogspot.com/2007/08/automating-outlook-with-ruby-address.html"&amp;target="_blank"&gt;&lt;br /&gt;&lt;img border="0" alt="Digg my article" src="http://digg.com/img/badges/91x17-digg-button.gif"/&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-3348217251067732422?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/3348217251067732422/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=3348217251067732422' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/3348217251067732422'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/3348217251067732422'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/08/automating-outlook-with-ruby-address.html' title='Automating Outlook with Ruby: Address Books'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-2317428263854557496</id><published>2007-08-22T20:17:00.000-05:00</published><updated>2007-08-22T20:29:51.581-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tools'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Windows Developer Power Tools</title><content type='html'>While taking a brief respite from tutorialism and browsing the stacks at Barnes &amp; Noble recently, I stumbled across &lt;i&gt;&lt;a href="http://www.amazon.com/Windows-Developer-Power-Tools-Turbocharge/dp/0596527543/ref=pd_bbs_sr_1/002-8138837-4432832?ie=UTF8&amp;s=books&amp;qid=1187829354&amp;sr=1-1"&gt;Windows Developer Power Tools&lt;/a&gt;&lt;/i&gt;, an O'Reilly book by James Avery and Jim Holmes that covers over 170 free tools for Windows developers.&lt;br /&gt;&lt;br /&gt;I've only scratched the surface thus far, but have already begun using several of the tools mentioned in the book, including &lt;a href="http://sqliteadmin.orbmu2k.de/"&gt;SQLite Administrator&lt;/a&gt;, &lt;a href="http://www.icsharpcode.net/OpenSource/SD/"&gt;SharpDevelop&lt;/a&gt;, and &lt;a href="http://www.sliver.com/dotnet/SnippetCompiler/"&gt;Snippet Compiler&lt;/a&gt;. Some tools are .NET-specific, but many more are not.&lt;br /&gt;&lt;br /&gt;The companion web site is &lt;a href="http://www.windevpowertools.com"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;If you work with SQLite, &lt;a href="http://rubyonwindows.blogspot.com/2007/03/ruby-excel-and-sqlite-instead-of-ms.html"&gt;my personal favorite database&lt;/a&gt;, you'll probably find &lt;a href="http://sqliteadmin.orbmu2k.de/"&gt;SQLite Administrator&lt;/a&gt; quite helpful. It provides a GUI front end that allows you to create, design, and manage SQLite database files. Check out the website for full details and screenshots.&lt;br /&gt;&lt;a href="http://digg.com/submit?phase=2&amp;url=http://rubyonwindows.blogspot.com/2007/08/windows-developer-power-tools.html"&amp;target="_blank"&gt;&lt;br /&gt;&lt;img border="0" alt="Digg my article" src="http://digg.com/img/badges/91x17-digg-button.gif"/&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-2317428263854557496?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/2317428263854557496/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=2317428263854557496' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/2317428263854557496'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/2317428263854557496'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/08/windows-developer-power-tools.html' title='Windows Developer Power Tools'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-7049495064065021323</id><published>2007-08-15T21:13:00.000-05:00</published><updated>2007-08-15T21:26:25.703-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='outlook'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Automating Outlook with Ruby: Contacts</title><content type='html'>We've looked at &lt;a href="http://rubyonwindows.blogspot.com/2007/07/automating-outlook-with-ruby-sending.html"&gt;sending email&lt;/a&gt;, &lt;a href="http://rubyonwindows.blogspot.com/2007/07/automating-outlook-with-ruby-calendar.html"&gt;working with calendar appointments&lt;/a&gt;, and &lt;a href="http://rubyonwindows.blogspot.com/2007/07/automating-outlook-with-ruby-tasks.html"&gt;creating and managing tasks&lt;/a&gt; with Outlook. Next we'll take a look at how to create new contacts and read contacts data from Outlook.&lt;br /&gt;&lt;br /&gt;You know by now how this starts out: we'll use the win32ole library to create a new instance (or connect to a currently running instance) of the Outlook application object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;outlook = WIN32OLE.new('Outlook.Application')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Next we'll get the MAPI namespace:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;mapi = outlook.GetNameSpace('MAPI')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;Creating a New Contact&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;To create a new contact, call the Outlook Application object's &lt;b&gt;CreateItem&lt;/b&gt; method, passing it the number 2, which represents a Contact Item. This returns a new Contact object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;contact = outlook.CreateItem(2)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Then set values for various properties of the new Contact object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;contact.FullName = 'Stan Musial'&lt;br /&gt;contact.CompanyName = 'St. Louis Cardinals'&lt;br /&gt;contact.JobTitle = 'Hitter'&lt;br /&gt;contact.BusinessTelephoneNumber = '(314)555-1234'&lt;br /&gt;contact.Email1Address  = 'stan_the_man@stlcardinals.com'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;What properties are available to set? Dozens. To see a list, you could call &lt;a href="http://rubyonwindows.blogspot.com/2007/04/ruby-win32ole-inspecting-objects.html"&gt;contact.ole_methods&lt;/a&gt;, Google for it (Outlook Contact Properties), or use an OLE object browser.&lt;br /&gt;&lt;br /&gt;When you've completed setting property values, call the Contact object's Save method:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;contact.Save&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;Getting Existing Contacts Data&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;To obtain a collection of Contacts, call the MAPI object's &lt;b&gt;GetDefaultFolder&lt;/b&gt; method, passing it the integer 10, which represents the Contacts folder. Then call the &lt;b&gt;Items&lt;/b&gt; method on this object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;contacts = mapi.GetDefaultFolder(10).Items&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You can now iterate over this collection of Contact objects, calling the methods/properties to get the data you need:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;contacts.each do |contact|&lt;br /&gt;    puts contact.FullName&lt;br /&gt;    puts contact.Email1Address&lt;br /&gt;    puts contact.BusinessTelephoneNumber&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;For further information, check out these Microsoft TechNet articles:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.microsoft.com/technet/scriptcenter/resources/officetips/apr05/tips0426.mspx"&gt;Creating a New Contact in Microsoft Outlook&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.microsoft.com/technet/scriptcenter/resources/officetips/may05/tips0517.mspx"&gt;Exporting Contact Information&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;As always, feel free to post a comment here or send me email with questions, comments, or suggestions.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;br /&gt;&lt;br /&gt;&lt;a href="http://digg.com/submit?phase=2&amp;url=http://rubyonwindows.blogspot.com/2007/08/automating-outlook-with-ruby-contacts.html"&amp;target="_blank"&gt;&lt;br /&gt;&lt;img border="0" alt="Digg my article" src="http://digg.com/img/badges/91x17-digg-button.gif"/&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-7049495064065021323?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/7049495064065021323/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=7049495064065021323' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/7049495064065021323'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/7049495064065021323'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/08/automating-outlook-with-ruby-contacts.html' title='Automating Outlook with Ruby: Contacts'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-7909385862009872380</id><published>2007-07-29T19:00:00.000-05:00</published><updated>2007-07-29T19:38:25.342-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><category scheme='http://www.blogger.com/atom/ns#' term='wmp'/><title type='text'>Automating Windows Media Player with Ruby</title><content type='html'>My recent article about automating iTunes has resulted in several requests for a similar article about Windows Media Player (WMP). And so, without further ado...&lt;br /&gt;&lt;br /&gt;We start by using the &lt;b&gt;win32ole&lt;/b&gt; library to connect to the WMPlayer object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;player = WIN32OLE.new('WMPlayer.OCX')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Note that this will not display the WMP user interface. That happens next.&lt;br /&gt;&lt;br /&gt;To play a song, call the player object's &lt;b&gt;OpenPlayer&lt;/b&gt; method, passing it the path to the song file:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;player.OpenPlayer('c:\music\van halen\right now.wma')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;The Media Collection&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Next, we'll grab the MediaCollection object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;media_collection = player.mediaCollection&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To quote Microsoft, "The MediaCollection object represents all the items stored in the Windows Media Player media collection. You will typically query the MediaCollection object to return collections of media items."&lt;br /&gt;&lt;br /&gt;The MediaCollection object's &lt;b&gt;getAll&lt;/b&gt; method returns a collection of all items in the library:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;all_media = media_collection.getAll()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The &lt;b&gt;getByAttribute&lt;/b&gt; method can be used to get a collection of all audio files:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;audio_media = media_collection.getByAttribute("MediaType", "Audio")&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To get a collection of songs by a specific artist, use the &lt;b&gt;getByAuthor&lt;/b&gt; method:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;sinatra_songs = media_collection.getByAuthor("Frank Sinatra")&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You can also get items by Album:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;album = media_collection.getByAlbum('Come Fly with Me')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Or by Genre:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;jazz_tunes = media_collection.getByGenre('Jazz')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And, of course, you can get a collection of items by name:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;songs = media_collection.getByName('Fly Me to the Moon')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Note that this will return &lt;i&gt;a collection of items&lt;/i&gt;, even if it contains only one item. You reference a specific item by calling the collection's &lt;b&gt;Item&lt;/b&gt; method, passing it a (zero-based) index value. So to get the first item:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;first_song = songs.Item(0)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As mentioned earlier, to play a song, call the player object's &lt;b&gt;OpenPlayer&lt;/b&gt; method, passing it the song object's &lt;b&gt;sourceURL&lt;/b&gt; property. sourceURL is simply the path to the song file:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;player.OpenPlayer(first_song.sourceURL)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To add a song to your Media Collection, call the MediaCollection object's &lt;b&gt;Add&lt;/b&gt; method, passing it the path and filename:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;song = media_collection.Add('C:\music\Just in Time.wma')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To delete a song, from the Media Collection, call the MediaCollection object's &lt;b&gt;Remove&lt;/b&gt; method, passing it the song object and the boolean value 'true':&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;songs = media_collection.getByName('Fly Me to the Moon')&lt;br /&gt;media_collection.Remove(songs.Item(0), true)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You may need to restart the Media Player user interface to see additions and removals.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The PlayLists Collection&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;PlayLists are managed via the player's &lt;b&gt;PlaylistCollection&lt;/b&gt; object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;playlists = player.PlaylistCollection&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The PlaylistCollection object has methods similar to the MediaCollection object. Its &lt;b&gt;getAll&lt;/b&gt; method returns a collection of all PlayList objects:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;all_playlists = playlists.getAll()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Its &lt;b&gt;getByName&lt;/b&gt; method returns a collection of PlayList objects by name:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;split_enz_playlist = playlists.getByName('Split Enz')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Remember, this returns a collection, even if that collection contains a single item. So to get the first (and possibly only) item, we call the Item method on the returned collection:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;split_enz_playlist.Item(0)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;A PlayList is a collection of Song items, just like those returned by the MediaCollection object's methods. You can call each item in the collection by index. So to print the name of each song in our playlist:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(0..my_playlist.Count - 1).each do |i|&lt;br /&gt;    song = my_playlist.Item(i)&lt;br /&gt;    puts song.Name&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To create a new playlist, call the PlaylistCollection object's &lt;b&gt;newPlaylist&lt;/b&gt; method, passing it the name of the new playlist:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;playlists = player.PlaylistCollection&lt;br /&gt;playlists.newPlaylist('New Playlist')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This creates a .wpl playlist file. You then have to add the new playlist file to the MediaCollection object, by calling the &lt;b&gt;Add&lt;/b&gt; method and passing it the path and name of the newly created PlayList file:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;media_collection.Add('D:\Music\My Playlists\New Playlist.wpl')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The path to the playlist file will be defined in the 'Rip music to this location' setting in Media Player's options.&lt;br /&gt;&lt;br /&gt;To remove a playlist, call the PlaylistCollection object's &lt;b&gt;Remove&lt;/b&gt; method, passing it the name of the PlayList object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;playlists = player.PlaylistCollection&lt;br /&gt;split_enz_playlist = playlists.getByName('Split Enz').Item(0)&lt;br /&gt;playlists.Remove(split_enz_playlist)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To add a song to a playlist, call the PlayList object's &lt;b&gt;appendItem&lt;/b&gt; method, passing it the song object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;song = media_collection.getByName('Fly Me to the Moon').Item(0)&lt;br /&gt;playlist = playlists.getByName('Frank &amp; Dino').Item(0)&lt;br /&gt;playlist.appendItem(song)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To remove a song from a playlist, call the PlayList object's &lt;b&gt;removeItem&lt;/b&gt; method, passing it the song object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;playlist.removeItem(song)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Playing a playlist is much like playing an individual song. First get the individual PlayList object, then pass its &lt;b&gt;sourceURL&lt;/b&gt; value to the player object's &lt;b&gt;OpenPlayer&lt;/b&gt; method:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;playlist = playlists.getByName('Frank &amp; Dino').Item(0)&lt;br /&gt;player.OpenPlayer(playlist.sourceURL)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And there you have it.&lt;br /&gt;&lt;br /&gt;Further details can be found in &lt;a href="http://www.microsoft.com/technet/scriptcenter/funzone/player.mspx"&gt;this Microsoft TechNet article&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;As always, feel free to post a comment or send email with questions, comments, or suggestions.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;br /&gt;&lt;a href="http://digg.com/submit?phase=2&amp;url=http://rubyonwindows.blogspot.com/2007/07/automating-windows-media-player-with.html"&amp;target="_blank"&gt;&lt;br /&gt;&lt;img border="0" alt="Digg my article" src="http://digg.com/img/badges/91x17-digg-button.gif"/&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-7909385862009872380?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/7909385862009872380/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=7909385862009872380' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/7909385862009872380'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/7909385862009872380'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/07/automating-windows-media-player-with.html' title='Automating Windows Media Player with Ruby'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-321579227241359716</id><published>2007-07-22T10:36:00.000-05:00</published><updated>2007-07-22T18:20:50.394-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><category scheme='http://www.blogger.com/atom/ns#' term='itunes'/><title type='text'>Automating and Managing iTunes with Ruby</title><content type='html'>We've spent a lot of time on this blog looking at how to get work done with Ruby and Microsoft Office (&lt;a href="http://rubyonwindows.blogspot.com/search/label/excel"&gt;Excel&lt;/a&gt;, &lt;a href="http://rubyonwindows.blogspot.com/search/label/word"&gt;Word&lt;/a&gt;, &lt;a href="http://rubyonwindows.blogspot.com/search/label/access"&gt;Access&lt;/a&gt;, &lt;a href="http://rubyonwindows.blogspot.com/search/label/outlook"&gt;Outlook&lt;/a&gt;). Let's take a break and go play... with iTunes.&lt;br /&gt;&lt;br /&gt;Apple's popular iTunes application for Windows includes a COM interface. This allows you to automate and manage iTunes with Ruby. You can launch the application, search for and play songs, set user interface properties, and manage your iTunes library and PlayLists using Ruby code. Let's take a look...&lt;br /&gt;&lt;br /&gt;First, of course, we start by connecting to the iTunes application object, launching iTunes if it isn't already running:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;itunes = WIN32OLE.new('iTunes.Application')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;Controlling the iTunes User Interface&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;To place iTunes into MiniPlayer mode (or return it to Full Mode), set the &lt;b&gt;BrowserWindow.MiniPlayer&lt;/b&gt; property:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;itunes.BrowserWindow.MiniPlayer = true&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To toggle the play/pause button, simply call the &lt;b&gt;PlayPause&lt;/b&gt; method:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;itunes.PlayPause&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To increase or decrease sound volume, adjust the &lt;b&gt;SoundVolume&lt;/b&gt; property:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;itunes.SoundVolume = itunes.SoundVolume + 50&lt;br /&gt;itunes.SoundVolume = itunes.SoundVolume - 25&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To go to the previous or next track, call those methods:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;itunes.PreviousTrack&lt;br /&gt;itunes.NextTrack&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;Managing Your iTunes Library&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;OK, that covers several standard user interface features. Now, let's look at how you can work with your content.&lt;br /&gt;&lt;br /&gt;Let's create an instance of the Library, which is a child object of the Application object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;library = itunes.LibraryPlaylist&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We'll be working with this LibraryPlaylist object a lot going forward. It provides several methods that will return collections of &lt;b&gt;Track&lt;/b&gt; and &lt;b&gt;PlayList&lt;/b&gt; objects. For example, calling the &lt;b&gt;Tracks&lt;/b&gt; method returns a collection of all tracks in the library:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;tracks = library.Tracks&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You can then select a song by name from this collection of Track objects:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;song = tracks.ItemByName('At Long Last Love')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...and then play the track by calling its &lt;b&gt;Play&lt;/b&gt; method:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;song.Play&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To search the LibraryPlaylist, call its &lt;b&gt;Search&lt;/b&gt; method, passing it a string to search for, and an integer that determines what field to search, such as Artist, Album Title, or Track Title:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;artist_tracks = library.Search('Sinatra', 2)&lt;br /&gt;album_tracks = library.Search('Come Fly With Me', 3)&lt;br /&gt;title_tracks = library.Search('Fly Me To The Moon', 5)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The Search and Tracks methods return a collection of Track objects, which you could iterate over. We might want to add all track objects in this collection to a ruby array called &lt;i&gt;songs&lt;/i&gt;:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;songs = []&lt;br /&gt;for track in tracks&lt;br /&gt;    songs &lt;&lt; track&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This would then allow us to use Ruby's excellent &lt;b&gt;sort_by&lt;/b&gt; method to sort the songs array:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;songs = songs.sort_by{|song| [song.Artist, song.Year, song.Album, song.TrackNumber]}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The Track object includes dozens of attributes pertaining to this track. As indicated above, there's Artist, Album, Year, and TrackNumber. But there's also Time (in minutes and seconds), Duration (total seconds), BPM (beats-per-minutes), Composer, Genre, and many more.&lt;br /&gt;&lt;br /&gt;You could iterate over the songs collection and write data for each track to a file:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;songs.each do |song|&lt;br /&gt;    my_file.puts [song.Artist, song.Album, song.Name, song.Time].join("\t")&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...or add the data to an Excel worksheet or SQLite database.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Working with iTunes PlayLists&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;To create a new PlayList, call the Application object's &lt;b&gt;CreatePlaylist&lt;/b&gt; method, passing it the name of the new PlayList:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;playlist = itunes.CreatePlaylist('My Playlist')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To add a song to a PlayList object, first get a Track object by calling the &lt;b&gt;ItemByName&lt;/b&gt; method on a Tracks collection:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;song = library.Tracks.ItemByName('At Long Last Love')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...then call the PlayList's &lt;b&gt;AddTrack&lt;/b&gt; method, passing it the Track object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;playlist.AddTrack(song)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;In the iTunes object model, PlayLists are child objects of Source objects. So, to create an array of all PlayList objects, we iterate over each Source object and get each PlayList object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;playlists = []&lt;br /&gt;itunes.Sources.each do |source|&lt;br /&gt;    source.PlayLists.each do |playlist|&lt;br /&gt;        playlists &lt;&lt; playlist&lt;br /&gt;    end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This gives us a collection of PlayList objects that we can now work with:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;for playlist in playlists do&lt;br /&gt;    puts playlist.Name&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To select a single PlayList by name:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;playlist = itunes.Sources.ItemByName('Library').Playlists.ItemByName('All Sinatra')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To play the first track of a PlayList:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;playlist.PlayFirstTrack&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To exit the iTunes application, call its &lt;b&gt;Quit&lt;/b&gt; method:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;itunes.Quit&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;Further Reading&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Further details can be found in &lt;a href="http://www.microsoft.com/technet/scriptcenter/funzone/tunes.mspx"&gt;this Microsoft TechNet article&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;For further examples in Ruby, you might want to check out the &lt;a href="http://code.google.com/p/itunes-control/"&gt;itunes-control (itch) library&lt;/a&gt; source code.&lt;br /&gt;&lt;br /&gt;And for those running iTunes on Mac OS, you may find &lt;a href="http://www.underthenetwork.com/2007/05/remote-control-fun-with-itunes-and-ruby.html"&gt;this blog post&lt;/a&gt; by Zack Hobson to be of interest.&lt;br /&gt;&lt;br /&gt;I hope you have found this information helpful. As always, post a comment here or send me an email if you have questions, comments, or suggestions.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;br /&gt;&lt;br /&gt;&lt;a href="http://digg.com/submit?phase=2&amp;url=http://rubyonwindows.blogspot.com/2007/07/automating-and-managing-itunes-with.html"&amp;target="_blank"&gt;&lt;br /&gt;&lt;img border="0" alt="Digg my article" src="http://digg.com/img/badges/91x17-digg-button.gif"/&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-321579227241359716?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/321579227241359716/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=321579227241359716' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/321579227241359716'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/321579227241359716'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/07/automating-and-managing-itunes-with.html' title='Automating and Managing iTunes with Ruby'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-3084032260397716294</id><published>2007-07-18T20:35:00.000-05:00</published><updated>2007-07-22T10:58:47.405-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='outlook'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Automating Outlook with Ruby: Tasks</title><content type='html'>In our &lt;a href="http://rubyonwindows.blogspot.com/2007/07/automating-outlook-with-ruby-calendar.html"&gt;last episode&lt;/a&gt;, we used Ruby to extract appointments data from the Outlook Calendar, delete an appointment, and create a new appointment. Today we'll look at managing Outlook Tasks with Ruby.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Getting Existing Tasks&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;As usual, we'll use the win32ole library to create a new instance (or connect to a currently running instance) of the Outlook application object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;outlook = WIN32OLE.new('Outlook.Application')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Next we'll get the MAPI namespace:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;mapi = outlook.GetNameSpace('MAPI')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Outlook consists of several Folder objects, including Inbox, Tasks, and Calendar. To get a folder object, call the MAPI object's &lt;b&gt;GetDefaultFolder&lt;/b&gt; method, passing it an integer representing the folder to return. For the Tasks folder, this number is 13:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;tasks = mapi.GetDefaultFolder(13)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The Tasks folder's &lt;b&gt;Items&lt;/b&gt; method returns a collection of all tasks, which you can iterate over. The following code prints out some of the most-often used properties for each task:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;for task in tasks.Items&lt;br /&gt;    puts task.Subject&lt;br /&gt;    puts task.Body&lt;br /&gt;    puts task.DueDate&lt;br /&gt;    puts task.PercentComplete&lt;br /&gt;    puts task.Status&lt;br /&gt;    puts task.Importance&lt;br /&gt;    puts task.LastModificationTime&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The &lt;b&gt;Status&lt;/b&gt; method/property returns an integer, representing one of the following values:&lt;br /&gt;&lt;br /&gt;0 -- Not started&lt;br /&gt;1 -- In progress&lt;br /&gt;2 -- Complete&lt;br /&gt;3 -- Waiting on someone else&lt;br /&gt;4 -- Deferred&lt;br /&gt;&lt;br /&gt;The &lt;b&gt;Importance&lt;/b&gt; method/property returns an integer, representing one of the following values:&lt;br /&gt;&lt;br /&gt;0 -- Low&lt;br /&gt;1 -- Normal&lt;br /&gt;2 -- High&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Filtering Your Tasks Collection&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;You can filter your collection of Task items by calling the Items collection's &lt;b&gt;Restrict&lt;/b&gt; method, passing it a filter string. The following code gets all 'Order Yankees World Series Tickets' tasks, and then deletes each one:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;for task in tasks.Items.Restrict("[Subject] = 'Order Yankees World Series Tickets'")&lt;br /&gt;    task.Delete&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Other possible filters include:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;tasks.Items.Restrict("[Status] = 'Not started'")&lt;br /&gt;tasks.Items.Restrict("[Importance] = 'High'")&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As illustrated in the above example, to delete a Task, call its &lt;b&gt;Delete&lt;/b&gt; method. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;Creating New Tasks&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;To create a new task, call the Outlook Application object's CreateItem method, passing it the number 3, which represents a Task Item. This returns a new Task object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;task = outlook.CreateItem(3)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Then set values for various properties of the new Task object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;task.Subject = 'Send flowers to wife'&lt;br /&gt;task.Body = 'Call flower shop and send roses.'&lt;br /&gt;task.ReminderSet = true&lt;br /&gt;task.ReminderTime = '7/19/2007 12:00 PM'&lt;br /&gt;task.DueDate = '7/20/2007 12:00 PM'&lt;br /&gt;task.ReminderPlaySound = true&lt;br /&gt;task.ReminderSoundFile = 'C:\Windows\Media\Ding.wav'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;When you've completed setting property values, call the Task object's &lt;b&gt;Save&lt;/b&gt; method:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;task.Save&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Further details can be found in this &lt;a href="http://www.microsoft.com/technet/scriptcenter/resources/officetips/sept05/tips0908.mspx"&gt;Microsoft TechNet article&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;As always, feel free to post a comment here or send me email with questions, comments, or suggestions.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;br /&gt;&lt;br /&gt;&lt;a href="http://digg.com/submit?phase=2&amp;url=http://rubyonwindows.blogspot.com/2007/07/automating-outlook-with-ruby-tasks.html"&amp;target="_blank"&gt;&lt;br /&gt;&lt;img border="0" alt="Digg my article" src="http://digg.com/img/badges/91x17-digg-button.gif"/&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-3084032260397716294?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/3084032260397716294/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=3084032260397716294' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/3084032260397716294'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/3084032260397716294'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/07/automating-outlook-with-ruby-tasks.html' title='Automating Outlook with Ruby: Tasks'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-1678766782317264510</id><published>2007-07-14T21:01:00.000-05:00</published><updated>2008-01-31T08:36:22.931-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='outlook'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Automating Outlook with Ruby: Calendar Appointments</title><content type='html'>In &lt;a href="http://rubyonwindows.blogspot.com/2007/07/automating-outlook-with-ruby-sending.html"&gt;a previous article&lt;/a&gt;, we looked at how to automate Microsoft Outlook to create and send a new email message. This time around, we'll use Ruby to extract appointments data from the Outlook Calendar, delete an appointment, and create a new appointment.&lt;br /&gt;&lt;br /&gt;As usual, we'll use the &lt;b&gt;win32ole&lt;/b&gt; library to create a new instance (or connect to a currently running instance) of the Outlook application object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;outlook = WIN32OLE.new('Outlook.Application')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Next we'll get the MAPI namespace:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;mapi = outlook.GetNameSpace('MAPI')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Outlook consists of several Folder objects, including Inbox, Tasks, and Calendar. To get a folder object, call the MAPI object's &lt;b&gt;GetDefaultFolder&lt;/b&gt; method, passing it an integer representing the folder to return. For the Calendar folder, this number is 9:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;calendar = mapi.GetDefaultFolder(9)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The calendar folder's &lt;b&gt;Items&lt;/b&gt; method returns a collection of all appointments, which you can iterate over. Each appointment object includes dozens of properties. The following code prints out six of the most-often used properties:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;calendar.Items.each do |appointment|&lt;br /&gt;    puts appointment.Subject&lt;br /&gt;    puts appointment.Location&lt;br /&gt;    puts appointment.Start&lt;br /&gt;    puts appointment.Duration&lt;br /&gt;    puts appointment.End&lt;br /&gt;    puts appointment.Body&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To delete an appointment, call its &lt;b&gt;Delete&lt;/b&gt; method. For example, the following code locates an appointment based on its Subject value, then deletes it, if found:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;calendar.Items.each do |appointment|&lt;br /&gt;    if appointment.Subject == 'Punch Bud Selig in the Nose'&lt;br /&gt;        appointment.Delete&lt;br /&gt;    end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To create a new appointment, call the Outlook Application object's &lt;b&gt;CreateItem&lt;/b&gt; method, passing it the number 1, which represents a Calendar Item. This returns a new Appointment object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;appointment = outlook.CreateItem(1)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Then set values for various properties of the new Appointment object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;appointment.BusyStatus = 2&lt;br /&gt;appointment.Start = '7/29/2007 11:00 AM'&lt;br /&gt;appointment.Duration = 300&lt;br /&gt;appointment.Subject = 'Baseball Hall of Fame Induction'&lt;br /&gt;appointment.Body = 'Tony Gwynn and Cal Ripken Jr.'&lt;br /&gt;appointment.Location = 'Cooperstown, NY'&lt;br /&gt;appointment.ReminderMinutesBeforeStart = 15&lt;br /&gt;appointment.ReminderSet = true&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The &lt;b&gt;BusyStatus&lt;/b&gt; value is an integer with the following possible values:&lt;br /&gt;&lt;br /&gt;olFree = 0&lt;br /&gt;olTentative = 1&lt;br /&gt;olBusy = 2&lt;br /&gt;olOutOfOffice = 3&lt;br /&gt;&lt;br /&gt;The &lt;b&gt;Duration&lt;/b&gt; value is an integer representing the appointment length in minutes.&lt;br /&gt;&lt;br /&gt;For an all-day event, forget the &lt;b&gt;Duration&lt;/b&gt; property and instead set the &lt;b&gt;AllDayEvent&lt;/b&gt; property to &lt;b&gt;true&lt;/b&gt;:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;appointment.AllDayEvent = true&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To make an appointment recurring, you'll want to work with the Appointment object's Recurrence child object. You get this object by calling the &lt;b&gt;GetRecurrencePattern&lt;/b&gt; method:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;recurrence = appointment.GetRecurrencePattern&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now you'll set the &lt;b&gt;RecurrenceType&lt;/b&gt; to one of the following values:&lt;br /&gt;&lt;br /&gt;0 -- To set an appointment that occurs each and every day.&lt;br /&gt;1 -- To set an appointment that occurs on the same day each week.&lt;br /&gt;2 -- To set an appointment that occurs on the same day each month.&lt;br /&gt;5 -- To set an appointment that occurs on the same day each year.&lt;br /&gt;&lt;br /&gt;So, for an appointment that occurs on this day and time (as defined in your &lt;b&gt;Start&lt;/b&gt; property above) each week:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;recurrence.RecurrenceType = 1&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Next, we'll set the &lt;b&gt;PatternStartDate&lt;/b&gt; property. This value needs to be on or before the &lt;b&gt;Start&lt;/b&gt; value defined above:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;recurrence.PatternStartDate = '8/6/2007'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Finally, we set the &lt;b&gt;PatternEndDate&lt;/b&gt; property:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;recurrence.PatternEndDate = '8/27/2007'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;When you've completed setting property values, call the Appointment object's &lt;b&gt;Save&lt;/b&gt; method:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;appointment.Save&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And there you have it. Further details can be found in this &lt;a href="http://www.microsoft.com/technet/scriptcenter/resources/officetips/apr05/tips0405.mspx"&gt;Microsoft TechNet article&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Questions? Comments? Suggestions? Post a comment here or send me email.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;br /&gt;&lt;br /&gt;&lt;a href="http://digg.com/submit?phase=2&amp;url=http://rubyonwindows.blogspot.com/2007/07/automating-outlook-with-ruby-calendar.html"&amp;target="_blank"&gt;&lt;br /&gt;&lt;img border="0" alt="Digg my article" src="http://digg.com/img/badges/91x17-digg-button.gif"/&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-1678766782317264510?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/1678766782317264510/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=1678766782317264510' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/1678766782317264510'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/1678766782317264510'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/07/automating-outlook-with-ruby-calendar.html' title='Automating Outlook with Ruby: Calendar Appointments'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-8288242618891642288</id><published>2007-07-08T21:44:00.000-05:00</published><updated>2007-07-22T11:03:46.656-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><category scheme='http://www.blogger.com/atom/ns#' term='wmi'/><title type='text'>Using Ruby &amp; WMI to Get Win32 Process Information</title><content type='html'>In &lt;a href="http://rubyonwindows.blogspot.com/2007/06/using-ruby-wmi-to-detect-usb-drive.html"&gt;an earlier post&lt;/a&gt;, we looked at using Windows Management Instrumentation (WMI) to determine if a "USB Mass Storage Device" is inserted. Today we'll use WMI to get information about the Win32 processes being run.&lt;br /&gt;&lt;br /&gt;WMI is installed and already running on all recent versions of Windows, so we'll connect to it using the win32ole library's connect method:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;wmi = WIN32OLE.connect("winmgmts://")&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Next, we'll call WMI's ExecQuery method to query the Win32_Process table:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;processes = wmi.ExecQuery("select * from win32_process")&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This returns a collection of all WMI Win32_Process objects.&lt;br /&gt;&lt;br /&gt;The Win32_Process class has dozens of properties. You can find a complete list of those properties &lt;a href="http://msdn2.microsoft.com/en-us/library/aa394372.aspx"&gt;here&lt;/a&gt;. You could also produce a list of these properties by calling the &lt;b&gt;Properties_&lt;/b&gt; method (note the underscore) on one of the Win32_Process objects. This returns a collection of Properties; then call the &lt;b&gt;Name&lt;/b&gt; method/property for each of these Property objects. For example:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;for process in processes do&lt;br /&gt;    for property in process.Properties_ do&lt;br /&gt;        puts property.Name&lt;br /&gt;    end&lt;br /&gt;    break&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To obtain data about a process, call that Process object's relevant property/method. For example, the following code prints four properties for each process:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;for process in processes do&lt;br /&gt;    puts "Name: #{process.Name}"&lt;br /&gt;    puts "CommandLine: #{process.CommandLine}"&lt;br /&gt;    puts "CreationDate: #{process.CreationDate}"&lt;br /&gt;    puts "WorkingSetSize: #{process.WorkingSetSize}"&lt;br /&gt;    puts&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That's all for now. Got a question or suggestion? Post a comment or send me email.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;br /&gt;&lt;br /&gt;&lt;a href="http://digg.com/submit?phase=2&amp;url=http://rubyonwindows.blogspot.com/2007/07/using-ruby-wmi-to-get-win32-process.html"&amp;target="_blank"&gt;&lt;img border="0" alt="Digg my article" src="http://digg.com/img/badges/91x17-digg-button.gif"/&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-8288242618891642288?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/8288242618891642288/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=8288242618891642288' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/8288242618891642288'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/8288242618891642288'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/07/using-ruby-wmi-to-get-win32-process.html' title='Using Ruby &amp; WMI to Get Win32 Process Information'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-8820854660370745704</id><published>2007-07-02T21:23:00.000-05:00</published><updated>2007-07-22T11:03:10.947-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Querying the Windows Registry for the Default Mail Client</title><content type='html'>Following up on my post about &lt;a href="http://rubyonwindows.blogspot.com/2007/07/automating-outlook-with-ruby-sending.html"&gt;sending email with MS Outlook&lt;/a&gt;, a reader asks, "I wonder how one can mine out the chosen default mail client... Is that somewhere in the Registry?"&lt;br /&gt;&lt;br /&gt;It does indeed appear to be in the Windows Registry, and can therefore be extracted using the &lt;b&gt;win32/registry&lt;/b&gt; library. I've just started hacking around with this library, but this bit of code seems to do the trick...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32/registry'&lt;br /&gt;&lt;br /&gt;def get_mail_client&lt;br /&gt;    Win32::Registry::HKEY_LOCAL_MACHINE.open('Software\Clients\Mail') do |reg|&lt;br /&gt;        reg_typ, reg_val = reg.read('')&lt;br /&gt;        return reg_val&lt;br /&gt;    end&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;mail_client = get_mail_client&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...though I am sure it can be improved upon.&lt;br /&gt;&lt;br /&gt;On my machine, this returns 'Microsoft Outlook'.&lt;br /&gt;&lt;br /&gt;Well, there you have it. As always, post comments or send email with enhancements, questions, or suggestions for future topics.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;br /&gt;&lt;br /&gt;&lt;a href="http://digg.com/submit?phase=2&amp;url=http://rubyonwindows.blogspot.com/2007/07/querying-windows-registry-for-default.html"&amp;target="_blank"&gt;&lt;br /&gt;&lt;img border="0" alt="Digg my article" src="http://digg.com/img/badges/91x17-digg-button.gif"/&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-8820854660370745704?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/8820854660370745704/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=8820854660370745704' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/8820854660370745704'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/8820854660370745704'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/07/querying-windows-registry-for-default.html' title='Querying the Windows Registry for the Default Mail Client'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-5101841613668363579</id><published>2007-07-01T10:12:00.000-05:00</published><updated>2007-07-22T11:02:32.180-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='outlook'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Automating Outlook with Ruby: Sending Email</title><content type='html'>Just as we have done with Excel and Word, we can use the &lt;b&gt;win32ole&lt;/b&gt; library to automate various tasks in Microsoft Outlook. Let's take a look at how to create an new email message.&lt;br /&gt;&lt;br /&gt;Of course, we'll want to require the &lt;b&gt;win32ole&lt;/b&gt; library:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Create an instance of the Outlook Application object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;outlook = WIN32OLE.new('Outlook.Application')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Outlook is a single-instance application, so if it is already running, calling the &lt;b&gt;WIN32OLE.new&lt;/b&gt; method will behave the same as the &lt;b&gt;WIN32OLE.connect&lt;/b&gt; method, returning the running instance.&lt;br /&gt;&lt;br /&gt;To create a new item in Outlook, call the Application object's &lt;b&gt;CreateItem&lt;/b&gt; method and pass it a numeric value that represents the type of item to create. Valid arguments include:&lt;br /&gt;&lt;br /&gt;olMailItem = 0&lt;br /&gt;olAppointmentItem = 1&lt;br /&gt;olContactItem = 2&lt;br /&gt;olTaskItem = 3&lt;br /&gt;olJournalItem = 4&lt;br /&gt;olNoteItem = 5&lt;br /&gt;olPostItem = 6&lt;br /&gt;&lt;br /&gt;So, to create a new email message (ie, MailItem), we call the &lt;b&gt;CreateItem&lt;/b&gt; method with an argument of 0 (zero):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;message = outlook.CreateItem(0)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This returns the newly-created mail item object. We'll proceed by setting values for this MailItem object's properties and calling some of its methods.&lt;br /&gt;&lt;br /&gt;To define the subject line of the message, set its &lt;b&gt;Subject&lt;/b&gt; value with a string:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;message.Subject = 'Double-Header Today'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To define the message body, set the &lt;b&gt;Body&lt;/b&gt; value to a string:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;message.Body = 'This is the message body.'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Alternatively, for an HTML message body, set the &lt;b&gt;HtmlBody&lt;/b&gt; value to an HTML-encoded string.&lt;br /&gt;&lt;br /&gt;Define the recipient by setting the &lt;b&gt;To&lt;/b&gt; property...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;message.To = 'ted.williams@redsox.com'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...or call the &lt;b&gt;Recipients.Add&lt;/b&gt; method one or more times:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;message.Recipients.Add 'ted.williams@redsox.com'&lt;br /&gt;message.Recipients.Add 'joe.dimaggio@yankees.com'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Got attachments to add? Call the &lt;b&gt;Attachments.Add&lt;/b&gt; method one or more times, passing it the path and name of the attachment:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;message.Attachments.Add('c:\my_folder\my_file.txt', 1)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The second argument, 1, indicates that this is an attached file, rather than a link to the original file.&lt;br /&gt;&lt;br /&gt;You can save the unsent message in your Outbox by calling the &lt;b&gt;Save&lt;/b&gt; method...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;message.Save&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This would then allow you to open and review the message before manually clicking the Send button.&lt;br /&gt;&lt;br /&gt;Of course, you can send the message with the &lt;b&gt;Send&lt;/b&gt; method:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;message.Send&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Note that when using the &lt;b&gt;Send&lt;/b&gt; method, Outlook may display a security alert that forces the user -- after a forced pause of about five seconds -- to click 'Yes'  to allow the message to be sent. I am unaware of a method for avoiding this alert dialog.&lt;br /&gt;&lt;br /&gt;UPDATE: I have discovered, but not yet used, &lt;a href="http://www.mapilab.com/outlook/security/"&gt;a free Outlook add-in from MAPILab&lt;/a&gt; that allows the end user to set Outlook security settings. And the related ($99) &lt;a href="http://www.add-in-express.com/outlook-security/"&gt;Security Manager&lt;/a&gt; claims to allow you to bypass the Outlook security alerts, with a single line of code. I can't recommend either solution, not having tried them, but mention them here for your information.&lt;br /&gt;&lt;br /&gt;That about wraps our show for today. Would you like to see more about Automating Outlook with Ruby? As always, feel free to post a comment here or email me with questions, comments, or suggestions.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;br /&gt;&lt;br /&gt;&lt;a href="http://digg.com/submit?phase=2&amp;url=http://rubyonwindows.blogspot.com/2007/07/automating-outlook-with-ruby-sending.html"&amp;target="_blank"&gt;&lt;br /&gt;&lt;img border="0" alt="Digg my article" src="http://digg.com/img/badges/91x17-digg-button.gif"/&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-5101841613668363579?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/5101841613668363579/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=5101841613668363579' title='25 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/5101841613668363579'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/5101841613668363579'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/07/automating-outlook-with-ruby-sending.html' title='Automating Outlook with Ruby: Sending Email'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>25</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-8290046972569908571</id><published>2007-06-29T20:49:00.000-05:00</published><updated>2007-07-22T11:01:38.984-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='ado'/><category scheme='http://www.blogger.com/atom/ns#' term='excel'/><title type='text'>Using Ruby &amp; ADO to Work with Excel Worksheets</title><content type='html'>In &lt;a href="http://rubyonwindows.blogspot.com/2007/06/using-ruby-ado-to-work-with-ms-access.html"&gt;an earlier article&lt;/a&gt;, I discussed using ActiveX Data Objects (ADO) to access a Microsoft Access database. A reader commented that ADO can also be used to access data in an Excel worksheet. Here's a brief demonstration...&lt;br /&gt;&lt;br /&gt;As usual, we'll use the &lt;b&gt;win32ole&lt;/b&gt; library:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Create a new &lt;b&gt;ADODB.Connection&lt;/b&gt; object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;connection = WIN32OLE.new('ADODB.Connection')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To open a connection to your Excel workbook, we'll call the Connection object's &lt;b&gt;Open&lt;/b&gt; method and pass it a connection string. You can use same the Microsoft Jet driver used for accessing an MS Access database, but we need to append an "Extended Property" to specify that this is an Excel woorkbook:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;conn_string =  'Provider=Microsoft.Jet.OLEDB.4.0;'&lt;br /&gt;conn_string &lt;&lt; 'Data Source=c:\my_folder\my_workbook.xls;'&lt;br /&gt;conn_string &lt;&lt; 'Extended Properties=Excel 8.0;'&lt;br /&gt;connection.Open(conn_string)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now, we'll create an ADO recordset object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;recordset = WIN32OLE.new('ADODB.Recordset')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;When calling the RecordSet object's &lt;b&gt;Open&lt;/b&gt; method, pass it your SQL statement and the open connection object. When working with an Excel worksheet as your table, append '$' to the worksheet table name and wrap it in brackets:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;recordset.Open("select * from [Sheet1$];", connection)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The Recordset object's &lt;b&gt;GetRows&lt;/b&gt; method returns an array of columns (not rows, as you might expect), so we'll use the Ruby array's transpose method to convert it to an array of rows:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;data = recordset.GetRows.transpose&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Close the Connection object by calling its &lt;b&gt;Close&lt;/b&gt; method:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;connection.close&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;There you have it! My thanks to reader Khaoz for the suggestion of using ADO with Excel.&lt;br /&gt;&lt;br /&gt;Other articles about working with ADO can be found under the &lt;a href="http://rubyonwindows.blogspot.com/search/label/ado"&gt;ado label&lt;/a&gt; to the right.&lt;br /&gt;&lt;br /&gt;As always, feel free to post a comment here or email me with questions, comments, or suggestions.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;br /&gt;&lt;br /&gt;&lt;a href="http://digg.com/submit?phase=2&amp;url=http://rubyonwindows.blogspot.com/2007/06/using-ruby-ado-to-work-with-excel.html"&amp;target="_blank"&gt;&lt;br /&gt;&lt;img border="0" alt="Digg my article" src="http://digg.com/img/badges/91x17-digg-button.gif"/&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-8290046972569908571?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/8290046972569908571/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=8290046972569908571' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/8290046972569908571'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/8290046972569908571'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/06/using-ruby-ado-to-work-with-excel.html' title='Using Ruby &amp; ADO to Work with Excel Worksheets'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-9197860072876873183</id><published>2007-06-24T22:04:00.000-05:00</published><updated>2007-07-22T11:04:53.202-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='excel'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Automating Excel with Ruby: Formatting Worksheets</title><content type='html'>If you're going to automate the generation of an Excel worksheet, you might as well make it look good. There are a near-infinite number of methods that can be called upon to format &lt;a href="http://rubyonwindows.blogspot.com/2007/04/automating-excel-with-ruby-rows-columns.html"&gt;rows, columns, ranges, and cells&lt;/a&gt;. Let's take a look at some of the most common.&lt;br /&gt;&lt;br /&gt;To format the first row of a worksheet as bold-font, set the &lt;b&gt;Font.Bold&lt;/b&gt; value:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;worksheet.Rows(1).Font.Bold = true&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To format numbers in a range, set the &lt;b&gt;NumberFormat&lt;/b&gt; value.&lt;br /&gt;&lt;br /&gt;To format column D as Currency:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;worksheet.Columns(4).NumberFormat = "$###,##0.00"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To format column A as a date (mm/dd/yy):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;worksheet.Column("A").NumberFormat = "mm/dd/yy"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Alternatively, you can set the &lt;b&gt;Style&lt;/b&gt; value:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;worksheet.Column("A").Style = "Currency"&lt;br /&gt;worksheet.Column("B").Style = "Percent"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To set the alignment on a range, set its &lt;b&gt;HorizontalAlignment&lt;/b&gt; value:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;worksheet.Rows(1).HorizontalAlignment = 2          # Left&lt;br /&gt;worksheet.Columns("A:F").HorizontalAlignment = 4   # Right&lt;br /&gt;worksheet.Cells(3, 5).HorizontalAlignment = -4108  # Center&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To auto-fit the width of a column, or the height of a row, call its &lt;b&gt;AutoFit&lt;/b&gt; method:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;worksheet.Columns.AutoFit&lt;br /&gt;worksheet.Rows(1).AutoFit&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To set the width of a column, set its &lt;b&gt;ColumnWidth&lt;/b&gt; value:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;worksheet.Columns(4).ColumnWidth = 25.0&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To set the height of a row, set its &lt;b&gt;RowHeight&lt;/b&gt; value:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;worksheet.Rows.RowHeight = 15.0&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To apply highlighting to a range, set its &lt;b&gt;Interior.ColorIndex&lt;/b&gt; value:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;worksheet.Rows(10).Interior.ColorIndex = 6   # Yellow&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That's all for now, but feel free to post a comment here or send me email with questions, comments, or suggested topics.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;br /&gt;&lt;br /&gt;&lt;a href="http://digg.com/submit?phase=2&amp;url=http://rubyonwindows.blogspot.com/2007/06/automating-excel-with-ruby-formatting.html"&amp;target="_blank"&gt;&lt;img border="0" alt="Digg my article" src="http://digg.com/img/badges/91x17-digg-button.gif"/&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-9197860072876873183?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/9197860072876873183/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=9197860072876873183' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/9197860072876873183'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/9197860072876873183'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/06/automating-excel-with-ruby-formatting.html' title='Automating Excel with Ruby: Formatting Worksheets'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-6497635227885724875</id><published>2007-06-20T20:53:00.000-05:00</published><updated>2007-07-22T11:05:48.453-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Making Use of Ruby's ENV Object</title><content type='html'>Ruby provides the ENV object for easy access to a variety of environment variables. ENV is not actually a hash, but you access individual values like you would a hash:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ENV['Path']&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If you need to, you can convert it into a Hash using the &lt;b&gt;to_hash&lt;/b&gt; method, so to see a complete list of keys and values stored in ENV, you could do this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ENV.to_hash.each do |key, value|&lt;br /&gt;    puts("#{key}\t#{value}")&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Many of these values you may never use, but a few are worth noting...&lt;br /&gt;&lt;br /&gt;&lt;b&gt;HOMEDRIVE&lt;/b&gt; returns the home drive, for example:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ENV['HOMEDRIVE']    # returns "C:"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;APPDATA&lt;/b&gt; returns the path to the Application Data folder(ie, "C:")&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ENV['APPDATA']    # returns "C:\Documents and Settings\Joe DiMaggio\Application Data"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;USERPROFILE&lt;/b&gt; returns the user's home directory:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ENV['USERPROFILE']    # returns "C:\Documents and Settings\Joe DiMaggio"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;USERNAME&lt;/b&gt; returns the Windows user's name or Windows login:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ENV['USERNAME']    # returns "Joe DiMaggio"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;ProgramFiles&lt;/b&gt; returns the path to the Program Files folder:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ENV['ProgramFiles]    # returns "C:\Program Files"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;windir&lt;/b&gt; returns the path to the Program Files folder:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ENV['windir']    # returns "C:\Windows"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;Writing to Environment Variables&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;To quote &lt;a href="http://www.rubycentral.com/book/rubyworld.html"&gt;The Pickaxe&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;"A Ruby program may write to the ENV object, which on most systems changes the values of the corresponding environment variables. However, this change is local to the process that makes it and to any subsequently spawned child processes. This inheritance of environment variables is illustrated in the code that follows. A subprocess changes an environment variable and this change is seen in a process that it then starts. However, the change is not visible to the original parent. (This just goes to prove that parents never really know what their children are doing.)"&lt;br /&gt;&lt;br /&gt;A tip of the hat to reader Revence, who shared a code snippet that utilized the ENV object, reminding me of its value.&lt;br /&gt;&lt;br /&gt;That's all for now. As always, leave a comment here or via email if you have questions or suggestions for future topics.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;br /&gt;&lt;br /&gt;&lt;a href="http://digg.com/submit?phase=2&amp;url=http://rubyonwindows.blogspot.com/2007/06/making-use-of-rubys-env-object.html"&amp;target="_blank"&gt;&lt;img border="0" alt="Digg my article" src="http://digg.com/img/badges/91x17-digg-button.gif"/&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-6497635227885724875?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/6497635227885724875/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=6497635227885724875' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/6497635227885724875'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/6497635227885724875'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/06/making-use-of-rubys-env-object.html' title='Making Use of Ruby&apos;s ENV Object'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-4664420851164335409</id><published>2007-06-15T20:55:00.000-05:00</published><updated>2007-07-22T11:06:19.246-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='word'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Automating Word with Ruby: Formatting Text</title><content type='html'>A reader asks "Can you tell me how to format text in Word using Ruby? I'm most interested in controlling justification, indenting paragraphs, changing fonts, using italics, bold, and underlining, etc."&lt;br /&gt;&lt;br /&gt;This is the latest in a &lt;a href="http://rubyonwindows.blogspot.com/search/label/word"&gt;series of articles about Automating Word with Ruby&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;There's a variety of formatting that you can apply to &lt;a href="http://rubyonwindows.blogspot.com/2007/04/automating-word-with-ruby-range-object.html"&gt;Range objects&lt;/a&gt; in Word. Let's look at some of the most common...&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Fonts&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;To change the font of a Range object, set its &lt;b&gt;Font.Name&lt;/b&gt; value to a valid font name:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;document.Words(1).Font.Name = 'Verdana'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To change the font size of a Range object, set its &lt;b&gt;Font.Size&lt;/b&gt; value:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;document.Words(1).Font.Size = 18&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To set Bold, Italic, and Underline properties of a Range object, supply a boolean value:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;document.Sentences(1).Font.Bold = true&lt;br /&gt;document.Sentences(1).Font.Italic = true&lt;br /&gt;document.Sentences(1).Font.Underline = true&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;Indenting&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;To indent a paragraph, call its &lt;b&gt;Indent&lt;/b&gt; method. For example, to indent all paragraphs in the document:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;document.Paragraphs.Indent&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To un-indent a paragraph, call its &lt;b&gt;Outdent&lt;/b&gt; method. For example, to un-indent only the first paragraph:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;document.Paragraphs(1).Outdent&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;Alignment&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;To align a paragraph, set its &lt;b&gt;Alignment&lt;/b&gt; value to one of the following values:&lt;br /&gt;&lt;br /&gt;Left = 0&lt;br /&gt;Center = 1&lt;br /&gt;Right = 2&lt;br /&gt;Justify = 3&lt;br /&gt;&lt;br /&gt;To center-align the second paragraph, for example:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;document.Paragraphs(2).Alignment = 1&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That's all for now, and I hope you find it helpful. As always, post a comment here or send me an email to request future topics.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;br /&gt;&lt;br /&gt;&lt;a href="http://digg.com/submit?phase=2&amp;url=http://rubyonwindows.blogspot.com/2007/06/automating-word-with-ruby-formatting.html"&amp;target="_blank"&gt;&lt;img border="0" alt="Digg my article" src="http://digg.com/img/badges/91x17-digg-button.gif"/&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-4664420851164335409?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/4664420851164335409/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=4664420851164335409' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/4664420851164335409'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/4664420851164335409'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/06/automating-word-with-ruby-formatting.html' title='Automating Word with Ruby: Formatting Text'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-1874129904983347073</id><published>2007-06-12T21:05:00.000-05:00</published><updated>2007-06-21T07:45:31.909-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><category scheme='http://www.blogger.com/atom/ns#' term='wmi'/><title type='text'>Using Ruby &amp; WMI to Detect a USB Drive</title><content type='html'>While I was away, a reader asked, "Anyone know how to monitor Mass Storage Insert events (eg. When someone inserts a USB drive) from Ruby?" So let's take a look at how to use Windows Management Instrumentation (WMI) to determine if a "USB Mass Storage Device" is inserted.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.microsoft.com/technet/scriptcenter/guide/sas_wmi_overview.mspx?mfr=true"&gt;Microsoft&lt;/a&gt; says that WMI "is the primary management technology for Microsoft® Windows® operating systems." WMI is installed and already running on all recent versions of Windows, so we'll connect to it using the &lt;b&gt;win32ole&lt;/b&gt; library's &lt;b&gt;connect&lt;/b&gt; method:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;wmi = WIN32OLE.connect("winmgmts://")&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Next, we'll call WMI's &lt;b&gt;ExecQuery&lt;/b&gt; method to query the Win32_USBControllerDevice to obtain a collection of USB Controller Device objects:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;devices = wmi.ExecQuery("Select * From Win32_USBControllerDevice")&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Each device in the collection has a &lt;b&gt;Dependent&lt;/b&gt; method that returns a string something like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;\\CASABLANCA\root\cimv2:Win32_PnPEntity.DeviceID="USB\\ROOT_HUB20\\3&amp;41601B64&amp;0"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To extract the Device ID value, we want to remove the double-quotes and grab the portion of the string after the "=":&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;device_name = device.Dependent.gsub('"', '').split('=')[1]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now we'll use that Device ID to query the Win32_PnPEntity to obtain a collection of USB Device objects:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;usb_devices = wmi.ExecQuery("Select * From Win32_PnPEntity Where DeviceID = '#{device_name}'")&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Finally, we will iterate over the collection and call the &lt;b&gt;Description&lt;/b&gt; method for each, looking for a value of 'USB Mass Storage Device':&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;for usb_device in usb_devices do&lt;br /&gt;    if usb_device.Description == 'USB Mass Storage Device'&lt;br /&gt;        # DO SOMETHING HERE&lt;br /&gt;    end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Putting it all together, we have something like the following:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;&lt;br /&gt;wmi = WIN32OLE.connect("winmgmts://")&lt;br /&gt;&lt;br /&gt;devices = wmi.ExecQuery("Select * From Win32_USBControllerDevice")&lt;br /&gt;for device in devices do&lt;br /&gt;    device_name = device.Dependent.gsub('"', '').split('=')[1]&lt;br /&gt;    usb_devices = wmi.ExecQuery("Select * From Win32_PnPEntity Where DeviceID = '#{device_name}'")&lt;br /&gt;    for usb_device in usb_devices do&lt;br /&gt;        puts usb_device.Description&lt;br /&gt;        if usb_device.Description == 'USB Mass Storage Device'&lt;br /&gt;            # DO SOMETHING HERE&lt;br /&gt;        end&lt;br /&gt;    end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The above code is functional, but can certainly be improved upon and modified to meet your specific needs.&lt;br /&gt;&lt;br /&gt;That's all for now. Got a question or suggestion? Post a comment or send me email.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;br /&gt;&lt;br /&gt;&lt;a href="http://digg.com/submit?phase=2&amp;url=http://rubyonwindows.blogspot.com/2007/06/using-ruby-wmi-to-detect-usb-drive.html" target="_blank"&gt;&lt;img border="0" alt="Digg my article" src="http://digg.com/img/badges/91x17-digg-button.gif"/&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-1874129904983347073?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/1874129904983347073/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=1874129904983347073' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/1874129904983347073'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/1874129904983347073'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/06/using-ruby-wmi-to-detect-usb-drive.html' title='Using Ruby &amp; WMI to Detect a USB Drive'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-5435977521020768370</id><published>2007-06-04T21:55:00.001-05:00</published><updated>2009-07-13T12:28:13.516-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='ado'/><category scheme='http://www.blogger.com/atom/ns#' term='access'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Using Ruby &amp; ADO to Work with MS Access Databases</title><content type='html'>A reader in the &lt;a href="http://www.ruby-forum.com/forum/4"&gt;Ruby forum&lt;/a&gt; recently asked about using Ruby to get data from a Microsoft Access database. This can be done easily using the ActiveX Data Objects (ADO), via the &lt;b&gt;win32ole&lt;/b&gt; library. Let's walk through it...&lt;br /&gt;&lt;br /&gt;As usual, require the &lt;b&gt;win32ole&lt;/b&gt; library:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Next, create a new ADODB Connection object, which will manage the connection to the database:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;connection = WIN32OLE.new('ADODB.Connection')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To open a connection to the Access database, call the Connection object's &lt;b&gt;Open&lt;/b&gt; method, passing it the Provider and Data Source (your Access mdb file) parameters:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;connection.Open('Provider=Microsoft.Jet.OLEDB.4.0;&lt;br /&gt;                 Data Source=c:\baseball\lahman54.mdb')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To execute an SQL statement that does not return a recordset (ie, CREATE, ALTER, INSERT), you can call the Connection object's &lt;b&gt;Execute&lt;/b&gt; method:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;connection.Execute("INSERT INTO HallOfFame VALUES ('Dave', 'Concepcion');")&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To perform a query that returns a recordset of data, first create a new ADODB Recordset object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;recordset = WIN32OLE.new('ADODB.Recordset')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Then call the Recordset object's &lt;b&gt;Open&lt;/b&gt; method, passing it the SQL statement and your existing Connection object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;recordset.Open(sql, connection)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This loads the query results into the Recordset object. The Recordset object's &lt;b&gt;GetRows&lt;/b&gt; method returns an array of columns (not rows, as you might expect), so we'll use the Ruby array's &lt;b&gt;transpose&lt;/b&gt; method to convert it to an array of rows:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;data = recordset.GetRows.transpose&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Close the Connection object by calling its &lt;b&gt;Close&lt;/b&gt; method:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;recordset.Close&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We could create a simple class to wrap up this functionality:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class AccessDb&lt;br /&gt;    attr_accessor :mdb, :connection, :data, :fields&lt;br /&gt;&lt;br /&gt;    def initialize(mdb=nil)&lt;br /&gt;        @mdb = mdb&lt;br /&gt;        @connection = nil&lt;br /&gt;        @data = nil&lt;br /&gt;        @fields = nil&lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;    def open&lt;br /&gt;        connection_string =  'Provider=Microsoft.Jet.OLEDB.4.0;Data Source='&lt;br /&gt;        connection_string &lt;&lt; @mdb&lt;br /&gt;        @connection = WIN32OLE.new('ADODB.Connection')&lt;br /&gt;        @connection.Open(connection_string)&lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;    def query(sql)&lt;br /&gt;        recordset = WIN32OLE.new('ADODB.Recordset')&lt;br /&gt;        recordset.Open(sql, @connection)&lt;br /&gt;        @fields = []&lt;br /&gt;        recordset.Fields.each do |field|&lt;br /&gt;            @fields &lt;&lt; field.Name&lt;br /&gt;        end&lt;br /&gt;        begin&lt;br /&gt;            @data = recordset.GetRows.transpose&lt;br /&gt;        rescue&lt;br /&gt;            @data = []&lt;br /&gt;        end&lt;br /&gt;        recordset.Close&lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;    def execute(sql)&lt;br /&gt;        @connection.Execute(sql)&lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;    def close&lt;br /&gt;        @connection.Close&lt;br /&gt;    end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This class is very similar to an &lt;a href="http://rubyonwindows.blogspot.com/2007/03/ruby-ado-and-sqlserver.html"&gt;SqlServer class I previously discussed&lt;/a&gt;.&lt;br /&gt;We would use this AccessDb class as follows:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;db = AccessDb.new('c:\Baseball\lahman54.mdb')&lt;br /&gt;db.open&lt;br /&gt;&lt;br /&gt;db.query("SELECT * FROM AllStar WHERE playerID = 'conceda01';")&lt;br /&gt;field_names = db.fields&lt;br /&gt;rows = db.data&lt;br /&gt;&lt;br /&gt;db.execute("INSERT INTO HallOfFame VALUES ('Dave', 'Concepcion');")&lt;br /&gt;&lt;br /&gt;db.close&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That about wraps up our show for today. As always, feel free to post a comment here or email me with questions, comments, or suggestions.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;br /&gt;&lt;br /&gt;&lt;a href="http://digg.com/submit?phase=2&amp;url=http://rubyonwindows.blogspot.com/2007/06/using-ruby-ado-to-work-with-ms-access.html" target="_blank"&gt;&lt;img border="0" alt="Digg my article" src="http://digg.com/img/badges/91x17-digg-button.gif"/&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-5435977521020768370?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/5435977521020768370/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=5435977521020768370' title='24 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/5435977521020768370'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/5435977521020768370'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/06/using-ruby-ado-to-work-with-ms-access.html' title='Using Ruby &amp; ADO to Work with MS Access Databases'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>24</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-1708513744593483807</id><published>2007-06-02T18:41:00.000-05:00</published><updated>2007-06-21T07:44:02.550-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='api'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Displaying a MessageBox Using the Windows API</title><content type='html'>Sometimes the only user interface you need is a message box. Maybe you just need to alert the user to the completion of a process; or maybe you need to ask a question to which the user responds Yes or No. You'd rather not resort to a console window; but neither do you want to load an entire GUI library to display a simple message box. "Dave", you say, "there must be a better way."&lt;br /&gt;&lt;br /&gt;Indeed, there is. Use the &lt;b&gt;DL&lt;/b&gt; library to call the &lt;b&gt;MessageBoxA&lt;/b&gt; Windows API function.&lt;br /&gt;&lt;br /&gt;First, require the &lt;b&gt;dl&lt;/b&gt; library:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'dl'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We'll feed the function our message text, a dialog box title, and an integer that defines what buttons to display. Let's define some meaningful constants to represent the possible button values:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;BUTTONS_OK = 0&lt;br /&gt;BUTTONS_OKCANCEL = 1&lt;br /&gt;BUTTONS_ABORTRETRYIGNORE = 2&lt;br /&gt;BUTTONS_YESNO = 4&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The function will return an integer representing the button that the user clicked, so let's define some meaningful constants to represent the possible return code values:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;CLICKED_OK = 1&lt;br /&gt;CLICKED_CANCEL = 2&lt;br /&gt;CLICKED_ABORT = 3&lt;br /&gt;CLICKED_RETRY = 4&lt;br /&gt;CLICKED_IGNORE = 5&lt;br /&gt;CLICKED_YES = 6&lt;br /&gt;CLICKED_NO = 7&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Here's our method to call the &lt;b&gt;MessageBoxA&lt;/b&gt; function from the &lt;b&gt;user32&lt;/b&gt; DLL:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def message_box(txt='', title='', buttons=0)&lt;br /&gt;    user32 = DL.dlopen('user32')&lt;br /&gt;    msgbox = user32['MessageBoxA', 'ILSSI']&lt;br /&gt;    r, rs = msgbox.call(0, txt, title, buttons)&lt;br /&gt;    return r&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Here's how we call it display a message box with an OK button:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;message_box("Hello, World!", "Hi!", BUTTONS_OK)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Here's how we call it to display a message box with 'Yes' and 'No' buttons, and process the response:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;response = message_box("Are you sure you want to proceed?", "Proceed?", BUTTONS_YESNO)&lt;br /&gt;if response == CLICKED_YES&lt;br /&gt;    # insert your code here&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Finally, here it is in its entirety:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'dl'&lt;br /&gt;&lt;br /&gt;# button constants&lt;br /&gt;BUTTONS_OK = 0&lt;br /&gt;BUTTONS_OKCANCEL = 1&lt;br /&gt;BUTTONS_ABORTRETRYIGNORE = 2&lt;br /&gt;BUTTONS_YESNO = 4&lt;br /&gt;&lt;br /&gt;# return code constants&lt;br /&gt;CLICKED_OK = 1&lt;br /&gt;CLICKED_CANCEL = 2&lt;br /&gt;CLICKED_ABORT = 3&lt;br /&gt;CLICKED_RETRY = 4&lt;br /&gt;CLICKED_IGNORE = 5&lt;br /&gt;CLICKED_YES = 6&lt;br /&gt;CLICKED_NO = 7&lt;br /&gt;&lt;br /&gt;def message_box(txt, title=APP_TITLE, buttons=BUTTONS_OK)&lt;br /&gt;    user32 = DL.dlopen('user32')&lt;br /&gt;    msgbox = user32['MessageBoxA', 'ILSSI']&lt;br /&gt;    r, rs = msgbox.call(0, txt, title, buttons)&lt;br /&gt;    return r&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;response = message_box("Are you sure you want to proceed?", "Proceed?", BUTTONS_YESNO)&lt;br /&gt;if response == CLICKED_YES&lt;br /&gt;    # insert your code here&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;There you have it. As always, let me know if you have questions, comments, or suggestions.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;br /&gt;&lt;br /&gt;&lt;a href="http://digg.com/submit?phase=2&amp;url=http://rubyonwindows.blogspot.com/2007/06/displaying-messagebox-using-windows-api.html" target="_blank"&gt;&lt;img border="0" alt="Digg my article" src="http://digg.com/img/badges/91x17-digg-button.gif"/&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-1708513744593483807?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/1708513744593483807/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=1708513744593483807' title='14 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/1708513744593483807'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/1708513744593483807'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/06/displaying-messagebox-using-windows-api.html' title='Displaying a MessageBox Using the Windows API'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>14</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-281071765076570742</id><published>2007-05-30T07:55:00.000-05:00</published><updated>2007-07-22T11:08:09.961-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><category scheme='http://www.blogger.com/atom/ns#' term='shell'/><title type='text'>Automating Applications with Ruby &amp; The Windows Script Host</title><content type='html'>We've talked at length about automating Windows applications through COM/OLE, using the &lt;b&gt;win32ole&lt;/b&gt; library. But not all applications expose themselves (so to speak) to such automation. The Windows Script Host can automate the activation of windows, and the sending of keystrokes. This may sometimes be all that you need to get the job done.&lt;br /&gt;&lt;br /&gt;The Windows Script Host (WSH) has been part of the Windows operating system since Windows 98. You can use WSH's Shell object (via COM/OLE) to send keystokes to windows.&lt;br /&gt;&lt;br /&gt;First, require the &lt;b&gt;win32ole&lt;/b&gt; library:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now we'll create an instance of the Wscript Shell object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;wsh = WIN32OLE.new('Wscript.Shell')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To send keystrokes to a window, you must first activate the window, bringing it to the forefront. This can be done with the Wscript Shell's &lt;b&gt;AppActivate&lt;/b&gt; method, which returns &lt;b&gt;true&lt;/b&gt; if the window was successfully activated, and &lt;b&gt;false&lt;/b&gt; otherwise. The AppActivate method takes the window title text as it's argument:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;wsh.AppActivate('Title')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The string passed to the AppActivate method can be a partial, but must be the start or ending of the window title. The method is not case sensitive, and does not accept  regular expressions. To quote &lt;a href="http://msdn2.microsoft.com/en-us/library/wzcddbek.aspx"&gt;Microsoft&lt;/a&gt;: "In determining which application to activate, the specified title is compared to the title string of each running application. If no exact match exists, any application whose title string begins with title is activated. If an application still cannot be found, any application whose title string ends with title is activated. If more than one instance of the application named by title exists, one instance is arbitrarily activated."&lt;br /&gt;&lt;br /&gt;Once you have the window activated, you may use the Wscript Shell's &lt;b&gt;SendKeys&lt;/b&gt; method to send keystrokes to the window. The SendKeys method takes a string in quotes. Special keys (ie, ENTER, TAB, PGDN, PGUP, Function Keys) may be embedded in the string, if surrounded by braces:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;wsh.SendKeys('Ruby{TAB}on{TAB}Windows{ENTER}')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The SHIFT key is represented by '+', the ALT key is represented by '%', and the CTRL key is represented by '^', so to quit an application by sending ALT-F4:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;wsh.SendKeys('%{F4}')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Further details on the syntax of the SendKeys method can be found&lt;br /&gt;&lt;a href="http://msdn2.microsoft.com/en-us/library/8c6yea83.aspx"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Timing is important using these methods, so you may need to insert a &lt;b&gt;sleep&lt;/b&gt; method here and there, to get the optimal performance. For example, a 1-second (or less) wait between activating a window and sending keystrokes, or vice-versa.&lt;br /&gt;&lt;br /&gt;So, putting it all together, here's a brief example that activates Notepad (which must be running first), inserts text, saves the file to a specific name, and quits Notepad:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;# Require the win32ole library:&lt;br /&gt;require 'win32ole'&lt;br /&gt;# Create an instance of the Wscript Shell:&lt;br /&gt;wsh = WIN32OLE.new('Wscript.Shell')&lt;br /&gt;# Try to activate the Notepad window:&lt;br /&gt;if wsh.AppActivate('Notepad')&lt;br /&gt;    sleep(1)&lt;br /&gt;    # Enter text into Notepad:&lt;br /&gt;    wsh.SendKeys('Ruby{TAB}on{TAB}Windows{ENTER}')&lt;br /&gt;    # ALT-F to pull down File menu, then A to select Save As...:&lt;br /&gt;    wsh.SendKeys('%F')&lt;br /&gt;    wsh.SendKeys('A')&lt;br /&gt;    sleep(1)&lt;br /&gt;    if wsh.AppActivate('Save As')&lt;br /&gt;        wsh.SendKeys('c:\temp\filename.txt{ENTER}')&lt;br /&gt;        sleep(1)&lt;br /&gt;        # If prompted to overwrite existing file:&lt;br /&gt;        if wsh.AppActivate('Save As')&lt;br /&gt;            # Enter 'Y':&lt;br /&gt;            wsh.SendKeys('Y')&lt;br /&gt;        end&lt;br /&gt;    end&lt;br /&gt;    # Quit Notepad with ALT-F4:&lt;br /&gt;    wsh.SendKeys('%{F4}')&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The above code snippet can be improved upon, and I encourage you to do so. But it, hopefully, demonstrates what can be done.&lt;br /&gt;&lt;br /&gt;Mimicing keystrokes is certainly not the ultimate in program automation, but it may sometimes be all that you need to get the job done. For example, back in the days before pop-up blockers, I had written a script that would simply run in the background, look for pop-up ads (based on a list of title strings), and close them. Simple, yet effective.&lt;br /&gt;&lt;br /&gt;I should probably also mention &lt;a href="http://www.autoitscript.com"&gt;AutoIt&lt;/a&gt;, "a freeware Windows automation language. It can be used to script most simple Windows-based tasks." I've not used it myself, but I believe that the Watir library leverages it.&lt;br /&gt;&lt;br /&gt;That's all for now. As always, let me know if you have questions, comments, or requests for future topics.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;br /&gt;&lt;br /&gt;&lt;a href="http://digg.com/submit?phase=2&amp;url=http://rubyonwindows.blogspot.com/2007/05/automating-applications-with-ruby.html"&amp;target="_blank"&gt;&lt;br /&gt;&lt;img border="0" alt="Digg my article" src="http://digg.com/img/badges/91x17-digg-button.gif"/&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-281071765076570742?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/281071765076570742/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=281071765076570742' title='13 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/281071765076570742'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/281071765076570742'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/05/automating-applications-with-ruby.html' title='Automating Applications with Ruby &amp; The Windows Script Host'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>13</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-1622144334314480422</id><published>2007-05-24T21:42:00.000-05:00</published><updated>2007-06-03T07:57:13.146-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><category scheme='http://www.blogger.com/atom/ns#' term='shell'/><title type='text'>Launching Apps and Printing Docs with the Windows Shell</title><content type='html'>A reader recently asked how to launch an application from within a Ruby script. A quick answer is to use the &lt;b&gt;system&lt;/b&gt; or &lt;b&gt;exec&lt;/b&gt; methods. But you can also leverage the Windows Shell to launch applications, and have control over the window state. You can also use the shell to print documents. Let's get right down to it, shall we?...&lt;br /&gt;&lt;br /&gt;Require the &lt;b&gt;win32ole&lt;/b&gt; library...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Create an instance of the Windows Shell object...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;shell = WIN32OLE.new('Shell.Application')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The shell object's &lt;b&gt;ShellExecute&lt;/b&gt; method performs a specified operation on a specified file. The syntax is...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;shell.ShellExecute(FILE, ARGUMENTS, DIRECTORY, OPERATION, SHOW)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;FILE: Required. String that contains the name of the file on which ShellExecute will perform the action specified by OPERATION.&lt;br /&gt;&lt;br /&gt;ARGUMENTS: Optional. The parameter values for the operation.&lt;br /&gt;&lt;br /&gt;DIRECTORY: Optional. The fully qualified path of the directory that contains the file specified by FILE. If this parameter is not specified, the current working directory is used.&lt;br /&gt;&lt;br /&gt;OPERATION: Specifies the operation to be performed. It should be set to one of the verb strings that is supported by the file (Examples: 'open', 'edit', or 'print'). If this parameter is not specified, the default operation is performed.&lt;br /&gt;&lt;br /&gt;SHOW: Recommends how the window that belongs to the application that performs the operation should be displayed initially (0 = hidden, 1 = normal, 2 = minimized, 3 = maximized). The application can ignore this recommendation. If this parameter is not specified, the application uses its default value.&lt;br /&gt;&lt;br /&gt;So, to launch Excel in a maximized window...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;shell.ShellExecute('excel.exe', '', '', 'open', 3)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I suppose you could also launch your rails app with something like this...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;shell.ShellExecute('ruby.exe', 'c:\my_rails_app\script\server', '', 'open', 1)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To print a document, hiding the application window...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;shell.ShellExecute('C:\MyFolder\Document.txt', '', '', 'print', 0)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That's about it. As always, post a comment here or send me email if you have questions, comments, or would like to request a topic for discussion.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-1622144334314480422?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/1622144334314480422/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=1622144334314480422' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/1622144334314480422'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/1622144334314480422'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/05/launching-apps-and-printing-docs-with.html' title='Launching Apps and Printing Docs with the Windows Shell'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-2177651279745083546</id><published>2007-05-22T20:21:00.000-05:00</published><updated>2007-06-03T07:57:13.146-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='ie'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><category scheme='http://www.blogger.com/atom/ns#' term='shell'/><title type='text'>The Shell Windows Collection of Internet Explorer Objects</title><content type='html'>As &lt;a href="http://rubyonwindows.blogspot.com/2007/05/automating-internet-explorer-with-ruby_20.html"&gt;mentioned previously&lt;/a&gt;, you cannot use the &lt;b&gt;WIN32OLE.connect&lt;/b&gt; method to connect to a running instance of Internet Explorer, as you would do, for example, with Excel or Word. I'll explain now how to do this via the Windows Shell.&lt;br /&gt;&lt;br /&gt;First, here's the code snippet, which grabs an instance of IE that has this blog displayed...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;for window in WIN32OLE.new('Shell.Application').Windows&lt;br /&gt;    begin&lt;br /&gt;        if window.Document.Title =~ /Ruby on Windows/&lt;br /&gt;            ie = window&lt;br /&gt;        end&lt;br /&gt;    rescue&lt;br /&gt;    end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The &lt;a href="http://rubyonwindows.blogspot.com/2007/05/automating-windows-shell-with-ruby.html"&gt;Windows Shell object&lt;/a&gt; includes a &lt;b&gt;Windows&lt;/b&gt; method which returns a collection of all of the open windows that belong to the Shell...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;shell = WIN32OLE.new('Shell.Application')&lt;br /&gt;windows = shell.Windows&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This is actually a collection of Internet Explorer objects, though some of these may be Internet Explorer web browser windows and others may be Windows Explorer windows.&lt;br /&gt;&lt;br /&gt;To get a count of the number of windows, call the &lt;b&gt;Count&lt;/b&gt; method...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;windows.Count&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To reference a member of this collection by index, call the &lt;b&gt;Item&lt;/b&gt; method, passing it the (zero-based) index...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;first_window = windows.Item(0)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Internet Explorer windows will normally have a Document object, which will have a Title property, so to find the IE window that you want to work with, iterate over the Windows collection and check the &lt;b&gt;Document.Title&lt;/b&gt; value...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;for window in windows&lt;br /&gt;    begin&lt;br /&gt;        if window.Document.Title =~ /Ruby on Windows/&lt;br /&gt;            ie = window&lt;br /&gt;        end&lt;br /&gt;    rescue&lt;br /&gt;    end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...and now you have your IE application object to work with &lt;a href="http://rubyonwindows.blogspot.com/2007/05/automating-internet-explorer-with-ruby_20.html"&gt;as previously discussed&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Note that not all Shell Window objects will have a Document object, so (in the example above) wrapping the code within a begin... rescue... end block would handle the error that occurs with non-IE windows.&lt;br /&gt;&lt;br /&gt;Make sense? Let me know if you have questions or comments, and thanks for stopping by!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-2177651279745083546?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/2177651279745083546/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=2177651279745083546' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/2177651279745083546'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/2177651279745083546'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/05/shell-windows-collection-of-internet.html' title='The Shell Windows Collection of Internet Explorer Objects'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-3160390778719935299</id><published>2007-05-20T10:34:00.000-05:00</published><updated>2007-07-22T11:09:27.779-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='ie'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Automating Internet Explorer with Ruby: Without Watir</title><content type='html'>In a &lt;a href="http://rubyonwindows.blogspot.com/2007/05/automating-internet-explorer-with-ruby.html"&gt;previous post&lt;/a&gt;, I discussed how to use the &lt;b&gt;watir&lt;/b&gt; library to automate Microsoft Internet Explorer. I should also mention that you can also do this directly, using the &lt;b&gt;win32ole&lt;/b&gt; library. Let's take a look at how to do this...&lt;br /&gt;&lt;br /&gt;First, require the &lt;b&gt;win32ole&lt;/b&gt; library...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Create a new instance of Internet Explorer...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ie = WIN32OLE.new('InternetExplorer.Application')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;(NOTE: You cannot use the &lt;b&gt;WIN32OLE.connect&lt;/b&gt; method to connect to a running instance of Internet Explorer, as you would do, for example, with Excel or Word. I'll explain later how to do this via the Windows Shell. Stay tuned...)&lt;br /&gt;&lt;br /&gt;You can show or hide the IE window by setting the &lt;b&gt;Visible&lt;/b&gt; property...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ie.Visible = true&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Navigate to a URL using the &lt;b&gt;Navigate&lt;/b&gt; method...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ie.Navigate('http://google.com')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Wait until IE has completed loading the page, by checking the &lt;b&gt;ReadyState&lt;/b&gt; property...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;sleep(1) until ie.ReadyState == 4&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;When a web page is loaded into IE, the contents of the page are represented by the &lt;b&gt;Document&lt;/b&gt; object. The document object's &lt;b&gt;All&lt;/b&gt; method returns a collection of all elements within the document, such as input textboxes, links, buttons, etc. You may reference a document element by name, which you will find in the HTML code for that page. For example, viewing the source for the google.com top page shows us, among others things, that we have a textbox named 'q' and a button named 'btnG'...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;input maxlength=2048 name=q size=55 title="Google Search" value=""&lt;br /&gt;input name=btnG type=submit value="Google Search"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Let's enter a value into the textbox named 'q' by setting its &lt;b&gt;Value&lt;/b&gt; property...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ie.Document.All.q.Value = 'ruby on windows'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...and then click the button named 'btnG', by calling its &lt;b&gt;Click&lt;/b&gt; method...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ie.Document.All.btnG.click&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...and wait until IE has completed loading the page...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;sleep(1) until ie.ReadyState == 4&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The &lt;b&gt;Document.All.Tags&lt;/b&gt; method returns a collection of all elements with the specified HTML tag, such as 'a', 'tr', or 'input'. So, to get a collection of all links, you could do this...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;links = ie.Document.All.Tags('a')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You could then iterate over this collection...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;for link in links do&lt;br /&gt;    puts link.InnerText  # print the link's text&lt;br /&gt;    puts link.href       # print the link's URL&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To quit IE, call the application object's &lt;b&gt;Quit&lt;/b&gt; method...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ie.Quit&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;There is much more to automating IE than I what I have presented here, but this will hopefully get you started. As always, feel free to post a comment here or email me if there is a specific task or topic that you would like to see discussed here. &lt;br /&gt;&lt;br /&gt;This is all about helping you make better use of Ruby on Windows.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;br /&gt;&lt;br /&gt;&lt;a href="http://digg.com/submit?phase=2&amp;url=http://rubyonwindows.blogspot.com/2007/05/automating-internet-explorer-with-ruby_20.html"&amp;target="_blank"&gt;&lt;br /&gt;&lt;img border="0" alt="Digg my article" src="http://digg.com/img/badges/91x17-digg-button.gif"/&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-3160390778719935299?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/3160390778719935299/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=3160390778719935299' title='16 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/3160390778719935299'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/3160390778719935299'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/05/automating-internet-explorer-with-ruby_20.html' title='Automating Internet Explorer with Ruby: Without Watir'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>16</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-9211040419787230832</id><published>2007-05-16T20:10:00.000-05:00</published><updated>2007-07-29T19:45:22.646-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Adding Sound to Your Ruby Apps</title><content type='html'>Have you ever thought about including sounds in your Ruby application? Used sparingly, sound may enhance your applications by adding audio cues or a custom touch. You could, for example, play a beep or chime that announces the completion of a lengthy process. Perhaps a &lt;a href="http://www.palantir.net/2001/tma1/wav/cantdo.wav"&gt;humorous sound&lt;/a&gt; to accompany an error message .&lt;br /&gt;&lt;br /&gt;The &lt;b&gt;win32-sound&lt;/b&gt; library makes using sounds really simple.&lt;br /&gt;&lt;br /&gt;If you installed Ruby with the One-Click Ruby Installer, then you probably already have the win32-sound library installed, along with other win32 utilities. Otherwise, you can install it in seconds via gem. Open up a console window and enter..&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;gem install win32-sound&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To use the win32-sound library, add these require and include statements to the top of your script...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32/sound'&lt;br /&gt;include Win32&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Then, to play a sound file on the PC, call the &lt;b&gt;Sound.play&lt;/b&gt; method, passing it the name of the file...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Sound.play('chimes.wav')&lt;br /&gt;Sound.play('c:\sounds\hal9000.wav')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;In the example above, you don't have to include the path to the file 'chimes.wav', because 'chimes.wav' is usually installed in the Windows folder. But in most cases, you'll want to include the full path to the sound file.&lt;br /&gt;&lt;br /&gt;To generate a simple beep, call the &lt;b&gt;Sound.beep&lt;/b&gt; method, passing it the tone frequency (in Hertz, between 37 and 32767) and the duration in milliseconds. For example, to play a low tone for half a second...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Sound.beep(100, 500)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...or to play an annoyingly high-pitched tone for 3 full seconds...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Sound.beep(5000, 3000)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The complete docs for this library can be found &lt;a href="http://rubyforge.org/docman/view.php/85/1709/Sound.html"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Distributing your sound files with your application is also simple. You can embed your sound files in an executable created with &lt;a href="http://www.erikveen.dds.nl/rubyscript2exe/index.html"&gt;RubyScript2Exe&lt;/a&gt;, or include them in an install package produced with &lt;a href="http://www.jrsoftware.org/isinfo.php"&gt;Inno Setup&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Be careful not to overdo it, though, if your program is to be used by others besides yourself. It's a fine line between clever and annoying.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-9211040419787230832?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/9211040419787230832/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=9211040419787230832' title='15 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/9211040419787230832'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/9211040419787230832'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/05/adding-sound-to-your-ruby-apps.html' title='Adding Sound to Your Ruby Apps'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>15</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-126017733254216243</id><published>2007-05-15T19:52:00.000-05:00</published><updated>2007-06-03T07:57:13.147-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='faq'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>FAQ:  "script.rb" versus "script.rbw"</title><content type='html'>What's the difference between ".rb" and ".rbw" files?&lt;br /&gt;&lt;br /&gt;As you probably know, Windows associates certain filename extensions with certain programs. For example, double-click on a file with the ".xls" filename extension, and the file will be opened in Microsoft Excel (if it is installed). Use the One-Click Ruby Installer, and your ".rb" and ".rbw" files will be associated with the Ruby interpreter, so that double-clicking on a Ruby script automatically runs the script in the Ruby interpreter.&lt;br /&gt;&lt;br /&gt;But there are two Ruby interpreters installed on Windows, ruby.exe and rubyw.exe. ruby.exe is the standard interpreter, which runs your script in a command/console window. rubyw.exe is essentially the same as ruby.exe, but without the console window, so any output to the console (ie, 'puts' statements or error messages) will not be seen.&lt;br /&gt;&lt;br /&gt;Files with a ".rb" filename extension are associated with ruby.exe, while files with a ".rbw" filename extension are associated with rubyw.exe. Double-click on a ".rb" file in Windows Explorer and the script will open up a console window -- while it is running. The console window will automatically close when the script stops running. Double-click on a ".rbw" file in Windows Explorer and the script will run, but with no console window (and therefore no error messages).&lt;br /&gt;&lt;br /&gt;To ensure you see the necessary console output from your script, run it from a &lt;a href="http://rubyonwindows.blogspot.com/2007/05/how-do-i-get-to-command-prompt.html"&gt;command prompt&lt;/a&gt;, or from an editor/IDE that includes an output window.&lt;br /&gt;&lt;br /&gt;For more answers to Frequently-Asked Questions, check out posts with the &lt;a href"http://rubyonwindows.blogspot.com/search/label/faq"&gt;faq&lt;/a&gt; tag.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-126017733254216243?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/126017733254216243/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=126017733254216243' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/126017733254216243'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/126017733254216243'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/05/faq-scriptrb-versus-scriptrbw.html' title='FAQ:  &quot;script.rb&quot; versus &quot;script.rbw&quot;'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-5872532629941017823</id><published>2007-05-14T20:49:00.000-05:00</published><updated>2007-06-03T07:57:13.148-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='faq'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>FAQ: But don't I need an IDE?</title><content type='html'>No. A good lightweight text editor is all you'll need to start developing serious applications. I recommend that you start out with a lightweight editor, so that you can first get comfortable with the language (which won't take long). But as you become more comfortable using Ruby and your projects grow in complexity, you may wish to investigate a full-blown Integrated Development Environment (IDE).&lt;br /&gt;&lt;br /&gt;When you do go looking for an IDE, you'll now have several to choose from...&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.sapphiresteel.com"&gt;Ruby in Steel&lt;/a&gt; allows you to develop Ruby in Microsoft's Visual Studio IDE, while &lt;a href="http://aptana.com"&gt;Aptana&lt;/a&gt; integrates the former RadRails for developing Ruby on the Eclipse platform. &lt;a href="http://activestate.com/products/komodo_ide/"&gt;ActiveState's Komodo IDE&lt;/a&gt; is the big brother of the Komodo Editor. &lt;a href="http://www.codegear.com"&gt;Borland's CodeGear division&lt;/a&gt; (the Delphi people) just announced plans to jump into the Ruby IDE market later this year.&lt;br /&gt;&lt;br /&gt;As usual, Google for further details and opinions on this topic.&lt;br /&gt;&lt;br /&gt;For more answers to Frequently-Asked Questions, check out posts with the &lt;a href"http://rubyonwindows.blogspot.com/search/label/faq"&gt;faq&lt;/a&gt; tag.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-5872532629941017823?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/5872532629941017823/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=5872532629941017823' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/5872532629941017823'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/5872532629941017823'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/05/faq-but-dont-i-need-ide.html' title='FAQ: But don&apos;t I need an IDE?'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-8395750510967532252</id><published>2007-05-13T09:27:00.000-05:00</published><updated>2007-06-03T07:57:13.148-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='faq'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Ruby on Windows FAQs</title><content type='html'>This series of posts is an attempt to consolidate and address some of the most frequently asked questions (FAQs) regarding using Ruby on Windows. You'll find these FAQ posts located under the &lt;a href="http://rubyonwindows.blogspot.com/search/label/faq"&gt;faq&lt;/a&gt; tag.&lt;br /&gt;&lt;br /&gt;There's no doubt more questions to be asked and answers to be provided. Please post a comment here or send me email.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-8395750510967532252?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/8395750510967532252/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=8395750510967532252' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/8395750510967532252'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/8395750510967532252'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/05/ruby-on-windows-frequently-asked.html' title='Ruby on Windows FAQs'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-7286540515397304145</id><published>2007-05-13T09:14:00.000-05:00</published><updated>2007-06-03T07:57:13.149-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='faq'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>FAQ: What text editor should I use?</title><content type='html'>Any text editor -- even Windows Notepad -- will do, so long as you can save your documents as straight text. You'll eventually want something a bit more robust than Notepad, such as the &lt;a href="http://scintilla.org/SciTE.html"&gt;SciTE code editor&lt;/a&gt; that is installed by the One-Click Ruby Installer. More fully-featured code editors include the &lt;a href="http://www.e-texteditor.com/"&gt;e Text Editor&lt;/a&gt; and &lt;a href="http://www.activestate.com/products/komodo_edit/"&gt;ActiveState's Komodo Editor&lt;/a&gt;. Google for further details and recommendations.&lt;br /&gt;&lt;br /&gt;For more answers to Frequently-Asked Questions, check out posts with the &lt;a href"http://rubyonwindows.blogspot.com/search/label/faq"&gt;faq&lt;/a&gt; tag.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-7286540515397304145?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/7286540515397304145/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=7286540515397304145' title='13 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/7286540515397304145'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/7286540515397304145'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/05/what-text-editor-should-i-use.html' title='FAQ: What text editor should I use?'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>13</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-7909168822162855843</id><published>2007-05-13T09:01:00.000-05:00</published><updated>2007-06-03T07:57:13.149-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='faq'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>FAQ: How do I install new code libraries?</title><content type='html'>The One-Click Ruby Installer includes RubyGems. RubyGems is a standard format for distributing Ruby programs and libraries, and provides an easy-to-use tool for managing the installation of gem packages. To install a new library such as watir via gem, open a command window and enter...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;gem install watir&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To uninstall, enter (you probably guessed this by now)...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;gem uninstall watir&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;For complete details, check out the easy-to-read &lt;a href="http://www.rubygems.org/read/book/1"&gt;RubyGems User Guide&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-7909168822162855843?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/7909168822162855843/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=7909168822162855843' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/7909168822162855843'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/7909168822162855843'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/05/how-do-i-install-new-code-libraries.html' title='FAQ: How do I install new code libraries?'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-9100806961469625259</id><published>2007-05-13T08:58:00.000-05:00</published><updated>2007-06-03T07:57:13.150-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='faq'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>FAQ: How do I get to a command prompt?</title><content type='html'>Click your Windows Start button, then select Programs, then Accessories, then Command Prompt. Or click the Start button, select Run, type in 'cmd' and click OK. If you're not very familiar with the Windows command prompt, I recommend &lt;a href="http://www.bleepingcomputer.com/tutorials/tutorial76.html#intro"&gt;this brief but informative overview&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-9100806961469625259?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/9100806961469625259/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=9100806961469625259' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/9100806961469625259'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/9100806961469625259'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/05/how-do-i-get-to-command-prompt.html' title='FAQ: How do I get to a command prompt?'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-7682541600612792869</id><published>2007-05-13T08:53:00.000-05:00</published><updated>2007-06-03T07:57:13.150-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='faq'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>FAQ: How do I install Ruby on Windows?</title><content type='html'>The &lt;a href="http://rubyforge.org/projects/rubyinstaller"&gt;One-Click Ruby Installer&lt;/a&gt; should meet your needs for a painless Ruby installation that includes most of the Ruby libraries you'll need starting out. It includes the SciTE Text Editor (my code editor of choice) and the WIN32OLE library, essential for COM automation.&lt;br /&gt;&lt;br /&gt;For more answers to Frequently-Asked Questions, check out posts with the &lt;a href"http://rubyonwindows.blogspot.com/search/label/faq"&gt;faq&lt;/a&gt; tag.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-7682541600612792869?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/7682541600612792869/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=7682541600612792869' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/7682541600612792869'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/7682541600612792869'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/05/how-do-i-install-ruby-on-windows.html' title='FAQ: How do I install Ruby on Windows?'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-7597151938722078636</id><published>2007-05-06T11:18:00.000-05:00</published><updated>2007-06-03T07:57:13.151-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><category scheme='http://www.blogger.com/atom/ns#' term='shell'/><title type='text'>Automating the Windows Shell with Ruby</title><content type='html'>The Microsoft Windows Shell provides a set of objects and methods that allow you to automate the Windows Shell with Ruby. You can use these objects and methods to access many of the Shell's functions.&lt;br /&gt;&lt;br /&gt;Let's start with an example involving accessing your CD-ROM drive...&lt;br /&gt;&lt;br /&gt;As usual, we'll start by requiring the &lt;b&gt;win32ole&lt;/b&gt; library...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Next, we'll create an instance of the Windows Shell object...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;shell = WIN32OLE.new("Shell.Application")&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now, we'll call the shell object's &lt;b&gt;NameSpace&lt;/b&gt; method to obtain a reference to the "My Computer" folder...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;my_computer = shell.NameSpace(17)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The value passed to the NameSpace method represents a special folder ("My Computer" = 17).&lt;br /&gt;&lt;br /&gt;To obtain a reference to the drive object, we'll call the NameSpace object's &lt;b&gt;ParseName&lt;/b&gt; method, passing it the drive letter string for the CD-ROM drive...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;cdrom = my_computer.ParseName("E:\\")&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Shell objects such as drives and folders have a collection of Verbs that can be called upon. We can see the list if verbs available for a drive by iterating over the drive's &lt;b&gt;Verbs&lt;/b&gt; collection and printing out the &lt;b&gt;Name&lt;/b&gt; value...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;cdrom.Verbs.each do |verb|&lt;br /&gt;    puts verb.Name&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The list of verbs may vary depending on the type of disc in the drive, but you may see something like this...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;Play&lt;br /&gt;S&amp;earch...&lt;br /&gt;&amp;Open&lt;br /&gt;E&amp;xplore&lt;br /&gt;Auto&amp;Play&lt;br /&gt;Form&amp;at&lt;br /&gt;&amp;Use with DLA&lt;br /&gt;S&amp;haring and Security...&lt;br /&gt;Scan with &amp;AVG Free&lt;br /&gt;E&amp;ject&lt;br /&gt;&amp;Copy&lt;br /&gt;Create &amp;Shortcut&lt;br /&gt;P&amp;roperties&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Note the ampersand (&amp;) in these verb names, which represent the context menu shortcut keys.&lt;br /&gt;&lt;br /&gt;To perform an action represented by a Verb, locate the verb by &lt;b&gt;Name&lt;/b&gt;, then call that Verb's &lt;b&gt;doIt&lt;/b&gt; method. So, to eject your CD-ROM drive, you can do this...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;cdrom.Verbs.each do |verb|&lt;br /&gt;    verb.doIt if verb.Name == "E&amp;ject"&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Putting it all together, we could whip up a little CdRom class that encapsulates such functionality...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class CdRom&lt;br /&gt;&lt;br /&gt;    attr_accessor :drive, :drive_letter, :verbs&lt;br /&gt;&lt;br /&gt;    def initialize(drive_letter)&lt;br /&gt;        my_computer = 17&lt;br /&gt;        @drive_letter = drive_letter&lt;br /&gt;        sh = WIN32OLE.new("Shell.Application")&lt;br /&gt;        @drive= sh.NameSpace(my_computer).ParseName("#{@drive_letter}")&lt;br /&gt;        @verbs = []&lt;br /&gt;        @drive.Verbs.each do |verb|&lt;br /&gt;            @verbs &lt;&lt; verb.Name if verb.Name != ''&lt;br /&gt;        end&lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;    def invoke_verb(verb_name)&lt;br /&gt;        @drive.Verbs.each do |verb|&lt;br /&gt;            verb.doIt if verb.Name== verb_name&lt;br /&gt;        end&lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;    def eject&lt;br /&gt;        self.invoke_verb("E&amp;ject")&lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;    def open&lt;br /&gt;        self.invoke_verb("&amp;Open")&lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;    def explore&lt;br /&gt;        self.invoke_verb("E&amp;xplore")&lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;    def play&lt;br /&gt;        self.invoke_verb("&amp;Play")&lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...which could be used like this...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;cd = CdRom.new('d:\\')&lt;br /&gt;puts cd.verbs&lt;br /&gt;cd.eject&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;A tip of the hat goes to Masaki Suketa, who informed me (via the comp.lang.ruby group) that the standard InvokeVerb method does not currently work in the win32ole library, and to use the &lt;b&gt;verb.doIt&lt;/b&gt; method instead.&lt;br /&gt;&lt;br /&gt;That's all for now. As always, feel free to comment here or email me if you have special requests.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-7597151938722078636?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/7597151938722078636/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=7597151938722078636' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/7597151938722078636'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/7597151938722078636'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/05/automating-windows-shell-with-ruby.html' title='Automating the Windows Shell with Ruby'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-8071923906185217335</id><published>2007-05-03T21:36:00.000-05:00</published><updated>2007-07-22T11:10:41.482-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='ie'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><category scheme='http://www.blogger.com/atom/ns#' term='watir'/><title type='text'>Automating Internet Explorer with Ruby: Watir</title><content type='html'>A reader has asked about automating Internet Explorer (IE) with Ruby. While you can do this using the &lt;b&gt;win32ole&lt;/b&gt; library, I strongly recommend using the &lt;b&gt;watir&lt;/b&gt; library instead.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://wtr.rubyforge.org/"&gt;WATIR&lt;/a&gt; (pronounced "water") stands for "Web Application Testing in Ruby". Watir is "a free, open-source functional testing tool for automating browser-based tests of web applications." Though it's designed for driving IE to test web applications, Watir is also very handy for automating IE in non-test scenarios. It leverages the win32ole library under the hood, but provides objects and methods that simplify the process.&lt;br /&gt;&lt;br /&gt;To use Watir, first install the watir gem. Open up a Command Prompt and type the following...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;gem install watir&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...then, in your script, require the &lt;b&gt;watir&lt;/b&gt; library...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'watir'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To create an instance of IE and navigate to a website...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ie = Watir::IE.new&lt;br /&gt;ie.goto('http://rubyonwindows.blogspot.com')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To click on a link...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ie.link(:text, "win32ole").click&lt;br /&gt;ie.link(:url, 'http://rubyonwindows.blogspot.com/search/label/win32ole').click&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To input text into a text field...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ie.text_field(:name, 'login').set('MyLogin')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To select an option from a drop-down list...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ie.select_list( :name , "HallOfFame").select("Dave Concepcion")&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To click a button...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ie.button(:value, 'Submit').click&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To close the browser, call the &lt;b&gt;close&lt;/b&gt; method...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;ie.close&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That's the basics. For further details and examples, you'll definitely want to check out the &lt;a href="http://wtr.rubyforge.org/watir_user_guide.html"&gt;Watir User Guide&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;For the complete Watir API reference, check out the rdocs &lt;a href="http://wtr.rubyforge.org/rdoc/index.html"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Various extensions for Watir have popped up, including WatirRecorder, WatirMaker, and WET Web Tester. Google for further details and updated links.&lt;br /&gt;&lt;br /&gt;As always, post a comment here or send me an email with further questions or to request a topic for discussion.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;br /&gt;&lt;br /&gt;&lt;a href="http://digg.com/submit?phase=2&amp;url=http://rubyonwindows.blogspot.com/2007/05/automating-internet-explorer-with-ruby.html"&amp;target="_blank"&gt;&lt;img border="0" alt="Digg my article" src="http://digg.com/img/badges/91x17-digg-button.gif"/&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-8071923906185217335?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/8071923906185217335/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=8071923906185217335' title='19 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/8071923906185217335'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/8071923906185217335'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/05/automating-internet-explorer-with-ruby.html' title='Automating Internet Explorer with Ruby: Watir'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>19</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-3457898574456966375</id><published>2007-04-29T12:22:00.000-05:00</published><updated>2007-06-03T07:57:13.152-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='msproject'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Automating Microsoft Project with Ruby</title><content type='html'>A reader recently asked for a discussion of automating Microsoft Project with Ruby. Let's dive right into some examples...&lt;br /&gt;&lt;br /&gt;Create an instance of the MSProject Application class...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;app = WIN32OLE.new("MSProject.Application")&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Note that Project (like Outlook and PowerPoint) is a single-instance application. This means that if an instance of Project is already running, that instance will be returned, even though you called the &lt;b&gt;WIN32OLE.new&lt;/b&gt; method. If Project is not currently running, a new instance will be launched.&lt;br /&gt;&lt;br /&gt;To show or hide the Application window, set the Visible property...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;app.Visible = true&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The application object contains a Projects collection of all open projects. To create a new project, call its &lt;b&gt;Add&lt;/b&gt; method...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;project = app.Projects.Add&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Oddly, the Projects collection doesn't have an &lt;b&gt;Open&lt;/b&gt; method. To open an existing project, call the application object's &lt;b&gt;FileOpen&lt;/b&gt; method...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;app.FileOpen('c:\temp\project1.mpp')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...then reference the active project...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;project = app.ActiveProject&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Use the &lt;b&gt;Title&lt;/b&gt; method to set or get the Project's title...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;project.Title = 'My Project Title'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You can iterate over the &lt;b&gt;Tasks&lt;/b&gt; collection, and call its &lt;b&gt;Add&lt;/b&gt; method to create a new Task...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;tasks = project.Tasks&lt;br /&gt;(1..100).each do |i|&lt;br /&gt;    task = tasks.Add&lt;br /&gt;    task.Type = 1&lt;br /&gt;    task.Name = "Task #{i.to_s}"&lt;br /&gt;    task.Notes = "Your comments here..."&lt;br /&gt;    task.Text30 = "My Specific Text"&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;A Task object has a variety of properties/methods (some shown above), and includes a Resources collection, over which you can iterate...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;project.Tasks.each do |task|&lt;br /&gt;    puts task.Name&lt;br /&gt;    puts task.Resources.Count&lt;br /&gt;    task.Resources.each do |resource|&lt;br /&gt;        puts resource.Name&lt;br /&gt;    end&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Save the active project by calling the Application object's &lt;b&gt;FileSaveAs&lt;/b&gt; or &lt;b&gt;FileSave&lt;/b&gt; methods...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;app.FileSaveAs('c:\temp\project3.mpp')&lt;br /&gt;app.FileSave&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To exit, call the Application's &lt;b&gt;Quit&lt;/b&gt; method...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;app.Quit&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As &lt;a href="http://rubyonwindows.blogspot.com/2007/04/ruby-win32ole-inspecting-objects.html"&gt;mentioned previously&lt;/a&gt;, there are various means for exploring the Project object model, and I encourage you to pursue these.&lt;br /&gt;&lt;br /&gt;As always, post a comment or send me email if you have questions, comments, or a request for future topics.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-3457898574456966375?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/3457898574456966375/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=3457898574456966375' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/3457898574456966375'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/3457898574456966375'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/04/automating-microsoft-project-with-ruby.html' title='Automating Microsoft Project with Ruby'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-51378938883300057</id><published>2007-04-26T19:46:00.000-05:00</published><updated>2007-06-03T07:57:13.152-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='excel'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Ruby &amp; Excel: The InputBox Hack</title><content type='html'>A ruby-talk (comp.lang.ruby) reader asked &lt;a href="http://groups.google.com/group/comp.lang.ruby/browse_thread/thread/5248ee2b2e488d50/"&gt;  how to get user input on Windows&lt;/a&gt; without resorting to console input.&lt;br /&gt;&lt;br /&gt;While you can use Windows API calls to create a Yes/No message box, there doesn't appear to be a simple API means for creating a dialog for accepting user string input.&lt;br /&gt;&lt;br /&gt;As others on the thread have mentioned, you can use one of the GUI toolkits (ie, wxRuby) and, if you have other GUI needs, this is the best option. If, however, the only GUI need you have is for a single input dialog, you could use the InputBox function from Excel, via the win32ole library (assuming Excel is installed):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;&lt;br /&gt;def get_input(prompt='', title='')&lt;br /&gt;    excel = WIN32OLE.new('Excel.Application')&lt;br /&gt;    response = excel.InputBox(prompt, title)&lt;br /&gt;    excel.Quit&lt;br /&gt;    excel = nil&lt;br /&gt;    return response&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;response = get_input('My Prompt', 'My Title') &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Granted, this is somewhat of a hack if you're loading Excel into memory merely to display an input dialog. On the other hand, it may be simpler than adding an entire GUI library to your project, especially if you'll eventually be wrapping up all those dependencies into a distributed package, a la rubyscript2exe.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-51378938883300057?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/51378938883300057/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=51378938883300057' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/51378938883300057'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/51378938883300057'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/04/ruby-excel-inputbox-hack.html' title='Ruby &amp; Excel: The InputBox Hack'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-7709588214243474540</id><published>2007-04-25T06:59:00.000-05:00</published><updated>2007-06-03T07:57:13.153-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='excel'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Ruby &amp; Excel: Inserting and Deleting Rows and Columns</title><content type='html'>A reader has asked how to insert a row into a worksheet.&lt;br /&gt;&lt;br /&gt;To insert a row, call the &lt;b&gt;Insert&lt;/b&gt; method on the Row object where you wish to insert a new row. So this...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;worksheet.Rows(2).Insert&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...will move row 2 down one row and insert a new row above it.&lt;br /&gt;&lt;br /&gt;You can insert multiple rows by passing the Insert method a colon-separated string, so...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;worksheet.Rows('2:5').Insert&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...inserts 4 new rows and moves rows 2 to 5 down. Note that this is a string and must therefore be quoted.&lt;br /&gt;&lt;br /&gt;Similarly, to insert a new column, call the &lt;b&gt;Insert&lt;/b&gt; method on the Column object where you wish to insert a new column. So either of these...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;worksheet.Columns(2).Insert&lt;br /&gt;worksheet.Columns('B').Insert&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...will move column B/2 over one column (to the right) and insert a new column B.&lt;br /&gt;&lt;br /&gt;This...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;worksheet.Columns('2:5').Insert&lt;br /&gt;worksheet.Columns('B:E').Insert&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...inserts 4 new columns and moves columns 2 to 5 to the right. Note that this is a string and must therefore be quoted.&lt;br /&gt;&lt;br /&gt;Deleting Rows and Columns works the same way, using the &lt;b&gt;Delete&lt;/b&gt; method...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;worksheet.Rows(2).Delete&lt;br /&gt;worksheet.Rows('2:5').Delete&lt;br /&gt;worksheet.Columns(2).Delete&lt;br /&gt;worksheet.Columns('B').Delete&lt;br /&gt;worksheet.Columns('C:E').Delete&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Keep in mind that after inserting or deleting rows, your row (or column) indexes change -- when you delete Rows(2), Rows(3) becomes Rows(2), etc.&lt;br /&gt;&lt;br /&gt;That's all for now. Got a question or suggestion? Post a comment or send me email.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-7709588214243474540?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/7709588214243474540/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=7709588214243474540' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/7709588214243474540'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/7709588214243474540'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/04/ruby-excel-inserting-and-deleting-rows.html' title='Ruby &amp; Excel: Inserting and Deleting Rows and Columns'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-6084583438407197201</id><published>2007-04-23T21:03:00.000-05:00</published><updated>2007-06-03T07:57:13.154-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='word'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Automating Word with Ruby: The Range Object</title><content type='html'>In &lt;a href="http://rubyonwindows.blogspot.com/2007/04/automating-word-with-ruby-document.html"&gt;a previous installment&lt;/a&gt;, we looked at the Word Document object. Now we're going to work with the contents of the Document object, via the Range object.&lt;br /&gt;&lt;br /&gt;First, let's create an instance of the Word Application object and add a new Document object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;word = WIN32OLE.new('Word.Application')&lt;br /&gt;word.Visible = true&lt;br /&gt;document = word.Documents.Add&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;The Range Object&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;As the name implies, a Range object represents a range within the Document, with a defined starting point and end point. You can define a Range by calling the Document object's Range method and passing it two arguments. The starting point is the character index &lt;i&gt;&lt;b&gt;before&lt;/b&gt;&lt;/i&gt; the start of the Range, and the end point is the character index at the end of the Range. So, to get a Range containing the first five characters of a document...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;first5characters = document.Range(0, 5)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To get a Range containing characters 6 through 15...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;next10characters = document.Range(5, 15)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Keep in mind that the Range method returns a Range object. To get the text for the Range object, call its Text method:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;txt = document.Range(0, 5).Text&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The Word document object's &lt;b&gt;Characters&lt;/b&gt;, &lt;b&gt;Words&lt;/b&gt;, and &lt;b&gt;Sentences&lt;/b&gt; methods are shortcuts that return Range objects.&lt;br /&gt;&lt;br /&gt;A range may represent the entire document...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;document.Range&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...a single word or sentence...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;word5 = document.Words(5)&lt;br /&gt;fifth_sentence = document.Sentences(5)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...or a single character...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;first_character = document.Characters(1)&lt;br /&gt;first_character = document.Characters.first&lt;br /&gt;first_character = document.Range(0, 1)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To get (or set) the text of a Range, call its Text method...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;fifth_sentence_text = document.Sentences(5).text&lt;br /&gt;document.Sentences(5).text = "New text for sentence five."&lt;br /&gt;all_text = document.Range.Text&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To insert text into a Document, define a single-point Range and call its Text method. For example, to insert text at the start of the document...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;document.Range(0, 0).Text = "New text at start of document. "&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To be continued...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-6084583438407197201?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/6084583438407197201/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=6084583438407197201' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/6084583438407197201'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/6084583438407197201'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/04/automating-word-with-ruby-range-object.html' title='Automating Word with Ruby: The Range Object'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-6914890165385870658</id><published>2007-04-17T19:30:00.000-05:00</published><updated>2007-06-03T07:57:13.154-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='word'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Automating Word with Ruby: The Document Object</title><content type='html'>In &lt;a href="http://rubyonwindows.blogspot.com/2007/04/automating-word-with-ruby-application.html"&gt;a previous episode&lt;/a&gt;, we discussed the Word application object and looked at some of its properties and methods. Now we'll take a look at the Document object, which (of course) represents a Word document.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Creating and Opening Documents&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The &lt;b&gt;application.Documents&lt;/b&gt; method returns a collection representing the currently open documents. To create a new document, call this collection's &lt;b&gt;Add&lt;/b&gt; method:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;word = WIN32OLE.new('Word.Application')&lt;br /&gt;word.Visible = true&lt;br /&gt;document = word.Documents.Add&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You can optionally pass this method the name of a template to use for the new document:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;document = word.Documents.Add('Normal')&lt;br /&gt;document = word.Documents.Add('Normal.dot')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To open an existing Word document file, call this collection's &lt;b&gt;Open&lt;/b&gt; method, passing it the file name:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;word = WIN32OLE.new('Word.Application')&lt;br /&gt;word.Visible = true&lt;br /&gt;document = word.Documents.Open('c:\WordDocs\MyWordFile.doc')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Calling the Documents collection's &lt;b&gt;Count&lt;/b&gt; method returns the number of documents in the collection, and you can iterate over this collection:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;puts word.Documents.Count&lt;br /&gt;for document in word.Documents&lt;br /&gt;    # ...code...&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To reference the currently active document, call the application object's &lt;b&gt;ActiveDocument&lt;/b&gt; method:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;word = WIN32OLE.connect('Word.Application')&lt;br /&gt;document = word.ActiveDocument&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The document object contains numerous collections, including:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;document.Paragraphs&lt;br /&gt;document.Sentences&lt;br /&gt;document.Words&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Need to know the name and/or path of the document? There are three read-only methods you may find helpful:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;document.FullName   # returns the full path and filename&lt;br /&gt;document.Name       # returns just the filename, without the path&lt;br /&gt;document.Path       # returns the full path, without the filename&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;b&gt;Printing, Saving, and Closing Your Document&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;To print a document, call its &lt;b&gt;PrintOut&lt;/b&gt; method:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;document.PrintOut&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To save a document, call its &lt;b&gt;Save&lt;/b&gt; or &lt;b&gt;SaveAs&lt;/b&gt; methods:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;document.SaveAs('c:\temp\MyDocument.doc')&lt;br /&gt;document.Save&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You can pass the &lt;b&gt;SaveAs&lt;/b&gt; method an optional second argument to specify the file format. A few examples:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;document.SaveAs('c:\temp\MyDocument.doc', 0)    # Word document (default)&lt;br /&gt;document.SaveAs('c:\temp\MyDocument.dot', 1)    # Word template&lt;br /&gt;document.SaveAs('c:\temp\MyDocument.txt', 2)    # Text&lt;br /&gt;document.SaveAs('c:\temp\MyDocument.rtf', 6)    # Rich Text Format (RTF)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To close a document, call its &lt;b&gt;Close&lt;/b&gt; method:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;document.Close&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That about wraps up our show for today. Coming up, we'll start working with document contents. As always, let me know if there is anything specific that you would like to see discussed here.&lt;br /&gt;&lt;br /&gt;Thanks for stopping by!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-6914890165385870658?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/6914890165385870658/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=6914890165385870658' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/6914890165385870658'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/6914890165385870658'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/04/automating-word-with-ruby-document.html' title='Automating Word with Ruby: The Document Object'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-1193902824191683249</id><published>2007-04-15T08:50:00.000-05:00</published><updated>2007-06-03T07:57:13.155-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='win32ole'/><category scheme='http://www.blogger.com/atom/ns#' term='excel'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Automating Excel with Ruby: Defining a Range and Inserting Data</title><content type='html'>As I &lt;a href="http://rubyonwindows.blogspot.com/2007/04/automating-excel-with-ruby-rows-columns.html"&gt;previously mentioned&lt;/a&gt;, calling the Value method on an Excel Range object returns an array of arrays of values. Here's a quick way to get a 2-dimensional array of all values from a worksheet...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;data = worksheet.UsedRange.Value&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;But, as I failed to mention in that article, you can also &lt;i&gt;insert&lt;/i&gt; a 2-dimensional array into a Range of cells:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;worksheet.Range("A2:G7").Value = data&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If you have a lot of data to insert, this will be much faster than inputting the data cell-by-cell. But it is important that the range of cells be the same size (number of rows, number of columns) as your 2d array.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Your Array: Number of Rows&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Assuming you have &lt;b&gt;data&lt;/b&gt;, a 2-dimensional array, you can determine the number of rows by calling &lt;b&gt;data.size&lt;/b&gt;:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;rows = data.size&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Your Array: Number of Columns&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;You can determine the number of columns by calling &lt;b&gt;size&lt;/b&gt; on one of the &lt;b&gt;data&lt;/b&gt; array's rows:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;cols = data[0].size&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Defining Your Worksheet Range&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;But to use the Range method, you need to define the column &lt;b&gt;letters&lt;/b&gt; (ie, "&lt;b&gt;A&lt;/b&gt;2:&lt;b&gt;G&lt;/b&gt;7"). You probably already know that you want your starting point to be cell A1 or A2. But how do you programmatically determine the final column letter for an array that is 104 columns wide?&lt;br /&gt;&lt;br /&gt;Each cell (or row, or column) object has an &lt;b&gt;Address&lt;/b&gt; method that returns its location in letter-number format, so...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;worksheet.Cells(5, 3).Address&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...returns "$C$5", and we can use that string in our upcoming Range definition. So if you have a range that is 30 columns wide by 100 rows in length, which we want to insert into a Range, starting at cell A1:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;first = worksheet.Cells(1,1).Address        # =&gt; "$A$1"&lt;br /&gt;last = worksheet.Cells(100,30).Address      # =&gt; "$AD$100"&lt;br /&gt;worksheet.Range("#{first}:#{last}").Value = data&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If your worksheet has a header row and you will be inserting your data after that, bump up the row value by 1:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;first = worksheet.Cells(2,1).Address        # =&gt; "$A$2"&lt;br /&gt;last = worksheet.Cells(101,30).Address      # =&gt; "$AD$101"&lt;br /&gt;worksheet.Range("#{first}:#{last}").Value = data&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I hope that some of you find this useful. My thanks goes to Charlie for suggesting the topic.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-1193902824191683249?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/1193902824191683249/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=1193902824191683249' title='10 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/1193902824191683249'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/1193902824191683249'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/04/automating-excel-with-ruby-defining.html' title='Automating Excel with Ruby: Defining a Range and Inserting Data'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-2525157240575512762</id><published>2007-04-14T07:31:00.000-05:00</published><updated>2007-06-03T07:57:13.155-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='word'/><category scheme='http://www.blogger.com/atom/ns#' term='win32ole'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Automating Word with Ruby: The Application Object</title><content type='html'>Automating Microsoft Word can involve hundreds of objects, each with its own properties and methods. You can use the &lt;b&gt;ole_methods&lt;/b&gt; method, or Word's built-in Object Browser, to browse the objects, properties, and methods. For further details, see &lt;a href="http://rubyonwindows.blogspot.com/2007/04/ruby-win32ole-inspecting-objects.html"&gt;this article&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;If you've read some of my &lt;a href="http://rubyonwindows.blogspot.com/search/label/excel"&gt;articles about automating Excel with Ruby&lt;/a&gt;, you will see similarities between the two object models.&lt;br /&gt;&lt;br /&gt;Let's start by looking at Microsoft Word's top-level Application object.&lt;br /&gt;&lt;br /&gt;You'll use the win32ole library to create a new instance of the Word application object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;require 'win32ole'&lt;br /&gt;word = WIN32OLE.new('Word.Application')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...or to connect to an existing instance of the Word application object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;word = WIN32OLE.connect('Word.Application')&lt;br /&gt;document = word.ActiveDocument&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I often use the &lt;b&gt;connect&lt;/b&gt; method for ad hoc scripts to perform a series of actions on a Word document (or Excel workbook) that I already have open.&lt;br /&gt;&lt;br /&gt;Note that a new instance of Word will be not visible by default. To show it, set the application object's Visible property to true:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;word.Visible = true&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You may want to first hide Word until your data input and formatting process is complete, then make it visible. This may speed things up, and prevents the user from interfering with your program (and vice versa).&lt;br /&gt;&lt;br /&gt;You'll spend most of your time working with Document objects, but first let's look at some of the properties and methods for the Application object.&lt;br /&gt;&lt;br /&gt;Call the &lt;b&gt;Version&lt;/b&gt; method to determine the version of Word, which is returned as a decimal string (ie, "11.0"):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;if word.Version.to_i =&gt; 11&lt;br /&gt;    # do something&lt;br /&gt;else&lt;br /&gt;    # do something else&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You can set the &lt;b&gt;PrintPreview&lt;/b&gt; property to place Word in print preview mode:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;word.PrintPreview = true&lt;br /&gt;word.PrintPreview = false   # return to previous view mode&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The &lt;b&gt;System&lt;/b&gt; object includes a number of (read-only) properties about the system that Word is running on. Most of these will be of no use whatsoever to you, but a few may prove helpful:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;word.System.OperatingSystem         # =&gt; "Windows NT"&lt;br /&gt;word.System.HorizontalResolution    # =&gt; 1440&lt;br /&gt;word.System.VerticalResolution      # =&gt; 900&lt;br /&gt;word.System.LanguageDesignation     # =&gt; "English (U.S.)"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The &lt;b&gt;FontNames, PortraitFontNames, and LandscapeFontNames&lt;/b&gt; methods return collections of font names:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;for font in FontNames&lt;br /&gt;    puts font&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The &lt;b&gt;Tasks&lt;/b&gt; method returns a collection of Task objects, representing tasks being run on the PC. Most of these tasks will be processes that you have no interest in. But you can, for example, use this to determine quickly if a particular application is currently running and, if so, address that application's window:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;if word.Tasks.Exists('Firefox')&lt;br /&gt;    word.Tasks('Firefox').Activate&lt;br /&gt;    word.Tasks('Firefox').WindowState = 1   # maximize&lt;br /&gt;    word.Tasks('Firefox').WindowState = 2   # minimize&lt;br /&gt;    word.Tasks('Firefox').WindowState = 0   # restore to normal&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Calling &lt;b&gt;&lt;a href="http://rubyonwindows.blogspot.com/2007/04/ruby-win32ole-inspecting-objects.html"&gt;ole_methods&lt;/a&gt;&lt;/b&gt; on one of these Task objects reveals what we may do with it:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;irb(main):004:0&gt; word.Tasks('Firefox').ole_methods&lt;br /&gt;&lt;br /&gt;=&gt; [QueryInterface, AddRef, Release, GetTypeInfoCount, &lt;br /&gt;GetTypeInfo, GetIDsOfNames, Invoke, Application, Creator, &lt;br /&gt;Parent, Name, Left, Left, Top, Top, Width, Width, Height, &lt;br /&gt;Height, WindowState, WindowState, Visible, Visible, &lt;br /&gt;Activate, Close, Move, Resize, SendWindowMessage, &lt;br /&gt;GetTypeInfoCount, GetTypeInfo, GetIDsOfNames, Invoke]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So we see that we can, for example, hide, unhide, move, resize, and close other application windows through the Task object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;word.Tasks('Firefox').Visible = false&lt;br /&gt;word.Tasks('Firefox').Visible = true&lt;br /&gt;word.Tasks('Firefox').Top = 100&lt;br /&gt;word.Tasks('Firefox').Left = 300&lt;br /&gt;word.Tasks('Firefox').Height = 500&lt;br /&gt;word.Tasks('Firefox').Width = 500&lt;br /&gt;word.Tasks('Firefox').Close&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You should, naturally, use the Tasks collection with care, as you could wreak havoc otherwise.&lt;br /&gt;&lt;br /&gt;To quit Word, call the &lt;b&gt;Quit&lt;/b&gt; method:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;word.Quit       #   Do not save changes&lt;br /&gt;word.Quit(0)    #   Do not save changes&lt;br /&gt;word.Quit(-1)   #   Save changes&lt;br /&gt;word.Quit(-2)   #   Prompt user to save changes&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That's all for now. Next up, we'll look at working with the Document object. As always, let me know if there are any specific topics you would like to see discussed here.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-2525157240575512762?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/2525157240575512762/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=2525157240575512762' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/2525157240575512762'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/2525157240575512762'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/04/automating-word-with-ruby-application.html' title='Automating Word with Ruby: The Application Object'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-1419559983327466086</id><published>2007-04-07T10:16:00.000-05:00</published><updated>2007-06-03T07:57:13.156-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='win32ole'/><category scheme='http://www.blogger.com/atom/ns#' term='excel'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Ruby &amp; WIN32OLE: Inspecting Objects</title><content type='html'>So, you know how to create the major WIN32OLE objects that you need, such as Excel's application, workbook, and worksheet objects. But what can you really &lt;b&gt;&lt;i&gt;do&lt;/i&gt;&lt;/b&gt; with them? Ruby allows you to use &lt;b&gt;Object.methods&lt;/b&gt; to get a list of methods that can be called on the Object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;"Baseball".methods&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...returns the following array of String methods...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;["methods", "instance_eval", "%", "rindex", "map", "&lt;&lt;", "split", "any?",&lt;br /&gt;"dup", "sort", "strip", "size", "instance_variables", "downcase", "min", "gsub!",&lt;br /&gt;"count", "include?", "succ!", "instance_of?", "extend", "downcase!", "intern",&lt;br /&gt;...&lt;br /&gt;"object_id", "length", "entries", "chomp", "=~", "require", "public_methods",&lt;br /&gt;"upcase", "sub!", "squeeze", "__send__", "upcase!", "crypt", "delete!", "equal?",&lt;br /&gt;"freeze", "detect", "zip", "[]", "lstrip!", "center", "[]=", "to_f"]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;For easier browsing, you can sort this array by calling the sort method...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;"Baseball".methods.sort&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This is handy when you need to call a Ruby method that you don't recall the exact name of. Scan the list of methods for the object you're working with.&lt;br /&gt;&lt;br /&gt;Call this on an object created through win32ole, and you see this...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;excel = WIN32OLE.new('Excel.Application')&lt;br /&gt;excel.methods&lt;br /&gt;["methods", "instance_eval", "dup", "_setproperty", "instance_variables",&lt;br /&gt;"instance_of?", "extend", "eql?", "ole_func_methods", "each", "hash", "id",&lt;br /&gt;"singleton_methods", "setproperty", "taint", "frozen?", "instance_variable_get",&lt;br /&gt;...&lt;br /&gt;"ole_get_methods", "nil?", "untaint", "gem", "send", "ole_obj_help", "display",&lt;br /&gt;"inspect", "clone", "=~", "object_id", "_getproperty", "require",&lt;br /&gt;"public_methods", "__send__", "equal?", "freeze", "ole_put_methods", "[]", "[]="]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Looks like the fairly generic Object methods, doesn't it? What would be helpful, in regards to OLE/COM automation, would be a method that returns the &lt;b&gt;&lt;i&gt;OLE&lt;/i&gt;&lt;/b&gt; methods of the object. Fortunately, the win32ole library provides just that in &lt;b&gt;Object.ole_methods&lt;/b&gt;. This method returns an array of WIN32OLE objects. To get a list of methods for the Excel application object...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;excel = WIN32OLE.new('Excel.Application')&lt;br /&gt;excel.ole_methods&lt;br /&gt;=&gt; [QueryInterface, AddRef, Release, GetTypeInfoCount, GetTypeInfo, GetIDsOfNames,&lt;br /&gt;Invoke, Application, Creator, Parent, ActiveCell, ActiveChart, ActiveDialog,&lt;br /&gt;ActiveMenuBar, ActivePrinter, ActivePrinter, ActiveSheet, ActiveWindow,&lt;br /&gt;...&lt;br /&gt;CalculateFull, FindFile, CalculationVersion, ShowWindowsInTaskbar, ShowW&lt;br /&gt;indowsInTaskbar, FeatureInstall, FeatureInstall, GetTypeInfoCount, GetTypeInfo,&lt;br /&gt;GetIDsOfNames, Invoke]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You can't sort this array of WIN32OLE objects as-is, but you can convert each element to a string and then sort the array of strings...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;excel.ole_methods.collect!{ |e| e.to_s }.sort&lt;br /&gt;["ActivateMicrosoftApp", "ActiveCell", "ActiveChart", "ActiveDialog",&lt;br /&gt;"ActiveMenuBar", "ActivePrinter", "ActivePrinter", "ActiveSheet", "ActiveWindow",&lt;br /&gt;"ActiveWorkbook", "AddChartAutoFormat", "AddCustomList", "AddIns", "AddRef",&lt;br /&gt;...&lt;br /&gt;"Width", "Width", "WindowState", "WindowState", "Windows", "WindowsForPens",&lt;br /&gt;"Workbooks", "WorksheetFunction", "Worksheets", "_Default", "_Evaluate",&lt;br /&gt;"_FindFile", "_Run2", "_WSFunction", "_Wait"]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Note that a similar method exists, &lt;b&gt;ole_get_methods&lt;/b&gt;, which does not return the exact same results as &lt;b&gt;ole_methods&lt;/b&gt;. The WIN32OLE documentation does not make clear (to me) the difference between these two methods. If ole_methods does not provide what you are looking for, try ole_get_methods.&lt;br /&gt;&lt;br /&gt;A graphical alternative to the above Ruby methods is to use an OLE Object Browser, such as the one included with Excel's Visual Basic Editor. To launch it from Excel or Word, select Tools =&gt; Macro =&gt; Visual Basic Editor. From the VB Editor, select View =&gt; Object Browser. Similar third-party and open source OLE browsers are also available.&lt;br /&gt;&lt;br /&gt;Okay, now you've got a list of WIN32OLE methods reference you can do something with. Browse through this list of meaningful method names and you'll be on the road to gaining some valuable insight into all that you can do with a given WIN32OLE object. Now that you have a method name, go to Google and search for it (ie, excel DisplayInfoWindow) and you'll find details on the method/property and how you may use it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-1419559983327466086?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/1419559983327466086/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=1419559983327466086' title='13 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/1419559983327466086'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/1419559983327466086'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/04/ruby-win32ole-inspecting-objects.html' title='Ruby &amp; WIN32OLE: Inspecting Objects'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>13</thr:total></entry><entry><id>tag:blogger.com,1999:blog-284600789737488130.post-2198149193542470441</id><published>2007-04-06T16:15:00.000-05:00</published><updated>2007-06-03T07:57:13.156-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='excel'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><title type='text'>Automating Excel with Ruby: Rows, Columns, Ranges, and Cells</title><content type='html'>An Excel worksheet object contains rows, columns, ranges, and cells. Let's look at some of the most common methods for working with these objects.&lt;br /&gt;&lt;br /&gt;The Cells method of the Worksheet object returns a single cell. It is called with the row number and column number...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;cell = worksheet.Cells(2, 5)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The Range method (of either the Application or Worksheet object) returns a collection of cells. It is called with a string value of First Cell and Last Cell, separate by a colon. The following code returns a collection of 20 cells in the rectangle from cell A1 to cell D5....&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;range = worksheet.Range("A1:D5")&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The worksheet.UsedRange method returns a collection of cells, from the upper-left-most used cell to the lower-right-most used cell. This is useful, because the used range is usually much smaller then the entire range of cells in a worksheet.&lt;br /&gt;&lt;br /&gt;A Worksheet or Range object also contains a (1-based index) collection of Rows and a (1-based index) collection of Columns, which you can iterate over...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;for row in worksheet.Rows&lt;br /&gt;    # ...code...&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;for column in worksheet.Columns&lt;br /&gt;    # ...code...&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;for row in worksheet.Range("A1:D5").Rows&lt;br /&gt;    # ...code...&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;for row in worksheet.UsedRange.Rows&lt;br /&gt;    # ...code...&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To get the number of rows or columns in a range, call the range's Count method...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(1..worksheet.UsedRange.Rows.Count).each do |row|&lt;br /&gt;    #...code...&lt;br /&gt;end&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You can reference a single Row or Column by index. For example, to apply a bold font to the first row of the worksheet...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;worksheet.Rows(1).Font.Bold = true&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To set the width of column 4 (D) to 25 pixels...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;worksheet.Columns(4).ColumnWidth = 25.0&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;A cell object has numerous child objects and methods. The Value (read/write) method returns a Cell's value as it is stored internally. A cell's Text (read-only) method returns the value as it is currently displayed. For example, if cell B4 has the value "1234.56789" formatted as Currency, the Value and Text methods return different values...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;worksheet.Cells(4, 2).Value     # =&gt; "1234.56789"&lt;br /&gt;worksheet.Cells(4, 2).Text      # =&gt; "$1234.57"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Calling the Value method on a Range object returns an array of arrays of values. Here's a quick way to get a 2-dimensional array of all values in a worksheet...&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;data = worksheet.UsedRange.Value&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You cannot call the Text method on a Range object.&lt;br /&gt;&lt;br /&gt;As previously mentioned, one of the best hands-on ways of learning the various Excel objects and methods/properties is to record a macro in Excel, then review the macro's VBA code and translate it to Ruby.&lt;br /&gt;&lt;br /&gt;As always, feel free to post comments or send email if you have questions, comments, or suggestions for future topics.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/284600789737488130-2198149193542470441?l=rubyonwindows.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://rubyonwindows.blogspot.com/feeds/2198149193542470441/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=284600789737488130&amp;postID=2198149193542470441' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/2198149193542470441'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/284600789737488130/posts/default/2198149193542470441'/><link rel='alternate' type='text/html' href='http://rubyonwindows.blogspot.com/2007/04/automating-excel-with-ruby-rows-columns.html' title='Automating Excel with Ruby: Rows, Columns, Ranges, and Cells'/><author><name>David Mullet</name><uri>http://www.blogger.com/profile/05597492688893461137</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry></feed>
