August 2006 Archives

ruby memory visualizer

| | Comments (3)

I never ever (hardly ever) do graphics, much less GUI apps. So when I do, it is always a chore. But for fun I just whipped up a simple memory visualizer for ruby in less than 100 lines:

ruby memory visualizer [movie]

(key: String = gray, Array = green, Numeric = black (no fixnums), Class = purple, Hash = blue, everything else = red)

This has zero actual value to finding things out, so I will probably not release it.

as seen on IRC

| | Comments (2)
16:05 srbaker : using << self to replace the "duplication" in "def self."
16:05 srbaker : is like using method_missing to remove the duplication of leading a in
16:05 srbaker : apple and aardvark

risearch getting popular

| | Comments (2)

Apparently risearch got folded into ruby-doc.org! How cool is that? Check it out: http://www.ruby-doc.org/risearch/rand. I just made a shortcut in safari (using Stand but you can also use sogudi) so I can type "ri rand" in my url bar and go right to it.

Also note, if you have ruby 1.8 from CVS or the 1.8.5 preview then you also have ri/rubygems integration. This means that looking up rails doco is much more focused and you can now search the rails doco for goodies!

I miss perl's -B

| | Comments (1)

Perl has a bunch of tests in form of -X path (e.g., -f path returns true if path exists and is a file). We have a direct analog with the test method (e.g., test ?f, path). The one we're missing is -B, which tests whether or not a file is (probably) a binary file. It is great for things like Find-based scripts. Recently I needed it for a script I was writing so I didn't have to keep adding file extensions I didn't want to process. Here it is:

