All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.specs2.specification.Fragments.scala Maven / Gradle / Ivy

package org.specs2
package specification

import collection.Iterablex._
import execute.Executable
import main.Arguments
import org.specs2.internal.scalaz.Monoid
import Fragments._
import specification.StandardFragments.{End, Br}

/**
 * A Fragments object is a list of fragments with a SpecStart and a SpecEnd
 */
case class Fragments(specTitle: Option[SpecName] = None, middle: Seq[Fragment] = Vector(),
                     arguments: Arguments = Arguments(), linked: Linked = Linked()) {

  def fragments: Seq[Fragment] = if (middle.isEmpty && !linked.isLink) Vector() else (specStart +: middle :+ specEnd)

  def specTitleIs(name: SpecName): Fragments = copy(specTitle = specTitle.filterNot(_.title.isEmpty).map(_.overrideWith(name)).orElse(Some(name)))

  def add(e: Fragment): Fragments = append(e)
  def add(fs: Seq[Fragment]): Fragments = copy(middle = middle ++ fs)
  def add(fs: Fragments): Fragments = add(fs.fragments)
  def add(a: Arguments): Fragments = copy(arguments = arguments.overrideWith(a))

  def insert(e: Fragment): Fragments = prepend(e)
  def insert(fs: Seq[Fragment]): Fragments = copy(middle = fs ++ middle)
  def insert(fs: Fragments): Fragments = insert(fs.fragments)

  private def prepend(e: Fragment) = copy(middle = e +: middle)
  private def append(e: Fragment) = copy(middle = middle :+ e)

  def urlIs(url: String)         = copy(specTitle = specTitle.map(_.urlIs(url)),     linked = linked.urlIs(url))
  def baseDirIs(dir: String)     = copy(specTitle = specTitle.map(_.baseDirIs(dir)), linked = linked.baseDirIs(dir))

  def linkIs(htmlLink: HtmlLink) = copy(linked = linked.linkIs(htmlLink))
  def seeIs(htmlLink: HtmlLink)  = copy(middle = Vector(), linked = linked.seeIs(htmlLink))
  def hide                       = copy(linked = linked.linkIs(HtmlLink(this)).hide)

  def executables: Seq[Executable] = fragments.collect { case e: Executable => e }
  def examples: Seq[Example] = fragments.collect(isAnExample)

  def overrideArgs(args: Arguments) = copy(arguments = arguments.overrideWith(args))
  def map(function: Fragment => Fragment) = Fragments.create(fragments.map(function):_*)

  override def toString = fragments.mkString("\n")

  def specName = specStart.specName
  def name     = specStart.name

  lazy val specStart: SpecStart = SpecStart(specTitle.getOrElse(SpecName("")), arguments, linked)
  lazy val specEnd:   SpecEnd   = SpecEnd(specName)
}

/**
 * Utility methods for fragments
 */
object Fragments {

  def apply(t: SpecName): Fragments = Fragments().specTitleIs(t)
  /**
   * @return a Fragments object containing only a seq of Fragments.
   */
  def createList(fs: Fragment*) = Fragments(middle = fs)
  /**
   * @return a Fragments object, where the SpecStart might be provided by the passed fragments
   */
  def create(fs: Fragment*) = {
    fs match {
      case (s @ SpecStart(n, a, l)) +: rest :+ SpecEnd(_,_) => Fragments(Some(n), rest, a, l)
      case (s @ SpecStart(n, a, l)) +: rest                 => Fragments(Some(n), rest, a, l)
      case _                                                => createList(fs:_*)
    }
  }

  /** @return true if the Fragment is a Text */
  def isText: Function[Fragment, Boolean] = { case Text(_) => true; case _ => false }
  /** @return the text if the Fragment is a Text */
  def isSomeText: PartialFunction[Fragment, Text] = { case t @ Text(_) => t }
  /** @return true if the Fragment is an Example */
  def isExample: Function[Fragment, Boolean] = { case Example(_, _) => true; case _ => false }
  /** @return the example if the Fragment is an Example */
  def isAnExample: PartialFunction[Fragment, Example] = { case e @ Example(_,_) => e }
  /** @return true if the Fragment is a step */
  def isStep: Function[Fragment, Boolean] = { case Step(_,_) => true; case _ => false }
  /** @return true if the Fragment is a SpecStart or a SpecEnd */
  def isSpecStartOrEnd: Function[Fragment, Boolean] = { case SpecStart(_,_,_) | SpecEnd(_,_) => true; case _ => false }
  /** @return the spec start if the Fragment is a SpecStart */
  def isASpecStart: PartialFunction[Fragment, Fragment] = { case s @ SpecStart(_,_,_) => s }
  /** @return the spec end if the Fragment is a SpecEnd */
  def isASpecEnd: PartialFunction[Fragment, Fragment] = { case s @ SpecEnd(_,_) => s }
  /** @return true if the Fragment is a SpecStart */
  def isSpecStart: Function[Fragment, Boolean] = { case s @ SpecStart(_,_,_) => true; case _ => false }
  /** @return true if the Fragment is a SpecEnd */
  def isSpecEnd: Function[Fragment, Boolean] = { case s @ SpecEnd(_,_) => true; case _ => false }
  /** @return true if the Fragment is an Example or a Step */
  def isExampleOrStep: Function[Fragment, Boolean] = (f: Fragment) => isExample(f) || isStep(f)
  /** @return the step if the Fragment is a Step */
  def isAStep: PartialFunction[Fragment, Step] = { case s @ Step(_,_) => s }
  /** @return the action if the Fragment is an Actino */
  def isAnAction: PartialFunction[Fragment, Action] = { case a @ Action(_) => a }
  /** @return the step if the Fragment is a Br fragment */
  def isABr: PartialFunction[Fragment, Fragment] = { case br @ Br() => br }
  /** @return the step if the Fragment is an End fragment */
  def isAnEnd: PartialFunction[Fragment, Fragment] = { case e @ End() => e }

  /** @return a Fragments object with the appropriate name set on the SpecStart fragment */
  def withSpecName(fragments: Fragments, name: SpecName): Fragments = fragments.specTitleIs(name)
  
  /**
   * @return a Fragments object with the appropriate name set on the SpecStart fragment
   *
   * That name is derived from the specification structure name
   */
  def withSpecName(fragments: Fragments, s: SpecificationStructure): Fragments = withSpecName(fragments, SpecName(s))
  /**
   * @return a Fragments object with creation paths set on Examples and Actions
   *
   * The path of a Fragment is either:
   *
   *  - set at construction time when it comes from a mutable specification
   *  - its index in the sequence of fragments for an acceptance specification
   */
  def withCreationPaths(fragments: Fragments): Fragments = fragments.copy(middle = fragments.middle.zipWithIndex.map {
    case (e @ Example(_,_), i) => e.creationPathIs(AcceptanceCreationPath(Seq(i+1)))
    case (a @ Action(_), i)    => a.creationPathIs(AcceptanceCreationPath(Seq(i+1)))
    case (other, i)            => other
  })

  /**
   * Fragments can be added as a monoid
   */
  implicit def fragmentsIsMonoid = new Monoid[Fragments] {
    val zero = new Fragments()
    def append(s1: Fragments, s2: => Fragments) = s1 add s2
  }
}

/** encapsulation of the linking behaviour */
case class Linked(link: Option[HtmlLink] = None, seeOnly: Boolean = false, hidden: Boolean = false) {
  def isSeeOnlyLink = isLink && seeOnly
  def isIncludeLink = isLink && !seeOnly
  def isLink        = link.isDefined

  def urlIs(url: String)     = copy(link = link.map(_.urlIs(url)))
  def baseDirIs(dir: String) = copy(link = link.map(_.baseDirIs(dir)))

  def linkIs(htmlLink: HtmlLink) = copy(link = Some(htmlLink))
  def seeIs(htmlLink: HtmlLink)  = copy(link = Some(htmlLink), seeOnly = true)
  def hide                       = copy(hidden = true)

  def linkToString = link.map(l => ", link:"+l.toString+", seeOnly:"+seeOnly).getOrElse("")
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy