Saturday, June 26, 2010

Using Object#tap

Object#tap is new to Ruby 1.9. This new method will allow us to modify the object its called upon and, after the block finishes, will return to us the original object.

We can use this feature to make our specs a bit more concise.

Here is an example using this spec...


describe Array do
   before(:all) do
    @array = Array.new
    @array << 1
   end
   
   it "should have a length of 1" do
    @array.should have(1).element
   end   
end

We change it so.

describe Array do
   before(:all) {@array = Array.new.tap{|me| me << 1}}
   
   it "should have a length of 1" do
    @array.should have(1).element
   end   
end

This code says "create a new instance of class Array, pass it to the 'tap' block, modify it, and return the original now modified object."

@array = Array.new.tap{|me| me << 1}

Some Ruby Built In Global Variables

These maybe helpful to know:


$0 --- Returns the name of the file that ruby is executing
$: --- Returns the directories Ruby searches to load external files
$$ --- Returns the process id of the Ruby process

Friday, June 25, 2010

let, its, and subject

We're going to refactor a spec to a more concise size.


First here is our starting point:


describe Foobar do
   before(:each) do
      @foobar = Foobar.new
   end
   
   it "should not be nil"
      @foobar.should_not be_nil
   end
   
   describe "#length" do
      before(:each) do
         @length = @foobar.length
      end
    
      it "should == 10" do
         @length.should == 10
      end
   end
end


The above example uses 15 lines of code. Lets see if we can do better.


First we're going to replace the 'before' blocks with 'let' blocks.
The 'let' block returns an evaluated block. It really helps you define
players in your spec.


describe Foobar do
   let(:foobar) {Foobar.new}
   
   it "should not be nil"
      foobar.should_not be_nil
   end
   
   describe "#length" do
      let(:length) {foobar.length}
    
      it "should == 10" do
         length.should == 10
      end
   end
end


That's a bit better. We are now at 11 lines of code.
But, we can do better.

We're going to use 'subject' block. This allows us to call
'should' right within the block. We're also going to use an 'its' block.
The 'its' block will return to us the value of the message retrieved represented
by the symbol we passed to it.


describe Foobar do
   subject {Foobar.new}
   
   it {should_not be_nil}
   its(:length) {should == 10}
end


Very good. We have brought it down to 5 lines.

What does the specdoc output look like?

Here it is:


Foobar
- should not be nil
Foobar length
- should == 10

Modifying an Existing Ruby Class

First example is reopening the class and adding methods to it.


First, the original class:


class OriginalGangsta
   def shoot
      puts 'bang!' 
   end

   def flee
      puts 'run muther###'
   end
end


Now, we modify the class:


#load the original class
require 'original_gangsta'

#add some new methods
class OriginalGangsta
   def lie
      puts "he dunnit"
   end

   def dance
      puts "I'm a dancin' fool"
   end
end


We can also just modify an instance of the class:


og = OriginalGangsta.new

class << og
   def cry
      puts "I never had a good home!"
   end
end

or we can do it this way:

og = OriginalGangsta.new

def og.cry
   puts "I never had a good home!"
end

Sunday, May 16, 2010

Redirecting Console Output: File

If you have occasion to require to redirect some console output produced by your code to a file, you can accomplish this with a few lines of code like so:

  f = File::open('console.log', 'w')
  begin
    temp_std_out = STDOUT.clone
    STDOUT.reopen(f)
    puts 'this will go to the file'
  ensure
    STDOUT.reopen(temp_std_out)
    puts 'this will go to the screen'
  end

Friday, May 14, 2010

Suppressing Console Output

There are times when suppressing console output would be very useful. Lets say you have a class your writing a spec for which produces allot of console output. When your spec runs, the output can be very noisy. In such a case you can do the following:

   describe NoisyClass do
     before :all do
        @noisy = NoisyClass.new
        silence_stream(STDOUT) do
           #The console output in this block
           #will never be seen
           @noisy.generate_console_output
        end
     end
   end


The 'silence_stream' method is defined in 'vendor/rails/activesupport/lib/active_support/core_ext/kernel/reporting.rb' and is used to silence any stream for the duration of the block.

Monday, March 29, 2010

Better Looking Console Output

I was listening recently to one of Ryan Bates great screencasts where he briefly mentions how to setup script/console to display better looking output after querying ActiveRecord Models. This comes in handy when trying to debug certain things.

We start by looking at what the default output looks like.

jleal@ubuntu-ruby:~/swapme$ ruby script/console
Loading development environment (Rails 2.3.2)

>> User.find(:first, :select => "id, email, created_at, updated_at")

=> #<User id: 1, email: "sell@gmail.com", created_at: "2010-03-29 04:51:37", 
    updated_at: "2010-03-29 04:51:37">


Now lets see if we can improve things a bit. In order to accomplish this we're going to do the following things:

  • Install and enable the 'hirb' ruby gem.
  • Redirect the ActiveRecord logging to display on the console.

Hirb gem


Hirb is described on github as:
Hirb provides a mini view framework for console applications and uses it to improve irb’s default inspect output.
Hirb presents outputs for several objects much prettier than their default. You can even use hirb to create custom outputs for those objects hirb has not defined.


First we install the gem.

jleal@ubuntu-ruby:~$ sudo gem install hirb
Successfully installed hirb-0.3.1
1 gem installed

Next we load the gem in the console.

jleal@ubuntu-ruby:~/swapme$ ruby script/console
Loading development environment (Rails 2.3.2)
>> require 'hirb'
=> []
>> Hirb.enable
=> true
>> 



Our output looks allot nicer.

>> User.find(:first, :select => "id, email, created_at, updated_at")
+----+---------------------------+-------------------------+--------------+
| id | email          | created_at              | updated_at              |
+----+---------------------------+-------------------------+--------------+
| 1  | sell@gmail.com | 2010-03-29 04:51:37 UTC | 2010-03-29 04:51:37 UTC |
+----+---------------------------+-------------------------+--------------+
1 row in set
>> 


Redirecting ActiveRecord Logging.


Finally, we'll redirect ActiveRecord logging to display on the console. This is done with one line of code. Remember to reload so it takes effect.

>> ActiveRecord::Base.logger = Logger.new(STDOUT)
=> #<Logger:0xb6fb2b04 @formatter=nil, @level=0, 
@default_formatter=#<Logger::Formatter:0xb6fb2adc @datetime_format=nil>, 
@progname=nil, @logdev=#<Logger::LogDevice:0xb6fb2ab4 
@mutex=#<Logger::LogDevice::LogDeviceMutex:0xb6fb2a8c @mon_waiting_queue=[], 
@mon_entering_queue=[], @mon_count=0, @mon_owner=nil>, 
@dev=#<IO:0xb7807570>, @shift_size=nil, @shift_age=nil, @filename=nil>>
>> reload!
Reloading...
=> true

Now let's see how things look.

>> User.find(:first, :select => "id, email, created_at, updated_at")
  User Load (15.7ms)   SELECT id, email, created_at, updated_at FROM "users" LIMIT 1
+----+---------------------------+-------------------------+--------------+
| id | email          | created_at              | updated_at              |
+----+---------------------------+-------------------------+--------------+
| 1  | sell@gmail.com | 2010-03-29 04:51:37 UTC | 2010-03-29 04:51:37 UTC |
+----+---------------------------+-------------------------+--------------+
1 row in set

Very nice. We now have a more readable output plus we can see the actual SQL that was generated.

Hope you enjoyed this!