Learning Ruby reimplementing it: attr_writer

This is how attr_writer may be (re)implemented:

module Kernel
  def attribute_writer(attribute)
    define_method("#{attribute}=".to_sym) do |value|
      instance_variable_set("@#{attribute}", value)
    end
  end
end

class MyClass
  attribute_writer :my_attribute
end

m = MyClass.new
m.my_attribute = 23

puts m.instance_variable_get "@my_attribute" # => 23

Learning Ruby reimplementing it: attr_reader

How handy is the attr_reader method? Very handy indeed.

Never asked yourself how it may be implemented? Just do it!

This is my take:

module Kernel
  def attribute_reader(attribute)
    define_method(attribute) do
      instance_variable_get("@#{attribute}")
    end
  end
end


class MyClass
  attribute_reader :my_attribute
end

m = MyClass.new
m.instance_variable_set("@my_attribute", 42)

puts m.my_attribute # => 42

What’s yours?

Have fun!

A Rails project file for Sublime Text 2

The simplest thing that you can do to open up the Sublime Text 2 editor on your Rails project is to just type in

$ subl .

on the Rails root directory¹.

A slightly better solution is to create a .sublime-project file in your project root directory, so that you may launch the editor with something like

$ subl --project my_rails_project.sublime-project

A question then arises, “How do I exclude useless directories as tmp or log from the project file?”

Answer: just edit the sublime-project file this way:

{
 "folders":
 [
   {
     "path": ".",
     "folder_exclude_patterns": ["tmp", "log"],
     "file_exclude_patterns": [".tmtags"]
   }
 ]
}

(and of course you should adapt it to your actual preferences on what to exclude from your project).

Note that you can also specify a “file_exclude_patterns” property to filter out some files (in my case I wanted to exclude the ctag index file).

  1. to install the command line tool, just follow the docs here.

A review of “Great Bash” video by Carl Albing (O’Reilly Media)

Shell programming is a topic that every professional programmer should care about, and keep improving on, for several reasons.
Here are the first two I can think about:

  1. Because is essential in order to promote the automation of many manual processes (and automation is damn important!).
  2. Because I like to be a programmer as much as a devop, and I want to be able to deploy what I develop, and take care of all the operational and system stuff related to the system I contribute to build.

Me myself made the mistake of underestimate the relevance of this topic for way too time in the past: don’t make my same mistake :-)

Carl Albing’s “Great Bash” by O’Reilly Media is a collection of several short video lessons on the basics of the shell programming.

I watch the “Great Bash” lessons hoping to learn more about shell programming, but unfortunately Carl Albing’s video lessons are too introductory (IMHO).

It was nevertheless great to recap many things I learned here and there, and so my time watching it was not wasted at all, but I would recommend this video only to shell programmer beginners.

There are also some (really) minor defects in the technical way the video is recorded: the audio quality may be improved and sometimes the speaker take some long inexplicable pauses. But, that said, the overall quality is really good.

In the end I recommend to watch this video if you are a shell programmer beginner and you want to start understanding more about this topic.

Dynamically add data accessor methods on “static” Rails data model

An useful metaprogramming spell I recently played with is the Module#define_method(), which dynamically adds an instance method to the class on which is called.

 

I found it particularly useful to add data accessor methods on “static” Rails data model: suppose I’m working an e-commerce Rails webapp, and I have a Country model which maps the countries suitable for shipping, or a PaymentType model which represents all the possible payment types.

For these kind of models (and tables), which are typically static (they don’t change often), you often have to access specific values, say Country.italy or PaymentType.credit_card.

In these cases, defining dynamically an accessor method may be useful and more clear than always perform a find_by_name("my value").

So, for example, I open up my country.rb model class and add these lines

class << self
  Country.all.each do |each_country|
    define_method(each_country.name.downcase.gsub('.', '').gsub(' ', '_')) do
      Country.find_by_iso_code(each_country.iso_code)
    end
  end
end

And then opening the Rails console I will be able to type

1.8.7@epistore > Country.sri_lanka
# {
                :id => 59,
              :zone => "U9",
           :enabled => true,
        :created_at => Tue, 20 Apr 2010 17:01:45 CEST +02:00,
        :updated_at => Tue, 20 Apr 2010 17:01:45 CEST +02:00,
          :iso_code => "LK",
    :country_set_id => nil
}

Just a note: as I said, Module#define_method() will add an instance method on the class. To add a class method, which is what I want, we have to use a different approach, using the class << self syntax to add a singleton method in the receiver.

I may also add a query method on each Country instance to check that country against another country (for example, I may ask my_country.italy?)

  Country.all.each do |each_country|
    define_method(each_country.name.downcase.gsub('.', '').gsub(' ', '_').concat('?')) do
      has_iso_code? each_country.iso_code
    end
  end

And then, after issuing a reload! command in the Rails console, I may type:

1.8.7@epistore > Country.usa.usa?
true
1.8.7@epistore > Country.usa.italy?
false
1.8.7@epistore > Country.usa.south_korea?
false
1.8.7@epistore > Country.south_korea.south_korea?
true

