Friday, December 29, 2006

Questions? Lets talk about the ruby way

In The Ruby Way By Nicholas Petreley on Tue, 2006-12-05 11:09.
He asks "So here's my question to you Ruby aficianados. Did you have trouble adjusting to The Ruby Way of doing things? How long did it take for you to get used to Ruby's approach to objects, classes, instances, and the various oddities? How long was it before you started to feel like you really began tapping the power inherent in the language, and how much of that power do you attribute to Ruby's unique approach?"

I am quickly becoming a "Ruby aficianado", so I will take a stab at answering his questions.

I was able to adjust to the Ruby Way of doing things fairly quickly. Having recently spent a fair amount of time with python, Ruby's dynamic nature was welcoming, and familiar; However, up front blocks and procs were not. I would also add Ruby's open class nature was a surprise.

It took me about a month to get used to most of the various oddities. I tripped over small things, but found answers quickly online, or in a book. Irb is a great tool to work out understanding how to wield Ruby syntax, or code in general. I found I could read and understand most Ruby code quickly. Love the show source link on online rdoc, something sorely missing from javadoc.

I wrote an enterprise glue application that needed to be completed in one week. I was almost immediately able to tap the power inherent in the language. I found that I could express my thoughts more rapidly and adapt the codebase quickly to the changing requirements. Without going into any further detail, I will say that Ruby, ActiveRecord, and Rake saved the day. Not sure anything else would have done as well for the same task. Yes, Ruby has a unique approach and well worth using within the enterprise.

By the way the book The Ruby Way 2nd edition is well worth it. Like Nicholas I too was in a way put off by the list of Ruby oddities in the first chapter. I would recommend skipping that at first and coming back to it later.

Thursday, December 14, 2006

Get the edge, that is edgemocha

I had read over the past month, or so, a few blog posts on stubba and mocha. I especially appreciated Jay Fields coverage in his blog and Chris at err.the-blog. Here are some links that will get you started.

So, all that reading made me want to get immediately started with Mocha. When the time came to put it to work for me I installed the gem only to discover that at least version 0.3.2 did not appear to be working. The first surprise was that test/unit had to be required, or loaded first. But, even though that eliminated the unknown constant Test error, mocha still did not work. I am avoiding detailing the errors, etc, because their usefulness has expired. Why? Take a look if you dare at the head revision. I found the mocha team has been busy refactoring, apparently removing the test/unit dependency, and possibly other improvements.

I decided maybe I should try the head revision, that is go to the edge. I did not want to go back in versions as I was not sure how far I might need to and I thought going forward might allow me the chance to help with the project in some fashion. After following the directions at the project rubyforge site I launched an irb session to test.
irb(main):001:0> require 'mocha_standalone'
=> true
irb(main):002:0> include Mocha::SetupAndTeardown
=> Object
irb(main):003:0> setup_stubs
=> #<Mocha::Central:0xb791ac04 @stubba_methods=[]>
irb(main):004:0> $stubba
=> #<Mocha::Central:0xb791ac04 @stubba_methods=[]>
irb(main):005:0> class Foo
irb(main):006:1> end
=> nil
irb(main):007:0> foo = Foo.new
=> #<Foo:0xb7913f30>
irb(main):008:0> foo.expects(:nil?).returns("hello, mocha")
=> #<Mocha::Expectation:0xb790c384>
irb(main):009:0> foo.nil?
=> "hello, mocha"
irb(main):010:0> foo.nil?
=> "hello, mocha"
irb(main):011:0>

As you can see from the irb session you need to invoke the setup_stubs module function, or you will get an error about "stub" method not found on NilClass ($stubba). Once that is done, it appeared things were working. I wish I could say edgemocha is ready to go, but I am not sure, and hope to find out soon. Leave a comment if your experience was different, etc.

