2009-08-09 12 views
2

Ich mache ein kleines Issue Management System in Lift, um sowohl Scala als auch Lift zu lernen.Methode Abhängigkeiten und Fehlerbehandlung

Ich habe eine Ansicht, die ein einzelnes Problem anzeigt, das zu einem Projekt gehört. Bevor ich Daten an die View-Template binden, möchte ich überprüfen, dass ich alle Daten erforderlich ist, so möchte ich ausdrücklich, dass der Check:

  • Ein Projekt ID param hat
  • Ein Projekt existiert mit dem mitgelieferten geliefert worden Projekt ID
  • Ein Problem ID param wurde
  • geliefert
  • es besteht ein Problem mit dem mitgelieferten Heft ID

Und diese braucht, um zu bewerten, so dass, wenn ich es jetzt mit meinem aktuellen Verständnis bin zu schreiben von Scala, würde ich folgendes tun:

class Project { 
    def findByID(xhtml:NodeSeq): NodeSeq = 
     param("projectID") match { 
      case Full(projectID) => 
       Project.findByID(projectID) match { 
        case Full(projectID) => 
         param("issueID") match { 
          .... 
         } 
        case _ => Text("Project does not exist") 
       } 
      case _ => Text("projectIDNotSupplied") 
     } 
} 

Also ich frage mich, ob es eine einfachere Möglichkeit ist, dies zu tun? Ich denke, dass ein for expression etwas Ähnliches leisten könnte. Beachten Sie, dass Project.findByID eine Box [Project] zurückgibt.

Antwort

4

Sorry, ich bin so spät, um die Show, aber wie Daniel sagt kann man in der Tat Lift Box verwenden und? ~, So etwas zu tun. Zum Beispiel:

import net.liftweb.util.Box 
import net.liftweb.util.Box._ 

class Project { 
    def findByID(xhtml:NodeSeq): NodeSeq = 
    (for { 
     projectID <- param("projectID") ?~ "projectIDNotSupplied" 
     project <- Project.findById(projectID) ?~ "Project does not exist" 
     issueID <- param("issueID") ?~ "issueIDNotSupplied" 
     ... 
    } yield { 
     ... 
    }) match { 
     case Full(n) => n 
     case Failure(msg, _, _) => Text(msg) 
     case _ => Text("fail") 
    } 
} 

Was ~ tut, ist ein leeres Feld in eine Failure Box drehen mit der String Fehlermeldung gegeben, aber tut nichts, um ein Full (Erfolg) Box. Der Rückgabewert von findByID ist also Full, wenn alles erfolgreich ist, andernfalls Failure (mit der angegebenen Fehlermeldung). Wenn Sie möchten, dass die Fehler ketten, verwenden Sie? ~! stattdessen.

+0

Hey Jorge sieht so aus, das wird ein Leckerbissen funktionieren, nur dass dies ein Schnipsel ist, so weit ich weiß, muss der Rückgabetyp sein: NodeSeq. Wenn nicht, bekomme ich eine Fehlermeldung, dass die Methode nicht gefunden werden kann. Um das zu umgehen, kann ich das ganze for() in einen Block schreiben und openOr (Text ("fail")) darauf setzen. Aber jetzt, wenn eine der Abhängigkeiten fehlschlägt, bekomme ich die Nachricht "fail". Ich habe versucht, die Methode run() zu verwenden, aber ich verstehe es nicht ganz. irgendwelche Ideen? –

+0

Ich habe das obige Code-Snippet bearbeitet, um NodeSeq zurückzugeben. –

+0

Ausgezeichnet, Danke Jorge. Das war einer jener "warum habe ich nicht an diese Fälle gedacht". Danke noch einmal –

1

Ich weiß nicht Lift, aber es gibt ein paar Dinge, die ich in seiner Implementierung gesehen habe. Eine davon sind die Fehlermethoden: ?~ und ?~!. Ich bin mir nicht sicher, wie man sie benutzen könnte, aber es scheint nützlich zu sein. Ein anderer ist open_!, um Ausnahmen zu werfen.

nun eine Box Unterstützung Karte, flatMap, Filter und foreach, so kann es vollständig in einem für das Verständnis verwendet werden:

for(projectID <- param("projectID"); 
    if Project.findByID(projectID).isDefined; 
    issueID <- param("issueID"); 
    if Issue.findByID(issueID).isDefined) 
yield ... 

Dies wird nicht die Fehlermeldungen erhalten. Für diejenigen, ich denke, die Methoden, die ich erwähnte, oder andere wie ~>, könnten die Antwort liefern.

+0

Dank Daniel, ich habe versucht, die für den Ausdruck in die falsche Richtung, um damit umzugehen, ich verstehe sie noch nicht vollständig. Ich werde dies versuchen, wenn ich nach Hause komme und sehen, ob die Box-Fehlerbehandlungsmethoden spezifische Informationen über den Fehler zurückgeben. –

1

Ich weiß nicht Lift, damit ich keine Lift-spezifischen Fragen beantworten kann. Ich habe jedoch einen Weg gefunden, um eines Ihrer Probleme zu lösen, nämlich wie man eine Sequenz von check-then-operate-Aktionen schreibt, ohne auf den geschachtelten Mustervergleich zurückzugreifen.

Der Hauptdatentyp, der hier verwendet wird, ist Option, aber ich bin mir sicher, dass es ziemlich einfach ist, sich an Ihre Bedürfnisse anzupassen. Was wir hier erreichen wollen ist eine Folge von

  1. Prüfbedingung
  2. fortgesetzt werden, wenn erfolgreich
  3. beenden und zurück etwas anders

Der Code macht eine Art Kurzschluss, sobald es zu tun auf eine None, so wird der Rückgabewert beibehalten, wenn die Sequenz der Aktionen zurückkehrt. Zu verwenden, beginnt mit einer Option, dann schreibe "ifSome", wenn Option ein Some ist und "ifNone" wenn Option eine None ist, fahre fort, bis die Sequenz beendet ist. Wenn zu einem beliebigen Zeitpunkt in der Sequenz eine None auftritt, wird die Option, die vom Parameter name_des_Anrufnamens zurückgegeben wird, beibehalten und zurückgegeben, wenn der letzte Aufruf von toOption erfolgt. Verwenden Sie "toOption", um das tatsächliche Optionsergebnis zurückzuerhalten.

Schauen Sie sich das Beispiel in "main" für einige Anwendungsfälle an. Viel Glück!

object Options { 

    class RichOption[A](a: Option[A]) { 

    def ifSome[B](f: A => Option[B]): RichOption[B] = a match { 
     case Some(x) => new RichOption(f(x)) 
     case None => this.asInstanceOf[RichOption[B]] 
    } 

    def ifNone[B](f: => Option[B]): RichOption[B] = a match { 
     case Some(_) => this.asInstanceOf[RichOption[B]] 
     case None => new RichNone(f) 
    } 

    def toOption[A] = a 
    } 

    class RichNone[A](a: Option[A]) extends RichOption[A](a) { 

    override def ifSome[B](f: A => Option[B]): RichOption[B] = this.asInstanceOf[RichOption[B]] 

    override def ifNone[B](f: => Option[B]): RichOption[B] = this.asInstanceOf[RichOption[B]] 
    } 

    implicit def option2RichOption[A](a: Option[A]): RichOption[A] = new RichOption(a) 

    def main(args: Array[String]) { 
    println(Some("hello") ifSome(s => Some(s.toUpperCase)) toOption) // prints Some(HELLO) 
    println(Some("hello") ifNone(Some("empty")) toOption) // prints Some(hello) 
    println(Some("hello") ifSome(s => Some(s.toUpperCase)) ifNone(Some("empty")) toOption) // prints Some(HELLO) 
    println(Some("hello") ifNone(Some("empty")) ifSome(s => Some(s.toUpperCase)) toOption) // prints Some(HELLO) 
    println((None: Option[String]) ifSome(s => Some(s.toUpperCase)) toOption) // prints None 
    println((None: Option[String]) ifNone(Some("empty")) toOption) // prints Some(empty) 
    println((None: Option[String]) ifSome(s => Some(s.toUpperCase)) ifNone(Some("empty")) toOption) // prints Some(empty) 
    println((None: Option[String]) ifNone(Some("empty")) ifSome(s => Some(s.toUpperCase)) toOption) // prints Some(empty) 
    println(Some("hello world") ifSome(s => Some(s.toUpperCase)) ifNone(Some("empty")) ifSome(s => Some(s.length)) ifNone(None) toOption) // prints Some(11) 
    println(Some("hello world") ifSome(_ => None) ifNone(Some("goodbye world")) ifSome(s => Some(s.length)) ifNone(None) toOption) // prints Some(goodbye world) 
    println((None: Option[String]) ifSome(s => Some(s.toUpperCase)) ifNone(Some("empty")) ifSome(s => Some(s.length)) ifNone(None) toOption) // prints Some(empty) 
    println((None: Option[String]) ifSome(_ => None) ifNone(Some("goodbye world")) ifSome(s => Some(s.length)) ifNone(None) toOption) // prints Some(goodbye world) 
    } 
} 
+0

Danke Walter, das ist eine interessante Lösung. Ich werde es ausprobieren, wenn ich nach Hause komme, meine Scala Gehirnmuskeln trainieren. –