.NET Open Spaces 2008

Vom 17.-19. Oktober bin ich in Leipzig beim .NET Open Spaces 2008. Falls jemand Lust hat sich zwecks Keysigning o.ä, dort mit mir zu treffen, lässt sich das bestimmt arrangieren.

Tags

Posted in , | Posted on 08 Oct 2008 10:14by Tobi | no comments

RSpec'ing a Rake TaskLib

I thought it would be a good idea to use RSpec for creating custom Rake TaskLib’s, but I couldn’t find any examples for this. So here’s a pretty simple one from a project I’m working on at the moment. A TaskLib for creating a Mono resource generator task.

This TaskLib should create a file task for a *.resources file, depending on a *.resx file:

require 'rake'

describe ResgenTask do
    it 'should create a file task with the *.resource file as target and *.resx as dependency' do
        ResgenTask.new 'foo.resources'
        Rake::Task.tasks.should satisfy do |tasks|
            for task in tasks
                if task.name == 'foo.resources' and task.prerequisites.include?('foo.resx')
                    break true
                end
            end
            false
        end
    end
end

This doesn’t work because of the missing ResgenTask, so we create it:

require 'rake/tasklib'

class ResgenTask < Rake::TaskLib
    def initialize(target)
    end
end

The specification now fails:

>spec resgentask_spec.rb --format specdoc

ResgenTask
- should create a file task with the *.resource file as target and *.resx as dependency (FAILED - 1)

1)
'ResgenTask should create a file task with the *.resource file as target and *.resx as dependency' FAILED
expected  to satisfy block
./resgentask_spec.rb:11:

Finished in 0.005009 seconds

1 example, 1 failure

But before we implement this specification, let’s refactor it a little bit. This is much more readable:

it 'should create a file task with the *.resource file as target and *.resx as dependency' do
    ResgenTask.new 'foo.resources'
    Rake::Task.tasks.should have_file_task('foo.resources').depending_on('foo.resx')
end

Because there is no have_file_task, we need to create a custom matcher:

module RakeMatchers

class FileTaskMatcher
    def initialize(filename)
         @target = filename
         @source = nil
    end

    def depending_on(source)
        @source = source
        return self
    end

    def matches?(tasks)
        @tasks = tasks
        for task in tasks
            if (task.name == @target) and (not @sources or task.prerequisites.include?(@source))
                return true
            end
        end
        return false
    end

    def failure_message
         "expected file target #{@target} with dependency #{@source} to be in task list [#{@tasks.join(' ')}]"
    end

    def negative_failure_message
         "expected file target #{@target} with dependency #{@source} not to be in task list [#{@tasks.join(' ')}]"
    end
end

def have_file_task(filename)
    return FileTaskMatcher.new(filename)
end

end

In order to be able to use this matcher, we simply include it:

describe ResgenTask do
    include RakeMatchers

    it 'should create a file task with the *.resource file as target and *.resx as dependency' do
        ResgenTask.new 'foo.resources'
        Rake::Task.tasks.should have_file_task('foo.resources').depending_on('foo.resx')
    end
end

It still fails, but with a much nicer output:

>spec resgentask_spec.rb --format specdoc

ResgenTask
- should create a file task with the *.resource file as target and *.resx as dependency (FAILED - 1)

1)
'ResgenTask should create a file task with the *.resource file as target and *.resx as dependency' FAILED
expected file target foo.resources with dependency foo.resx to be in task list []
./resgentask_spec.rb:52:

Finished in 0.005116 seconds

1 example, 1 failure

Time to implement this specification:

class ResgenTask < Rake::TaskLib
    def initialize(target)
        file target => target.gsub('.resources', '.resx')
    end
end

The next specification will describe the behavior of the file task:

it 'should create a file task that runs resgen2' do
        ResgenTask.new 'foo.resources'
end

But wait a moment! Before letting ResgenTask.new create a new file task, the old tasks should be dropped:

describe ResgenTask do
    include RakeMatchers

    before :each do
        Rake::Task.clear
        @tasklib = ResgenTask.new 'foo.resources'
    end

Ok - now we can think about, how to test the generated task. What it should do, is run sh(‘resgen2 foo.resources foo.resx’). We don’t want to actually run sh(), so we need to mock it. sh() is defined in the module FileUtils. My first guess was, to use FileUtils.should_receive(:sh), but this didn’t work. To be honest, I have no idea, what’s the right place to mock sh(). But the simplest solution is to create a mocked sh() member method on the task lib:

it 'should create a file task that runs resgen2' do
    @tasklib.should_receive(:sh).with('resgen2 foo.resources foo.resx')
    Rake::Task['foo.resources'].execute(nil)
end

The specification fails, so let’s implement it:

class ResgenTask < Rake::TaskLib
    def initialize(target)
       file target => target.gsub('.resources', '.resx') do |task|
           sh("resgen2 #{task.name} #{task.prerequisites}")
       end
    end
end

To add an extra goody, the compiled resource file should be removed on the clean target automatically:

it 'should add the *.resources file to the clean target' do
    CLEAN.should include('foo.resources')
end

This can be implemented as:

def initialize(target)
    CLEAN.include(target) 
    file target => target.gsub('.resources', '.resx') do |task|

The final set of specs now looks like this:

describe ResgenTask do
    include RakeMatchers

    before :each do
        Rake::Task.clear
        CLEAN.celar
        @tasklib = ResgenTask.new 'foo.resources'
    end

    it 'should create a file task with the *.resource file as target and *.resx as dependency' do
        Rake::Task.tasks.should have_file_task('foo.resources').depending_on('foo.resx')
    end

    it 'should create a file task that runs resgen2' do
        @tasklib.should_receive(:sh).with('resgen2 foo.resources foo.resx')
        Rake::Task['foo.resources'].execute(nil)
    end 

    it 'should add the *.resources file to the clean target' do
        CLEAN.should include('foo.resources')
    end
end

…and is implemented by:

class ResgenTask < Rake::TaskLib
    def initialize(target)
        CLEAN.include(target) 
        file target => target.gsub('.resources', '.resx') do |task|
            sh("resgen2 #{task.name} #{task.prerequisites}")
        end
    end
end

It’s a pretty simple example, but it should be enough to get started.

Tags ,

Posted in , | Posted on 04 Oct 2008 12:01by Tobi | no comments

e-tobi.net Downtime

In den letzten Jahren hab ich den kleinen Root-Server, der freundlicher Weise von DomainBox gesponsert wird, problemlos via SSH von Sarge auf Etch und kürzlich von Etch auf Lenny geupdated. Ich saß zwar immer etwas zähneknirschend davor und hoffte, dass “apt-get dist-upgrade” nicht die SSH-Verbindung killt und mich aussperrt, aber es lief immer alles gut.

Da ich auf diesem Server demnächst noch ein anderes kleines OpenSource-Projekt hosten möchte (altdotmono.org) und der Server oft bis ans Limit ausgelastet ist, wollte ich, zumindest für die Rails-Anwendungen, auf Lighthttpd umsteigen und Apache nur noch als Proxy einsetzen. Dumm nur, wenn versehentlich die Rails-Anwendung in Apache UND Lighttpd gleichzeitig läuft. Wie auch immer scheint das dazu geführt zu haben, dass ein Server-Prozess die ganze Kiste lahm gelegt hat und damit auch den SSH-Daemon. Da musste ich also mal den netten Support von DomainBox bemühen und nach einem Reset ist nun wieder alles online und hoffentlich auch etwas flotter.

Tags ,

Posted in | Posted on 22 Sep 2008 10:10by Tobi | no comments

Tomaten...

Erntezeit:

…da muss ich wohl ne ganze Menge einkochen!

Tags

Posted in | Posted on 06 Sep 2008 15:43by Tobi | no comments

Pimp your HP Notebook Teil 1

Auch wenn mein neues Notebook mehr oder weniger “state-of-the-art” ist, lässt sich da doch noch einiges mehr rausholen.

Ein Mehr an Laufzeit ist z.B. durch “Undervolting” möglich. D.h. die Spannung des Prozessors wird bis auf das äußerste Minimum reduziert, womit Leistungsaufnahme und Temperatur sinken.

Gerade Penryn-Prozessoren, wie der T9300, erlauben deutlich geringere Spannungen als die Default-Einstellungen vorgeben.

Eine sehr gute Anleitung zum Undervolting findet sich hier:

http://www.overclock.net/laptops-notebook-computers/308654-undervolt-laptop-guide-intel-c2d-t7500.html

Im Normalbetrieb bei 1h Vollast zeigte HWMonitor bei mir eine Temperatur von 78° an. Bei maximaler Geschwindigkeit (ca. 2.5 GHz / 12x) wurde der Prozessor hier mit 1.1375V versorgt. Diese Spannung konnte ich um sagenhafte 0.175 V auf 0.9625 Volt reduzieren. Erst bei 0.95V kam es zu Abstürzen. Bei Vollast mit 0.9625V steigt die Temperatur gerade mal noch auf ein Maximum von 65°. Ein Temperaturunterschied von 13° ist schon recht beachtlich! Ob die angezeigten Temperaturen wirklich stimmen, kann ich nicht sagen. Andere Tools zeigen andere Temperaturen an. Die Differenz ist jedoch immer messbar.

Es lohnt sich, bei einem neuen Notebook das ganze mit dem vorinstallierten Windows zu testen, denn unter Linux wird das schon etwas schwieriger. Hier ist wohl zumindest ein Kernel-Patch notwendig. Aber wenn meine 320’er Platte endlich da ist, kann ich auch das testen.

Tags , , ,

Posted in | Posted on 26 Jun 2008 18:12by Tobi | no comments