Blog
what did i learn today
setting word bookmarks from ruby
The easy solution to set a bookmark inside a Word-document: [ruby] require 'win32ole' word = WIN32OLE.new('Word.Application') doc = word.Documents.Add("#{path to your template here}") doc.Bookmarks("bookmark-name").Range.Text = "new content here" [/ruby] This will work, and will replace the bookmark with the text you wanted to insert. Recently i had to convert a document that was first built using mail-merge. I am not very familiar with mail-merge, but it seems a specific field can occur multiple times inside the document. This is very convenient. So i looked for a way to replace this behaviour, and still use bookmarks. I know how to set bookmarks programmatically, and this seemed easier than preparing a temporary document first for mail-merging. My first thought was that i create bookmarks, and give the duplicate bookmarks names like "#{original -bookmark-name}_2" , and inside my program i would go looking if there would be any copies (_2, _3, _4) and set them accordingly. But i looked further. There should be a better way. Luckily there does exist something called "REF" fields inside Word, which are fields that inherit their content from a certain bookmark! A-ha! But the above code won't work, because i actually replace the bookmark with the text, and then nothing is left to be reffered to :) So the code to set a bookmark turns a bit more complex: [ruby] require 'win32ole' word=WIN32OLE.new('Word.Application') doc = word.Documents.Add("#{your-template-path}") ## wdGoToBookmark = -1, wdCharacter = 1 word.Selection.GoTo("What" => -1, "Name" => "#{your-bookmark-name}") word.Selection.Delete("Unit" => 1, "Count" => 1) word.Selection.InsertAfter "#{your-new-text}" # re-create our bookmark doc.Bookmarks.Add("Range" => word.Selection.Range, "Name" => "#{your-bookmark-name}") # update all (referring) fields doc.Fields.Update # in Word2007, filetype 16 saves as a Word2003 compatible document, 12 is docx, 17 is pdf doc.SaveAs "#{your-filename}.doc", 16 [/ruby] [UPDATE] Actually, although this works, it doesn't work if two bookmarks are really close, e.g. only seperated by a single space. So the above is not the correct way. Some googling revealed the correct way: [ruby] # new version of code bm_name = "#{your-bookmark-name}" bm_range = doc.Bookmarks(bm_name).Range bm_range.Text = "insert something interesting here" doc.Bookmarks.Add bm_name, bm_range [/ruby] I hope this helps :)
working on Windows Server 2008R2 64-bit, installing ruby 1.8.7, ruby-oci8 ran into the following error, after requiring 'oci8' : LoadError 193: %1 is not a valid Win32 application On this machine a 64bit version of Oracle was installed. So the OCI.dll was a 64-bit version. The fix seemed easy: copy a 32-bit version of the OCI.dll. But I had to copy (from another 32-bit machine)
  • OCI.dll (32-bit)
  • MSVCR71.dll
  • ... and ORAOCIEI10.dll
    to my ruby\bin folder, and then everything worked. Copying just the OCI.dll and MSVCR71.dll gave the puzzling error OCIError: OCI library initialization error.
refactoring ruby code
Most of my ruby/rails development is against a legacy Oracle database. One of the things we needed to fix, was a user-table with not enough fields. Because the user table was shared with another application, we were not able to alter the table. So we added another table, user_params, containing possibly extra parameters for each user. Now it would be nice if each of those possible parameters would behave like a real attribute. So, first implementation: [ruby] class User < ActiveRecord::Base set_primary_key "user_name" set_table_name "stca_user" set_sequence_name "autogenerated" # do not use a sequence at insert! validates_presence_of :user_name validates_presence_of :password # define some virtual attributes for attributes defined in the UserParam table def group_name UserParam.get_param_value(self.user_name, 'UserGroup') end def group_name=(value) UserParam.set_param_value(self.user_name, 'UserGroup', value) end def title UserParam.get_param_value(self.user_name, 'UserTitle') end def title=(value) UserParam.set_param_value(self.user_name, 'UserTitle', value) end def language UserParam.get_param_value(self.user_name, 'UserLanguage') end def language=(value) UserParam.set_param_value(self.user_name, 'UserLanguage', value) end def full_name UserParam.get_param_value(self.user_name, 'UserFullName') end def full_name=(value) UserParam.set_param_value(self.user_name, 'UserFullName', value) end def email UserParam.get_param_value(self.user_name, 'UserEmail') end def email=(value) UserParam.set_param_value(self.user_name, 'UserEmail', value) end # ... snipped away more code end [/ruby] Obviously, this code is not DRY. At lot of repetition, like the stuff i hate about C# or java properties. This could be done better, let's just generate the methods: [ruby] class User < ActiveRecord::Base set_primary_key "user_name" set_table_name "stca_user" set_sequence_name "autogenerated" # do not use a sequence at insert! validates_presence_of :user_name validates_presence_of :password # define some virtual attributes for attributes defined in the UserParam table instance_eval do [['group_name', 'UserGroup'], ['title', 'UserTitle'], ['language','UserLanguage'], ['full_name', 'UserFullName'], ['email', 'UserEmail']].each do |arr| define_method arr[0].to_sym do UserParam.send('get_param_value', self.user_name, arr[1]) end define_method "#{arr[0]}=" do |value| UserParam.send("set_param_value", self.user_name, arr[1], value) end end end end [/ruby] This looks nice. One disadvantage might be that this is not very readable. At first glance it is no longer clear which attributes are available. But with models that is always the case. A few things can still be improved. Every get and set is a query against the database. We might want to improve upon that, and cache results. Also saving the virtual fields is now done when they are being set, but at that time the parent "User" might still not be saved. So this still calls for a better solution. But this does show why i like ruby so much. It makes me feel powerful :)
multiple ruby versions on windows
I started developing ruby more than a year ago, on windows, which might not be the ideal platform :) But i started out with the standard one-click installer, ruby version 1.8.6, which up until recently served me fine and is an easy way to start. But when i felt the need to install metric_fu, i found out it just does not install on top of the old ruby 1.8.6 (mswin32). Now i know that for ruby 1.8.7 (mingw32) a development kit exists, which would allow compilation of all native code. So, normally, all plugins that were giving me problems before (e.g. thin!) should be able to be installed using that. Also the new rubyinstaller versions are known to be quicker, because they are using a more recent and efficient compiler (mingw), and ruby 1.9.1 should inherently be even more efficient (by design). But i was looking for an easy way to migrate and test ruby versions. I know that there exist a ruby version manager (rvm), but it only works on linux and mac. But luckily, for windows, there is pik. It is really easy to install: [bash] > gem install pik > pik_install c:\windows\system32 [/bash] [the choice to use windows\system32 is maybe not entirely kosher, but it is an easy way to add something to the path, always ;) Maybe i should have used c:\ruby\pik\bin and added that to the path.] Then add the current version as the default: [bash] > ruby -v ruby 1.8.6 (2008-08-11 patchlevel 287) [i386-mswin32] > pik add Adding: 186: ruby 1.8.6 (2008-08-11 patchlevel 287) [i386-mswin32] Located at: c:/ruby/bin [/bash] Then you can download the new versions, e.g. ruby 1.8.7. and 1.9.1 from rubyinstaller and install them each into their own folder. E.g. c:\ruby\187-p249\ and then issue the following command: [bash] > pik add c:\ruby\1.8.7-p249\bin ... > pik add c:\ruby\1.9.1-p378\bin ** Adding: 191: ruby 1.9.1p378 (2010-01-10 revision 26273) [i386-mingw32] Located at: c:\ruby\1.9.1-p378\bin > pik list 186: ruby 1.8.6 (2008-08-11 patchlevel 287) [i386-mswin32] * 187: ruby 1.8.7 (2010-01-10 patchlevel 249) [i386-mingw32] 191: ruby 1.9.1p378 (2010-01-10 revision 26273) [i386-mingw32] [/bash] But of course, the second problem is that the one-click installer also includes a lot gems by default, while the new rubyinstaller is almost empty. So first i installed the development kit from rubyinstaller. I needed to extract that inside the each ruby folder, and change a few settings (configure), like described here, i needed to edit my /devkit/msys/1.0.11/etc/fstab settings file, and replace c:\ruby by my actual ruby-installation folder, in my case something like c:\ruby\1.8.7-p249. I create a small batch-file to install all the missing gems: [bash] rem default windows gems call gem install win32-api call gem install win32-clipboard call gem install win32-dir call gem install win32-eventlog call gem install win32-file call gem install win32-file-stat call gem install win32-process call gem install win32-sapi call gem install win32-service call gem install win32-sound call gem install win32console call gem install windows-api call gem install windows-pr call gem install hpricot call gem install nokogiri call gem install wxruby rem RAILS call gem install rails call gem install will_paginate call gem install mongrel call gem install mongrel_service call gem install formtastic rem database gems call gem install sqlite3-ruby call gem install ruby-oci8 call gem install activerecord-oracle_enhanced-adapter call gem install builder call gem install calendar_date_select call gem install cgi_multipart_eof_fix call gem install composite_primary_keys call gem install log4r call gem install ezcrypto rem testing call gem install cucumber call gem install cucumber-rails call gem install factory_girl call gem install remarkable call gem install rspec call gem install rspec-rails [/bash] But i still encountered an error on the gems that needed to build native extensions: [bash] C:\WINDOWS>gem install hpricot Building native extensions. This could take a while... ERROR: Error installing hpricot: ERROR: Failed to build gem native extension. c:/Ruby/1.9.1-p378/bin/ruby.exe extconf.rb checking for stdio.h... yes creating Makefile make MAKE Version 5.2 Copyright (c) 1987, 2000 Borland Fatal: '/c/Ruby/1.9.1-p378/include/ruby-1.9.1/ruby.h' does not exist - don't know how to make it Gem files will remain installed in c:/Ruby/1.9.1-p378/lib/ruby/gems/1.9.1/gems/hpricot-0.8.2 for inspection. Results logged to c:/Ruby/1.9.1-p378/lib/ruby/gems/1.9.1/gems/hpricot-0.8.2/ext/fast_xs/gem_make.out [/bash] It took a good nights rest to figure that one out: i have Borland's Cbuilder 5 installed on my system to be able to maintain ancient software. So i had to remove that entry from my path, and after that all gems that needed native building went smoothly, except mongrel_service. Now everything installed just fine, inside my 1.8.7 system. Doing the same on ruby 1.9.1 took a lot longer, or so it seemed definitely. I think partly that is because ruby 1.9.1 seems to maintain a class cache? But i am not sure of that. But what surprised me even more is that actually all gems installed without any problem, except win32-service and because of that, mongrel_service. As explained here, this issue is known and due to the fact that the library relies on specific MSVC behaviour that mingw does not support. But i think i can create a service from about any executable or batch file. But i read here that i have to issue the following command: [bash] c:\ruby> gem install mongrel_service --prerelease [/bash] The pre-release version removes the dependency of win32-service, and so it works again. So now i can start testing my applications inside 1.8.7 and 1.9.1 on windows, try using thin, and get metric_fu up and running.
exception notifier configuration troubles
I installed and configured exception notifier to be notified of any unexpected errors in our production environment. At first i tried this out in development, used gmail as my smtp server and all was working fine. My action mailer configuration looked as follows, in environment.rb: [ruby] # configure mailing config.action_mailer.delivery_method = :smtp config.action_mailer.smtp_settings = { :enable_starttls_auto => true, :address => "smtp.gmail.com", :port => "587", :domain => "localhost", :authentication => :plain, :user_name => "<snipped>", :password => "<snipped>" } ExceptionNotifier.exception_recipients = %w(bla@my_clients_domain.com) ExceptionNotifier.sender_address = %("<snipped>@gmail.com") ExceptionNotifier.email_prefix = "[My Application ERROR] " [/ruby] Using this configuration worked, and i received the e-mails. Alas, as usual, on my client's production server the port to reach gmail was blocked, and when i asked to open it, they instead proposed to use their own smtp-server. So i changed the configuration as follows: [ruby] config.action_mailer.smtp_settings = { :address => "192.168.0.10", :port => "25", :domain => "production-domain.be", :authentication => nil } ExceptionNotifier.exception_recipients = %w(bla@my_clients_domain.com) ExceptionNotifier.sender_address = %("info@production-domain.com") ExceptionNotifier.email_prefix = "[My Application ERROR] " [/ruby] The first thing i did wrong was the domain-name. At first i wrote "@production-domain.be" in my action-mailer configuration, so my HELO request was blocked. Secondly, apparently, because i wrote ExceptionNotifier.sender_address = %("info@production-domain.com") the sender address was not recognised and the mail was rejected by our own SMTP-server. This took me a while to figure out. Luckily our sys-admin noticed the difference. The fix: [ruby] ExceptionNotifier.sender_address = %(info@production-domain.com) [/ruby] And now i know for sure that if i get no mails, my application really is working :)
missing 'a' with rspec and cucumber
I am developing Ruby on Rails on Windows platforms mostly. But using Rspec and cucumber on windows has a very strange side-effect: all a's are missing as can be seen from the following screenshot: rspec-without-a-smaller Luckily, after some very extensive googling, i found a single blogpost with a fix! Apparently it has something to do with UTF-8 encoding, and the simple solution is that you need to change the encoding of the current command prompt. This can be achieved via a simple call before you start: [sourcecode] chcp 1252 [/sourcecode] The aforementioned post then proposes to adjust the cucumber.bat to not have to type this every time. This is all good for cucumber, but not for rspec, and anyhow, every new update of the cucumber i would need to apply this fix again. I was thinking that it might be possible to set the default codepage, which is 850 on my machine, to 1252 permanently. As this blogpost mentions, there are two different ways to achieve the wanted result.

Change the codepage system-wide

This can be done in the registry. [sourcecode] [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\CodePage] OEMCP=1252 [/sourcecode] But one commenter notes this is not without risk.

Only for command prompt

An alternative way is to put make sure that each time a console is opened (cmd.exe) the codepage is set automatically. [sourcecode] [HKEY_LOCAL_MACHINE\Software\Microsoft\Command Processor] Autorun=chcp 1252 [/sourcecode] This will only work for console windows in which you run cmd.exe, which is just what i needed.