Using Bundler with rails 2.3.X

Bundler is optional for rails 2.3.X. However, it is not a bad idea to upgrade to bundler for several reasons:
- It is stable now and will solve your gem dependency conflicts better than rubygems require.
- You will be better prepared to migrate to rails3 when you are ready.
Instructions for using bundler with rails 2.3 will get you off to a good start. We use shoulda and mocha for our test suite. If you do too, you may run into an issue that Yehuda rightly blogged about here.
Read the rest of this entry »
Login as multiple users simultaneously for testing

Ever wondered how to login as two different users at the same time? The situation manifests in any project where you want to be able to test a workflow involving several actors with different roles. The problem is that browsers allow only one login for a given domain at any one time. So, you are required to logout as current_user before logging in as another. One of the solution people use is install multiple browsers.
Disabling form post in google chrome and safari

So, I found this issue with google chrome and safari web browser and I believe this applies to any webkit based browser. When a form submit is disabled by setting the submit button to disabled=true attribute, the browser is usually expected to prevent submission when the user hits “ENTER” on any of its text fields. This works fine in Firefor, my default browser.
In my rails app, I had to do this to work around this.
We use haml rendering engine for our project and my form looks like this:
- remote_form_for @product_search, :url => product_searches_path, | :html => {:method => :post, :id => 'product_search_form'} do |form|
Rails Email Unit Testing

ActionMailer classes, for sending out emails, typically reside with models, inside app/models folder. Yet, they have traits that are similar to controllers. For example, each actionmailer class has a view folder inside app/views where email templates are defined. And testing support for email is provided as functional tests with assert_select macros such as assert_select_email.
In my current project, most of our email hooks are defined on model classes as after_filter. This is in line with recommendation to keep controllers thin and models fat. However, testing email body’s html format is a pain since assert_select_email support is only available in ActionController::TestCase hierarchy, and not in ActionMailer::TestCase hierarchy.
Consider following ActionMailer test:
class MortgageBorrowerEmailerTest < ActionMailer::TestCase should "invite borrowers" do realtor, borrower = User.make, User.make response = MortgageBorrowerEmailer.deliver_invite_borrowers(realtor, borrower) # test from/to/subject works now :-) assert_same_elements([borrower.email], response.to) assert_equal([realtor.email], response.from) assert_equal("#{realtor.full_name} has invited you to manage your mortgage application in Lendable", response.subject) # testing body is still primitive :-( assert response.body.include?(realtor.full_name) assert response.body.include?("/account_preferences/#{borrower.perishable_token}/edit") end end
ActionMailer::TestCase has decent support for intercepting mail delivery and hold onto mails for introspection. This allows us to assert smaller pieces like from, to, subject etc. However, what it doesn’t support is more fine grained assertion on email’s html body. it would be nice to be able to apply assert_select assertion on email’s response body for critical pieces of information that needs to be present in an email.
So, I dug into ActionController::TestCase’s assert_select_email to see how I can duplicate it for my unit test. Here is what the original method looks like:
# [rails]/actionpack/lib/action_controller/assertions/selector_assertions.rb module ActionController::Assertions::SelectorAssertions def assert_select_email(&block) deliveries = ActionMailer::Base.deliveries assert !deliveries.empty?, "No e-mail in delivery list" for delivery in deliveries for part in delivery.parts if part["Content-Type"].to_s =~ /^text\/html\W/ root = HTML::Document.new(part.body).root assert_select root, ":root", &block end end end end end
As this implementation depicts, it is interesting to see how easy it is to take any html code snipped and create an HTML::Document out of it so that assert_select can be applied to it. Anyway, the default implementation for assert_select_email assumed multipart email with one or more parts in it. Since my email are simplistic text/html rendered as email body, I hacked to get what I needed. As follows:
class MortgageBorrowerEmailerTest < ActionMailer::TestCase include ActionController::Assertions::SelectorAssertions def assert_select_with_string(html_text, &block) root = HTML::Document.new(html_text).root assert_select root, ":root", &block end end
With this in place, I could now take my email response’s body and assert-select on it:
should "share mortgage with existing client" do ... assert_select_with_string(response.body) do assert_select "h1", :text => "Greetings from Lendable" assert_select "tr#sender_identity td strong", :text => @realtor.full_name assert_select "tr#rate_search_criteria" do assert_select ".purchase_price" assert_select ".loan_amount" end assert_select "tr#rate_info td a[href=?]", "http://#{DEFAULT_HOST}/rate_selections/#{@rate_selection.id}", :text => "#{@rate.program.type_and_term} Mortgage at #{number_with_precision(@rate.rate, :precision => 3)}% from #{@rate.program.lender.name}" end end
As we can imagine, this assert_select_with_string() can be useful in testing helper methods that return html snippets. I am not sure what the downside is for testing email body format as unit test vs. funcational test but I am happy that I can test it in isolation as unit test.
Do you know of any?
Internet, Email, Blogs, Forums … did we need twitter?

