diff --git a/.travis.yml b/.travis.yml index 9efc032bb..d8d486961 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,10 +9,8 @@ jdk: before_install: - sudo ./.travis_scripts/beforeInstall.sh $TRAVIS_SCALA_VERSION $MONGO_SSL script: ./.travis_scripts/validate.sh +after_success: ./.travis_scripts/afterSuccess.sh env: - global: - - secure: AHBkdpd7+WwsSlyeDSt3dyM+hadpjbVDuImvWpWTOBdgCwUI6aOSUc0bwHNmEMrmZ/tY2bPEOT8lebPL7av4kBYYEk/K/lORHLWinrB8THjRwvi4/mtzTBs2Wdo3gj8BNuThEJhTwFtVF/HiuxG0yyIODFb8Ubp7Vp5ySgpdCto= - - secure: mL+CDHYec/SQXPjF4+EPHVTYkzFScTtlZWpt+4u2puZoG7aYRvsvSAyL5cA9AOPYNlnamZjelu5zRHb9AWfZwA6EAcBOdA836isdqS2Lm9Y2AIl/iu0ZRDJoUkIG2eWu8nCU01ANoV/qpqj4Zw8K1w5si8xaxPjLgmMcPM2tHrY= matrix: - MONGO_SSL=true - MONGO_SSL=false \ No newline at end of file diff --git a/.travis_scripts/afterSuccess.sh b/.travis_scripts/afterSuccess.sh new file mode 100755 index 000000000..d46c84eee --- /dev/null +++ b/.travis_scripts/afterSuccess.sh @@ -0,0 +1,22 @@ +#! /bin/sh + +SCRIPT_DIR=`dirname $0` +JAVA_COMPAT=`javac -version 2>&1 | grep 1.6 | wc -l` +SCALA_COMPAT=`echo "$TRAVIS_SCALA_VERSION" | grep "2.11" | wc -l` + +# TRAVIS_BRANCH +# -o "$TRAVIS_BRANCH" != "master" +if [ "$MONGO_SSL" = "false" -o "$SONATYPE_USER" = "" -o "$SONATYPE_PASS" = "" -o $JAVA_COMPAT -ne 1 -o $SCALA_COMPAT -ne 1 ]; then + echo "skip the snapshot publication: $JAVA_COMPAT $SCALA_COMPAT" + exit 0 +fi + +cd "$SCRIPT_DIR/.." + +export PUBLISH_REPO_NAME="Sonatype Nexus Repository Manager" +export PUBLISH_REPO_ID="oss.sonatype.org" +export PUBLISH_REPO_URL=https://oss.sonatype.org/content/repositories/snapshots/ +export PUBLISH_USER="$SONATYPE_USER" +export PUBLISH_PASS="$SONATYPE_PASS" + +sbt '+publish' diff --git a/.travis_scripts/publishSnapshots.sh b/.travis_scripts/publishSnapshots.sh deleted file mode 100755 index 93aa9c222..000000000 --- a/.travis_scripts/publishSnapshots.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -echo -e "Starting publish to Sonatype...\n" - -sbt ";project ReactiveMongo-BSON ;+publishSnapshotsFromTravis ;project ReactiveMongo ;+publishSnapshotsFromTravis ;project ReactiveMongo-BSON-Macros; +publishSnapshotsFromTravis" -RETVAL=$? - -if [ $RETVAL -eq 0 ]; then - echo "Snapshots successfully published" -else - echo "Error while publishing snapshots" - exit 1 -fi diff --git a/bson/src/main/scala/handlers.scala b/bson/src/main/scala/handlers.scala index 7271bdd03..0486bc3ad 100644 --- a/bson/src/main/scala/handlers.scala +++ b/bson/src/main/scala/handlers.scala @@ -99,18 +99,28 @@ class VariantBSONReaderWrapper[B <: BSONValue, T](reader: VariantBSONReader[B, T def read(b: B) = reader.read(b) } -trait BSONHandler[B <: BSONValue, T] extends BSONReader[B, T] with BSONWriter[T, B] { self => - def as[R](to: T => R, from: R => T): BSONHandler[B, R] = new BSONHandler[B, R] { - def write(r: R) = self.write(from(r)) - def read(b: B) = to(self.read(b)) - } +trait BSONHandler[B <: BSONValue, T] extends BSONReader[B, T] with BSONWriter[T, B] { + def as[R](to: T => R, from: R => T): BSONHandler[B, R] = + new BSONHandler.MappedHandler(this, to, from) } object BSONHandler { - def apply[B <: BSONValue, T](read: B => T, write: T => B) = new BSONHandler[B, T] { - override def read(x: B): T = read(x) - override def write(x: T): B = write(x) + private[bson] class MappedHandler[B <: BSONValue, T, U]( + parent: BSONHandler[B, T], + to: T => U, + from: U => T) extends BSONHandler[B, U] { + def write(u: U) = parent.write(from(u)) + def read(b: B) = to(parent.read(b)) + } + + private[bson] class DefaultHandler[B <: BSONValue, T](r: B => T, w: T => B) + extends BSONHandler[B, T] { + def read(x: B): T = r(x) + def write(x: T): B = w(x) } + + def apply[B <: BSONValue, T](read: B => T, write: T => B): BSONHandler[B, T] = + new DefaultHandler(read, write) } trait DefaultBSONHandlers { @@ -141,6 +151,13 @@ trait DefaultBSONHandlers { def write(xs: Array[Byte]) = BSONBinary(xs, Subtype.GenericBinarySubtype) } + import java.util.Date + + implicit object BSONDateTimeHandler extends BSONHandler[BSONDateTime, Date] { + def read(bson: BSONDateTime) = new Date(bson.value) + def write(date: Date) = BSONDateTime(date.getTime) + } + // Typeclasses Handlers import BSONNumberLike._ import BSONBooleanLike._ @@ -151,6 +168,7 @@ trait DefaultBSONHandlers { case long: BSONLong => BSONLongNumberLike(long) case double: BSONDouble => BSONDoubleNumberLike(double) case dt: BSONDateTime => BSONDateTimeNumberLike(dt) + case ts: BSONTimestamp => BSONTimestampNumberLike(ts) case _ => throw new UnsupportedOperationException() } } @@ -241,16 +259,6 @@ trait DefaultBSONHandlers { def write(b: BSONJavaScript) = b } - /*// Generic Handlers - class BSONValueIdentity[B <: BSONValue] extends BSONWriter[B, B] with BSONReader[B, B] { - def write(b: B): B = b - def readTry(b: B): Try[B] = Try(b.asInstanceOf[B]) - } - - implicit def findIdentityWriter[B <: BSONValue]: BSONWriter[B, B] = /*new VariantBSONWriterWrapper(*/new BSONValueIdentity[B]//) - - implicit def findIdentityReader[B <: BSONValue]: BSONReader[B, B] = /*new VariantBSONReaderWrapper(*/new BSONValueIdentity[B]//)*/ - implicit def findWriter[T](implicit writer: VariantBSONWriter[T, _ <: BSONValue]): BSONWriter[T, _ <: BSONValue] = new VariantBSONWriterWrapper(writer) diff --git a/bson/src/main/scala/typeclasses.scala b/bson/src/main/scala/typeclasses.scala index 60f3e5053..ca07a3f7b 100644 --- a/bson/src/main/scala/typeclasses.scala +++ b/bson/src/main/scala/typeclasses.scala @@ -73,6 +73,9 @@ object BSONNumberLike { implicit class BSONDateTimeNumberLike(private[bson] val underlying: BSONDateTime) extends BSONNumberLikeClass[BSONDateTime] with IsNumeric[Long] { private[bson] lazy val number = ExtendedNumeric(underlying.value) } + implicit class BSONTimestampNumberLike(private[bson] val underlying: BSONTimestamp) extends BSONNumberLikeClass[BSONTimestamp] with IsNumeric[Long] { + private[bson] lazy val number = ExtendedNumeric(underlying.value * 1000L) + } } /** diff --git a/bson/src/main/scala/types.scala b/bson/src/main/scala/types.scala index d22e6a2a4..d752a1436 100644 --- a/bson/src/main/scala/types.scala +++ b/bson/src/main/scala/types.scala @@ -556,6 +556,18 @@ case class BSONTimestamp(value: Long) extends BSONValue { val ordinal = value.toInt } +/** Timestamp companion */ +object BSONTimestamp { + /** + * Returns the timestamp corresponding to the given `time` and `ordinal`. + * + * @param time the 32bits time value (seconds since the Unix epoch) + * @param ordinal an incrementing ordinal for operations within a same second + */ + def apply(time: Long, ordinal: Int): BSONTimestamp = + BSONTimestamp((time << 32) ^ ordinal) +} + /** BSON Long value */ case class BSONLong(value: Long) extends BSONValue { val code = 0x12.toByte } diff --git a/bson/src/test/scala/Handlers.scala b/bson/src/test/scala/Handlers.scala index de2a057ef..e43739299 100644 --- a/bson/src/test/scala/Handlers.scala +++ b/bson/src/test/scala/Handlers.scala @@ -14,6 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import java.util.Date import org.specs2.mutable._ import reactivemongo.bson._ import scala.util._ @@ -100,7 +101,7 @@ class Handlers extends Specification { array.get(1).get mustEqual BSONInteger(1) array.getAs[Int](1) mustEqual Some(1) } - "get bsondocunment at index 3" in { + "get bsondocument at index 3" in { val maybedoc = array.getAs[BSONDocument](3) maybedoc.isDefined mustEqual true val maybename = maybedoc.get.getAs[String]("name") @@ -122,6 +123,33 @@ class Handlers extends Specification { } } + "BSONDateTime" should { + val time = System.currentTimeMillis() + val bson = BSONDateTime(time) + val date = new Date(time) + val handler = implicitly[BSONHandler[BSONDateTime, Date]] + + "be read as date" in { + handler.read(bson) must_== date + } + + "be written from a date" in { + handler.write(date) must_== bson + } + } + + "BSONNumberLike" should { + val reader = implicitly[BSONReader[BSONValue, BSONNumberLike]] + + "read BSONTimestamp" in { + val time = System.currentTimeMillis() + val num = time / 1000L + val bson = BSONTimestamp(num) + + reader.readOpt(bson).map(_.toLong) must beSome(num * 1000L) + } + } + case class Album( name: String, releaseYear: Int, diff --git a/bson/src/test/scala/Types.scala b/bson/src/test/scala/Types.scala index 9cc5f3167..1563425e9 100644 --- a/bson/src/test/scala/Types.scala +++ b/bson/src/test/scala/Types.scala @@ -87,5 +87,9 @@ class Types extends Specification { ts.time aka "time" must_== 1412180887L) and ( ts.ordinal aka "ordinal" must_== 6) } + + "be created from the time and ordinal values" in { + BSONTimestamp(1412180887L, 6) must_== BSONTimestamp(6065270725701271558L) + } } } diff --git a/project/ReactiveMongo.scala b/project/ReactiveMongo.scala index 2ad211679..ad61e133a 100644 --- a/project/ReactiveMongo.scala +++ b/project/ReactiveMongo.scala @@ -3,7 +3,7 @@ import sbt.Keys._ import scala.language.postfixOps object BuildSettings { - val buildVersion = "0.11.5" + val buildVersion = "0.11.6" val filter = { (ms: Seq[(File, String)]) => ms filter { @@ -28,25 +28,24 @@ object BuildSettings { mappings in (Compile, packageBin) ~= filter, mappings in (Compile, packageSrc) ~= filter, mappings in (Compile, packageDoc) ~= filter) ++ - Publish.settings ++ Travis.settings ++ Format.settings + Publish.settings ++ Format.settings } object Publish { + @inline def env(n: String): String = sys.env.get(n).getOrElse(n) - def targetRepository: Def.Initialize[Option[Resolver]] = Def.setting { - val nexus = "https://oss.sonatype.org/" - val snapshotsR = "snapshots" at nexus + "content/repositories/snapshots" - val releasesR = "releases" at nexus + "service/local/staging/deploy/maven2" - val resolver = if (isSnapshot.value) snapshotsR else releasesR - Some(resolver) - } + private val repoName = env("PUBLISH_REPO_NAME") + private val repoUrl = env("PUBLISH_REPO_URL") lazy val settings = Seq( publishMavenStyle := true, - publishTo := targetRepository.value, publishArtifact in Test := false, + publishTo := Some(repoUrl).map(repoName at _), + credentials += Credentials(repoName, env("PUBLISH_REPO_ID"), + env("PUBLISH_USER"), env("PUBLISH_PASS")), pomIncludeRepository := { _ => false }, - licenses := Seq("Apache 2.0" -> url("http://www.apache.org/licenses/LICENSE-2.0")), + licenses := Seq("Apache 2.0" -> + url("http://www.apache.org/licenses/LICENSE-2.0")), homepage := Some(url("http://reactivemongo.org")), pomExtra := ( @@ -123,7 +122,7 @@ object Resolvers { } object Dependencies { - val netty = "io.netty" % "netty" % "3.9.4.Final" cross CrossVersion.Disabled + val netty = "io.netty" % "netty" % "3.10.4.Final" cross CrossVersion.Disabled val akkaActor = "com.typesafe.akka" %% "akka-actor" % "2.3.6" @@ -195,51 +194,3 @@ object ReactiveMongoBuild extends Build { settings(libraryDependencies += specs). dependsOn(bson) } - -object Travis { - val travisSnapshotBranches = - SettingKey[Seq[String]]("branches that can be published on sonatype") - - val travisCommand = Command.command("publishSnapshotsFromTravis") { state => - val extracted = Project extract state - import extracted._ - import scala.util.Properties.isJavaAtLeast - - val thisRef = extracted.get(thisProjectRef) - - val isSnapshot = getOpt(version).exists(_.endsWith("SNAPSHOT")) - val isTravisEnabled = sys.env.get("TRAVIS").exists(_ == "true") - val isNotPR = sys.env.get("TRAVIS_PULL_REQUEST").exists(_ == "false") - val isBranchAcceptable = sys.env.get("TRAVIS_BRANCH").exists(branch => getOpt(travisSnapshotBranches).exists(_.contains(branch))) - val isJavaVersion = !isJavaAtLeast("1.7") - - if (isSnapshot && isTravisEnabled && isNotPR && isBranchAcceptable) { - println(s"publishing $thisRef from travis...") - - val newState = append( - Seq( - publishTo := Some("Sonatype Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots/"), - credentials := Seq(Credentials( - "Sonatype Nexus Repository Manager", - "oss.sonatype.org", - sys.env.get("SONATYPE_USER").getOrElse(throw new RuntimeException("no SONATYPE_USER defined")), - sys.env.get("SONATYPE_PASSWORD").getOrElse(throw new RuntimeException("no SONATYPE_PASSWORD defined")) - ))), - state - ) - - runTask(publish in thisRef, newState) - - println(s"published $thisRef from travis") - } else { - println(s"not publishing $thisRef to Sonatype: isSnapshot=$isSnapshot, isTravisEnabled=$isTravisEnabled, isNotPR=$isNotPR, isBranchAcceptable=$isBranchAcceptable, javaVersionLessThen_1_7=$isJavaVersion") - } - - state - } - - val settings = Seq( - Travis.travisSnapshotBranches := Seq("master"), - commands += Travis.travisCommand) - -}