Newbie-Frage: Is this the Rails way?

Hallo,

um mich ein wenig mit Rails vertraut zu machen, schreibe ich als
»Fingerübung« ein kleines Blogsystem. Das klappt bisher sehr gut, meine
gefühlte Produktivität liegt bereits deutlich über meiner bisherigen mit
PHP, und mit ein wenig Google-Suche bekomme ich überraschenderweise
tatsächlich alles so hin, wie ich es möchte. Doch frage ich mich, ob
mein gewählter Lösungsweg der Rails-»Philosophie« entspricht oder eher
einem wilden Hack gleicht. Ich möchte im folgenden ein Problem und
meine gewählte Lösung darstellen. Über Kommentare zu dieser wäre ich
dankbar.

Die Aufgabe: Blogautoren sollen ihre Posts mit Stichworten taggen
können. Die Eingabe der Stichworte soll Komma-separiert während des
Erstellens des Blogeintrags erfolgen. Weiter soll der Blogeintrag
später noch verändert werden können; in derselben Maske sollen dabei
auch die Stichworte geändert werden können.

Meine Lösung: Zwei Tabellen: entries und tags, die über eine
many-to-many-(n:m)-Relation (»has_and_belongs_to_many :tags« im
Entry-Model und vice versa im Tag-Model) miteinander verbunden sind.

In app/views/entry/new.html.erb habe ich ein Textfeld tags[list]
hinzugefügt:
[app/views/entry/new.html.erb]
<% form_for(@entry) do |f| %>
[…]

Comma separated list of tags: <%= text_field "tags", :list %>

[...] <% end %>

Im controller wandle ich den String dann in einen Array, erstelle ggf.
einen neuen Eintrag in der tags-Tabelle und füge einen Verweis auf den
aktuellen Entry hinzu:

[app/controllers/entry_controller.rb]
[…]

def create
@entry = Entry.new(params[:entry])
@entry.user = User.find(:first, :conditions => [ “id = ?”,
session[:user].id])

respond_to do |format|
  if @entry.save
    params[:tags][:list].split(", ").each do |tag|
      @tag = Tag.find(:first, :conditions => ["name = ?", tag]) || 

@tag = Tag.new
@tag.name = tag
@tag.entries << @entry
@tag.save
end
flash[:notice] = ‘Entry was successfully created.’
[…]

Das Editieren des Eintrags erwies sich als etwas schwieriger, da die
bereits referenzierten Tags ja im Edit-View auftauchen sollten.
Zunächst habe ich im Entry-Model eine neue Klasse Tags erstellt, die
über die zwei Methoden list und list=
verfügte.
[app/models/entry.rb]
[…]

class Tags
def list
@list
end
def list=(value)
@list = value
end
end

Im controller fülle ich eine Instanz dieser Klasse dann einem
komma-separierten String der vorhandenen Tags, so dass sie im View
angezeigt werden können:

[app/controllers/entry_controller.rb]
[…]

def edit
@entry = Entry.find(params[:id])
@tags = Tags.new
@tags.list = @entry.tags.collect { |tag| tag.name }.join(', ')
end

Schließlich lösche ich dann beim update zunächst die vorhandenen
Referenzen des entries auf Tags, um anschließend die nunmehr aktuelle
Liste zu schreiben:

[app/controllers/entry_controller.rb]
[…]

def create
@entry = Entry.new(params[:entry])
@entry.user = User.find(:first, :conditions => [ “id = ?”,
session[:user].id])

respond_to do |format|
  if @entry.save
    params[:tags][:list].split(", ").each do |tag|
      @tag = Tag.find(:first, :conditions => ["name = ?", tag]) || 

@tag = Tag.new
@tag.name = tag
@tag.entries << @entry
@tag.save
end
flash[:notice] = ‘Entry was successfully created.’
[…]

Ein Problem könnte sein, dass ich evtl. Tag»leichen« produzieren kann,
da Tags auch weiterexistieren können, ohne referenziert zu sein. Das
halte ich aber für vernachlässigbar.

Vielen Dank für eure Aufmerksamkeit,
olli


Homepage von Oliver Heins GnuPG-Key: gpg --recv-keys 0x9A00D827
GnuPG-Fingerprint: F27A BA8C 1CFB B905 65A8 2544 0F07 B675 9A00 D827
NP: Geoff Berner - Clown and Bard
Please avoid sending me Word or PowerPoint attachments.
See We Can Put an End to Word Attachments - GNU Project - Free Software Foundation

Hallo Oliver,

das klingt auf den ersten Blick im Großen und Ganzen ganz ok wie Du
das angehst. Vielleicht musst Du ja beim Updaten nicht alle löschen
und neu schreiben… und ich würde das Speichern der Tags in eine
after_save action des Entry-Models packen, dann wird Dein Controller
cleaner und es lässt sich leichter testen.

Als Inspiration schau Dir doch mal acts_as_taggable bzw.
acts_as_taggable_on_steroids an:

http://agilewebdevelopment.com/plugins/acts_as_taggable_on_steroids

Grüße, Niko.

Hallo Niko,

vielen Dank für deine Antwort.

Niko D. [email protected] writes:

das klingt auf den ersten Blick im Großen und Ganzen ganz ok wie Du
das angehst. Vielleicht musst Du ja beim Updaten nicht alle löschen
und neu schreiben…

Mir erschien das als einfacher und schneller, als zuerst zu überprüfen,
welche Tags denn nun wegfallen und nur deren Referenzen zu löschen.
Ehrlich gesagt fällt mir dazu kein schöner Code ein. (Ich weiß aber
nicht, welchen Overhead ich so in Rails produziere.)

und ich würde das Speichern der Tags in eine after_save action des
Entry-Models packen, dann wird Dein Controller cleaner und es lässt
sich leichter testen.

Ich muss gestehen, dass ich an den Model Callbacks gerade verzweifle.
Wie kann ich im Callback denn auf die Formulardaten zugreifen?

Als Inspiration schau Dir doch mal acts_as_taggable bzw.
acts_as_taggable_on_steroids an:

http://agilewebdevelopment.com/plugins/acts_as_taggable_on_steroids

Hui, das ist schon recht komplex. Bis ich mich durch diesen Code
gewühlt habe, dürfte einige Zeit vergehen.

Viele Grüße,
olli