class File
  def self.binary? (path)
    s = (File.read(path, 4096) || "").split(//)
    ((s.size - s.grep(" ".."~").size) / s.size.to_f) > 0.30
  end
end

Now you can write things like:

Find.find dir do |f|
  next if File.binary? f
  # ... process text files only ...
end
During hacking night last night Eric and I added path independence and searching of gems and local installed ri/rdoc. Enjoy!
#!/usr/local/bin/ruby -w

require 'rdoc/ri/ri_paths'
require 'find'
require 'yaml'

search = ARGV.shift

puts "Searching for #{search}"
puts

dirs = RI::Paths::PATH
dirs.each do |dir|
  Dir.chdir dir do
    Find.find('.') do |path|
      next unless test ?f, path
      yaml = File.read path
      if yaml =~ /#{search}/io then
        full_name = $1 if yaml[/full_name: (.*)/]
        puts "** FOUND IN: #{full_name}"
        
        data = YAML.load yaml.gsub(/ \!.*/, '')
        desc = data['comment'].map { |x| x.values }.flatten.join("\n").gsub(/&quot;/, "'").gsub(/&lt;/, "<").gsub(/&gt;/, ">").gsub(/&amp;/, "&")
        puts
        puts desc
        puts
      end
    end
  end
end

UPDATE: See the latest version.

Check it out. Quick and dirty searching of ri content:

#!/usr/local/bin/ruby -w

require 'find'
require 'yaml'

search = ARGV.shift

puts "Searching for #{search}"
puts

Dir.chdir '/usr/local/share/ri/1.8/system' do
  Find.find('.') do |path|
    next unless test ?f, path
    yaml = File.read path
    if yaml =~ /#{search}/io then
      full_name = $1 if yaml[/full_name: (.*)/]
      puts "** FOUND IN: #{full_name}"
      
      data = YAML.load yaml.gsub(/ \!.*/, '')
      desc = data['comment'].map { |x| x.values }.flatten.join("\n").gsub(/"/, "'").gsub(/</, "<").gsub(/>/, ">").gsub(/&/, "&")
      puts
      puts desc
      puts
    end
  end
end
Lets you do stuff like:
% ./risearch.rb duplicate
Searching for duplicate
[...]
** FOUND IN: Array#uniq!

Removes duplicate elements from self. Returns nil if no changes are made (that is, no duplicates are found).
   a = [ 'a', 'a', 'b', 'b', 'c' ]
   a.uniq!   #=> ['a', 'b', 'c']
   b = [ 'a', 'b', 'c' ]
   b.uniq!   #=> nil

** FOUND IN: Array#|

Set Union---Returns a new array by joining this array with other_array, removing duplicates.
   [ 'a', 'b', 'c' ] | [ 'c', 'd', 'a' ]
          #=> [ 'a', 'b', 'c', 'd' ]
[...]

Upgrade rails now

| | Comments (3)

You need to upgrade rails now. Right now.

If you prefer to freeze your rails checkout. I recommend stealing this rule:

namespace :rails do
  namespace :freeze do
    desc "Lock to a specific rails version. Defaults to 1.1.5 or specify with RELEASE=x.y.z"
    task :version do
      rel = ENV['RELEASE'] || '1.1.5'
      tag = 'rel_' + rel.split(/[.-]/).join('-')
      rails_svn = "http://dev.rubyonrails.org/svn/rails/tags/#{tag}"

      puts "Freezing to #{tag} using #{rails_svn}"
      sh "type svn"
      
      dir = 'vendor/rails'
      rm_rf dir
      mkdir_p dir
      for framework in %w( railties actionpack activerecord actionmailer activesupport actionwebservice )
        checkout = "#{dir}/#{framework}"
        sh "svn export #{rails_svn}/#{framework} #{checkout}"
        unless test ?d, checkout then
          puts "ERROR: checkout missing: #{checkout}"
          exit 1
        end
      end
    end
  end
end

and running:

rake rails:freeze:version

It'll default to 1.1.5 or you can specify the tagged version you want using RELEASE=x.y.z.

using ZenTest on rails

| | Comments (0)

Currently to run zentest on rails you have to jump through some extra hoops. This is because you need to require config/environment to get the magic const loader. BUT, because that maps ClassName to class_name.rb, you also have to load app/controllersapplication.rb (because the class inside is ApplicationController, not Application):

ruby -I.:lib:test -r ./config/environment.rb -r app/controllers/application.rb \
  -S zentest -r app/controllers/groups_controller.rb \
  test/functional/groups_controller_test.rb

I've got some unreleased changes that should make it as easy as:

zentest -r app/controllers/groups_controller.rb \
  test/functional/groups_controller_test.rb

you might just fit here...

| | Comments (0)

From sfbay.craigslist.org:

"The application is built on your classic open source Java stack of Spring and Hibernate running on Tomcat and talking to a MySQL database, so if you haven't yet switched to Ruby, you might just fit in here."

read: if you haven't yet been enlightened, you might not hate your job here...

I recently read How to create a Ruby extension in C in under 5 minutes by Peter Cooper. I decided to replicate the example to see how things fare these days. I basically followed his lead, typing in everything, but cleaning up the code to suit my tastes a bit better (and in some cases, to be more correct in C-land).

First, I wrote the following:

#!/usr/local/bin/ruby -w

require 'mkmf'
extension_name = 'example'
dir_config(extension_name)
create_makefile(extension_name)

Nothing fancy, just a simple mkmf generator. I don't like mkmf (esp. its insides), but this is clean enough. After that, I wrote (and cleaned up) the C file:

#include "ruby.h"

static VALUE method_test1(VALUE self) {
  int x = 10;
  return INT2NUM(x);
}

void Init_example() {
  VALUE Example = rb_define_module("Example");
  rb_define_method(Example, "test1", method_test1, 0);
}

Nothing fancy. The only real differences from the original were made to keep the symbol table clean. All in all, it took me about 7 minutes, not too far off from the original article's title. And sure enough, it can be run just fine via:

./example-build.rb
make
ruby -I. -rexample -e 'include Example; puts test1'

Not too bad. I don't have any real complaints about that... except that the alternative is so much easier and so much cleaner that I don't really see why anyone would choose this way of going about it. Examine:

#!/usr/local/bin/ruby -w

require 'rubygems'
require 'inline'

class Example
  inline(:C) do |builder|
    builder.c "int test1() {
                 int x = 10;
                 return x;
               }"
  end
end

p Example.new.test1

and you run it with:

./example.rb

Notice how I just run the script? That is because compiling, linking, and loading are all handled for you, as needed, and you can never accidentally skip a step. Notice how I just write plain C? int test1() is about as clean as you can get. Notice how I return x straight up? No type conversions here.

The point is to remove the distractions so you can get your work done. No more wondering "did I type make?" or "what is the proper macro to convert a string?" as it is all handled for you. Give developers the tools they need so they can stay in thought, not in process.

It doesn't get simpler than this... and really, it shouldn't ever be more difficult than this.