Sebastians Blog http://sgaul.de Neues aus den Softwareminen... Tue, 18 Nov 2014 15:05:00 +0000 de-DE hourly 1 http://wordpress.org/?v=4.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
Vim-Zwischenablage unter Ubuntu 14.04 http://sgaul.de/2014/05/03/vim-zwischenablage-unter-ubuntu-14-04/ http://sgaul.de/2014/05/03/vim-zwischenablage-unter-ubuntu-14-04/#comments Sat, 03 May 2014 09:33:20 +0000 http://sgaul.de/?p=2664 Weiterlesen ]]> Wenn in Vim von Ubuntu 14.04 die Zugriffe auf das System-Clipboard (etwa "+p oder "+y) nicht funktionieren, ist in aller Regel eine Version ohne Clipboard-Modul installiert:

vim --version
VIM - Vi IMproved 7.4 (2013 Aug 10, compiled Jan  2 2014 19:39:32)
...
Riesige Version ohne GUI. Ein- (+) oder ausschließlich (-) der Eigenschaften:
...
-clipboard       +iconv           +path_extra      -toolbar
...

Um dies zu beheben einfach das Paket vim-gtk installieren, was auch gleich Gvim auf den Rechner schaufelt. Wer sich an den drei MB extra stört kann es auch mit dem Paket vim-gui-common versuchen.

sudo apt-get install vim-gtk
vim --version
VIM - Vi IMproved 7.4 (2013 Aug 10, compiled Jan  2 2014 19:39:59)
Inklusive der Korrekturen: 1-52
Verändert von pkg-vim-maintainers@lists.alioth.debian.org
Übersetzt von buildd@
Riesige Version mit GTK2 GUI.
 Ein- (+) oder ausschließlich (-) der Eigenschaften:
+acl             +farsi           +mouse_netterm   +syntax
+arabic          +file_in_path    +mouse_sgr       +tag_binary
+autocmd         +find_in_path    -mouse_sysmouse  +tag_old_static
+balloon_eval    +float           +mouse_urxvt     -tag_any_white
+browse          +folding         +mouse_xterm     +tcl
++builtin_terms  -footer          +multi_byte      +terminfo
+byte_offset     +fork()          +multi_lang      +termresponse
+cindent         +gettext         -mzscheme        +textobjects
+clientserver    -hangul_input    +netbeans_intg   +title
+clipboard       +iconv           +path_extra      +toolbar
+cmdline_compl   +insert_expand   +perl            +user_commands
+cmdline_hist    +jumplist        +persistent_undo +vertsplit
+cmdline_info    +keymap          +postscript      +virtualedit
+comments        +langmap         +printer         +visual
+conceal         +libcall         +profile         +visualextra
+cryptv          +linebreak       +python          +viminfo
+cscope          +lispindent      -python3         +vreplace
+cursorbind      +listcmds        +quickfix        +wildignore
+cursorshape     +localmap        +reltime         +wildmenu
+dialog_con_gui  +lua             +rightleft       +windows
+diff            +menu            +ruby            +writebackup
+digraphs        +mksession       +scrollbind      +X11
+dnd             +modify_fname    +signs           -xfontset
-ebcdic          +mouse           +smartindent     +xim
+emacs_tags      +mouseshape      -sniff           +xsmp_interact
+eval            +mouse_dec       +startuptime     +xterm_clipboard
+ex_extra        +mouse_gpm       +statusline      -xterm_save
+extra_search    -mouse_jsbterm   -sun_workshop    +xpm
]]>
http://sgaul.de/2014/05/03/vim-zwischenablage-unter-ubuntu-14-04/feed/ 0
RetroPie: Master System und Game Gear mit PS3-Controller http://sgaul.de/2014/05/02/retropie-master-system-und-game-gear-mit-ps3-controller/ http://sgaul.de/2014/05/02/retropie-master-system-und-game-gear-mit-ps3-controller/#comments Fri, 02 May 2014 12:35:49 +0000 http://sgaul.de/?p=2621 Weiterlesen ]]> Das Image vom Retro-Pie-Projekt setzt für Sega Master System und Sega Game Gear auf den Emulator Osmose, für den die Tastenbelegung leider nicht durch eine Oberfläche konfiguriert werden kann. Stattdessen sind die Keycodes des Controllers als Argumente zu übergeben. Der folgende Test funktioniert mit meinem Playstation-3-Controller:

./RetroPie/emulators/osmose-0.8.1+rpi20121122/osmose RetroPie/roms/mastersystem/rom.sms -joy -joy1 15 -joy2 14 -joystart 3

Dies belegt die Tasten Quadrat, X und Start. Somit kann dies in Emulationstation hinterlegt werden, damit dies auch aus dem Kioskmodus funktioniert.

~/.emulationstation/es_systems.cfg

DESCNAME=Sega Game Gear
...
COMMAND=/home/pi/RetroPie/emulators/osmose-0.8.1+rpi20121122/osmose %ROM% -joy -joy1 15 -joy2 14 -joystart 3 -tv -fs

...

DESCNAME=Sega Master System II
...
COMMAND=/home/pi/RetroPie/emulators/osmose-0.8.1+rpi20121122/osmose %ROM% -joy -joy1 15 -joy2 14 -tv -fs

Leider bleibt bei mir das doch recht fatale Problem, dass der Emulator keinen Ton ausgibt. In den meisten Foren wird von Osmose auf dem Pi abgeraten, man solle statt Fehler zu beheben einen anderen konfigurieren. Ob mir das ein paar Sega-Klassiker wert sind habe ich noch nicht entschieden.

]]>
http://sgaul.de/2014/05/02/retropie-master-system-und-game-gear-mit-ps3-controller/feed/ 0