Depending on the kind of Rails app you have, these may be a useful tip.

assert_select_rjs reloaded!

If you ever dared to unit-test a Rails RJS action, for example something like this:
def my_ajax_action
   ...
   render(:update) do |page|
     page.replace_html 'shoppinglist', :partial => 'cart'
     page.replace_html 'items', :partial => 'layouts/items', :locals => { :cart => @cart }
   end
end
you may already know and use the assert_select_rjs testing helper, which basically will verify the structure of your RJS response.

This testing method may really help you shortening the TDD feedback loop in an AJAX-based Rails webapp, and then you’ll may even be confident enough and save one or two brittle Selenium tests.

The only problem with assert_select_rjs is that is (IMHO) poorly documented and rarely googled about.
So, this is my turn to give back what we discovered.

If you have a Rails webapp using jQuery as javascript framework, you may have a hard time using assert_select_rjs correctly, and this is why:

for jQuery, this is the correct way to use assert_select_rjs:
assert_select_rjs :replace_html, '#shoppinglist'
it’s important the ‘#’ prefix here to refer to DOM element IDs, since the notation without ‘#’ will work only if your app uses Prototype.
Another nice thing to know is the way to make assertion on the selection matched by the assert_select_rjs.
For example, this code
assert_select_rjs :replace_html, '#shoppinglist' do
    assert_select '#shipping_cost_description', /Shipping costs for France/
    assert_select '#shipping_cost_value', /&euro; 12,30/
end
will verify that the section replaced inside the ‘shoppinglist’ element will match the two followings assetions.

My first test using webdriver (aka Selenium 2.0)!

As many say, a good solution to selenese flu is Webdriver (see more at http://code.google.com/p/selenium).

Webdriver has been accepted by the Selenium guys as the new approach to web application testing, opposed to the classical “selenium 1.0″ approach, based on a javascript driver, which suffers from way too many issues.
Unfortunately, Selenium 2.0, which plan to fully support Webdriver, is still on an alpha release, and actually is very difficult to find ruby-based web testing tools supporting this alpha version of selenium 2.0.
One of those tools is actually Watir (though Webrat too is planning to support Selenium 2.0 sooner or later), and more precisely this project is quite stable to allow a first test drive.

So this is what I did:

First: installed required gems

  sudo gem install selenium-webdriver
  sudo gem install watir-webdriver --pre

Second: configure my Rails testing configuration to use watir

config/environments/test.rb
  ...
  config.gem "watir-webdriver"
  ...
test/test_helper.rb
  require 'test_help'
  ...
  require 'watir-webdriver'
  ...

Third: write a test

test/integration/paypal_integration_test.rb
require 'test_helper'

class PaypalIntegrationTest < ActionController::IntegrationTest
  include LocaleHelper
  self.use_transactional_fixtures = false

  def setup
    ... some setup stuff here ...   
    @browser = Watir::Browser.new(:firefox)
  end

  def teardown
    @browser.close
  end

  test "something interesting" do
    @browser.goto "https://developer.paypal.com/"
    @browser.text_field(:name, "login_email").set "my_test_account@sourcesense.com"
    @browser.text_field(:name, "login_password").set "mysecret"
    @browser.button(:name, "submit").click

    @browser.goto "https://localhost"

    @browser.link(:id, 'loginlink').click
    @browser.text_field(:name, "email").set @user.email
    @browser.text_field(:name, "password").set @user.password
    @browser.button(:text, "Login").click

    # add_a_product_to_cart
    product = Factory(:product, :code => "a code", :categories => [@juve_store])
    Factory(:product_variant, :code => "03", :availability => 99, :product => product)
    @browser.goto "https://localhost/frontend/products/show/#{product.id}"
    @browser.button(:id, "add_to_cart").click

    @browser.link(:text, "Checkout").click
    @browser.link(:id, "gotobuy").click

    # choose "Paypal"
    @browser.radios.last.set

    @browser.link(:id, "gotobuy").click

    sleep 5
    assert @browser.text.include?("Payment for order #{last_order_number()}")

    @browser.text_field(:name, "login_email").set "my_test_buyer@sourcesense.com"
    @browser.text_field(:name, "login_password").set "yetanothersecrethere"
    @browser.button(:text, "Accedi").click
    @browser.button(:text, "Paga ora").click

    sleep 5
    assert @browser.text.include?("Il pagamento è stato inviato")

    @browser.button(:id, "merchantReturn").click
    assert_contain_waiting("Your purchase")
    assert_contain_waiting(last_order_number())

  end

private

  def last_order_number
    Order.last ? Order.last.number : ""
  end

end

Some comments here:

  • This is a spike, so please don’t say this test is too long and not well refactored
  • I had to put two sleep calls in two places (I gotta say that this specific test, involving paypal sandbox, is really slow due to the slowness in the paypal response time).
  • Anyway, this alpha version of webdriver is still lacking: I cannot say wheather this is a problem I’ll have even with future (possibly more stable) version of Webdriver.

Some references: