Mit ‘Ruby-on-rails’ getaggte Artikel

Symbole zu Methoden von Objekten

Freitag, 27. November 2009

Bei der Programmierung eines Suchalgorithmus in Rails habe ich bei der Methode Enumerable#find einen einfachen Weg gesucht, die optionale Callback-Methode _ifnone_ zu setzen.

Die Intention des Algorithmus ist, falls es kein passendes Element (item) zur gestellten Suchbedingung gibt (item search condition), ein bestimmtes (hier: das letzte) Element zurückzugeben, statt nil.

Dafür bin ich unter Funktionale Programmierung auf den folgenden für mich sehr hilfreichen Code-Snippet gestoßen:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# Dieser Ausdruck verhindert eine doppelte Definition der Operatoren [] und []=
unless :test.respond_to?(:[])
  #
  # Operatoren [] und []= zur Klasse Symbol hinzufügen, um Singleton-Methoden
  # von Objekten zu lesen und zu setzen. Lies : als "Methode" und [] als "von".
  # Also bedeutet zum Beispiel :m[o] "Methode m von o".
  #
  class Symbol
    # Die durch dieses Symbol benannte Methode von obj zurückgeben. Dies kann
    # eine Singleton-Mehtode von obj (etwa eine Klassenmethode) oder eine Instanz-
    # methode sein, die in obj.class definiert oder von einer Oberklasse übernommen
    # wurde.
    # Beispiele:
    #   creator = :new[Object]  #Klassenmethode Object.new
    #   doubler = :*[2]           # *-Methode der Fixnum 2
    #
    def [](obj)
      obj.method self
    end
 
    # Eine Singleton-Methode für Objekt o definieren und Proc oder Method f
    # als Rumpf verwenden. Dieses Symbol dient als Name der Methode.
    # Beispiele:
    #   :singleton[o] = lamba { puts "Dies ist eine Singleton-Methode von o" }
    #   :class_method[String] = lamba { puts "Dies ist eine Klassenmethode" }
    #
    # Beachten: Sie können so keine Instanzmethoden erzeugen. Siehe Module.[]=
    #
    def []=(o,f)
      # self einer Variablen zuweisen, da der folgende Block einen anderen Kontext
      # haben wird.
      sym = self
      # Dies ist das Objekt, für das wir Singleton-Methoden definieren.
      eigenclass = (class << o; self end)
      # define_method ist privat, so dass wir instance_eval verwenden müssen.
      eigenclass.instance_eval { define_method(sym, f) }
    end
  end
end

Mein Anwendungsfall sieht dann folgendermaßen aus:

34
35
36
 ...
 valuearray.find(:last[valuearray]) {|item| /* item search condition */ }
 ...

Als Fallback für eine nicht erfolgreiche Suche verwende ich :last[valuearray], die last-Methode von dem Array-Objekt valuearray.

Auch wenn dieses Beispiel für den durchschnittlichen Ruby-Entwickler etwas überzogen daherkommt,
soll es die Aufmerksamkeit für diesen Themenkomplex der Programmiersprache anregen und die
Vielseitigkeit und Flexibilität von Ruby zeigen.


Hendrik Lange


Ruby On Rails – Vorsicht bei der Spaltenbenennung!

Montag, 19. Mai 2008

Getreu den Grundprinzipien von Ruby On Rails „Don’t repeat yourself“ und „Convention over configuration“, versucht das Webframework dem Entwickler die Datenbankanbindung zu erleichtern. Hält man sich an die Konventionen, so genügt es zum Beispiel, die Spalten einer Tabelle nur in der Datenbank festzulegen. Zur Laufzeit stehen einem automatisch Zugriffsmethoden für die Spalten zur Verfügung. Diese werden einfach nach den Spaltennamen benannt. Es werden hierfür also keine Konfigurations- bzw. Mappingdateien mehr benötigt.

Problem

Allerdings führt dieser Mechanismus zu Einschränkungen bei der Auswahl der Spaltennamen. Tabellenspalten sollten nicht den selben Namen tragen wie Methoden der Klasse AciveRecord::Base oder der Klasse Object. Werden diese Einschränkungen nicht beachtet, resultiert dies unter Umständen in schwierig aufzufindenden Fehlern. Nennt man beispielsweise die Spalte einer Tabelle “class”, so wäre der konventionelle Zugriffsweg auf diesen Spaltenwert über eine Instanz t des Models dieser Tabelle: t.class

Da class aber eine Methode der Klasse Object (Basisklasse von ActiveRecord::Base) ist, erhält man mit t.class nicht den erwarteten Spaltenwert, sondern die Klasse der Instanzvariablen t. Benennt man eine Tabellenspalte nach einer Methode von ActiveRecord::Base, so wird also die Spaltenzugriffsmethode nicht zur Verfügung gestellt, da es ja bereits eine gleichnamige Methode gibt. Verwendet man nun diese entsprechend seiner Tabellenspalte benannte Methode, führt dies zu einem unerwartetem Programmverhalten.

Lösung

Es ist empfehlenswert, die erwähnten Einschränkungen bei der Spaltenbenennung zu beachten und damit die Probleme zu vermeiden. Möchte man dennoch eine seiner Spalten genau wie eine Methode von ActiveRecord::Base benennen, so kann man sich einfach einen eigenen getter schreiben:

    def get_class
 
    self.attributes["class"]
 
    end

Die Bezeichner, die man bei der Spaltenbenennung vermeiden sollte, können der Ruby- bzw. der Rails-API entnommen werden. (Members der Klassen Object und ActiveRecord::Base)


Christian Borkowski