Subscribed unsubscribe Subscribe Subscribe

Playのバージョンを比較するやつ書いた、が

Scala

Play はプロジェクトのバージョンと使用している Play コマンドのバージョンが食い違っていると警告が出るんですが、バージョンの比較が同じかどうかしか見ていないのにエラーメッセージが Update しろとかいうやつなので
https://github.com/playframework/Play20/blob/2.1.1/framework/src/sbt-plugin/src/main/scala/play/Project.scala#L23

  Option(System.getProperty("play.version")).map {
    case badVersion if badVersion != play.core.PlayVersion.current => {
      println(
        Colors.red("""
|This project uses Play %s!
|Update the Play sbt-plugin version to %s (usually in project/plugins.sbt)
""".stripMargin.format(play.core.PlayVersion.current, badVersion))
      )
    }
    case _ =>
  }


こういう風に、ん?なエラーメッセージになることがあります。


で、気持ち悪いので直そうと思ってバージョンを比較するのを書いたんですが、
よく考えたらエラーメッセージをちょっと直せばいいだけじゃん。このコード全然いらねー!バージョンパースするのにパーサコンビネータとか持ち出して自分ばっかじゃねーのってことに気づきました。プログラム書く前に必要かどうかちゃんと考えようって話ですね。


せっかく書いたので貼っておきますね。。。
そういえば、Ordering はモノイドだからうんぬんって話がありましたけど、Scalaz ではなく Scala の場合はまあ Ordering.by で Tuple の比較とかに落とすのがシンプルでいいのかなと思いました。

import scala.util.parsing.combinator._

class PlayVersionException(val message: String) extends Exception(message)


sealed abstract class VersionMilestone(val value: Int)
case object Alpha extends VersionMilestone(0)
case object Beta extends VersionMilestone(1)
case class RC(number: Int) extends VersionMilestone(2)
case object Regular extends VersionMilestone(3)

case class PlayVersion(major: Int, middle: Int, minor: Int, versionMilestone: VersionMilestone) {

  def >(x: PlayVersion)(implicit ord: Ordering[PlayVersion]): Boolean = {
    if (implicitly[Ordering[PlayVersion]].lteq(this, x)) false else true
  }

  def <(x: PlayVersion)(implicit ord: Ordering[PlayVersion]): Boolean = {
    ! this.>(x)(ord)
  }

}

object PlayVersionParser extends RegexParsers {
  def number = """[0-9]+""".r
  def major = number ^^ { _.toInt }
  def middle = number ^^ { _.toInt }
  def minor = number ^^ { _.toInt }
  def rc: Parser[VersionMilestone] = "-RC" ~> number ^^ { n => RC(n.toInt) }
  def beta: Parser[VersionMilestone] = "-beta" ^^ { _ => Beta }
  def alpha: Parser[VersionMilestone] = "-alpha" ^^ { _ => Alpha }
  def earlyVersion: Parser[VersionMilestone] = rc | beta | alpha

  def version: Parser[PlayVersion] = major ~ ("." ~> middle) ~ opt("." ~> minor) ~ opt(earlyVersion) ^^ {
    case major ~ middle ~ minor ~ earlyVersion => PlayVersion(major, middle, minor.getOrElse(0), earlyVersion.getOrElse(Regular))
  }
  def parse(in: String): PlayVersion = parseAll(version, in) match {
    case Success(result, _) => result
    case failure : NoSuccess => throw new PlayVersionException("Unexpected version naming convention.")
  }

}

implicit val versionMilestoneOrdering = new Ordering[VersionMilestone] {
  def compare(x: VersionMilestone, y: VersionMilestone): Int = {
    (x, y) match {
      case (x, y) if x == y => 0
      case (RC(i), RC(j)) => i - j
      case (x, y) => x.value - y.value
    }
  }
}

implicit val playVersionOrdering: Ordering[PlayVersion] = {
  Ordering.by[PlayVersion, (Int, Int, Int, VersionMilestone)] { v: PlayVersion =>
    (v.major, v.middle, v.minor, v.versionMilestone)
  }
}

object PlayVersion {
  def apply(versionString: String): PlayVersion = {
    PlayVersionParser.parse(versionString)
  }
}

assert(PlayVersion("2.1.1") > PlayVersion("2.0"))
assert(PlayVersion("2.1.1") > PlayVersion("2.1.0"))
assert(PlayVersion("2.1.1") > PlayVersion("2.1.1-RC1"))
assert(PlayVersion("2.1.1-RC1") == PlayVersion("2.1.1-RC1"))
assert(PlayVersion("2.1.1-RC2") > PlayVersion("2.1.1-RC1"))
assert(PlayVersion("2.1.1-RC2") > PlayVersion("2.1.1-beta"))
assert(PlayVersion("2.1.1-alpha") < PlayVersion("2.1.1-beta"))