Für ein wenig Verwirrung sorgte bei mir, daß es inzwischen gut 5 verschiedene PostgreSQL-Gems für Ruby gibt. Neben dem pure-Ruby-Treiber (postgres-pr) gibt es noch postres, ruby-postgres, ruby-pg und pg. Das postgres-Gem jedenfalls funktioniert mit dem aktuellen Rails 2.2 nicht mehr. Es wird anscheinend nicht mehr weitergepflegt. Der Quelltext wird nun in Form des pg-Gems weitergeführt. Wer also Probleme mit PostgreSQL und dem neuen Rails 2.2 hat, sollte überprüfen, ob er auch das aktuelle pg-Gem installiert hat.
Außer dem Gem ändert sich nichts. Der Adapter in der database.yml heißt weiterhin “postgresql”.
Da ich derzeit bei der Arbeit eher mit administrativen Aufgaben als mit Programmierung beschäftigt bin, gibt es auch hier eine kleine Flaute an neuen Artikeln. Meistens stammen diese aus Erfahrungen, die ich bei der Arbeit gemacht habe. Da mir nichts besseres einfällt, dachte ich mir, ich poste mal, was mir letzte Woche in den Sinn kam.
respond_to do |can_has|
can_has.html {...}
can_has.xml {...}
end
Nach dem Ausfall des Servers letzte Woche, mußte ich dieses Blog auf einem neuen Server aufsetzen. Ein passender Augenblick, das ganze sauber mit Capistrano zu machen. Dies ist eine kurze Zusammenfassung, wie auch du Mephisto auf einem Server mit openSuSE 10.2 und Plesk 8.4 (wie sie bei 1&1 angeboten werden) deployen kannst. Die Beispiele haben “acts-as-blog.net” als Beispieldomain. Du solltest entsprechend die Daten deinen Anforderungen anpassen.
Den ganzen Artikel lesen
Some time ago I wrote a german article on how to install PostgreSQL on Leopard. It seems I struck a chord with many people. So I decided to make this article accessible to a wider audience by translating it to English. I modified and updated parts of it where appropriate. So here it is without further ado.
Den ganzen Artikel lesen
Ein Plugin, was mir heute ein wenig Arbeit erspart hat ist active_record_defaults. Damit kann man in ActiveRecord-Modellen Defaultwerte für einzelne Attribute festlegen, ohne sie im Schema zu verankern. Das Plugin findet sich unter http://svn.viney.net.nz/things/rails/plugins/active_record_defaults/. Die Benutzung ist denkbar einfach:
class Person < ActiveRecord::Base
defaults :age => 16
end
Schon ist jede Person 16 Jahre alt, sofern nichts anderes angegeben wurde.
Holt es euch, solange es heiß ist!
Eine recht ausführliche Liste aller Neuerungen gibt es hier.
Ich werde mal gleich eine kleine Testanwendung versuchen. Mal schauen, wie es sich anfühlt.
Update: 2.0.1 ist bereits raus. Ist wohl beim Release etwas schiefgelaufen.
Vor einer Weile habe ich in meinem Blog geschrieben, wie man PostgreSQL auf Mac OS X 10.4 (aka Tiger) installiert. Seitdem ist eine Weile ins Land gegangen. PostgreSQL ist inzwischen bei 8.2.5 angekommen und es existiert eine 8.3 beta 2. Nicht ganz unerwähnt bleiben sollte auch die Tatsache, daß vor kurzem Mac OS X 10.5 (aka Leopard) erschien. Änderungen in Mac OS X 10.5 machen haben dazu geführt, daß meine alte Anleitung als überholt angesehen werden kann. Deshalb folgt hier die aktualisierte Version.
Den ganzen Artikel lesen
Und noch eine Arbeitserleichterung in Rails Edge (und damit auch im kommenden Rails 2.0). Simply Helpful war bislang als Plugin für Rails 1.2.x verfügbar. In Verbindung mit REST sind Formulare jetzt noch einfacher zu schreiben. In Rails 1.2.x muß man noch folgendes schreiben, um ein Edit-Formular zu bekommen:
form_for :foo, @foo, :url => foo_path(@foo), :html => {:method => :put} do |f|
...
end
Das geht nun deutlich kürzer:
form_for @foo do |f|
...
end
Jepp, das ist alles. Simply Helpful erkennt automatisch, ob es sich bei @foo um ein neues ActiveRecord-Objekt handelt und baut automagisch einen Link zur create- bzw. update-Action.
Und wo ich gerade bei Änderungen in Rails Edge bin. Eine weitere fingerschonene Änderung hat es in Rails geschafft. Mußte man bislang noch folgendes schreiben:
render :partial => "projects", :collection => @projects
genügt demnächst schon:
render :partial => @projects
Seit Changeset 7541 hat Rails Edge (und damit höchstwahrscheinlich auch das kommende Rails 2.0) einen neuen Helper, um Labels für Formularfelder zu erzeugen. Hier ist ein kleines Beispiel, um zu sehen, wie der Helper benutzt werden kann.
<% form_for(:foo, @foo, :url => foo_path(@foo)) do |f| -%>
<%= f.label :bar, 'Bar' %>
<%= f.text_field :bar %>
<% end -%>
Läßt man den zweiten Parameter hinter label weg, so wird die Beschriftung aus dem Attributnamen mit humanize abgeleitet.
Ein Rails-Projekt ohne Plugins ist nahezu undenkbar. Plugins wie attachment_fu, restful_authentication und will_paginate sind fast immer Pflicht. Mit script/plugin, was bei jedem Rails-Projekt beiliegt, können diese Plugins installiert werden.
Das script/plugin-Skript funktioniert recht gut, hat jedoch ein paar Schönheitsfehler. Standardmäßig kopiert es einfach alle Dateien in ein Verzeichnis in vendor/plugins/mein_plugin. Ein update des Plugins ist so jedoch unmöglich. Es kann nur erneut installiert werden. Dabei werden etwaige eigene Änderungen am Plugin gnadenlos überschrieben.
Um diesem Problem entgegenzuwirken, kann script/plugin mit der Option -x aufgerufen werden. Das Plugin wird dann mit Subversion als External
ausgecheckt. Dies löst das Problem mit den Updates. Sollte jedoch das externe Repository nicht erreichbar sein, so wird Subversion Fehlermeldungen beim update und commit ausspucken. Grafische Frontends für Subversion kommen mit diesen Fehlermeldungen selten klar. Auch wer von der Kommandozeile arbeitet, wird auf die Dauer von diesen Fehlermeldungen genervt sein.
An dieser Stelle kommt Piston ins Spiel. Es checkt die Plugins aus ohne externals, vermerkt jedoch für sich, woher das Plugin stammt. Damit ist ein Update jederzeit möglich. Und da sich Subversion nicht um externals kümmern muß, ist es auch kein Problem, wenn das externe Repository nicht erreichbar ist.
Piston wird wie jedes andere Gem einfach mit gem install piston -y installiert. Damit ist es nun einsatzbereit. Benötige ich nun will_paginate in meinem Projekt, so installiere ich es einfach mit folgendem Befehl:
piston import svn://errtheblog.com/svn/plugins/will_paginate \
vendor/plugins/will_paginate
Um dieses Plugin auf den neuesten Stand zu bringen benutze ich dann diesen Befehl:
piston update vendor/plugins/will_paginate
Weitere Optionen finden sich auf der Piston-Website.
Ein Rails-Projekt mit Capistrano zu deployen ist eigentlich gar nicht so schwer. Es kann jedoch leicht passieren, daß man einen Schritt vergißt und dann erstmal dumm aus der Wäsche schaut. Deshalb habe ich mal kurze Anleitung hier geschrieben, wie der grundsätzliche Ablauf aussieht. Ich setze Capistrano 2.0 ein. Wer noch die Vorgängerversion (1.4.x) einsetzt sollte sein Gem updaten, um dieser Anleitung zu folgen.
Projekt capistranisieren
Um ein Rails-Projekt mit Capistrano zu deployen, brauchst du zu allererst das Capistrano-Gem. Dies kannst du mit gem install capistrano -y
installieren. Auf dem Zielserver (auf den das Projekt deployed werden soll) benötigst du außerdem noch Subversion. Ich muß hoffentlich nicht extra erwähnen, daß ein Zugriff per ssh auf den Zielserver Pflicht ist? Damit sollte alles installiert sein, was du benötigte. Capistranisiere dein Projekt mit capify. In meinem Beispiel sieht dies so aus:
[~] capify /Users/fastjack/Sites/DrawBridge
[add] writing `./Capfile’
[add] writing `./config/deploy.rb’
[done] capified!
Auf die Plätze
In der config/deploy.rb mußt du Capistrano mitteilen, wohin es dein Projekt beim Deployment befördern soll. Die Konfiguration sieht bei mir in etwa so aus:
set :application, "DrawBridge"
set :repository, "http://svn.meinserver.irgendwo/DrawBridge/trunk"
role :app, "192.168.1.182"
role :web, "192.168.1.182"
role :db, "192.168.1.182", :primary => true
set :deploy_to, "/usr/local/www/frigg/#{application}"
set :use_sudo, false
namespace :deploy do
desc "upload database configuration"
task :add_database_configuration, :roles => :web do
run "mkdir -p #{shared_path}/config"
config = File.read(File.join(File.dirname(__FILE__), "database.yml"))
put config, "#{shared_path}/config/database.yml"
run "ln -s #{shared_path}/config/database.yml \
#{latest_release}/config/database.yml"
end
after "deploy:finalize_update", "deploy:add_database_configuration"
end
Mit dieser Konfiguration wird Capistrano mein Projekt auf den Server mit der IP-Adresse 192.168.1.182 unter /usr/local/www/frigg/DrawBridge befördern. Capistrano wird hierzu einen Checkout mit Subversion durchführen. Der Subversion-Server sollte also vom Zielserver erreichbar sein. Zu guter Letzt wird noch die Datenbankkonfiguration von meinem Rechner auf den Zielserver kopiert (weil ich die Datenbankkonfiguration nicht in das Repository einchecke). Damit sollte auch klar sein, daß die config/database.yml einen Abschnitt für die production-Umgebung besitzen muß, der auf dem Zielserver funktioniert.
Damit Capistrano nach dem Deployment auch den Mongrel starten kann, brauchst du noch ein spin-Skript im scripts-Verzeichnis. Dieses sollte ausführbar sein und auch als ausführbar in Subversion (svn propset svn:executable on script/spin) markiert sein. Mein script/spin sieht so aus:
#!/bin/sh
script/process/spawner -a 127.0.0.1 -p 15000 -i 3
Damit werden 3 Mongrel-Server auf den Ports 15000-15002 gestartet, die nur auf Anfragen vom Localhost reagieren. Die Mongrels werden bei mir hinter einem Apache, der als Proxy fungiert, laufen, deshalb sind sie auch nur an den Localhost gebunden.
Fertig
Damit ist hoffentlich alles bereit, um das Projekt zu deployen. Mit cap deploy:check kannst du noch schnell prüfen, ob es irgendwelche schwerwiegenden Probleme gibt, die das Deployment behindern könnten. Warnmeldungen über ein Fehlendes releases-Verzeichnis kannst du dabei ignorieren, dies wird im nächsten Schritt angelegt. Nun ist es an der Zeit, das Deployment vorzubereiten. Mit cap deploy:setup läßt du Capistrano die noch fehlenden releases- sowie shared-Verzeichnisse anlegen. Überprüfe noch schnell, ob du auch wirklich alle lokalen Änderungen wieder ins Repository eingecheckt habst, sonst werden sie nicht auf den Zielserver gelangen.
Los!
Jetzt kann es losgehen. Mit cap deploy:cold startest du nun den Deploymentprozess. Dies dauert ein paar Minuten und endet hoffentlich damit, daß am Ende folgende Zeilen stehen:
[192.168.1.182] executing command
** [out :: 192.168.1.182] appending output to nohup.out
command finished
Nun laufen 3 Instanzen des Mongrels und warten auf Anfragen vom Apache.
Herzlichen Glückwunsch. Dein Projekt wurde erfolgreich deployed. Lehn dich zurück und genieße ein erfrischendes (alkoholisches) Getränk.
Wer sich in Rails über die etwas unverständliche Fehlermeldung wie diese wundert session_url failed to generate from {:controller=>"session", :action=>"show"} - you may have ambiguous routes, or you may need to supply additional parameters for this route. content_url has the following required parameters: ["session", :id] - are they all satisfied?
, der hat mit Sicherheit in seiner routes.rb folgendes stehen:
map.resources :session
Das Problem dabei ist, daß die Resource session
im Singular ist, während die map.resources-Anweisung eine Resource im Plural erwartet. Schreibt man stattdessen:
map.resource :session
so klappt es auch wieder mit dem Routing. (Ja, es ist nur das Plural-s verschwunden).
Ich hatte einen ähnlichen Artikel bereits in meinem anderen Blog darüber geschrieben. Doch hier ist es nochmal, damit alles an einem Ort zu finden ist, und weil ich in der Zwischenzeit auch einiges daran geändert habe.
Angenfangen hat es, als ich ein paar Wochen über diesen Artikel von John Nunemaker in meinem Feed-Reader gestolpert bin. Das war gleich doppelt interessant. Ich fand die Idee mit Growl supergeil. Und von autotest hatte ich bis dahin auch noch nie gehört. Autotest ist Teil des ZenTest-Gems und kann ganz einfach mit gem install ZenTest -y installiert werden. (Die meisten werden noch ein sudo davor schreiben müssen)
Nachdem ich meine erste ~/.autotest-Datei erstellt habe, ging das eigentliche Experimentieren erst richtig los. Ich war nicht der einzige, der über diesen Artikel gestolpert war, denn kurze Zeit später präsentierte Jamie Hill etwas ansehnlichere Icons. Das sah schon richtig gut aus. Zu guter Letzt machte mein Wechsel auf RSpec einige Änderungen notwendig. Meine derzeitige ~/.autotest-Datei sieht also so aus. (download)
require 'autotest/timestamp'
module Autotest::Growl
IMAGE_ROOT = "~/Library/Autotest/Images"
def self.growl title, msg, img, pri=0, sticky=""
system "growlnotify -n autotest --image #{img} -p #{pri} -m '#{msg}' '#{title}' #{sticky}"
end
def self.find_test_summary(test_results)
test_summary = test_results.find {|result| result.include? "example"}.chomp
end
def self.display_test_summary(summary)
examples, failures, pending = summary.split(', ')
if failures.to_i > 0
growl "Tests fehlgeschlagen", summary, "#{IMAGE_ROOT}/fail.png", 2
elsif pending.to_i > 0
growl "Tests stehen aus", summary, "#{IMAGE_ROOT}/pending.png", 1
else
growl "Tests bestanden", summary, "#{IMAGE_ROOT}/pass.png", 0
end
end
Autotest.add_hook :ran_command do |at|
unless at.results.empty?
summary = find_test_summary(at.results)
display_test_summary(summary)
else # Irgendwas ging ganz furchtbar schief
growl "Panik!", "Die Tests lieferten keine Ergebnisse!", "#{IMAGE_ROOT}/panic.png", 3
end
end
end
Ich habe die Smileys um einen vierten Panic-Smiley ergänzt, falls etwas ganz fürchterlich schief gehen sollte. Da ich autotest immer in Netbeans starte und die Netbeans-Konsole keine Farbcodes beherrscht, entferne ich in meinen Projekten in $PROJEKT/spec/spec.opts immer die Zeile mit --colour. Folglich habe ich es auch nicht für nötig befunden, diese Farbcodes in meiner ~/.autotest-Datei herauszufiltern. Wem die Growl-Nachrichten nicht bunt genug sind, und auch farbige Meldungen in der Konsole haben möchte, muß sich einen entsprechenden Filter selbst in seine ~/.autotest-Datei einbauen.
Manchmal möchte ich (virtuelle) Attribute im Model haben, die sich nicht 1:1 auf Spalten in einer Tabelle abbilden lassen. Ein gutes Beispiel dafür ist mein Machine-Model. In dem Model möchte ich unter anderem das Baujahr der Maschine festhalten. Dies könnte ich zwar auch einfach als ein Integer in der Datenbank speichern, doch für diverse Datumsberechnungen habe ich mich dazu entschlossen, das Baujahr als Datum (jeweils als den 1. Januar des Jahres) zu speichern. Das Baujahr habe ich zu einem virtuellen Attribut es Models gemacht. Das ganze sieht dann so aus.
class Machine < ActiveRecord::Base
def build_year
read_attribute(:built_on).year if read_attribute(:built_on)
end
def build_year=(year)
write_attribute(:built_on, Date.new(year.to_i, 1, 1)) unless year.empty?
end
end
Damit läßt sich nun build_year wie jedes andere Attribut des Models in Views verwenden. Wie wäre es noch mit Validations? Hierzu habe ich folgende validate-Methode gebaut.
def validate
errors.add(:build_year, "can't be blank") unless build_year.is_a?(Fixnum)
errors.add(:build_year, "must be after 1944") if build_year && build_year<1945
errors.add(:build_year, "can't be in the future") if build_year && build_year>Date.today.year
end
Damit wird mein virtuelles Attribut nun auch entsprechend im View hervorgehoben, wenn die Validation fehlschlägt.