Screencast: MetaSearch und MetaWhere

MetaSearch und MetaWhere hilft bei Entwicklung komplexe Abfragen ohne SQL in ActiveRecord zu erstellen. In diesem Screencast wird gezeigt wie es eingesetzt werden kann.

 

Download:

Download(16 MB, 8:28)
Alternativer Download für iPod & Apple TV(14.4 MB, 8:28)

 

Resourcen:

 

Quellcode:

[ruby]
# Gemfile
gem "meta_where"
gem "meta_search"

# rails console
Product.where(:price.lt => 5)
Product.where({:price.lt => 5} | {:name.matches => "%video%"})
Product.order(:released_at.desc)
MetaWhere.operator_overload!
Product.where(:price < 5)

# products_controller.rb
def index
@search = Product.search(params[:search])
@products = @search.all
end
[/ruby]

[html]
<!– products/index.html.erb –>
<%= form_for @search do |f| %>
<p>
<%= f.label :name_contains %>
<%= f.text_field :name_contains %>
</p>
<p>
<%= f.label :price_gte, "Price ranges from" %>
<%= f.text_field :price_gte, :size => 8 %>
<%= f.label :price_lte, "to" %>
<%= f.text_field :price_lte, :size => 8 %>
</p>
<p class="button"><%= f.submit "Search" %></p>
<% end %>

<p>
Sort by:
<%= sort_link @search, :name %> |
<%= sort_link @search, :price %> |
<%= sort_link @search, :released_at %>
</p>
[/html]

Screencast: Ajax Historie anpassen

Die neue pushState Funktion in JavaScript erlaubt es die gesamte URL mit Ajax zu verändern bzw. an den eigenen Bedarf anzupassen. Das bedeutet das auf einfache Weise eine Unterstützung für die Buttons „Zurück“, „Neu Laden“ und „Lesezeichen“ implementiert werden kann. Ryan zeigt in diesem Screencast wie dies in Kombination mit Rails eingesetzt werden kann.

 

Download:

Download(19.9 MB, 10:35)
Alternativer Download für iPod & Apple TV(18.9 MB, 10:35)

 

Resourcen:

 

Quellcode:

[javascript]
/* application.js */
if (history && history.pushState) {
$(function() {
$("#products th a, #products .pagination a").live("click", function() {
$.getScript(this.href);
history.pushState(null, document.title, this.href);
return false;
});
$("#products_search input").keyup(function() {
$.get($("#products_search").attr("action"), $("#products_search").serialize(), null, "script");
history.replaceState(null, document.title, $("#products_search").attr("action") + "?" + $("#products_search").serialize());
return false;
});
$(window).bind("popstate", function() {
$.getScript(location.href);
});
});
}

/* products/index.js.erb */
$("#products").html("<%= escape_javascript(render("products")) %>");
document.title = "<%= escape_javascript("#{params[:search].to_s.titleize} Products by #{(params[:sort] || ’name‘).titleize} – Page #{params[:page] || 1}") %>";
[/javascript]

Scripttip: Datei- und Verzeichnis-Operationen mit Ruby

Das Bearbeiten von Dateien und Verzeichnissen ist eine sehr häufig wiederkehrende Arbeit in der Programmierung.
Daher gibt es in unserem heutigen Scripttip ein paar Tips zu diesem Thema. Wie aus anderen Bereichen bekannt, sind Zugriffe und Manipulation von Dateien und Verzeichnissen mit Ruby sehr einfach.

Im ersten Beispiel erstellen wir eine Datei. Sie wird dort angelegt von wo das Script ausgeführt wird.

Eine einfach Text-Datei erstellen:
[ruby]
datei = ‚datei.txt‘
File.open(datei, ‚w‘) {|d| d.write ‚Text für die neue Datei‘}
[/ruby]

Soll die Datei an einem bestimmten Ort/Pfad erstellt werden, müssen ggf. die entsprechenden Verzeichnisse erst angelegt werden.
Dazu kann die „mkdir_p“ Methode aus FileUtils benutzt werden, weil diese sicherstellt, dass alle benötigten Ordner im Pfad angelegt werden.

[ruby]
require ‚fileutils‘

datei = ‚/tmp/mein/pfad/zur/datei.txt‘
FileUtils.mkdir_p File.dirname(datei)
File.open(datei, ‚w‘) do |d|
d.write ‚Beispieltext für die neu erzeugte Datei‘
end
[/ruby]

Soll die Original-Datei unverändert bleiben, kann eine Kopie erzeugt werden um die Manipulationen an dieser vorzunehmen.
[ruby]
require ‚fileutils‘

