Beim Verfassen neuer Actions für einen Controller neigt man doch dazu, sehr viel Model-Logik direkt darin unterzubringen - gerade wenn man mit dem Motto „Erst mal kurz probieren und nachher verbessern“ an die Entwicklung herangeht. Schlimm ist das nicht. Dabei kommen schnell positive Ergebnisse ans Tageslicht.
Ich möchte hier einmal ein Vorher-Nachher-Beispiel vorstellen. Die Einzelheiten zum jeweiligen Code stehen jeweils darunter.
Vorher
def import
unless params[:feed_urls].empty?
params[:feed_urls].each do |feed_url|
url = Url.new
url.url = feed_url
url.user_id = current_user.id
url.save
end
end
spawn do
current_user.worse_to_try_urls.each do |url|
subscription = Subscription.new
temp_feed = Feed.find_or_create_by_feed_url( url.url )
begin
temp_feed.complete
rescue
url.tried = true
url.save!
next
end
feed = Feed.find_by_feed_url( temp_feed.feed_url )
begin
if feed.nil?
feed = Feed.new( :feed_url => temp_feed.feed_url )
flash[:gathered_url] = temp_feed.feed_url
feed.complete!
end
rescue
url.tried = true
url.save!
next
end
unless feed.title.blank?
url.destroy
else
url.tried = true
url.save!
end
subscription.user_id = current_user.id
subscription.feed_id = feed.id
subscription.save
end
end
current_user.subscriptions.each do |subscription|
if subscription.feed.title.blank?
subscription.feed.destroy
subscription.destroy
end
end
redirect_to user_readings_path( current_user )
end
Zuerst einmal war mir diese Methode viel zu lang und im Nachhinein sehr unübersichtlich. Des weiteren tauchen hier sehr viele Code-Fragmente auf, die auch in anderen Actions Verwendung finden und daher besser in die jeweiligen Models verlagert werden könnten. Das würde den Code nicht nur in diesem Controller vereinfachen.
Nachher
def import
# Speichere die abgesendeten URLs als URL-Objekte. Stelle sicher,
# dass eine URL nur einmal mit der gleichen Benutzer-ID gespeichert
# werden kann.
unless params[:feed_urls].empty?
params[:feed_urls].each do |feed_url|
url = Url.new
url.url = feed_url
url.user_id = current_user.id
url.save( perform_validation = true )
end
end
# Lade alle frischen (tried => false) URL-Objekte des akktuellen
# Benutzers. Versuche ein Abo anzulegen und setze die URL auf erledigt
# (tried => true).
spawn do
current_user.worse_to_try_urls.each do |url|
begin
sub = Subscription.new
sub.feed_url = url.url
sub.user_id = current_user.id
# Subscription.add sucht nach dem passenden Feed-Objekt
# (feed_url) oder ggf startet die Routine zum Hinzufügen eines
# Feeds mittels Feed Runner.
sub.add
url.tried = true
url.save
# Wenn das Anlegen eines Abos gescheitert ist, liegt das
# höchstwahrscheinlich daran, das Feed Runner den Feed nich lesen
# könnte oder der Benutzer bereits den Feed abonniert hat. Setze
# die Schleife also fort.
rescue
next
end
end
end
# Das Abonnieren eines Feeds dauert ~10 Sekunden. Versuche abzuschätzen,
# wie lange der gesamte Import dauern kann und gebe das Ergebnis als
# Hinweis aus.
estimated_time = params[:feed_urls].size.to_i * 10.seconds
flash[:notice] = "Deine Feeds werden nun importiert. Das kann noch ca. "
+ (estimated_time/60).to_s
+ " Minuten dauern."
# Leite auf die Benutzerseite weiter
redirect_to( user_path( current_user ) )
end
Hier steht nur noch Code, der auch wirklich nur in dieser Action gebraucht wird. Das Hinzufügen des Abos ist ausgelagert und damit auf 4 Zeilen minimiert worden. Die ganze Feed-Logik taucht überhaupt nicht mehr auf, denn darum kümmert sich die Methode Subscription.add (wie auch im Kommentar beschrieben). Die Fehlerbehandlung wurde auch sehr gekürzt, reicht aber allemal völlig aus.
Sicherlich lassen sich einige Dinge noch weiter verbessern, aber vorerst bin ich mit der grundlegenden Überholung dieser Methode absolut zufrieden.
Schreib’ einen Kommentar
Du kannst dich hier frei äußern, jedoch solltest du dich dabei fair gegenüber den anderen Lesern und dem Autor verhalten. Private Mitteilungen an Martin bitte per E-Mail an martin@labuschin.com. Danke.
Erforderliche Felder sind fettgedruckt.