Running rake task in background and not blocking the port

In this screencast Ryan Bates explains how to execute a rake task in background on run time. This will make controller return the response to the user fast. In the meantime email will be sent through the background job.

In short this is the solution he proposes.

def call_rake
  system "rake #{task} &" 
end  

It is a short and sweet solution when you do not want to roll out other background job processing tools. It works pretty well. However I discovered one issue with the solution.

While developing validation functionality in admin_data I needed to demand a background job on run time and I used the solution proposed above.

Let’s say that I start a rake task on background which lasts for 10 minutes. In the next 10 minutes if I kill my script/server ( I am in development mode) and if I try to start my server, I was getting following message.

Address already in use - bind(2) (Errno::EADDRINUSE)

It seems that when the rake job is pushed to background, it inherits all the properties of the parent process including the port number of the server. It means that as long as the child process is alive, the parent process’s port number is taken.

This solution fixes the problem.

command = "rake #{task} #{args.join(' ')}" 
p1 = Process.fork { system(command) } 
Process.detach(p1) 

This was my first introduction to the murky world of ruby fork and process. I read a few good blogs on this topic and will be posting more about the same topic soon.

hash method in ruby

I did not know about hash method in ruby object and I paid a heavy price. While developing admin_data plugin I proposed to pass certain values in configuration like this.

:column_settings => { Car => { :data => lambda{|model| model.send(:data).inspect}}}

With the above settings the code will work fine the very first time in development mode. However if you refresh the page then code was failing. It turns out that when I ask ruby to use a class as a hash key then ruby calls hash method of the class and that output is stored as a key.

Since rails loads new instances of classes in development every single time, the hash value of the class differs each time. Check this out.

>> User.hash
=> 18513330
>> reload!
Reloading...
=> true
>> User.hash
=> 18403950

The fix would be to use symbol (like :user) or string (like ‘User’) as key of a hash.

ensure method in ruby

Here is code.

def hello
    yield
  ensure
    puts 'in ensure block'
    true
end

puts hello { puts 'world'; false }

I thought if I call hello method then the final returned value will be true. The common ruby convention is that last value gets returned. In this case the very last value is true so I was expecting true to be the returned value.

If I execute above code this will be the output.

world
in ensure block
false

Question is why the final output is false and not true ?

The implicitly returned value from the ensure block is discarded and hence in the above case the final returned value is false. I said implicitly returned values are discarded.

However if ensure value explicitly uses return statement to return something then ensure behaves quite differently and Less Hill has a nice blog on this topic .

Also note than if you are not explicitly returning something and if yield block raises an exception then ensure will be called. However after the ensure block finishes then that exception will be raised. It means that ensure block will not eatup the exeption.

Once again if you are using explicit return statement in ensure block then exceptions are eaten up as mentioned by Less Hill.

What could be a good candidate for ensure. It depends on business problem but here is a one usage .

def pretend_zone_is(zone)
  original_zone = Time.zone
  begin
    Time.zone = zone
    yield
  ensure
    Time.zone = original_zone
  end
end

show_response to help debugging functional tests

While writing functional tests, I use assert_tag a lot. Many times the assertion fails and debugging with ‘response text’ dump on terminal is not easy. It would be much easier to debug if the response text is opened in a browser. This is how that can be done.

In your test/test_helper.rb add a new method called show_response.

class Test::Unit::TestCase
  def show_response
    Dir.mkdir(File.join(RAILS_ROOT, 'tmp')) unless File.directory?(File.join(RAILS_ROOT,'tmp'))
    response_html = File.join(RAILS_ROOT, 'tmp', 'response.html')
    File.open(response_html,'w') { |f| f.write(@response.body) }
    system 'open ' + File.expand_path(response_html) rescue nil
  end
end

This is how I use this method while debugging.

context 'get show for comment which belongs to another class' do
  setup do
    @comment = Factory(:comment, :article => @article)
    get :show, {:id => @comment.id, :klass => @comment.class.name.underscore }
    show_response
  end
  should_respond_with :success
  should 'have belongs_to message' do
    assert_tag( :tag => 'p',
                :attributes => {:class => 'belongs_to'},
                :descendant => {:tag => 'a', :child => /Article/})
  end
end

If you are using mac then running this test will open a new web page with full response text. Now I can easily see why my assertion might be failing. show_response also works with ajax request.

Use end more often in jQuery while building DOM elements

I want to create following markup dynamically using jQuery.

<div>
  <p>
    This is p
  </p>
</div>

Following jQuery code will do the work.

$(document).ready(function() {
  var div = $('<div></div>');
  var p = $('<p></p>').text('this is p')
                      .appendTo(div);

  $('body').append(div);
});

A better way to accomplish the same is presented below.

$('<div></div>')
  .append('<p></p>')
    .find('p')
    .text('this is p')
    .end()
  .appendTo('body');

Using .end() you can go back one level. And you can use .end() any number of times to get out of a deeply nested tag.

$('<div></div>')
  .append('<p></p>')
    .find('p')
    .append('<span></span>')
      .find('span')
      .text('this is span')
      .end()
    .end()
  .appendTo('body');

My name is Neeraj. You can contact me at neerajdotname@gmail.com . I am also on twitter , facebook and Linkedin .

Checkout my github account for admin_data and other projects.