UPDATE:
Since it was requested, here is the problem I was having with 0.3.2, my apologies for any confusion. Compare:
~$ sudo gem install mocha
Attempting local installation of 'mocha'
Successfully installed mocha-0.3.2
~$ irb
irb(main):001:0> require 'rubygems'
=> true
irb(main):002:0> require 'test/unit'
=> true
irb(main):003:0> require 'mocha'
=> false
irb(main):004:0> Object.methods.include? "expects"
=> false
irb(main):005:0>

Now using the head revision...
~$ irb
irb(main):001:0> require 'mocha'
=> true
irb(main):002:0> include Mocha::SetupAndTeardown
=> Object
irb(main):003:0> setup_stubs
=> #<Mocha::Central:0xb78bf49c @stubba_methods=[]>
irb(main):004:0> Object.ex
Object.expects Object.extend
irb(main):004:0> Object.methods.include? "expects"
=> true
irb(main):005:0>

Shortly after I discovered requiring stubba resolved my 0.3.2 issues it appears. Oddly examples I have seen only have a require 'mocha', for example: Start Trek Example

Monday, December 04, 2006

From "C" style printf to Ruby ERB, a better way to printf

Let's start with a quick "Hello, World" ruby program using ERB and the DATA pseudo file.
require "erb"
hw_end = "World"
ERB.new(DATA.read).run
__END__
Hello, <%=hw_end%>

So, this is a bit more complex than puts "Hello, World"; However, it served its purpose. Now could it be that this is a better way to express formated text output in a script versus printf? So, lets simulate a database table viewer with column headers and rows using text.
# create some sample data
Stats = Struct.new(:table_name, :ex_dt, :maint_dt, :maint_t)
data = [ Stats.new("table1", "Fri Dec 01 03:47:50 PST 2006", "2006-12-01", "03:47"), Stats.new("table2", "Fri Dec 01 03:48:50 PST 2006", "2006-12-01", "03:48") ]

For our text output we might be tempted to do the following:
# here is the a printf version
printf("%-26s|%-28s|%-10s|%-10s\n", "table_name", "extract_dt", "maint_dt", "maint_time")
printf("%-26s|%-28s|%-10s|%-10s\n", "-"*26, "-"*28, "-"*10, "-"*10)
data.each do |st|
printf("%-26s|%28s|%10s|%10s\n", st.table_name, st.ex_dt, st.maint_dt, st.maint_t)
end

While this works well, maybe it would be better expressed using ERB and the DATA pseudo file? Take a look.
require "erb"
ERB.new(DATA.read, 0, "%").run
__END__
table_name |extract_dt |maint_dt |maint_time
--------------------------|----------------------------|----------|----------
% data.each do |st|
<%="%-26s|%28s|%10s|%10s" % [st.table_name, st.ex_dt, st.maint_dt, st.maint_t]%>
% end

Now to me this is a much nicer, cleaner expression, do you agree?

I would like a better expression for the row format, spacing, and alignment. That is replace the "%%-26s|%28s|%10s|%10s"... with something simpler, any ideas?

The one caveat I should highlight is for each ruby file there is only one DATA pseudo file. I would suggest using here-docs when more templates are needed. The ERB rdoc has good examples and save the DATA pseudo file for .

For reference ERB recognizes the following tags in a template.
<% Ruby code -- inline with output %>
<%= Ruby expression -- replace with result %>
<%# comment -- ignored -- useful in testing %>
% a line of Ruby code -- treated as <% line %> (optional -- see ERB.new)
%% replaced with % if first thing on a line and % processing is used
<%% or %%> -- replace with <% or %> respectively

UPDATE:
I recently discovered that the DATA psuedo file is only for the main program and not the file it appears within. Sometimes this is the same file as in my example; However, it does not have to be. So, if you execute ruby some.rb, then DATA is the __END__ within some.rb. And while some.rb may require another.rb which has its own __END__, that one is ignored. So, when this occurs I would use HERE docs which provide the same clarity I was in search of.