FileUtils.copy(‚datei.txt‘, ‚kopie.txt‘)
[/ruby]

Auch das Verschieben einer Datei ist auch sehr einfach mit FileUtils.

[ruby]
require ‚fileutils‘

alter_pfad = "/der/alte/pfad/zur/datei.txt"
neuer_pfad = "/der/neue/ort/der/datei.txt"
# ggf. die benötigten Verzeichnisse erstellen
FileUtils.mkdir_p File.dirname(neuer_pfad)

FileUtils.mv(‚/der/alte/pfad/zur‘, ‚/opt/new/location/your_file‘)
[/ruby]

Dies ist nur ein kleiner Auszug aus den Möglichkeiten die FileUtils bietet.
Eine Liste weiterer Methoden sind in den RubyDocs verfügbar.
Neugierige können sich in der irb-Konsole einen Auszug der Methoden geben lassen.
Die meisten Funktions-Namen sind an die Unix-Kommandos angelehnt und in der Regel selbstsprechend.
[ruby]
irb(main):001:0> require ‚PP‘
=> true
irb(main):001:0> require ‚fileutils‘
=> true

irb(main):007:0> pp FileUtils.singleton_methods.sort
["cd",
"chdir",
"chmod",
"chmod_R",
"chown",
"chown_R",
"cmp",
"collect_method",
"commands",
"compare_file",
"compare_stream",
"copy",
"copy_entry",
"copy_file",
"copy_stream",
"cp",
"cp_r",
"getwd",
"have_option?",
"identical?",
"install",
"link",
"ln",
"ln_s",
"ln_sf",
"makedirs",
"mkdir",
"mkdir_p",
"mkpath",
"move",
"mv",
"options",
"options_of",
"private_module_function",
"pwd",
"remove",
"remove_dir",
"remove_entry",
"remove_entry_secure",
"remove_file",
"rm",
"rm_f",
"rm_r",
"rm_rf",
"rmdir",
"rmtree",
"safe_unlink",
"symlink",
"touch",
"uptodate?"]
=> nil
[/ruby]

Bevor wir diesen Scripttip abschließen, noch ein kleiner Tip zu temporären Dateien.
Häufig wird eine Datei nur zum „zwischenlagern“ benötigt. Das heißt die Datei wird spätestens nach der Asführung des Scripts nicht mehr benötigt.
Solche Dateien selber anzulegen, erfordert gleichzeitig, dass sie selber gelöscht werden (Aufräumscript, Cron, etc.). Weiterhin kann es zu Namenskollisionen kommen, wenn eine Datei mit dem gleichen Namen vorhanden, usw.
All diese Arbeit wird in Ruby von „Tempfile“ abgenommen und sollte für diese Zwecke eingesetzt werden.

[ruby]
require ‚tempfile‘

temp_datei = Tempfile.new(‚temporaere_datei.txt‘)
temp_datei << "Daten zum Zwischenlagern"
temp_datei.close
[/ruby]

temp_datei.close wird zwar bei Script-Ende automatisch ausgeführt, aber auf diese Weise behält man die Kontrolle.

Wir hoffen der Scripttip hilft euch bei euren zukünftigen Datei-Operationen.

Scripttip: Countdown in Ruby

Wie lange dauert es noch bis zum Release, Launch, Geburtstag… Die Dauer bis zu einem Event lässt sich sehr gut mit einem Countdown visualisieren. Um die Restzeit nicht selber in Minuten, Sekunden, etc. umzurechnen kann in Ruby hierfür das gem ‚chronic_duration‘ eingesetzt werden.

Falls nicht schon vorhanden muss erst das gem installiert werden:

[bash]
gem install chronic_duration # ggf. ein ’sudo‘ voranstellen
[/bash]

Wenn die Dauer für den Countdown bekannt ist, kann man diese direkt verwenden:
[ruby]
# Die benötigten Bibliotheken
require ‚rubygems‘
require ‚chronic_duration‘
require ‚time‘

dauer = 180 # Die Dauer in Sekunden

# Hier wird nun jede Sekunde die Restzeit berechnet und ausgegeben
dauer.times {|x| sleep 1; puts ChronicDuration.output(dauer – x)}

# Ausgabe
3 mins
2 mins 59 secs
2 mins 58 secs
2 mins 57 secs
2 mins 56 secs
2 mins 55 secs
2 mins 54 secs
2 mins 53 secs
2 mins 52 secs

2 mins 2 secs
2 mins 1 sec
2 mins
1 min 59 secs
1 min 58 secs

1 min 2 secs
1 min 1 sec
1 min
59 secs
58 secs

3 secs
2 secs
1 sec
[/ruby]

Wenn Anfangs- und Zielzeit verwendet werden sollen, müssen wir erst die Countdown-Zeit berechnen:

[ruby]
# Die benötigten Bibliotheken
require ‚rubygems‘
require ‚chronic_duration‘
require ‚time‘

# Anfangs- und Zieldatum setzen und die Differenz berechnen
anfangsDatum = Time.now
=> Thu Sep 09 10:13:23 +0200 2010

zielDatum = Time.parse ‚2010-09-10 08:02:00 +0100‘
=> Fri Sep 10 09:02:00 +0200 2010

countdownZeit = (zielDatum – anfangsDatum).to_i # Ergebnis auf volle Sekunden gerundet
=> 82116

# Hier wird nun jede Sekunde die Restzeit berechnet und ausgegeben
countdownZeit.times {|x| sleep 1; puts ChronicDuration.output(countdownZeit – x)}

# Ausgabe
22 hrs 48 mins 36 secs
22 hrs 48 mins 35 secs
22 hrs 48 mins 34 secs
22 hrs 48 mins 33 secs
22 hrs 48 mins 32 secs

[/ruby]

Scripttip: Doppelte Arrayschlüssel zusammenfassen

Im heutigen Scripttip möchten wir euch zeigen, wie doppelte Schlüssel in einem mehrdimensionalen Array zusammengeasst werden können.

In diesem Beispiel werden die Werte über einen join zusammengefasst, aber dies lässt sich einfach an die eigenen Bedürfnisse anpassen.

[ruby]
doppelteSchluesselArray = [[:b, ‚B1‘], [:d, ‚D2‘], [:a, ‚A1‘], [:a, ‚A2‘], [:a, ‚A3‘], [:d, ‚D1‘], [:c, ‚C1‘]]
Hash[doppelteSchluesselArray.group_by(&:first).map {|k,v| [k,v.map(&:last).join(‚, ‚)]}]
=> {:d=>"D2, D1", :a=>"A1, A2, A3", :b=>"B1", :c=>"C1"}
[/ruby]

Scripttip: Mehr Informationen im Bash-Prompt

Welche Ruby-Version verwende ich gerade und in welchem Git-Branch arbeite ich momentan…? Um nicht jedes mal „–version“ und mehr aufzurufen um an diese Information zu kommen, kann man einfach sein Bash-Prompt erweitern. So sind die aktuellen Daten immer präsent und kann u.U. auch Fehler vermeiden.

Um die entsprechenden Details im Bash-Prompt anzuzeigen kann folgende Ergänzung in der .bashrc vorgenommen werden. (In diesem Beispiel gehe ich davon aus, dass rvm zur Verwaltung der Ruby-Version eingesetzt wird.)

[bash]
# In dieser Funktion ermitteln wir den aktuellen Git-Branch in dem wir uns befinden
function find_git_branch {
git branch –no-color 2> /dev/null | sed -e ‚/^[^*]/d‘ -e ’s/* (.*)/[1] /‘
}

rvm system

# Hier wird die aktuelle Ruby-Version ermittelt
function get_rvm_ruby_version(){
RUBY_INFO=`rvm-prompt`
PROMPT_STR=""
if [ -n "$RUBY_INFO" ]; then
PROMPT_STR="[$RUBY_INFO]"
fi
echo $PROMPT_STR
}

# Diese Funktion erweitert den Prompt mit den Daten aus den vorherigen Funktionen
function prompt_func(){
export PS1="[e]0;wa]n[e[32m]u@h [\033[1;36m]$(get_rvm_ruby_version) [\033[0;34m]$(find_git_branch) [e[33m]w[ e[0m]n$ "
}

PROMPT_COMMAND=prompt_func
[/bash]

Nachdem die .bashrc um diese Funtionen ergänzt wurden findet man alle Information in seinem neuen Prompt:
benutzer@host [ruby language=“-1.8.7-p248″][/ruby] [master] ~/mein/pfad/im/git/projekt

Wenn keine Ruby-Version aktiviert ist oder man sich nicht in einem Git-Verzeichnis befindet, werden die entsprechenden Stellen im Prompt weggelassen.

Scripttip: Herausfinden ob ein Datum in einen bestimmen Zeitraum fällt

Häufig ist es nötig herauszufinden ob ein Datum oder eine Uhrzeit in einen bestimmten Zeitraum fällt.
In diesem Scripttip zeigen wir euch wie dies ganz einfach mit Ruby geprüft werden kann.

[ruby]
# Die nötigen Bibliothek einbinden
require ‚time‘

# Hier erstellen wir das zu prüfende Datum
pruef_datum = Time.parse ‚2010-09-06 08:00:00 +0100‘
# => Mon Sep 08 09:00:00 +0200 2010

# Das Anfangsdatum für den zu überprüfenden Zeitraum
anfangs_datum = Time.parse ‚2010-09-01 00:00:00 +0100‘
# => Wed Sep 01 01:00:00 +0200 2010

# Das Enddatum für den zu überprüfenden Zeitraum
end_datum = Time.parse ‚2010-09-10 00:00:00 +0100‘
# => Fri Sep 10 01:00:00 +0200 2010

# Nun können wir mittels "between?" abfragen ob das Prüfdatum in unserem Zeitraum befindet
pruef_datum.between? anfangs_datum,end_datum
# => true
[/ruby]

Scripttip: Herausfinden ob ein Wert numerisch ist

In unserem heutigen Scripttip möchten wir euch zeigen wie man herausfinden kann, ob der Wert einer Variable numerisch ist.

Um festzustellen ob eine Variable einen numerischen Wert besitzt, können folgende Hilfsmethoden eingesetzt werden:

Prüfen ob es ein Integer-Wert ist:

[ruby]
meine_variable = "1234"

def meine_variable.ist_der_wert_ein_int?()
self.to_i.to_s == self
end

meine_variable.ist_der_wert_ein_int?
# => true
[/ruby]

Prüfen ob es Float-Wert ist:

[ruby]
meine_variable = "1234"

def meine_variable.ist_der_wert_ein_float?()
self.to_f.to_s == self
end

meine_variable.ist_der_wert_ein_float?
# => true
[/ruby]

Subversion und .svn Verzeichnisse finden und löschen

Subversion, häufig abgekürzt SVN gennant, ist eine Quellcode-Versionierungssoftware und hat vor ein paar Jahren das bis dahin etablierte CVS (Concurrent Version System) an vielen Stellen abgelöst. Auch wenn GIT nun an vielen Stellen SVN ablöst, ist die Software noch in vielen Betrieben erfolgreich im Einsatz. Nicht zuletzt, weil es eine weite Unterstützung in Form von Plugins in IDEs, GUIs und Tools gibt.
Ein mit SVN versioniertes Software-Projekt erkennt man häufig an der Verzeichnisstruktur. Solange der Quellcode noch versioniert wird, befinden sich .svn Verzeichnisse in allen Projekt-Ordnern und das Repository, das die zentrale „Ablage-Stelle“ auf einem Server darstellt, ist in der Regel nach einem bestimmten Muster aufgebaut.

Viele folgen der Empfehlung das zentrale SVN-Repository/Archiv mit Unterverzeichnisse zu strukturieren, die den jeweiligen Software-Status widerspiegeln. „trunk“ für die aktuelle Entwicklung, „stable“ für den stabilen Stand, „branches“ und „tags“ für unabhängige Entwicklungslinien mit der gleichen Historie. Die letztgenannten fließen häufig in den Hauptzweig zurück.

 

Für jede Datei in einem Projekt merkt sich Subversion zwei essentielle Informationen in einem eigenen Verwaltungsbereich. Dieser Bereich sind die .svn-Verzeichnisse die in jedem Projekt-Order zu finden sind. Dort merkt sich SVN auf welcher Revision die Arbeitsdateien aufbauen und den Zeitstempel der letzten Aktualisierung.

 

Die .svn-Verzeichnisse werden nur während der Entwicklung benötigt und sind auf produktiven System eher unerwünscht. Subversion ermöglicht durch den Unterbefehl „export“ eine unversionierte Version eines Projekts zu erstellen. Die exportierte Version ist frei von den subversion-spezifischen Dateien und Verwaltungsbereichen. Dennoch kommt es hin und wieder vor, dass man in einer lokalen Kopie (auf dem eigenen Rechner oder auf dem Server) die .svn löschen möchte, weil zum Beispiel gerade in diesem Augenblick nicht die Möglichkeit besteht einen Export zu generieren.

 

Die folgende Bash-Scriptzeile kann benutzt werden, um alle .svn-Vezeichnisse und Dateien die mit „._“ beginnen zu finden und zu löschen.

[bash]
find . -name ".svn" -prune -o -name "._*" -exec rm -rf {} ;
[/bash]

Windows-Benutzer können die Script-Zeile in einer Cygwin-Shell verwenden.

 

Typische SVN-Kommandos die täglich eingesetzt werden:

[bash]
#———————————————————————-
# Eine lokale Kopie eines Projektes aus dem zentralen Archiv erstellen
#———————————————————————-
svn checkout svn://example.com/svn/trunk # über das svn-Protokoll
svn checkout https://example.com/svn/trunk # über HTTP

#———————————————————————-
# Basis-Befehle
#———————————————————————-
svn up # Kurzform für "svn update"; Aktualisiert die lokale
# Version auf den Stand des zentralen Repository
svn add [Datei] # Fügt eine Datei zu dem Projekt hinzu
svn ci -m "Nachricht" # Kurzform für ’svn commit -m "Nachricht"‘;
# Überträgt die eigenen Änderungen in das
# zentrale Repository
svn log # Zeigt die Historie eines Projektes

#———————————————————————-
# Arbeiten mit Verzeichnissen und Dateien
#———————————————————————-
svn copy Datei-A Datei-B # Erstellt eine Kopie von "Datei-A" unter
# dem Namen "Datei-B" und nimmt diese
# in die Versionierung auf
svn mkdir MeinNeuesVerzeichnis # Erstellt ein Verzeichnis mit dem Namen
# "MeinNeuesVerzeichnis" und nimmt es in
# die Versionierung auf
svn mv Datei-A Datei-B # Benennt eine Datei mit dem Namen
# "Datei-A" nach "Datei-B" um. Die Historie
# bleibt dabei erhalten
svn rm Datei-A # Löscht "Datei-A" und nimmt diese aus der
# Versionierung raus

#———————————————————————-
# Verschiedenes/Nützliches
#———————————————————————-
svn status # Zeigt den Status (Veränderugen) der lokalen Version an.
svn diff # Zeigt den Unterschiede im Unix-Diff-Format an.
# Die Ausgabe kann in eine Datei umgeleitet
# werden, um einen Patch zu erstellen
svn revert # Verwirft die eigenen und noch nicht übergebenen Änderungen.
# Dabei wird die lokale Version auf die letztbekannte
# Version gebracht
svn info # Zeigt allgemeine Informationen zu einem Projekt
[/bash]

Subversion bringt weit mehr Befehle mit, um die volle Kontrolle über die Versionierung zu behalten, aber dies würde den Rahmen dieses Artikels sprengen. Weiterhin kapseln Werkzeuge wie Tortoise-SVN diese Befehle und Funktionen in einer GUI, um die Arbeit zu vereinfachen. Dennoch ist es sinnvoll mit den direkten Befehlen umgehen zu können, falls eine GUI nicht zur Verfügung steht.
Interessierte, die mehr über Subversion und dessen Möglichkeiten erfahren wollen, finden in folgenden Büchern alles Wissenswerte über dieses Tool.

 

Dieses Buch ist auch in einer freien digitalen Version unter folgender Adresse erhältlich: http://svnbook.red-bean.com/

 

Script-Tip: Stadt, Land und Wetter aus der IP beziehen (IP2Location)

Anhand einer IP-Adresse lassen sich die verschiedensten Informationen beziehen. Neben dem Land, der Stadt und der Region, kann mittels der IP-Adresse auch die Postleitzahl, Zeitzone, ISP, Domain-Namen, Koordinaten und sogar das Wetter ermittelt werden.

 

Diese Daten können über die Bibliotheken von IP2Location ermittelt werden. Entwickler können die zur Verfügung gestellten APIs benutzen um die im binären Format vorliegenden Datenbanken abzufragen.

 
Folgendes wird dazu benötigt:
  • IP2Location C API Bibliothek (Download: http://www.ip2location.com/c.htm)
  • Ruby
  • GNU make oder ein vergleichbares make (Windows können ggf. auf Cygwin aufbauen)
 
Installation:

[bash]
ruby extconf.rb –with-libIP2Location-lib=<path to C API lib> –with-includeIP2Location-include=<path to C API lib>
# Beispiel: ruby extconf.rb –with-libIP2Location-lib=..C-IP2Location-3.0.0libIP2Location –with-includeIP2Location-include=..C-IP2Location-3.0.0libIP2Location

make
[/bash]

 
Sourcecode Beispiel

[ruby]
require ‚IP2Location‘

IP2LocObj = IP2Location.new()
IP2LocObj.open("data/IP-COUNTRY-SAMPLE.BIN")
rec = IP2LocObj.get_all("12.34.56.78")
puts rec.country_short
puts rec.country_long
puts rec.city
puts rec.region
puts rec.isp
puts rec.latitude
puts rec.longitude
puts rec.domain
puts rec.zipcode
puts rec.timezone
puts rec.netspeed
puts rec.iddcode
puts rec.areacode
puts rec.weatherstationcode
puts rec.weatherstationname
[/ruby]