org.specs.form.Prop.scala Maven / Gradle / Ivy
/**
* Copyright (c) 2007-2009 Eric Torreborre
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of
* the Software. Neither the name of specs nor the names of its contributors may be used to endorse or promote
* products derived from this software without specific prior written permission.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
package org.specs.form
import scala.xml.NodeSeq
import org.specs.execute._
import org.specs.matcher._
import org.specs.util.Property
/**
* The Prop class is a named property which holds:
* an expected value
* an actual value
* a constraint
*
*
* This property can then be executed, its results can be fetched and displayed as Html
* It has also the possibility of being inserted in the Layout of a Form.
*
* A Prop property is meant to be declared as "bound" to an actual value:
*
* val customerName = Prop("Customer name", person.name)
*
* Note that the actual value is not evaluated until the Prop is executed.
*
* Then it can be associated an expected value with the apply method (usually in a Form declaration):
*
* customerName("Bill")
*
* Different constraints can be set on a Prop, by using the companion object factory methods:
*
* // build a Prop with an AnyConstraint. The block will be executed if the property is executed
* val propWithABlock = Prop("label", actualValue, thisVariableInTheScope must_== thatVariableInTheScope)
*
* // build a Prop with a FunctionConstraint. The function will be executed with the actual and expected values if the property is executed
* val propWithAFunction = Prop("label", actualValue, (actual, expected) => actual must_== expected)
*
* // build a Prop with a MatcherConstraint. The function will be executed with the default matcher (BeEqualTo)
* // if the property is executed
* val propWithAMatcherExecutor = Prop("label", actualValue, m => actual must m)
*
* // Note that in that case the matcher set on the constraint can be changed with
* propWithAMatcherExecutor.matchesWith(beGreaterThan(_))
*
*
*
* Props can be temporarily commented out with the comment() method and thus won't be
* executed:
* name("Eric").comment() // won't check the expected value
*
* After execution, Props can be queried for their results using the HasResults trait.
* Then they can be outputed in Html
*
*/
class Prop[T](val label: String,
var expected: Property[T],
var actual: Property[T], constraint: Option[Constraint[T]])
extends DefaultExecutable with LabeledXhtml with ValueFormatter[T] with Copyable[Prop[T]] {
override def copy: Prop[T] = {
val p = new Prop[T](label, expected, actual, constraint)
super[ValueFormatter].copy(p)
p
}
def copy(p: Prop[T]) = {
super[ValueFormatter].copy(p)
super[LabeledXhtml].copy(p)
}
/**
* The apply method sets the expected value and returns the Prop
*/
def apply(e: T): Prop[T] = {
expected(e)
this
}
def actualValue = actual.optionalValue
/**
* shortcut method for this().get returning the contained expected value.
* @return the expected value if set and throws an exception otherwise
*/
def get: T = expected.get
/** execute the constraint set on this property, with the expected value */
protected def executeThis = constraint.map { c =>
c.execute(expected.optionalValue)
}
/**
* Display the property:
*
* label: "this" (expected: "that")
*/
override def toString = {
label + ": " + this.actual.map(format(_)).getOrElse("_") + " (expected: " + expected.map(format(_)).getOrElse("_") + ")"
}
/** format the expected value if set or else the actual value. */
private[form] def formattedValue = decorateValue(format(expected.orElse(actual).optionalValue))
/** @return the status of the execution or value if the cell hasn't been executed. */
override def statusClass = if (executed) super.statusClass else "info"
/**
* Display the Property expected value with a different class attribute
* depending if it hasn't been executed (white), has failed (yellow),
* isOk (green, yes!)
*/
override def toXhtml = {
if (label.isEmpty)
decorateValueCell(valueCell)
else
decorateLabelCell({ decorateLabel(label) } ) ++ decorateValueCell(valueCell)
}
/**
* execute the Prop and return the Xhtml
*/
def toXhtml_! = execute.toXhtml
/**
* execute the Prop and return the Xhtml
*/
def display_! = { toXhtml_! }
.toString
/**
* @return the formatted value with its status and error messages if any
*/
private[form] def valueCell = {
if (executed) {
if (isOk)
{ formattedValue }
else {
{ formattedValue }
{issueMessages}
}
}
else
{ formattedValue }
}
/**
* When embedded in an Html table, a Prop doesn't need a new cell.
*/
override def toEmbeddedXhtml = toXhtml
}
/**
* Companion object containing default factory methods
*/
case object Prop {
def apply[T](label: String, value: =>T, toCheck: => Any): Prop[T] = new Prop(label, Property[T](), Property(value), Some(AnyConstraint(() => toCheck)))
def apply[T](label: String, value: =>T, f: (T, T) => Any): Prop[T] = new Prop(label, Property[T](), Property(value), Some(FunctionConstraint(value, f)))
def apply[T](label: String, value: =>T, c: Constraint[T]): Prop[T] = new Prop(label, Property[T](), Property(value), Some(c))
def apply[T](label: String, value: =>T, c: MatcherConstraint[T]): MatcherProp[T] = new MatcherProp(label, Property[T](), Property(value), Some(c))
def apply[T](label: String, value: =>T): MatcherProp[T] = new MatcherProp(label, Property[T](), Property(value), None)
def apply[T](label: String): MatcherProp[T] = new MatcherProp(label, Property[T](), Property[T](), None)
def apply[T](value: =>T): MatcherProp[T] = new MatcherProp("", Property[T](), Property[T](value), None)
}
/**
* generic trait for anything having a label, to unify Props and Forms
*/
trait HasLabel {
val label: String
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy