Ruby
Using soap4r with .NET web services authenticated with WSSE
by Doug on Aug.22, 2008, under Ruby
Recently I had to consume a .NET web service that uses WSSE based authentication. Thanks to the intarweb, I was able to get started with soap4r and insert WSSE authentication headers into my SOAP requests.The samples available on the tracker are also quite comprehensive, other than being light on explanation.
I did encounter an error when using the automatically generated client application:
.rb:2
ruby WSDLService_PortClient.rb
./defaultMappingRegistry.rb:5: uninitialized constant SOAP::Mapping::EncodedRegistry (NameError) from ./defaultDriver.rb:2:in `require'
from ./defaultDriver.rb:2
from ./WSDLService_PortClient.rb:2:in `require'
from ./WSDLService_PortClient
It appears that the soap library provided with Ruby 1.8.6 is being used instead of the soap4r gem. The version bundled with Ruby doesn’t have some classes referenced by the generated stubs (from wsdl2ruby) as well as a bunch of other functionality.
The solution is simple (after knowing what’s wrong of course): explicitly reference the gem so that Ruby knows which one to use.
require 'rubygems'
gem 'soap4r'
Just insert this snippet before the code that references the missing classes and you’re good to go.
currency-convertor gem on github.com
by Doug on Aug.15, 2008, under Ruby
I took a moment to extract some code that’s being used at work for getting exchange rates between 2 currencies into a simple gem called bianster-currency-convertor at github.com.
If you haven’t already added the gem repository at github.com, do take a look at the concise instructions before trying to install the gem.
Sample Usage
>> require 'currency_convertor'
=> true
>> CurrencyConvertor.lookup('USD', 'SGD')
=> 1.4165
Painless Rails Deployment
by Doug on Mar.25, 2008, under Rails
Remember all that joy coding with Rails when you were a wee lad? Things seemed so blissful then, metaprogramming voodoo and that complex object relational mapping stuff were all taken care of behind the scenes.
Alas, whence it doth time to unleash your brilliant social networking app onto the unsuspecting public, your Ruby/Rails world came crashing down faster than FSJ can order a chai latte.
FastCGI, SCGI, Mongrel, Rack, Thin, Pound, Nginx, Apache.
Doing a virgin Rails application deployment would have been a trial by fire where you’ll trawl the Ruby and Rails forums looking for advice on which webserver is best suited to serve your future web application superstar or pray to the One and just deploy on faith.
You’ll settle for Mongrel or Rack if you were lucky enough to come across people who have been there before. Otherwise, be prepared to be regaled with war stories about how some web hosts *COUGH* Dreamhost *COUGH* arbitrarily shutdown your Rails processes for consuming a tad too much resources.
Wouldn’t it be nice if I could just do like what I did for PHP applications? Just upload the files onto the webserver and it just works(tm)?
Now you can! Well, technically only the people at Phusion can at this moment. Lai Hongli breaks the news about modrails, complete with requisite cool screencast.
I think this is a really cool development that makes life a whole lot simpler for people who just want to focus their energies on actual development rather than have to worry about how to configure the deployment environment properly. Even if this is Apache2 only now, it should be possible to port it over to other servers like Nginx, licence restrictions notwithholding.
Specifying raising of errors in RSpec
by Doug on Mar.24, 2008, under Behaviour Driven Development, Ruby
RSpec allows a developer to specify that an error is raised within a block with the raise_error method. It’s a nice expressive way of saying that your code should fail when it needs to.
But my tiny brain has often been confused with using it at times, more so when the error class requires parameters for instantiation and when used in conjunction with the and_raise method on a stub or a mock
Consider the snippet below where my Widget depends on Thingamajig to do its funky thing in order to run. But Thingamajig is rigged to explode in a mass of funkyness and make Widget all useless.
[code]
describe Widget do
it "should re-raise errors as a Widget::UnrecoverableError" do
# expectations
thingamajig = stub(Thingamajig)
thingamajig.should_receive(:do_funky_thing).and_raise(Thingamajig::FunkyExplosion.new('The funky thang exploded yo'))
# our SUT
widget = Widget.new(thingamajig)
#verification
lambda { widget.run }.should raise_error(Widget::UnrecoverableError, 'The funky thang exploded yo')
end
end
[/code]
Do you notice the inconsistency between the way errors are declared in the expectation and the actual verification?
[code]
# expectations
thingamajig.should_receive(:do_funky_thing).and_raise(Thingamajig::FunkyThingExplosion.new('The funky thang exploded yo'))
#verification
lambda { widget.run }.should raise_error(Widget::UnrecoverableError, 'The funky thang exploded yo')
[/code]
The expectation on the stub, ‘thingmajig’ needs the exception instantiated first while verification requires the class name and parameters used to instantiate the error instance.
And no, doing it like this doesn’t work as expected:
[code]
lambda { widget.run }.should raise_error(Widget::UnrecoverableError.new('The funky thang exploded yo'))
[/code]
It’s an unfortunate impedance mismatch that might be caused due to the way Ruby handles the raising of errors.
Using cruisecontrol.rb with repositories without anonymous access
by Doug on Feb.02, 2008, under Agile, Ruby
I decided to use cruisecontrol.rb for continuous integration of an application that I’m working on at Bezurk. So I downloaded the 1.2.1 release from RubyForge and proceeded to add the project repository to the local installation of cruisecontrol.rb.
douglas@macbookpro:~$ ./cruise add MyProject --url http://path.to/repository --username 'douglas' --password 'guessable' douglas@macbookpro:~$ ./cruise build MyProject Builder for project 'MyProject' started Logging to: /Users/douglas/Development/Ruby/cruisecontrolrb-1.2.1/log/MyProject_builder.log Build loop failed BuilderError: svn: PROPFIND request failed on '/svn/my_project/trunk' ./script/../config/../app/models/subversion.rb:98:in `execute_in_local_copy' ./script/../config/../lib/command_line.rb:86:in `call' ./script/../config/../lib/command_line.rb:86:in `e' ./script/../config/../lib/command_line.rb:84:in `popen' ./script/../config/../lib/command_line.rb:84:in `e' ./script/../config/../lib/command_line.rb:71:in `execute' ./script/../config/../lib/command_line.rb:70:in `chdir' ./script/../config/../lib/command_line.rb:70:in `execute' ./script/../config/../app/models/subversion.rb:89:in `execute_in_local_copy' ./script/../config/../app/models/subversion.rb:85:in `chdir' ./script/../config/../app/models/subversion.rb:85:in `execute_in_local_copy' ./script/../config/../app/models/subversion.rb:44:in `latest_revision' ./script/../config/../app/models/project.rb:228:in `new_revisions' ./script/../config/../app/models/change_in_source_control_trigger.rb:8:in `revisions_to_build' ./script/../config/../vendor/rails/actionpack/lib/../../activesupport/lib/active_support/core_ext/symbol.rb:10:in `__send__' ./script/../config/../vendor/rails/actionpack/lib/../../activesupport/lib/active_support/core_ext/symbol.rb:10:in `to_proc' ./script/../config/../app/models/project.rb:223:in `collect' ./script/../config/../app/models/project.rb:223:in `revisions_to_build' ./script/../config/../app/models/project.rb:202:in `build_if_necessary' ./script/../config/../app/models/polling_scheduler.rb:13:in `run' ./script/builder:79 ./script/builder:78:in `catch' ./script/builder:78 ./cruise:14:in `load' ./cruise:14:in `builder' ./cruise:68:in `send' ./cruise:68 /opt/local/lib/ruby/1.8/fileutils.rb:121:in `chdir' /opt/local/lib/ruby/1.8/fileutils.rb:121:in `cd' ./cruise:67
Hmm, what’s with the svn: PROPFIND error? Looking at the stracktrace doesn’t tell me alot about what’s going wrong here, let’s try logging errors to the console.
--SNIP-- douglas$ svn --non-interactive info --xml douglas$ svn --non-interactive log --revision HEAD:20 --verbose --xml svn: PROPFIND request failed on '/repository/trunk' svn: PROPFIND of '/repository/trunk': authorization failed (http://svnhost.com) --SNIP--
It happens that my repository does not have anonymous access and requires a subversion user account to do anything useful. So it should be obvious that cruisecontrol.rb is trying to get log info from the repository but subversion is quitting with authentication errors because no user credentials are being supplied.
I need to have cruisecontrol.rb make use of the –username and –password options when making queries to the repository when I give it the credentials for access.
My first stop is the app/models/subversion.rb. Only the checkout method uses the username and password instance variables. Subversion should only include the –username and –password options when executing svn commands when both the username and password instance variables are present.
[ruby]
test/unit/subversion_test.rb
def test_svn_command_uses_user_password_when_provided
svn = Subversion.new(:username => 'jer', :password => "crap")
svn.expects(:info).with(dummy_project).returns(Subversion::Info.new(10, 10))
svn.expects(:execute).with(["svn", "--non-interactive", "log", "--revision", "HEAD:10", "--verbose", "--xml",
"--username", "jer", "--password", "crap"],
{:stderr => './svn.err'}).yields(StringIO.new(LOG_ENTRY))
svn.latest_revision(dummy_project)
end
app/models/subversion.rb
def checkout(target_directory, revision = nil, stdout = $stdout)
@url or raise 'URL not specified'
options = [@url, target_directory]
options < < "--revision" << revision_number(revision) if revision
# need to read from command output, because otherwise tests break
execute(svn('co', options)) do |io|
begin
while line = io.gets
stdout.puts line
end
rescue EOFError
end
end
end
def svn(operation, *options)
command = ["svn"]
command << "--non-interactive" unless @interactive
command << operation
command += options.compact.flatten
command += ['--username', @username, '--password', @password] if @username and @password
command
end
[/ruby]
The username and password would then be injected into the project's Subversion instance in the cruise_config.rb file for each project.
[ruby]
Project.configure do |project|
project.source_control.username = 'douglas'
project.source_control.password = 'guessable'
end
[/ruby]
I’ve submitted a ticket along with a patch for this on cruisecontrol.rb’s tracker. Keep a lookout for it if you happen to encounter the same problem.
Serializing custom Ruby classes with YAML
by Doug on Jan.02, 2008, under Ruby
I needed to be able to save an array of POROs (plain old Ruby objects) to the database on a Rails project that I’m currently working on. That should be easy, right?
This ought to do the job, yes?:
class MyModel < ActiveRecord::Base
serialize attribute_name
end
However, this didn’t quite work as advertised, the objects being returned were typed as YAML::Object instead of the actual class being serialized. Turns out that YAML is unable to find the reference to the actual class and so its falling back to using YAML::Object as the generic class type for deserialised objects. The answer to this: Rails ticket #7537.
Take a look at the YAML documentation for all the gory details.
Visualising Log Activity with Ruby and OpenGL with glTail
by Doug on Oct.13, 2007, under Ruby
Here’s an interesting combination of Ruby and OpenGL, glTail lets you visualise log activity in realtime. Check out the screencasts on the project page!
One thing to note at the time of writing, it assumes that the command for getting incoming log data, i.e “tail -f” is non blocking. So if you need to use “sudo tail -f”, the application won’t work properly for this situation. I might give it a shot at adding sudo support when I have some time to spare.
The quick spike I did for this suggests that I’ll need to use a shell to respond to password challenges. Incidentally, Capistrano already has this figured out, so i’ll be a good place to look for clues.
Raising URI::InvalidURIError from a perfectly valid URI
by Doug on Sep.15, 2007, under Ruby
I was puzzled by URI::parse raising an URI::InvalidURIError on a perfectly well formed URI recently.
[ruby]
URI::InvalidURIError: bad URI(is not URI?): http://practicalguile.com/articles?query=latest
from /opt/local/lib/ruby/1.8/uri/common.rb:436:in `split’
from /opt/local/lib/ruby/1.8/uri/common.rb:485:in `parse’
from (irb):2
from :0
[/ruby]
What’s not apparent in this exception message is that the url contained a trailing space and this was causing URI.parse to fail. The following specifications demonstrate how it can trigger this particular exception.
uri.spec.rb
[ruby]
require ‘rubygems’
require ’spec’
require ‘uri’
describe URI do
it “should raise an InvalidURIException with leading whitespace in url” do
lambda{ URI.parse(‘ http://www.ruby-lang.org’) }.should raise_error(URI::InvalidURIError)
end
it “should raise an InvalidURIException with trailing whitespace in url” do
lambda{ URI.parse(‘http://www.ruby-lang.org ‘) }.should raise_error(URI::InvalidURIError)
end
end
[/ruby]
Running the spec will get you the result below.
ruby uri.spec.rb ..Finished in 0.030051 seconds 2 examples, 0 failures
Looking at the stacktrace in the exception, it’s being raised by URI.split after URI.parse is invoked with the offending URL.
RUBY_INSTALL/1.8/uri/common.rb
[ruby]
def self.parse(uri)
scheme, userinfo, host, port,
registry, path, opaque, query, fragment = self.split(uri)
if scheme && @@schemes.include?(scheme.upcase)
@@schemes[scheme.upcase].new(scheme, userinfo, host, port,
registry, path, opaque, query,
fragment)
else
Generic.new(scheme, userinfo, host, port,
registry, path, opaque, query,
fragment)
end
end
[/ruby]
Nothing weird happening in URI.parse, its a straightforward call to URI.split. So I’ll go into URI.split, comments removed for brevity.
[ruby]
def self.split(uri)
case uri
when ”
when ABS_URI
scheme, opaque, userinfo, host, port,
registry, path, query, fragment = $~[1..-1]
if !scheme
raise InvalidURIError,
“bad URI(absolute but no scheme): #{uri}”
end
if !opaque && (!path && (!host && !registry))
raise InvalidURIError,
“bad URI(absolute but no path): #{uri}”
end
when REL_URI
scheme = nil
opaque = nil
userinfo, host, port, registry,
rel_segment, abs_path, query, fragment = $~[1..-1]
if rel_segment && abs_path
path = rel_segment + abs_path
elsif rel_segment
path = rel_segment
elsif abs_path
path = abs_path
end
else
raise InvalidURIError, “bad URI(is not URI?): #{uri}”
end
path = ” if !path && !opaque # (see RFC2396 Section 5.2)
ret = [
scheme,
userinfo, host, port, # X
registry, # X
path, # Y
opaque, # Y
query,
fragment
]
return ret
end
[/ruby]
URI.split is matching the incoming url with an empty string as well as regular expressions for absolute and relative URIs. It’s obvious from the specifications earlier that urls with leading/trailing whitespace do not match any of these and the case statement raises InvalidURIError, with the rather misleading message.
The regexes used for matching absolute and relative URIs is shown below, if you really want to know.
[ruby]
require ‘uri’
include URI::REGEXP
ABS_URI
/^
([a-zA-Z][-+.a-zA-Z\d]*): (?# 1: scheme)
(?:
((?:[-_.!~*'()a-zA-Z\d;?:@&=+$,]|%[a-fA-F\d]{2})(?:[-_.!~*'()a-zA-Z\d;\/?:@&=+$,\[\]]|%[a-fA-F\d]{2})*) (?# 2: opaque)
|
(?:(?:
\/\/(?:
(?:(?:((?:[-_.!~*'()a-zA-Z\d;:&=+$,]|%[a-fA-F\d]{2})*)@)? (?# 3: userinfo)
(?:((?:(?:(?:[a-zA-Z\d](?:[-a-zA-Z\d]*[a-zA-Z\d])?)\.)*(?:[a-zA-Z](?:[-a-zA-Z\d]*[a-zA-Z\d])?)\.?|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|\[(?:(?:[a-fA-F\d]{1,4}:)*(?:[a-fA-F\d]{1,4}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|(?:(?:[a-fA-F\d]{1,4}:)*[a-fA-F\d]{1,4})?::(?:(?:[a-fA-F\d]{1,4}:)*(?:[a-fA-F\d]{1,4}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}))?)\]))(?::(\d*))?))?(?# 4: host, 5: port) |
((?:[-_.!~*'()a-zA-Z\d$,;+@&=+]|%[a-fA-F\d]{2})+) (?# 6: registry)
)
|
(?!\/\/)) (?# XXX: ‘\/\/’ is the mark for hostport)
(\/(?:[-_.!~*'()a-zA-Z\d:@&=+$,]|%[a-fA-F\d]{2})*(?:;(?:[-_.!~*'()a-zA-Z\d:@&=+$,]|%[a-fA-F\d]{2})*)*(?:\/(?:[-_.!~*'()a-zA-Z\d:@&=+$,]|%[a-fA-F\d]{2})*(?:;(?:[-_.!~*'()a-zA-Z\d:@&=+$,]|%[a-fA-F\d]{2})*)*)*)? (?# 7: path)
)(?:\?((?:[-_.!~*'()a-zA-Z\d;\/?:@&=+$,\[\]]|%[a-fA-F\d]{2})*))? (?# 8: query)
)
(?:\#((?:[-_.!~*'()a-zA-Z\d;\/?:@&=+$,\[\]]|%[a-fA-F\d]{2})*))? (?# 9: fragment)
$/xn
REL_URI
/^
(?:
(?:
\/\/
(?:
(?:((?:[-_.!~*'()a-zA-Z\d;:&=+$,]|%[a-fA-F\d]{2})*)@)? (?# 1: userinfo)
((?:(?:(?:[a-zA-Z\d](?:[-a-zA-Z\d]*[a-zA-Z\d])?)\.)*(?:[a-zA-Z](?:[-a-zA-Z\d]*[a-zA-Z\d])?)\.?|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|\[(?:(?:[a-fA-F\d]{1,4}:)*(?:[a-fA-F\d]{1,4}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})|(?:(?:[a-fA-F\d]{1,4}:)*[a-fA-F\d]{1,4})?::(?:(?:[a-fA-F\d]{1,4}:)*(?:[a-fA-F\d]{1,4}|\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}))?)\]))?(?::(\d*))? (?# 2: host, 3: port)
|
((?:[-_.!~*'()a-zA-Z\d$,;+@&=+]|%[a-fA-F\d]{2})+) (?# 4: registry)
)
)
|
((?:[-_.!~*'()a-zA-Z\d;@&=+$,]|%[a-fA-F\d]{2})+) (?# 5: rel_segment)
)?
(\/(?:[-_.!~*'()a-zA-Z\d:@&=+$,]|%[a-fA-F\d]{2})*(?:;(?:[-_.!~*'()a-zA-Z\d:@&=+$,]|%[a-fA-F\d]{2})*)*(?:\/(?:[-_.!~*'()a-zA-Z\d:@&=+$,]|%[a-fA-F\d]{2})*(?:;(?:[-_.!~*'()a-zA-Z\d:@&=+$,]|%[a-fA-F\d]{2})*)*)*)? (?# 6: abs_path)
(?:\?((?:[-_.!~*'()a-zA-Z\d;\/?:@&=+$,\[\]]|%[a-fA-F\d]{2})*))? (?# 7: query)
(?:\#((?:[-_.!~*'()a-zA-Z\d;\/?:@&=+$,\[\]]|%[a-fA-F\d]{2})*))? (?# 8: fragment)
$/xn
[/ruby]
Looks rather intimidating, doesn’t it? However, we’re more interested in the beginning and end of the regular expressions so its safe to ignore all the stuff in between. Narrowing our focus down to the regex anchors (^ and $), we can see that there is no matching of whitespace, thus preventing a valid URI from being matched in URI.split.
This all means that URI.split has a undocumented pre-condition on the uri parameter being stripped of any whitespace around it.
Using Factories for Rails Fixtures and Test Doubles
by Doug on Aug.11, 2007, under Behaviour Driven Development, Rails, Ruby
Chris Wanstrath has written about making Rails fixtures less painful than they need to be with the FixtureScenarios plugin. Personally, I prefer the Factory approach, nicely explained by Daniel Manges.
I’ve been using factory methods to create in-database ActiveRecord objects for a project that I’ve been working on in Bezurk. Reading Daniel’s article gave me a few ideas on improving the way I create fixtures and mocks. Since I’ve been using RSpec extensively in this project, I’ll present the examples in RSpec.
As the models evolve with the design and its behaviour change accordingly, there is a need to go through all the specifications that create this model and make sure that its created in a valid state. This is more pronounced with the use of test doubles, the test doubles also need to have its method stubs changed to reflect the latest state of the model that its is representing. I happen to make much use of test doubles for test isolation, so trying to manage all these objects became an exercise in patience. As it was getting painful, It’s time to change the way I create these models and test doubles.
As always, a layer of indirection will always go some way to solving a software problem. We introduce a Factory that encapsulates the creation of ActiveRecord objects by providing creation methods.
[ruby]
module FixtureFactory
def create_user(attributes = {})
User.create!(ModelAttributes.user(attributes))
end
end
[/ruby]
We’ll have a Factory for test doubles too.
[ruby]
module MockFactory
def mock_user(method_stubs = {})
mock_model(User, ModelAttributes.user(method_stubs))
end
end
[/ruby]
And the attributes for this model will be declared in a module that’s used by both Factories
[ruby]
module ModelAttributes
def self.user(attributes)
attributes.reverse_merge({:name => ‘doug’})
end
end
[/ruby]
The Factory modules are then included in Spec::Runnner
[ruby]
Spec::Runner.configure do |config|
include FixtureFactory
include MockFactory
end
[/ruby]
The objects can now be created using the factory methods available to all specifications.
[ruby]
doug = create_user
doppelganger = mock_user
[/ruby]
Update
Added links to Chris Wanstrath and Daniel Manges’ articles on managing Rails fixtures.
Behaviour Driven Development with RSpec slides
by Doug on Jul.26, 2007, under Behaviour Driven Development, Ruby
Here’s my slides for the talk on Behaviour Driven Development and RSpec at the July 2007 singapore.rb meeting. It might not make much sense if you weren’t at the meeting.