Yes, we did. And here’s why.
I was at a recent social gathering where we started talking about how funny and bizzare it is that people use twitter to post messages like .. sh**, woke up late .. having lunch at Gino’s East … driving to work … my dog peed on my bed … . .. Oh ok, that last one is probably out of thin air but posting those kinds of messages probably don’t add much value for anybody, the author or the readers.
To test the waters, I created an account about 6 months ago. Very quickly I realized that twitter fills in a void that none of the other communication tools do.
Keep in touch, I mean real touch:
We don’t make it a point to write email to every single one on our list of 200 or 500 contacts every once in a while. Why? Because you will have to walk down the list, write to each person individually. We would have to come up with something to talk about that this person might be interested in. Too much work. But we still want to peek into their life and vice versa to find out if there is anything that interests both of us. Twitter fits the bill. You tweet (by definition, short) about anything that touches your life. If the other person is interested in you, he/she will follow you and vaguely peruse over your tweets. You do the same and follow him. Even if you no longer meet each other (because one of you has moved to different city or different job), you can talk to each other over this channel. If everybody in my group did this, I’d be fully aware of what happens with them on a day by day, hour by hour basis without actually calling them up. And vice versa.
Another great advantage that isn’t apparent at first is that twitter allows me to follow person who I am not associated with directly but what he does has a large impact on my life. Since I am programming in Ruby these days, I want to know what ruby gurus (@dhh, @wycats) are upto each day. If you are a java programmer, you may want to follow RodJohnson. These guys (I thank them) tweet few times a day and knowing that DHH and his team is upto each day is huge… it is gold mine!
Mass conversation:
Twittering is like sitting at a bar with folks that share your interests and talk about interesting things, just that it happens in a very loosely coupled way. All the same conversation rules apply. You want to be having light and funny yet intelligent conversation without having to invest the time to walk to a bar. And you can be in multiple bars at the same time. It doesn’t replace the bar meetups but it does make it easier to have more of those in our daily life, a good thing isn’t it.
You are never alone, even when you are alone:
We always had internet to keep us busy surfing and killing time when alone. One problem with plain vanilla internet surfing is that you have manually visit several websites to dig relevant pieces out that are important to you. Twitter makes it easier to subscribe to channels (people or companies or search tags) that are relevant and in very short message bursts gives you lots of superficial information. This is great for quickly perusing on channels and then dig deeper into stuff that interests you. With twitter account and a smartphone, those 5 minute wait at bus station, 10 minute wait at restaurant could be good investment in keeping in touch.
Books, Articles, Blogs and now Twitter:
We couldn’t write books every month, so we started writing articles. We couldn’t write articles each week, so we invented blogs. We can’t write blogs every day, so we have twitter. It is just a very low cost way to get the message across.
After using it for a while here are some things I learnt as far as twitter etiquettes are concerned:
Keep audience engaged:
My leg is hurting … nice weather … are boring tweets. They don’t initiate interesting conversation. Instead what I’ve found useful is to post an opinion on some news you just heard. Or some article, blog that you came across or commenting on a live TV cast of Presidents’ speech. Ask quick yes/no type questions although not always. Talk about pair programming .. how about twitting a quick question to you programmer colleague on how to solve a pesky problem? beat that!
Post often but not too often:
Posting 15, 20, 30 tweets a day on an account is good. Anything more is annoyance and you run the risk of loosing your followers. Posting less isn’t good either. It isn’t blog or article after all. Its purpose is to keep in touch … how about a tweet an hour on average.
Have separate accounts:
You need separate accounts to match each of your personalities. By personalities, I mean you are a professional at work, music rockstar in your school circle and lazy bum in your family circle. Sometimes the differences in these personalities are so stark that you don’t want to mix those. For example, my mom and sister will have no interest what so ever in my ruby programming tweets. However, my colleague at work aren’t really interested (much) in my hindi movie reviews and cricket fan club conversations. Having separate accounts keeps things simple.
All in all, the concept of twitting is here to stay!
Certainly, a great innovation. Does twitter.com hold a patent for this
Optimizing has_role? in acl9

