When testing isn’t worth the price

I’m just starting to use RSpec for Rails instead of Test::Unit, and with it comes a little novelty: there are separate Controller and View tests (unlike TUnit’s functional tests). At first I thought “hm.. cool”. But after spending the first hours writing tests for views I started to feel very stupid, and the whole thing feels very awkward and unnecessary.

Views are too unstructured and change too often for it to be worth keeping it all tested, and most of the time you’re not testing Ruby code, but HTML, and I don’t think that’s what tests are for. If your controllers are well tested, views should do OK.

As convinced as I am about this, I was feeling a little guilty to just ditch testing like that, so I searched for some supporting opinion and found this post. It agrees with me, so it must be right :) The comments are also interesting. The main idea is that you should just test if the views render without errors and get on with life. Now I just have to find out how to test that little thing.

Yak Shaving: optimizing brain usage for code snippets

So you checked out how TextMate has all those wonderful snippets for every possible piece of code you could think of (and how now Emacs also does!), but you’re having second thoughts on wether it pays off to memorizing all those little abreviations and their meanings?

Not anymore!

With the cute one-liner below, you can fire up irb on your snippets directory and see instantly the TOP 5 winners in characters-saved / characters-typed !! Pretty neat huh? ;)

Here’s an example:

$ cd ~/.emacs.d/yasnippet/snippets/text-mode/ruby-mode
$ irb
irb(main):019:0> Dir['*'].map {|f| [f,File.read(f).reject{|s| s =~ /^#/}.join.size.to_f/f.size]}.sort {|a,b| b.last <=> a.last}.first(5).each {|f| puts "####" + f.first,File.read(f.first), "\n" *2}
####w
#name : attr_writer ...
# --
attr_writer :${attr_names}

####r
#name : attr_reader …
# –
attr_reader :${attr_names}

####mm
#name : def method_missing … end
# –
def method_missing(method, *args)
$0
end

####am
#name : alias_method new, old
# –
alias_method :${new_name}, :${old_name}

####bm
#name : Benchmark.bmbm(…) do … end
# –
Benchmark.bmbm(${1:10}) do |x|
$0
end

=> [["w", 26.0], ["r", 26.0], ["mm", 21.0], ["am", 19.5], ["bm", 19.5]]

It would probably be nice also to consider how much typing it saves you by mirroring variable names and stuff… And also how frequent that particular construct actually is in your code… Come to think of it, this one-liner is pretty useless, but at least picking an arbitrary 5 or 6 snippets to add to your dynamic cheatsheet is better than trying to randomly memorize them.

In case you didn’t catch it, here it is in full color (and we discover wordpress doesn’t use a full parser for syntax highlighting, what a shame :P):

Dir['*'].map {|f| [f,File.read(f).reject{|s| s =~ /^#/}.join.size.to_f/f.size]}.sort {|a,b| b.last => a.last}.first(5).each {|f| puts "####" + f.first,File.read(f.first), "\n" *2}

Rails vs SCM: resolving conflicts between local and upstream Migrations

If you’re working on a local branch of a Rais project for long enough, you’re bound to run into this irritating problem: you create a new migration, it gets the smallest unique number from the ones you got from upstream, BUT, before you get the chance to commit it, someone does it first, and in your next update (svn up || git pull) you have that tangled migration mess.

This little rake task might help you out. Warning: it assumes that all your local migrations have already been run, and that *none* of the new migrations from upstream have been run.

The code is definetely not very DRY and doesn’t take much advantage of Rake (I’m pretty n00b on Rake), so I accept suggestions/patches :)

To use it, just throw it in your lib/tasks folder and call it using “rake db:migrate:fast_forward”.

Next (and easy) step is making it receive a SCM parameter (git/svn) so it’ll use the proper “mv” command.


namespace :db do
  namespace :migrate do
    desc <<STR
Resolves conflicts between local and upstream migrations.

This task assumes the following scenario:
During your local development, you've created migrations and ran rake db:migrate;
Then, you updated from upstream (svn update || git svn rebase), and ended up with
pairs of migrations with the same number: one is the local you created, and the
other is the one from upstream that someone commited before you.

Besides that, there might be some other non-overlapping migrations *after* the
overlapping zone that are *also* local (you had more local migrations than new ones
that came from upstream on the update).

This tasks takes *all your local migrations*, **reverts them** (in reverse order),
and moves them (in order) to the end of the line.

After that you can run rake db:migrate again and it'll run first the migrations
from upstream, and yours last.
STR

    task :fast_forward => :environment do
      migrator = ActiveRecord::Migrator.new(:down, 'db/migrate')
      puts "Looking for migrations with repeated numbers"
      all_migrations = Dir['db/migrate/*'].sort
      pairs = all_migrations.group_by{|migration| migration =~ /(\d+)/; $1}.
        select {|number, migrations| 1 < migrations.size && migrations.size < 3}
      pairs = pairs.sort {|x, y| x[0] <=> y[0]}

      # Pick the range of (local) migrations that will be slided to the end
      migrations_to_move = []
      # First the ones that overlap (disambiguated by user)
      pairs.map{|pair| pair[1]}.each do |mig1, mig2|
        begin
          puts "\n[1]\t#{mig1}"
          puts "[2]\t#{mig2}"
          puts "\nWhich one is part of the range to be slided to the end of the list?"
          option = STDIN.gets.to_i
        end until option == 1 || option == 2

        migrations_to_move << (option == 1 ? mig1 : mig2)

      end
      # Then the (local) ones past the overlap zone
      unless pairs.empty?
        idx_last_overlapping_migration = all_migrations.index(pairs.last[1].last)
        migrations_to_move += all_migrations[idx_last_overlapping_migration+1..-1]
        # Assumes all (and only) the local ones past the overlap zone have already been run
        migrations_to_move.reject! { |m| m =~ /(\d+)/; $1.to_i > migrator.current_version }
      end

      migrations_to_move.first =~ /(\d+)/
      schema_version = $1.to_i # set_schema_version subtracts one

      # Slide the range to be slided to the end of the list
      upstream_migrations = all_migrations - migrations_to_move
      upstream_migrations.last =~ /(\d+)/
      next_number = $1.to_i + 1

      new_names = migrations_to_move.map { |migration|
        migration =~ /(\d+)(.*)/
        name_migration_to_move = $2

        new_name = 'db/migrate/' + ("%03d" % next_number) + name_migration_to_move
        next_number += 1
        new_name
      }

      # Confirm and execute
      unless migrations_to_move.empty?
        pp "Latest upstream migrations", upstream_migrations.last(5)
        pp "These are your local migrations: ", migrations_to_move
        pp "They will be reverted and renamed to: ", new_names
        puts "And the new schema version will be: #{schema_version-1}"

        begin
          puts "\nShould I proceed? [Y/n] "
          option = STDIN.gets.strip.downcase
        end until option == 'y' || option == 'n'

        if option == 'y'
          # Revert
          migrations_to_move.reverse.each do |migration|
            require migration
            migration_class = migrator.send(:migration_class, *(migrator.send(:migration_version_and_name, migration).reverse))
            migration_class.down
          end
          migrator.send(:set_schema_version, schema_version)
          # Move to end of line
          migrations_to_move.zip(new_names) do |old_name, new_name|
            File.rename old_name, new_name
          end
        end
      else
        puts "No overlapping migrations. You can safely run rake db:migrate."
      end
    end
  end
end

Update: Just after writing this, a friend told me about the Git Migration Buddy. It is git specific and seems to handle handle multiple branches better. Mine is kinda 1-n (main (svn in my case) repo syncing with multiple local branches). There’s the enhanced_migrations plugin that supposedly stops the problem at the root, having timestamps instead of increasing numbers for migrations. Zach in the comments also mentions a great solution he’s coming up with: a post-checkout hook to change database.yml and have a different db for each branch (dunno if it works too well with big dbs, but it’s a great idea nonetheless).

Making Vlad copy database.yml to shared folder

Found this at http://topfunky.net/svn/shovel/merb/vlad_config.rb

Also good example of how to make vlad tasks.

# config/deploy.rb
# ... 
set :config_files, ['database.yml']

 namespace :vlad do

   desc "Copy config files from shared/config to release dir"
   remote_task :copy_config_files, :roles => :app do
     config_files.each do |filename|
       run "cp #{shared_path}/config/#{filename} #{release_path}/config/#{filename}"
     end
   end

   desc "Deploys"
   remote_task :deploy do
   end

   task :deploy => [:setup, :update, :copy_config_files, :migrate, :start]

 end
end

As you can see, I couldn’t find a way to make :copy_config_files be called after :update by just using

namespace :vlad
  task :update do
    Rake::Task[:copy_config_files].invoke
  end
end

so I ended up just creating an all-encompassing task, and that takes care of my needs at least for now. If you have any ideas on how to make this better, plesae pitch in.

Power-Set Method For Ruby Hash

# The Array power set is stolen from http://snippets.dzone.com/posts/show/3524
class Array
  # Returns the "power set" for this Array. This means that an array with all
  # subsets of the array's elements will be returned.
  def power_set
    # the power set line is stolen from http://johncarrino.net/blog/2006/08/11/powerset-in-ruby/
    inject([[]]){|c,y|r=[];c.each{|i|r<<i;r<<i+[y]};r}
  end
end

class Hash
  def power_set
    # Returns the "power set" for this Hash. This means that a array with hashes of all
    # subsets of the hash's (key => value) pairs will be returned.
    # Example:
    # >> {:feedback_type=>"", :language_code=>"", :comment=>""}.power_set
    #
    # [{}, {:comment=>""}, {:language_code=>""}, {:language_code=>"", :comment=>""}, {:feedback_type=>""}, {:feedback_type=>"", :comment=>""}, {:feedback_type=>"", :language_code=>""}, {:feedback_type=>"", :language_code=>"", :comment=>""}]

    hash_to_array = self.to_a
    array_power_set = hash_to_array.power_set
    hash_power_set = array_power_set.collect { |pairs| pairs.inject({}) { |hash,pair| hash[pair[0]] = pair[1]; hash } }
    hash_power_set
  end
end

TestGen4Ruby: One Point Oh

From regular versioning culture, one would think that TestGen4Ruby has just graduated and is now an all-mature big boy. False. Just as a clarification, I’m using the versioning scheme suggested by Rubygem’s manual, which can be summed up as: left number => breaks compatibility; middle number => adds new feature without breaking previous stuff; right number => bugfix, cleanup or some other minor change that doesn’t affect the API.

More explanation and arguments in favor of this strategy can be found at the manual itself.

This new version basically fixes a little problem with the input from TestGen4Web. It’s a dirty little fix that doesn’t cover all cases, because that is impossible from inside tg4rb. To fix this for good, a good many changes must be made to tg4w itself. I’m currently working on this.

Just so the “One Point Oh” spirit doesn’t get totally blown away by the clever versioning policy, there is a graduation of sorts going on for TestGen4Web today: this is the last change that is part of my work during the Google Summer of Code (I didn’t have time to commit this before the “pencils down” deadline so I’m releasing now, but it was coded inside that time window). So from now on I’ll just keep fiddling with it on my own. It is a good feeling knowing that now I have a little (my first!) baby free software project on my hands :-)

Well, to the official release notes already:

Subject: [ANN] tg4rb 1.0.0 Released

tg4rb version 1.0.0 has been released!

* <by Helder Ribeiro>
* <(http://tg4rb.googlecode.com)>

## FEATURES:

* takes an XML with actions from tg4w and outputs stand-alone Ruby code that can be run directly or embedded into existing code (e.g. for test automation).
* can be used as a library or as an executable script.
* takes the xpath from tg4w and uses only enough info to guarantee the uniqueness of the referenced element, allowing the generated script to keep working even with some change to page structure.

## PROBLEMS:

* the generated code is damn ugly.
* a few action types from the XML input still aren’t recognized.
* some dependencies aren’t very clearly sorted out yet. you might find you need something that is not specified. if yes, please report this as an issue in our issue tracker.
* i don’t know, find more and let me know =)

## SYNOPSIS:

Changes:

## 1.0.0 / 2007-08-21
* NOTE: This version breaks compatibility with previous version! Scripts written in previous versions won’t work.
* fixed small bug with escaping single quotes
* API change: changed the visibility of element_by_least_restrictive_xpath in Tg4rbToolbox to private
* API change: created public method element() in Tg4rbToolbox
* provided workaround for bug in xpath from tg4w: now clicks on links work with the link’s text (requires HPricot)

## 0.0.2 / 2007-07-09
* fixed silly import problem on generated script

* <by Helder Ribeiro>
* <(http://tg4rb.googlecode.com)>

[ANN] tg4rb 0.0.2 Released!!

Here’s my Google Summer of Code baby!!! =D If you find any problem with the code or the documentation, please send an email to the mailing list or post an issue in our issue tracker. I’m off to Peru today, so I might not check email too often, but I’ll try my best to keep up. Enjoy!

* <by Helder Ribeiro>
* <(http://code.google.com/p/tg4rb)>

## FEATURES:

* takes an XML with actions from tg4w and outputs stand-alone Ruby code that can be run directly or embedded into existing code (e.g. for test automation).
* can be used as a library or as an executable script.
* takes the xpath from tg4w and uses only enough info to guarantee the uniqueness of the referenced element, allowing the generated script to keep working even with some change to page structure.

## PROBLEMS:

* the generated code is damn ugly.
* a few action types from the XML input still aren’t recognized.
* some dependencies aren’t very clearly sorted out yet. you might find you need something that is not specified. if yes, please report this as an issue in our issue tracker.
* i don’t know, find more and let me know =)

## SYNOPSIS:

Changes:

## 0.0.2 / 2007-07-09
* fixed silly import problem on generated script

## 0.0.1 / 2007-07-09
- Birthday
* <by Helder Ribeiro>
* <(http://code.google.com/p/tg4rb)>

Posted in Uncategorized. Tags: , , , , , , . 1 Comment »

Sweeet navigation

Oh boy how long was I longing for this! I think I was the only n00b in the block who didn’t know about it, but still:

$ gem install rtags
$ cd directory/with/your/ruby/files
$ rtags --vi -R *

Then fire up vim with your *.rb files, place the cursor on a method call and type ^] (Ctrl+]) to jump to its definition (remember the :help ?). To go back, Ctrl+O or Ctrl+T.

Halleluia!

P.S.: wordpress, eager the way it is to help make things pretty, converts the double dash before “vim” in the “rtags” command into a single long dash, so if you just paste it, it won’t work. type it in yourself.

Update: of course, the sweetness comes from the fact that it also works across different files. Enjoy ;)

Posted in Uncategorized. Tags: , , , , , . Leave a Comment »

Colorful Ruby code for your blog

Found out about this nice gem that does syntax highlighting of Ruby code (and C, Delphi, HML, RHML and Nitro-HTML for now) and outputs it as a stand-alone HTML. Perfect for blogging. I had found some other one some time ago that needed javascript, and it’s not every blogging service that lets you add that kinda stuff to your posts.

There are many configurable options, other types of output and stuff, you can find all that (very well documented) at CodeRay’s website.

So as a try-out, here’s the output of a little script to color ruby code, run on itself:

 1 #!/usr/bin/env ruby
 2 require 'rubygems'
 3 require 'coderay'
 4
 5 if ARGV.length != 1
 6   puts "Wrong number of arguments. Use: color_html <source_file>"
 7   exit
 8 end
 9
10 rb_file = File.expand_path(ARGV[0])
11 if rb_file.split('.')[-1] != 'rb'
12   puts "Not a .rb file"
13   exit
14 end
15
16 html_file = rb_file.split('.')[0..-2].join('.') << '.html'
17 if File.exists? html_file
18   puts "File #{html_file} exists. nOverwrite it? (y/N)"
19   # need to make STDIN explicit because there's a filename in the 
20   # command-line argument (gets defaults to reading from it)
21   if STDIN.gets.strip == 'y'
22     File.delete(html_file)
23   else
24     exit(true)
25   end
26 end
27
28 File.open(html_file,'w') do |f|
29   f << CodeRay.encode(
30          File.read(rb_file),
31          :ruby,
32          :html,
33          :line_numbers => :inline,
34          :hint => :info,
35          :css => :style,
36          :wrap => :div
37        )
38 end
39
40 puts "Done! Thanks for coming! =)"
Posted in Uncategorized. Tags: , , , . 4 Comments »

Between ’share-nothing’ and locks?

On this recent great article, Werner Schuster quoted Guido van Rossum on the whole green-threads vs. native-threads vs. Global-Interpreter-Lock debate:

“Nevertheless, you’re right the GIL is not as bad as you would initially think: you just have to undo the brainwashing you got from Windows and Java proponents who seem to consider threads as the only way to approach concurrent activities.

Just because Java was once aimed at a set-top box OS that didn’t support multiple address spaces, and just because process creation in Windows used to be slow as a dog, doesn’t mean that multiple processes (with judicious use of IPC) aren’t a much better approach to writing apps for multi-CPU boxes than threads.

Just Say No to the combined evils of locking, deadlocks, lock granularity, livelocks, nondeterminism and race conditions.”

Did anyone else hear “Software Transaction Memory” in that last line?

Besides, if the ’share-nothing’ approach is a conscient design decision, like in Erlang, that’s great. But it really shouldn’t be used as an excuse not to solve the Global Interpreter Lock problem.

Posted in Uncategorized. Tags: , , , , , . Leave a Comment »