Social Reader Development Blog

Wenn ich groß bin, werde ich Feed Reader.

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.