Random Updates

Posted by MSch Wed, 10 May 2006 10:22:00 GMT

I’m currently under quite some stress with the approaching final exams, but I’ve nevertheless two findings worth sharing:

  • Necklaces resembling neurotransmitters
  • A YAML parser in pure Ruby, that supports using a mapping as a key in one line.
    [123, 123]: asd
    This is something Syck won’t parse, although it’s correct YAML, as xitology from #yaml told me. He also informed me about his SoC project called libyaml, that would be a fast, feature complete alternative to Syck. If Google accepts his proposal he expects a release in 3 to 4 months.

Tags , ,  | 2 comments | 3 trackbacks

How to write DSLs with Ruby

Posted by MSch Sat, 01 Apr 2006 20:51:00 GMT

Recently I needed a DSL like the one in Rake for one of my projects. At first I just googled around a bit hoping to find a comprehensive tutorial. I did gain some more or less useful insights, but what I wanted was something as clean and coherent as a Rakefile:

  namespace "samples" do
    task :build do
      # Build the sample programs
    end
  end

After reading lots of tutorials and gaining come clues all I managed to code was:

  namespace do |n|
    n.task do |t|
      # Build the sample programs
    end
  end

I felt terrible and started reading the Pickaxe, desperate for a solution. Proc, Binding, Object, Kernel. I was looking everywhere for some clues on how to make my DSL as great as Rake’s. Finally I found what I was looking for: Object#instance_eval!

I’ll spare you any further explainations and present you the source (abridged, download full version) to my DSL implementation:

class PseudoRakefile
  def evaluate(&block)
    Tasks.new({}, {}, self, &block)
  end

  protected
  class Tasks
    attr_reader :parent
    def initialize(args, options, parent, &block)
      # snip
      instance_eval &block
    end

    def task(task, args = {}, &block)
     Tasks.new(@args.merge(args), @options.merge(:task => task), self, &block)
    end
    def namespace(name, &block)
      Tasks.new(@args, @options.merge(:namespace=>name), self, &block)
    end

    # snip
    def puts(msg)
      Kernel.puts "#{@options[:namespace]}:#{@options[:task]} #{msg}"
    end
  end
end

rakefile = PseudoRakefile.new

rakefile.evaluate do
  namespace "samples" do
    task :build do
      develop('deal', :do_it => :great)
      sleep 2
      develop('solution', :do_it => :sloppy)
    end
    task :destroy, :probability => 1 do
      puts "Tear everything down like a little kid"
      rm("Schroedinger's Cat", :probability => 0.5)
      rm("some unimportant file")
    end
  end
end

It’s free to use for anyone, but you’d make this boy very happy by leaving a comment, especially if you have improved the core idea or actually use my code in your projects.

Tags ,  | 1 comment | 2 trackbacks

Ruby Extensions Project

Posted by MSch Mon, 21 Nov 2005 13:04:00 GMT

After reading about the Ruby Extensions Project in Why’s Hopping Through Pipes and Closures I decided to try it out.

I installed it via gem install -r extensions and started reading the documentation.

One of the many useful features I stumbled upon is the autoinit method: In the following example radius, location will be automagically assigned to the corresponding instance variables, while area will have some additional processing applied. And with the saved keystrokes I was able to type this post.
class Circle
  attr_reader :radius, :location, :area
  autoinit(:radius, :location) do
    @area = Math::PI * @radius ** 2
  end
end

Circle::new(1,2,3)

New with 0.6.0 comes require_relative, which requires a file relative to the file containing the statement.

Finally my_object.in? some_other_object provides a pythonesque way of expressing some_other_object.include?(my_object).

Of course there are many many more features but these are the ones that seem particularly useful to me

Tags  | no comments | no trackbacks