Sebastians Blog http://sgaul.de Neues aus den Softwareminen... Fri, 19 Dec 2014 16:04:04 +0000 de-DE hourly 1 http://wordpress.org/?v=4.1 Bundler is using a binstub that was created for a different gem? http://sgaul.de/2014/12/04/bundler-is-using-a-binstub-that-was-created-for-a-different-gem/ http://sgaul.de/2014/12/04/bundler-is-using-a-binstub-that-was-created-for-a-different-gem/#comments Thu, 04 Dec 2014 14:45:23 +0000 http://sgaul.de/?p=2718 Weiterlesen ]]> In regelmäßigen Abständen wirft unsere Konfiguration aus Bundler 1.7, Rails 4.1, Rspec 2.14 und Spring 1.1 bei jedem Rails-, Rake- oder Rspec-Befehl die folgende Warnung:

Bundler is using a binstub that was created for a different gem.
This is deprecated, in future versions you may need to `bundle binstub rspec` to work around a system/bundle conflict.

Auch andere Nutzer scheinen davon verwirrt zu sein, eine klare Erklärung des Problems und seiner Lösung konnte ich bisher nicht finden. Folgender Reset der Binstubs funktioniert für mich als Workaround:

rm -rf bin/*
bundle exec spring binstub --all
bundle binstub rspec-core --force
]]>
http://sgaul.de/2014/12/04/bundler-is-using-a-binstub-that-was-created-for-a-different-gem/feed/ 0
Bracket-Balancing in Ruby http://sgaul.de/2014/11/29/bracket-balancing-in-ruby/ http://sgaul.de/2014/11/29/bracket-balancing-in-ruby/#comments Sat, 29 Nov 2014 20:35:44 +0000 http://sgaul.de/?p=2712 Weiterlesen ]]> Ich kann mir nicht helfen, nach zwei Jahren Ruby sieht Java manchmal nur noch umständlich aus. Gerade bei kleinen, in sich geschlossenen Programmen sollte man die Sprache mit Bedacht wählen. Wie aufwendig kann es sein herauszufinden, ob die verschiedenen Klammerarten in einem String ausbalanciert sind? Diese Frage stellte sich auch Code Corner und lieferte diese Lösung in Java:

http://www.corejavainterviewquestions.com/code-corner-bracket-balancing/

http://www.corejavainterviewquestions.com/code-corner-bracket-balancing/

Als Rubyist juckt es da in den Fingern, das muss doch schneller gehen.

require 'test/unit/assertions'
extend MiniTest::Assertions

class String

  BRACKETS = {
    "(" => ")",
    "[" => "]",
    "{" => "}",
  }

  def balanced?
    stack = []
    each_char do |char|
      if BRACKETS.keys.include? char
        stack.push char
      elsif BRACKETS.values.include? char
        return false if BRACKETS[stack.pop] != char
      end
    end
    stack.empty?
  end

end

assert "".balanced?
assert "([Hell{} T(h(e[r]e))]boom)".balanced?
assert not("(a[b{c)d]e}".balanced?)
assert not("([{".balanced?)
assert not("}])".balanced?)
]]>
http://sgaul.de/2014/11/29/bracket-balancing-in-ruby/feed/ 0
Spring ohne Bin-Präfix nutzen http://sgaul.de/2014/11/18/spring-ohne-bin-praefix-nutzen/ http://sgaul.de/2014/11/18/spring-ohne-bin-praefix-nutzen/#comments Tue, 18 Nov 2014 15:05:00 +0000 http://sgaul.de/?p=2707 Weiterlesen ]]> Spring lässt sich sehr einfach installieren: Gem eintragen und die Binstubs erzeugen:

bundle exec spring binstub --all

Um nun rake routes oder rails generate statt bin/rake routes oder bin/rails generate nutzen zu können, einfach die folgenden Funktionen in beispielsweise die bash_aliases eintragen:

rake() { if [ -f bin/rake ]; then bin/rake "$@"; else bundle exec rake "$@"; fi }
rails() { if [ -f bin/rails ]; then bin/rails "$@"; else bundle exec rails "$@"; fi }
rspec() { if [ -f bin/rspec ]; then bin/rspec "$@"; else bundle exec rspec "$@"; fi }

Mein Dankeschön für den Ansatz geht an Arne.

Um die ursprünglichen Varianten ohne Spring zu nutzen kann command verwendet werden:

command rake routes
]]>
http://sgaul.de/2014/11/18/spring-ohne-bin-praefix-nutzen/feed/ 0
Eleganterer Stringvergleich mit StringInquirer http://sgaul.de/2014/10/18/eleganterer-stringvergleich-mit-stringinquirer/ http://sgaul.de/2014/10/18/eleganterer-stringvergleich-mit-stringinquirer/#comments Sat, 18 Oct 2014 10:14:08 +0000 http://sgaul.de/?p=2703 Weiterlesen ]]> Unschön:

if user.role == "admin" or user.role == "guest"

Lösung:

class User
  def role
    ActiveSupport::StringInquirer.new(super)
  end
end
if user.role.admin? or user.role.guest?

Der ActiveSupport::StringInquirer ergänzt einen String um beliebige Testmethoden, welche die Gleichheit von String und Methodenname (ohne Fragezeichen) überprüfen. Ein prominenter Anwendungsfalls ist die Environment-Variable Rails.env von Rails, die sich auch in der Form Rails.env.production? prüfen lässt.

]]>
http://sgaul.de/2014/10/18/eleganterer-stringvergleich-mit-stringinquirer/feed/ 2
Methoden für Active-Record-Relationen definieren http://sgaul.de/2014/09/24/methoden-fuer-active-record-relationen-definieren/ http://sgaul.de/2014/09/24/methoden-fuer-active-record-relationen-definieren/#comments Wed, 24 Sep 2014 20:26:35 +0000 http://sgaul.de/?p=2693 Weiterlesen ]]> Ein Scope in Active Record ist nichts anderes als syntaktischer Zucker für das Definieren einer Klassenmethode. Die folgenden User-Models führen zum selben Ergebnis:

class User < ActiveRecord::Base
  scope :admins, -> { where(role: "admin") }
end
class User < ActiveRecord::Base
  def self.admins
    where(role: "admin")
  end
end

Da man Scopes verketten kann drängt sich der (berechtigte) Verdacht auf, dass dies mit jeder Form von Klassenmethode möglich ist.

class User < ActiveRecord::Base

  scope :admins, -> { where(role: "admin") }

  def self.names
    pluck(:name)
  end

end

Dies funktioniert. Man kann auf diese Weise jede zusammengestellte Relation beliebig verarbeiten. Obiges Beispiel ließe sich etwa folgendermaßen anwenden:

User.create(name: "Klaus", role: "admin")
User.create(name: "Markus", role: "admin")
User.create(name: "Max")

puts User.admins.names # ["Klaus", "Markus"]

Dies ist eine interessante Eigenschaft, die mir in den bisherigen Rails-Dokumentationen nicht aufgefallen ist. Erst nach gezielter Suche konnte ich einen Hinweis in den API-Docs finden.

]]>
http://sgaul.de/2014/09/24/methoden-fuer-active-record-relationen-definieren/feed/ 0
Pakyows Ansatz für Views in Web-Applikationen http://sgaul.de/2014/08/24/pakyows-ansatz-fuer-views-in-web-applikationen/ http://sgaul.de/2014/08/24/pakyows-ansatz-fuer-views-in-web-applikationen/#comments Sun, 24 Aug 2014 11:33:38 +0000 http://sgaul.de/?p=2685 Weiterlesen ]]> Auch wenn die Vertreter der Client-Seite den Kampf um die Frage, wo Views künftiger Web-Applikationen gerendert werden, schon lange gewonnen haben, so bleiben doch einige Diskussionspunkte offen. Was wird aus Suchmaschinen, Javascript-Verweigerern und den Nutzern von schwacher Hardware oder alter Browser? Der Schritt zur Client-Side-Web-Application ist an Konsequenzen geknüpft, die nicht jeder in Kauf nehmen möchte. Ich halte immer die Augen nach Projekten offen, die das Rendern sowohl auf Server als auch Client mit möglichst wenig Overhead ermöglichen.

Das Pakyow-Projekt erfüllt diese Anforderung nicht, zeigt aber einen interessanten Ansatz für Views. Sicher nicht der erste oder gar einzige seiner Art, aber einer mit konkreter Umsetzung. Zudem scheint eine weitere Entwicklung in Richtung Client- und Server-Seite möglich, da Entwickler Bryan Powell bei dem Thema recht euphorisch wirkt.

<article data-scope="post">
  <h1 data-prop="title">My First Post</h1>
  <div data-prop="body">Lorem ipsum dolor sit amet</div>
</article>

Das einfache Beispiel führt dabei zum selben Ergebnis wie das folgende HAML-Beispiel, nur dass die obigen Data-Attribute für spätere Updates erhalten bleiben.

- @posts.each do |post|
  %article
    %h1= post.title
    %div= post.body

Man beachte die Each-Schleife: Das obige Template erzeugt das Article-Element für jeden Post der Datenquelle.

Das obige Beispiel hat gegenüber der HAML-Notation entscheidende Vorteile.

Der Template-Entwickler kann die Templates mit beliebigen Inhalten befüllen (siehe oben „My First Post“) und ohne Backend-Logik ein sinnvolles Ergebnis sehen. Template und Backend sind besser getrennt.

Der wesentliche Vorteil ist aber, dass das Template mit einfachen Datenstrukturen, üblicherweise einem JSON-Dokument, bestückt werden kann. Eine solch relativ einfache Template-Engine ließe sich für Server (z. B. in Ruby) und in Javascript für den Client realisieren. So kann der Server den mit korrekten Werten befüllten View ausliefern. Weitere Updates würde er nur als JSON schicken, welches der Browser anhand der gegebenen Data-Notationen ins DOM einbaut.

Der Backend-Entwickler muss sich somit nur noch um die JSON-Struktur sorgen. Ob die Response als HTML oder JSON ausgeliefert wird, ließe sich weitgehend im Framework abstrahieren.

Für mich als Rails-Gefangenen ist das Hauptproblem mit Pakyow, dass es Rails ersetzen statt ergänzen würde. Einen Ansatz zur Integration konnte ich bisher nicht finden, so dass hier eigene Überlegungen notwendig wären.

]]>
http://sgaul.de/2014/08/24/pakyows-ansatz-fuer-views-in-web-applikationen/feed/ 0
Ruby: Testvollständigkeit testen http://sgaul.de/2014/05/20/ruby-testvollstaendigkeit-testen/ http://sgaul.de/2014/05/20/ruby-testvollstaendigkeit-testen/#comments Tue, 20 May 2014 14:23:59 +0000 http://sgaul.de/?p=2679 Weiterlesen ]]> Eine Wissenschaft für sich im Test-Driven Development ist die Frage, wie jede einzelne Funktion getestet werden muss: Sind die Tests vollständig, hat man jeden Spezialfall bedacht? Hat man es zu gut gemeint und eine schwer überschaubare, redundante Testsuite geschaffen?

Einen recht ungewöhnlichen Weg zur Beantwortung der ersten Frage geht Markus Schirp. Sein Gem Mutant betrachtet Code und die zugehörigen, grünen Tests. Es verändert nun immer wieder bestimmte Teile des Applikations-Codes (nicht die Tests) und prüft, ob die Tests dadurch fehlschlagen. Tun sie dies nicht lässt sich schlussfolgern, dass diese Stellen des Codes nicht vollständig durch Tests abgedeckt oder tot sind.

Die Code-Änderungen sind dabei vollautomatisch. Ifs werden durch Unless ersetzt, boolsche Default-Werte von Argumenten werden negiert oder ganze Statements weggelassen. Solange die Tests dadurch fehlschlagen ist es egal wie viel Unsinn sich hieraus ergibt.

Der Ansatz klingt auf jeden Fall interessant und erscheint um einiges aussagekräftiger als die üblichen Code-Coverage-Statistiken. Ob er wirklich praxistauglich ist kann ich aufgrund mangelnder Erfahrung nicht beurteilen. Im Weg steht leider auch der etwas sperrige Einstieg. Ich konnte etwa keine Beispiele finden wie man Code mit Rails-Abhängigkeiten testet – und von diesem Code habe ich eine Menge. Ein Gem Mutant-Rails gibt es zwar schon, ist aber mit einem entmutigenden „This gem does not yet work“ überschrieben. Aber der Kurs scheint zu stimmen.

Auf der Rails-Conf 2014 gab es einen Vortrag zu Mutant. Dieser kratzt leider nur an der Oberfläche, zeigt aber immerhin ein Live-Beispiel.

]]>
http://sgaul.de/2014/05/20/ruby-testvollstaendigkeit-testen/feed/ 0
Klassen- und Klassenhierarchievariablen in Ruby http://sgaul.de/2014/05/15/klassen-und-klassenhierarchievariablen-in-ruby/ http://sgaul.de/2014/05/15/klassen-und-klassenhierarchievariablen-in-ruby/#comments Thu, 15 May 2014 13:11:48 +0000 http://sgaul.de/?p=2663 Weiterlesen ]]> Sucht man nach einem Klassenvariablenkonzept für Ruby trifft man über kurz oder lang auf das @@-Konzept. Wer sich darauf einlässt kann schnell Probleme bekommen. Der Grund ist relativ einfach: @@-Variablen sind keine Klassenvariablen.

Einfache „Klassenvariablen“

class Cat
  
  @@size = :small
  
  def self.size
    @@size
  end

  def self.size= new_size
    @@size = new_size
  end

  def size
    @@size
  end

end

expect(Cat.size).to be :small
expect(Cat.new.size).to be :small
Cat.size = :medium
expect(Cat.size).to be :medium
expect(Cat.new.size).to be :medium

Vererbung

class HouseCat < Cat
end

class Lion < Cat
end
expect(Lion.size).to be :small
Lion.size = :big
expect(Lion.size).to be :big
expect(HouseCat.size).to be :big

Hauskatze und Löwe teilen die selbe Variable, was zu erwarten war. Lässt sich das trennen?

@@-Variablen sind keine Klassenvariablen

class HouseCat < Cat
end

class Lion < Cat
  @@size = :big
end
expect(Lion.size).to be :big
expect(HouseCat.size).to be :big # !!!

Nein. Die gesetzte Variable gilt für die gesamte Klassenhierarchie aufwärts.

Superklassen haben übrigens keinen Zugriff:

class Cat
  
  def self.size
    @@size
  end

end

class Lion < Cat
  @@size = :big
end
expect{ Lion.size }.to raise_error NameError

Richtige Klassenvariablen

Da in Ruby auch Klassen Objekte sind, sind Instanzvariablen dieser Objekte auch Klassenvariablen. Bemerkenswert ist hier die strenge Sichtbarkeitsbeschränkung: Selbst wenn @small und self.size in der selben Klassen definiert werden, kann self.size nach der Vererbung in die HouseCat nicht länger mit @small auf die Klassenvariable von Cat zurgreifen.

class Cat
  
  @size = :small
  
  def self.size
    @size
  end

end

class HouseCat < Cat
end

class Lion < Cat
  @size = :big
end
expect(Lion.size).to be :big
expect(Cat.size).to be :small
expect(HouseCat.size).to be_nil

Aber hier können ja wieder Klassenhierarchievariablen helfen.

]]>
http://sgaul.de/2014/05/15/klassen-und-klassenhierarchievariablen-in-ruby/feed/ 0
Was beim Unit-Testing getestet werden sollte http://sgaul.de/2014/05/10/was-beim-unit-testing-getestet-werden-sollte/ http://sgaul.de/2014/05/10/was-beim-unit-testing-getestet-werden-sollte/#comments Sat, 10 May 2014 20:30:21 +0000 http://sgaul.de/?p=2634 Weiterlesen ]]> Die zentrale Idee des Unit-Testings ist Isolation. Man testet einzelne Komponenten, Abhängigkeiten werden als gegeben und korrekt angesehen und ggf. gemockt. Dies macht das  Testen der einzelnen Komponente übersichtlicher, da nur ihre Pfade betrachtet werden müssen. In der Regel wird hierbei ein Blackbox-Ansatz verfolgt. Die zu testende Funktionalität (meist eine öffentliche Methode) wird nur von außen betrachtet: Was geht rein, was kommt raus, wie ändert sich der nach außen sichtbare Zustand?

Es gibt hilfreiche theoretische Betrachtungen, welche Funktionen getestet werden sollten und welche nicht. Zudem lässt sich argumentieren, wie ein bestimmer Funktionstyp zu testen ist. Dies kann helfen den Aufwand des Testschreibens zu reduzieren. Zudem sorgt die bessere Isolation dafür, dass eine Änderung in einer Komponente nicht länger das Umschreiben hiervon eigentlich unabhängiger Tests erfordert.

Query und Command

Um zu formalisieren was getestet werden sollte, unterscheidet man zwei Arten von Funktionen: Query-Methoden geben einen Wert zurück, ändern aber nicht den Zustand des Systems (Array#length). Command-Methoden sind das entsprechende Gegenteil. Sie ändern den Zustand der Komponenten oder des Systems, haben aber keinen Rückgabewert (Array#push). Es gilt als guter Stil diese Trennung zu achten, auch wenn es zahlreiche akzeptierte Ausnahmen gibt (Array#pop, ActiveRecord::Base#save uvm.).

Nachrichten

Beim Testen betrachtet man Nachrichten, die die zu testende Komponente erhält oder versendet. Eingehende Nachrichten erteilen der Komponente den Auftrag etwas zu ändern (Command) oder zurückzugeben (Query). In der objektorientierten Welt ist dies der Aufruf einer Methode des zu testenden Objekts von außen. Der zugehörige Test stellt sicher, dass die Query-Methode den korrekten Wert zurück gibt und die Command-Methode die gewünschte Zustandsänderung bewirkt.

Ausgehende Nachrichten treten auf, wenn die zu testende Methode eine Methode eines anderen Objekts aufruft. Schickt eine Command-Methode eine solche Nachricht, muss sichergestellt werden, dass diese auch wirklich verschickt wird. Dies lässt sich komfortabel durch Mocks lösen. Ausgehende Nachrichten einer Query-Methode werden nicht getestet. Treten hier Probleme auf, werden diese durch die Tests der eingehenden Nachrichten deutlich.

Interne Nachrichten beschreiben den Aufruf einer Methode durch eine Methode des selben Objekts. Dies sollte in aller Regel nicht getestet werden, da die beiden vorigen Nachrichtentypen-Tests bereits sicherstellen, dass die Komponente korrekt funktioniert. Dies impliziert, dass auch interne Aufrufe korrekt sind. Solche Tests sind nicht nur unnötig, sondern fixieren zusätzlich Implementierungsdetails und behindern somit ein mögliches Refactoring.

Beispiel

Das folgende Beispiel zeigt mögliche Tests der Klasse Array. #length ist eine Command-Methode, bei der nur das Ergebnis wichtig ist. Bei #push zählt nur, dass das eingefügte Element Teil des Arrays wird. Für #map muss nur sichergestellt werden, dass die angegebene Methode auf dem Array-Element aufgerufen wird:

Nachrichtentyp Query Command
Eingehend
a = [1, [2, [3]]]
expect(a.flatten).to(
  eq [1, 2, 3]
)
a = [1]
a.push(2)
expect(a).to include 2
Ausgehend keine Tests
mock = Object.new
array = [mock]
mock.should_receive(:foo)
array.map(&:foo)
Intern keine Tests keine Tests

Tabelle in Anlehnung an The Magic Tricks of Testing by Sandi Metz

Beim Flatten-Beispiel erscheint es verlockend zu testen, dass auch auf den Elementen des Arrays flatten aufgerufen wird, wenn diese wiederum Arrays sind. Tatsächlich wird dieses rekursive Verhalten aber bereits vom Test der eingehenden Nachricht sichergestellt, ein Test der ausgehenden Nachricht der Query-Methode ist somit unnötig.

]]>
http://sgaul.de/2014/05/10/was-beim-unit-testing-getestet-werden-sollte/feed/ 0
RetroPie über WLAN befüllen und aktualisieren http://sgaul.de/2014/05/05/retropie-ueber-wlan-befuellen-und-steuern/ http://sgaul.de/2014/05/05/retropie-ueber-wlan-befuellen-und-steuern/#comments Mon, 05 May 2014 17:22:48 +0000 http://sgaul.de/?p=2611 Weiterlesen ]]> Nachdem mein Pi für Retrospiele nun weitgehend konfiguriert ist, habe ich die Tastatur durch einen WLAN-Dongle ersetzt. Die Konfiguration habe ich etwas umständlich in die /etc/network/interfaces eintragen müssen, hierfür scheint es keinen Assistenten zu geben. Dafür wird das Romverzeichnis über Samba freigegeben, so dass es ohne jedes Zutun im Ubuntu-Dateimanager auftaucht und befüllt werden kann. Auch via SSH ist der kleine Rechenknecht erreichbar: Der Benutzername ist pi, das Passwort raspberry. Somit ist die Tastatur wirklich obsolet.

Dank der Internetverbindung bietet sich auch ein Update an. Debian lässt sich über Apt aktualisieren, für die Emulatorkomponenten gibt es einen eigenen Assistenten:

 sudo ./RetroPie-Setup/retropie_setup.sh

Hier sollte man zunächst das Setup-Script selbst und anschließend die Retro-Pie-Binaries auf den neuesten Stand bringen. Ich war etwas misstrauisch, ob die von mir gemachten Anpassungen durch das Update verloren gehen könnten. Dies war nicht der Fall. Im Gegenteil: Seit dem Update funktioniert auch der Ton des Osmose-Emulators, so dass ich nun auch zu Game-Gear- und Master-System-Klassikern greifen kann.

]]>
http://sgaul.de/2014/05/05/retropie-ueber-wlan-befuellen-und-steuern/feed/ 0