Script de compilation LaTeX en Ruby
Mon manuscrit de thèse sera rédigé en LaTeX (le premier qui me parle de Word se fera frapper avec violence
) et histoire d'automatiser les choses, j'ai écrit un script de compilation en Ruby.
J'aurai pu utiliser un Makefile, un des scripts existants (il y en a un très bien en Perl dont j'ai oublié le nom), du Python, du Groovy... mais ce script là me va très bien !
#!/usr/bin/env ruby require 'optparse' require 'fileutils' MAIN_FILE = "phd-thesis" PDFLATEX = "pdflatex #{MAIN_FILE}.tex" BIBTEX = "bibtex #{MAIN_FILE}.aux" def build_pdf return unless system PDFLATEX return unless system BIBTEX callcc do |stop_build| 3.times do status = system PDFLATEX stop_build.call unless status end end end def single_compilation system PDFLATEX end def clean_artifacts extensions = %w[blg log pdf aux bbl lof lot out toc tps] files = extensions.map { |ext| "#{MAIN_FILE}.#{ext}" } FileUtils::Verbose::rm files, { :force => true } end def open_pdf(how) system "#{how} #{MAIN_FILE}.pdf" end def run options = { :command => :pdf } OptionParser.new do |opts| opts.banner = "Usage: build.rb [options]" opts.on("--pdf", "Build the PDF output (default)") do |pdf| options[:command] = :pdf end opts.on("--oneshot", "Single LaTeX compilation") do |oneshot| options[:command] = :oneshot end opts.on("--clean", "Clean the build artifacts") do |clean| options[:command] = :clean end opts.on("--open-pdf [HOW]", "Open the generated PDF with HOW)") do |how| options[:openpdf] = how end opts.on_tail("-h", "--help", "Show this message") do puts opts exit end end.parse! case options[:command] when :pdf build_pdf open_pdf(options[:openpdf]) if options[:openpdf] when :oneshot single_compilation open_pdf(options[:openpdf]) if options[:openpdf] when :clean clean_artifacts end end run
Je ne garanti pas que le code soit parfait, alors n'hésitez pas à émettre des critiques constructives !
Rails-style Ruby meta-programming
I am giving a Ruby lecture tomorrow (some students happen to be quite lucky in fact). I wanted to present an example of a Ruby On Rails style meta programming technique. For example you can have a find_by_name or find_by_email method on your ActiveRecord objects. Those methods are in fact generated on the fly using a technique which looks like what follows. This has probably been explained a million times somewhere else on the web, but I don't care
So let's start with a very basic contacts book class which looks like:
class ContactBook def initialize @contacts = Hash.new end def add_contact(name, email) @contacts[name] = email end def contact_named(name) @contacts[name] end end book = ContactBook.new book.add_contact "Pierre", "pierre@zzland.fr" book.add_contact "Julien", "julien@zzland.fr" book.add_contact "Yoan", "yoan@zzland.fr" book.add_contact "Mr Bean", "info@mrbean.com"
Nothing impressive here, so now let's get all the contacts whose email contain a given substring:
class ContactBook def email_containing(str) @contacts.select { |key, value| value.include? str } end end def display_contact(contact) puts "#{contact[0]} <#{contact[1]}>" end zzland = book.email_containing "zzland" zzland.each { |contact| display_contact(contact) }
But what if we could have email_thestringtolookfor methods instead of this? Let's do it by evaluating some code on the fly:
class ContactBook def method_missing(id, *args) method_name = id.to_s if method_name[0..5] == 'email_' str = method_name[6..method_name.length] to_eval = <<-END_FUNC def email_#{str} @contacts.select { |key, value| value.include? "#{str}" } end END_FUNC instance_eval to_eval return eval("self.email_#{str}") end end end zzland = book.email_zzland bean = book.email_bean zzland.each { |contact| display_contact(contact) } puts "-----" bean.each { |contact| display_contact(contact) }
method_missingis invoked whenever a message is sent to the class instance, but no matching method name can be found.- If the name starts with
email_, then we are in luck! (note that I should throw an exception if the name doesn't start with this to comply with the Ruby semantics, but I was too lazy for that) - We generate a new method for the instance through evaluation.
- We do not forget to invoke the new method through evaluation, as the original call would return
nilsince the method had not been found.
Nice isn't it?
Ruby fun with Rinda
Today I had fun playing with Rinda, the Ruby implementation of the Linda distributed process coordination paradigm. I also had some time for blogging, something that I haven't done for a few days because of too much work (and there still a bunch left to be done
)
I won't go through the theory behind Linda, but the gross idea is to use a shared memory space that implements the distributed blackboard paradigm : the Tuple Space. It can be summarized on the following figure:

Data is represented as sets of tuples which often have a limited lifetime. The tuple space provides the distributed processes the following primitives:
- write: writes a tuple to the tuple space
- read: reads a tuple from the tuple space
- take: reads and removes a tuple from the tuple space
The beauty of the model lies in the fact that the tuple space guarantees atomic operations and periodically cleans up old tuples. Also, reading operations match a tuple on:
- the length of the requested and available tuples
- the matching (exact value, regular expressions, ...) of certain elements of the tuple.
I have put together a first Ruby program that reads text from the console, and writes tuples whenever a line is entered. The other program takes every new tuple that is emitted.
emitter.rb
#!/usr/bin/env ruby require 'drb/drb' require 'rinda/ring' require 'rinda/tuplespace' DRb.start_service tuple_space = Rinda::TupleSpace.new ring_server = Rinda::RingServer.new tuple_space while true tuple_space.write [:message, gets] end
receiver.rb
#!/usr/bin/env ruby require 'drb/drb' require 'rinda/ring' require 'rinda/tuplespace' DRb.start_service tuple_space = Rinda::RingFinger.primary while true puts tuple_space.take([:message, nil])[1] end
What I really like is the simplicity of the code, thanks to the cleanness of Linda and the elegance of Ruby
The other thing is that DRb is such a straightforward way to use distributed objects in Ruby...
BTW there is a Java implementation of this paradigm called JavaSpaces.

