Last active
December 26, 2015 23:04
-
-
Save irenelfeng/61b72835af3e3600d60e to your computer and use it in GitHub Desktop.
GirlRising example of inheritance on Scala, creating abstract class Versions.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.girlrising.models.resources.resources | |
import reactivemongo.bson._ | |
import com.github.nscala_time.time.Imports._ | |
case class AssetVersion | |
( | |
id: Int, | |
timeCreated: DateTime, | |
notes: Option[String] = None, | |
owner: String, | |
url: Option[String] | |
) extends Version(id, timeCreated, notes, owner, url) | |
object AssetVersion{ | |
implicit object AssetVersionReader extends BSONDocumentReader[AssetVersion]{ | |
def read(doc: BSONDocument) = { | |
AssetVersion( | |
doc.getAs[Int]("id").get, | |
doc.getAs[BSONDateTime]("timeCreated").map(dt => new DateTime(dt.value)).get, | |
doc.getAs[String]("notes"), | |
doc.getAs[String]("owner").get, | |
doc.getAs[String]("url") | |
) | |
} | |
} | |
implicit object AssetVersionWriter extends BSONDocumentWriter[AssetVersion]{ | |
def write(version: AssetVersion) = { | |
BSONDocument( | |
"id" -> version.id, | |
"timeCreated" -> BSONDateTime(version.timeCreated.getMillis), | |
"notes" -> version.notes, | |
"url" -> version.url, | |
"owner" -> version.owner | |
) | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.girlrising | |
import akka.actor.{ActorSystem, Props} | |
import akka.io.IO | |
import spray.can.Http | |
import akka.pattern.ask | |
import akka.util.Timeout | |
import scala.concurrent.duration._ | |
import scala.concurrent.ExecutionContext.Implicits.global | |
import com.typesafe.config.ConfigFactory | |
import com.girlrising.services.MainServiceActor | |
import reactivemongo.api._ | |
import reactivemongo.core.nodeset.Authenticate | |
import reactivemongo.bson._ | |
object Boot extends App { | |
// load configuration | |
val conf = ConfigFactory.load() | |
// we need an ActorSystem to host our application in | |
implicit val system = ActorSystem("on-spray-can") | |
// create and start our service actor | |
val service = system.actorOf(Props[MainServiceActor], "demo-service") | |
val mongoConfig = conf.getConfig("mongolab") | |
val driver = new MongoDriver | |
val servers = List(mongoConfig.getString("url")) | |
val dbName = mongoConfig.getString("dbName") | |
val userName = mongoConfig.getString("userName") | |
val password = mongoConfig.getString("password") | |
val credentials = List(Authenticate(dbName, userName, password)) | |
val conOpts = MongoConnectionOptions(authMode = ScramSha1Authentication) | |
val connection = driver.connection(servers, options = conOpts, authentications = credentials) | |
val db = connection(dbName) | |
println("Bind") | |
implicit val timeout = Timeout(5.seconds) | |
// start a new HTTP server on port 8080 with our service actor as the handler | |
IO(Http) ? Http.Bind(service, interface = "localhost", port = 8080) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.girlrising.controllers.database | |
import scala.concurrent.{ExecutionContext, Future,Await} | |
import ExecutionContext.Implicits.global | |
import reactivemongo.api.MongoDriver | |
import reactivemongo.core.nodeset.Authenticate | |
import reactivemongo.bson.BSONDocument | |
import com.girlrising._ | |
import scala.util.{Try, Success, Failure} | |
import reactivemongo.bson._ | |
class CRUDController[T](collection: String) | |
(implicit reader: BSONDocument => T, | |
writer: T => BSONDocument) extends MongoController{ | |
//maybe add ability to increment, fold in pre-db call and post db-call events | |
def add(value: T) = { | |
AddToDatabase[T](collection,value) | |
} | |
def edit(value: T, id: String) = { | |
val selector = BSONDocument("_id" -> id) | |
EditInDatabase[T](collection,selector,value) | |
} | |
def remove(id: String) = { | |
RemoveFromDatabase(collection,BSONDocument("_id" -> id)) | |
} | |
def all(map: Map[String,String]) = { | |
AllFromDatabase[T](collection,mapToFilter(map)) | |
} | |
def one(id: String) = { | |
val selector = BSONDocument("_id" -> id) | |
OneFromDatabase[T](collection,selector) | |
} | |
def mapToFilter(map:Map[String,String]): BSONDocument = { | |
val filters = List(map.get("bucket").map(category => BSONDocument("bucket" -> category)), | |
map.get("search").map(search => BSONDocument( | |
"$or" -> BSONArray( | |
BSONDocument( | |
"name" -> BSONRegex(search, "i") | |
), | |
BSONDocument( | |
"url" -> BSONRegex(search, "i") | |
), | |
BSONDocument( | |
"description" -> BSONRegex(search, "i") | |
), | |
BSONDocument( | |
"owner" -> BSONRegex(search, "i") | |
) | |
) | |
))).flatten | |
filters.fold(BSONDocument())((acc,filter) => acc ++ filter) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.girlrising.services.generic | |
import reactivemongo.bson._ | |
import spray.json._ | |
import DefaultJsonProtocol._ | |
import com.girlrising._ | |
import com.girlrising.controllers.database._ | |
import com.girlrising.models._ | |
import spray.routing.HttpService | |
import com.girlrising.controllers.authentication._ | |
/* | |
To follow conventions: | |
GET /value?params | |
POST /value | |
GET /value/id | |
PUT /value/id | |
DELETE /value/id | |
*/ | |
//abstract class GenericServiceClass[T](val database:String)(implicit val BSONreader: BSONDocument => T, val BSONwriter: T => BSONDocument, val JSONWriter: JsonFormat[T]) extends GenericService[T]{} | |
class GenericService[T,U](database: String)(implicit a: akka.actor.ActorRefFactory, | |
BSONreader: BSONDocument => T, | |
BSONwriter: T => BSONDocument, | |
JSONWriter: JsonFormat[T], | |
idWriter: U => BSONValue, | |
JSONIdWriter: JsonFormat[U]) extends HttpService{ | |
def actorRefFactory = a | |
val controller = new CRUDController[T](database) | |
import scala.concurrent.ExecutionContext.Implicits.global | |
import spray.httpx.SprayJsonSupport._ | |
import com.girlrising.models.serializers._ | |
val route = | |
parameterMap {params => | |
onSuccess(checkSession(params.getOrElse("email","none"),params.getOrElse("token","none"))){ email => | |
path(database){ | |
get{ | |
parameterMap {params => | |
complete{ | |
print("getting " + params) | |
controller.all(params) | |
} | |
} | |
} ~ | |
post{ | |
decompressRequest() { | |
entity(as[String]) { value => // transfer to newly spawned actor | |
detach() { | |
complete{ | |
println("\n" + value.parseJson + "\n") | |
controller.add(value.parseJson.convertTo[T]).map(_.toJson.asJsObject) | |
} | |
} | |
} | |
} | |
} ~ | |
options{ | |
complete{ | |
"option" | |
} | |
} | |
} ~ | |
pathPrefix(database / Segment) { id => | |
get{ | |
complete{ | |
println("the ID is "+id) | |
controller.one(id).map(_.toJson.asJsObject) | |
} | |
} ~ | |
options{ | |
complete{ | |
"option" | |
} | |
} ~ | |
put{ | |
entity(as[String]) { model => | |
complete{ | |
println("the model is "+model) | |
controller.edit(model.parseJson.convertTo[T],id).map(_.toJson.asJsObject) | |
} | |
} | |
} ~ | |
delete{ | |
complete{ | |
println("remove id"+id) | |
controller.remove(id).map(future => println(future)) | |
controller.remove(id) | |
} | |
} | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.girlrising.services | |
import akka.actor.Actor | |
import spray.routing._ | |
import spray.http._ | |
import MediaTypes._ | |
import com.girlrising.services.categories._ | |
import com.girlrising.services.resources._ | |
import spray.routing.authentication._ //basic auth | |
import scala.concurrent._ | |
import ExecutionContext.Implicits.global //for futures | |
// we don't implement our route structure directly in the service actor because | |
// we want to be able to test it independently, without having to spin up an actor | |
class MainServiceActor extends Actor | |
with CustomExceptions | |
with AuthenticationController | |
with AssetService | |
with StatService | |
with LoginService | |
with AssetCategoryService | |
with StatCategoryService | |
{ | |
// the HttpService trait defines only one abstract member, which | |
// connects the services environment to the enclosing actor or test | |
def actorRefFactory = context | |
def categoryRoutes = assetCategoryRoute ~ statCategoryRoute | |
def resourceRoutes = assetRoute ~ statRoute ~ tallyRoute ~ storyRoute | |
def controllerRoutes = uploadRoute ~ redirectRoute ~ emailRoute ~ deleteRoute | |
// this actor only runs our route, but you could add | |
// other things here, like request stream processing | |
// or timeout handling | |
def receive = runRoute(categoryRoutes ~ resourceRoutes ~ controllerRoutes) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.girlrising.controllers.database | |
import scala.concurrent.{ExecutionContext, Future,Await} | |
import ExecutionContext.Implicits.global | |
import reactivemongo.api.MongoDriver | |
import reactivemongo.core.nodeset.Authenticate | |
import reactivemongo.bson.BSONDocument | |
import reactivemongo.api.commands.LastError | |
import reactivemongo.api.commands.WriteResult | |
import reactivemongo.core.errors._ | |
import reactivemongo.api.MongoConnectionOptions | |
import reactivemongo.api._ | |
import com.girlrising._ | |
import com.girlrising.models._ | |
import scala.concurrent.duration._ | |
import scala.util.{Try, Success, Failure} | |
import com.girlrising.Boot._ | |
trait MongoController{ | |
// connecting to database moved to Boot. | |
//get one object from collection | |
//define enter/exit into collection | |
def OneFromDatabase[T](collection: String, selector: BSONDocument, orelse: Option[T] = None)(implicit converter: BSONDocument => T): Future[T] = { | |
println("SELECTOR:"+BSONDocument.pretty(selector)) //debugging | |
println("Look in collection "+collection+":"+db(collection).find(selector).cursor) //debugging | |
db(collection).find(selector).cursor.headOption.map(value => value match { | |
case Some(result) => converter(result) | |
case None => orelse match { | |
case Some(other) => other | |
case None => throw TBDException("No selection") | |
} | |
} | |
) | |
} | |
//get list of all objects from collection, if none, just empty list | |
def AllFromDatabase[T](collection: String, selector: BSONDocument)(implicit converter: BSONDocument => T): Future[List[T]] = { | |
val list = db(collection).find(selector).cursor[BSONDocument].collect[List]() | |
println("Got database from" + collection) | |
list.map(value => value.map(value => converter(value))) | |
} | |
def AddToDatabase[T](collection: String, value: T)(implicit converter: T => BSONDocument, convertBack: BSONDocument => T): Future[T] = { | |
// println("Adding "+ value +" to collection "+collection) | |
db(collection).insert(converter(value)) | |
.map(_ => value) //onSuccess | |
.recover { | |
case e => { | |
if(e.getMessage.contains("duplicate key error index")) | |
throw DuplicateKeyException("duplicate!") | |
else throw TBDException("Unhandled Adding to Database Exception") | |
} | |
} | |
} | |
def RemoveFromDatabase(collection: String, selector: BSONDocument): Future[String] = { | |
db(collection).remove(selector).map( | |
_ => "success" | |
).recover { | |
case _ => "Not found" | |
} | |
} | |
def EditInDatabase[T](collection: String, selector: BSONDocument, value: T, BSONmod: Option[BSONDocument]=None)(implicit conversion: T => BSONDocument, convertBack: BSONDocument => T): Future[T] = { | |
BSONmod match { | |
case Some(modifier) => db(collection).update(selector,modifier).map(_ => value) | |
case None => db(collection).update(selector,BSONDocument("$set" -> conversion(value))).flatMap( | |
_ => OneFromDatabase[T](collection,selector,None) | |
) | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.girlrising.models.resources.resources | |
import reactivemongo.bson._ | |
import com.github.nscala_time.time.Imports._ | |
case class StatVersion | |
( | |
id: Int, | |
timeCreated: DateTime, | |
notes: Option[String], | |
owner: String, | |
url: Option[String], | |
stat: Double, | |
validUntil: Option[DateTime] | |
) extends Version(id, timeCreated, notes, owner, url) | |
object StatVersion{ | |
implicit object StatVersionReader extends BSONDocumentReader[StatVersion]{ | |
def read(doc: BSONDocument) = { | |
StatVersion( | |
doc.getAs[Int]("id").get, | |
doc.getAs[BSONDateTime]("timeCreated").map(dt => new DateTime(dt.value)).get, | |
doc.getAs[String]("notes"), | |
doc.getAs[String]("owner").get, | |
doc.getAs[String]("url"), | |
doc.getAs[Double]("stat").get, | |
doc.getAs[BSONDateTime]("validUntil").map(dt => new DateTime(dt.value)) | |
) | |
} | |
} | |
implicit object StatVersionWriter extends BSONDocumentWriter[StatVersion]{ | |
def write(version: StatVersion) = { | |
BSONDocument( | |
"id" -> version.id, | |
"timeCreated" -> BSONDateTime(version.timeCreated.getMillis), | |
"notes" -> version.notes, | |
"owner" -> version.owner, | |
"url" -> version.url, | |
"stat" -> version.stat, | |
"validUntil" -> version.validUntil.map(time => BSONDateTime(time.getMillis)) | |
) | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.girlrising.models.resources.resources | |
import reactivemongo.bson.{BSONDocumentWriter, BSONDocumentReader, BSONDocument, BSONDateTime, BSONReader, BSONValue} | |
import com.github.nscala_time.time.Imports._ | |
abstract class Version | |
( | |
id: Int, | |
timeCreated: DateTime, | |
// clicks: Int, | |
notes: Option[String], | |
owner: String, | |
url: Option[String] | |
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.girlrising.models.resources.resources | |
import reactivemongo.bson._ | |
import com.github.nscala_time.time.Imports._ | |
case class VersionedResource[T <: Version] | |
( | |
id: String, | |
name: String, | |
description: Option[String] = None, | |
versions: List[T], | |
bucket: Option[String] = None, | |
owner: Option[String] = None, | |
permissions: Option[List[String]] = None, | |
currentVersion:Int, | |
watch: Option[List[String]] = None | |
) | |
object VersionedResource{ | |
implicit def ResourceReader[T <: Version](implicit converter: BSONReader[ _ <: BSONValue, T]): BSONDocumentReader[VersionedResource[T]] = new BSONDocumentReader[VersionedResource[T]] | |
{ | |
def read(doc: BSONDocument): VersionedResource[T] = { | |
VersionedResource( | |
doc.getAs[String]("_id").get, | |
doc.getAs[String]("name").get, | |
doc.getAs[String]("description"), | |
doc.getAs[List[T]]("versions").get, | |
doc.getAs[String]("bucket"), | |
doc.getAs[String]("owner"), | |
doc.getAs[List[String]]("permissions"), | |
doc.getAs[Int]("currentVersion").get, | |
doc.getAs[List[String]]("watch") | |
) | |
} | |
} | |
implicit def ResourceWriter[T <: Version](implicit converter: BSONWriter[T, _ <: BSONValue]): BSONDocumentWriter[VersionedResource[T]] = new BSONDocumentWriter[VersionedResource[T]] | |
{ | |
def write(obj: VersionedResource[T]): BSONDocument = { | |
BSONDocument( | |
"_id" -> obj.id, | |
"name" -> obj.name, | |
"description" -> obj.description, | |
"versions" -> obj.versions, | |
"bucket" -> obj.bucket, | |
"owner" -> obj.owner, | |
"permissions" -> obj.permissions, | |
"currentVersion" -> obj.currentVersion, | |
"watch" -> obj.watch | |
) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
For class design look at the files titled "package com.girlrising.models" (4). For database operations on objects of class Version, look at controller (2) and service scala files (2, including Boot.scala).