acl9 is a an authorization library for rails applications. It is one of the widely used library if not the most widely used now. Our experience with acl9 shows that it might be heavy weight if your authorization needs are simpler (which most projects are) but could be useful for other projects.
If you’ve used acegi/spring-security for authorization in your java apps, you know that acl9 is very similar in principle and hence very powerful. In addition to primary roles, it provides object level permissions which are stored in a generic way separately from the objects being controlled, all without the need for handcoding/distributing your authorization columns in each authorization-object tables.
One place where acl9 differs from acegi is how it doesn’t differentiate between a role and a permission. Acegi signifies roles as global permission level which allows you to do certain things (some action on any object of a given class). Where as, a “permission” controls whether your can take that action on a certain object of a class or not. Acl9 calls them all “roles” (primary-roles and object-roles). As you can imagine, a given user may have a few roles in system but end up with lot and lots of permissions in system depending on how many objects user owns etc. This may seem like good idea at first but it presents a unique problem which is not apparent at first. Since roles and permissions are not conceptually separate in acl9 – and that a user can have lots of them (few roles and lots of permissions) – prevents us from loading and caching them in memory. Why do we need to keep them in memory? Because you are querying user’s primary roles most often in your rendering of pages.
For example, consider navigation-bar which is common in most applications. Different users are presented with different tabs in navigation-bar and this bar gets rendered on each request/response cycle. Whether to render a particular tab is conditional to whether a user has certain role (primary role in particular) or not. Since acl9 cannot keep all roles (and permissions) in memory, it has to perform database query every time it has to find whether a user has_role?(admin) or not. Given that there can be only a few primary-roles that the user will have in any system, it seems in-efficient to not cache them and go to database each time.
The solution would be to separate these primary-roles from permission-roles and cache them for each request. In acl9 this means overriding User.has_role? and user.has_role!.
class User def has_role?(role, object = nil) if object || !Role.primary?(role) super else primary_roles.collect(&:name).include?(role.to_s) end end def has_role!(role, object = nil) super @primary_roles = extract_primary_roles if(Role.primary?(role)) end def primary_roles @primary_roles ||= extract_primary_roles end def extract_primary_roles self.role_objects.select { |r| r.primary? } end private :extract_primary_roles end
That does it. You cache the primary-roles and leverage those for has_role? queries.
3 ways to send javascript alerts from rails
-
render :update do |page| page << "alert('message');" end
-
render :update do |page| page.call 'alert', "message" end
-
render :js => "alert('message');"
Writing your Greasemonkey userscripts in jQuery
I’ve played with greasemonkey and like them in concept. They give users control of the webpage independent of the website where they are served from. I’ve used it to mostly replace default maps with google maps and to disable advertisement sections. Since, I am gaining more affinity to jQuery, it would be nice to write all my jQuery userscripts in jQuery. For this, we need to incorporate jQuery into userscript before doing custom stuff.
Here is a jquery userscript that I found simple to integrate:
var GM_JQ = document.createElement('script'); GM_JQ.src = 'http://jquery.com/src/jquery-latest.js'; GM_JQ.type = 'text/javascript'; document.getElementsByTagName('head')[0].appendChild(GM_JQ); // Check if jQuery's loaded function GM_wait() { if(typeof unsafeWindow.jQuery == 'undefined') { window.setTimeout(GM_wait,100); } else { $ = unsafeWindow.jQuery; letsJQuery(); } } GM_wait(); function letsJQuery() { // All your custom GM code must be inside this function }
I am not sure if we can put this in a separate JS file and import this in all our customer userscripts. But it is small enough to be copy/pasted (yuck! but necessary) into each file.
As an example, I visit NationalMortgageProfessional.com website often for mortgage related news these days. But I hate the flashing ads on the right bar. All I needed is to hide this bar. Of course, you can do this in 2 lines in javascript, but jQuery is so much better. Here is how I did it:
var GM_JQ = document.createElement('script'); GM_JQ.src = 'http://jquery.com/src/jquery-latest.js'; GM_JQ.type = 'text/javascript'; document.getElementsByTagName('head')[0].appendChild(GM_JQ); // Check if jQuery's loaded function GM_wait() { if(typeof unsafeWindow.jQuery == 'undefined') { window.setTimeout(GM_wait,100); } else { $ = unsafeWindow.jQuery; letsJQuery(); } } GM_wait(); function letsJQuery() { // hide the right sidebar with flashing ads $('#sidebar-right').hide(); }
And there I went.
Happy Reading!
Generating Time Entry from git log
Being able to track time automatically based on version control log is a recurring theme. Git makes it possible to generate ever more accurate since you can continue to perform tiny checkins to you local repository and then push it to main when you get a chance. Several folks have attempted to generate an approximation of time-log based on git commit hooks.
I make an attempt that is less intrusive and uses just the git logs. Here is the rake task that does it:
task :timelog => [:environment] do require 'logger' require '/home/sjain/projects/gems/ruby-git/lib/git' require 'fastercsv' logger = Logger.new(STDOUT) logger.level = Logger::WARN g = Git.open(Rails.root.to_s, :log => logger) logs = g.log(1000) log_entries = logs.entries.reverse worklog = {} log_entries.each_with_index do |commit, index| author_date = commit.author_date.to_date daylog = worklog[author_date] || OpenStruct.new(:date => author_date, :duration => 0) daylog.message = "#{daylog.message} --- #{commit.message}" daylog.duration = daylog.duration + calc_duration(log_entries, index) worklog[author_date] = daylog end FasterCSV.open("time_report.csv", "w") do |csv| worklog.keys.sort.each do |date| duration_in_hours = (worklog[date].duration/1.hour).round(2) puts "#{date.to_s(:db)} #{duration_in_hours}" csv << [date.to_s(:db), duration_in_hours, worklog[date].message] end end end def calc_duration(log_entries, index) commit = log_entries[index] if index > 1 previous_commit = log_entries[index-1] duration = commit.author_date - previous_commit.author_date # ASSUMPTION: if the gap between 2 commits is more than 3 hours, reduce it to 1-hour duration = 0.5.hour if duration > 3.hour return duration else # ASSUMPTION: first commit took 1/2 hour 0.5.hour end end
There are some sensible assumptions being made to account for that fact that you stopped working at night and continue next day. This script currently looks at all logs by everybody. On a typical project with several developers, we may want to select logs for particular authors. This script needs further enhancement in that sense.
What is nice about this script it doesn’t depend on any git commit hooks and can be run on any git repository, after the fact, to get an approximate hours count.
The more tiny commits you make, the more accurate the result is.
Enjoy!
Note: I used ruby-git for git api.
Setting up Motorola S9 Bluetooth headset with Trendnet TBW-105UB Bluetooth USB adapter on ubuntu
On our current ruby/rails project, we have a distributed team and remote pairing is a daily chore. I recently bought iPhone and Motorola S9 bluetooth headset to listen to music and radio. I decided to get wireless headset working with my ubuntu laptop to help with remote pairing. After looking up which USB adapter work with Ubuntu, I decided to buy Trendnet TBW-105UB.
As mentioned on wiki, the USB adapter installed out of the box, without installing any driver and apt-get packages on my Karmic Koala (Ubuntu version 9.04). The bluetooth icon showed in my taskbar and it offered option to add new device. The GUI wizard searched for other bluetooth devices but couldn’t locate my S9 headset. After a few trial, I realized that I was making a few mistakes.
- The S9 is in pairing mode only for a few seconds when turned on. So, I couldn’t get it to pair if the headset is already on. I had to turn it off and then on while the Ubuntu bluetooth software is searching for device.
- The other mistake I was making was having iPhone bluetooth ON. The headset was already configured to pair with iPhone so as soon as it found iPhone, it would stop and not pair with ubuntu box. I had to turn off the bluetooth on iPhone so that headset would not find anything else before ubuntu.
Once I did this, ubuntu was able to find headset. But then it failed to pair successfully, probably because the 4-digit pin didn’t apply properly. I tried again and this time, I selected the specific pairing digits of “0000″ for headset. That worked and it paired successfully.
Now, I started an audio stream on my ubuntu laptop and guess what. No, it would still use my laptop speaker phone. I was close but not there yet.
I opened up the “Sound” Preferences dialog from “System” menu. There I found option to switch from default microphone and speaker to motorola headset. Once I switched both, it all worked.
Happy pairing!