Added akka-sample-ants as a sample showcasing STM and Transactors
This commit is contained in:
parent
3aaaf94a7f
commit
d51c82047c
7 changed files with 338 additions and 2 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,5 +1,6 @@
|
||||||
*~
|
*~
|
||||||
*#
|
*#
|
||||||
|
src_managed
|
||||||
project/plugins/project/
|
project/plugins/project/
|
||||||
project/boot/*
|
project/boot/*
|
||||||
*/project/build/target
|
*/project/build/target
|
||||||
|
|
|
||||||
45
akka-samples/akka-sample-ants/README.md
Normal file
45
akka-samples/akka-sample-ants/README.md
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
Ants
|
||||||
|
====
|
||||||
|
|
||||||
|
Ants is written by Peter Vlugter.
|
||||||
|
|
||||||
|
Ants is based on the Clojure [ants simulation][ants.clj] by Rich Hickey, and ported to Scala using [Akka][akka] and [Spde][spde].
|
||||||
|
|
||||||
|
[ants.clj]:http://clojure.googlegroups.com/web/ants.clj
|
||||||
|
[akka]:http://akkasource.org
|
||||||
|
[spde]:http://technically.us/spde/
|
||||||
|
|
||||||
|
|
||||||
|
Requirements
|
||||||
|
------------
|
||||||
|
|
||||||
|
To build and run Ants you need [Simple Build Tool][sbt] (sbt).
|
||||||
|
|
||||||
|
[sbt]: http://code.google.com/p/simple-build-tool/
|
||||||
|
|
||||||
|
|
||||||
|
Running
|
||||||
|
-------
|
||||||
|
|
||||||
|
First time, 'sbt update' to get dependencies, then to run Ants use 'sbt run'.
|
||||||
|
Here is an example. First type 'sbt' to start SBT interactively, the run 'update' and 'run':
|
||||||
|
|
||||||
|
% sbt
|
||||||
|
> update
|
||||||
|
> run
|
||||||
|
|
||||||
|
|
||||||
|
Notice
|
||||||
|
------
|
||||||
|
|
||||||
|
Ants is based on the Clojure ants simulation by Rich Hickey.
|
||||||
|
|
||||||
|
Copyright (c) Rich Hickey. All rights reserved.
|
||||||
|
The use and distribution terms for this software are covered by the
|
||||||
|
Common Public License 1.0 ([http://opensource.org/licenses/cpl1.0.php][cpl])
|
||||||
|
which can be found in the file cpl.txt at the root of this distribution.
|
||||||
|
By using this software in any fashion, you are agreeing to be bound by
|
||||||
|
the terms of this license.
|
||||||
|
You must not remove this notice, or any other, from this software.
|
||||||
|
|
||||||
|
[cpl]: http://opensource.org/licenses/cpl1.0.php
|
||||||
20
akka-samples/akka-sample-ants/src/main/resources/akka.conf
Normal file
20
akka-samples/akka-sample-ants/src/main/resources/akka.conf
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
<log>
|
||||||
|
level = "off"
|
||||||
|
console = off
|
||||||
|
</log>
|
||||||
|
|
||||||
|
<akka>
|
||||||
|
version = "0.9"
|
||||||
|
|
||||||
|
<actor>
|
||||||
|
timeout = 5000
|
||||||
|
serialize-messages = off
|
||||||
|
</actor>
|
||||||
|
|
||||||
|
<stm>
|
||||||
|
service = on
|
||||||
|
fair = on
|
||||||
|
max-nr-of-retries = 10
|
||||||
|
timeout = 1000
|
||||||
|
</stm>
|
||||||
|
</akka>
|
||||||
207
akka-samples/akka-sample-ants/src/main/scala/Ants.scala
Normal file
207
akka-samples/akka-sample-ants/src/main/scala/Ants.scala
Normal file
|
|
@ -0,0 +1,207 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2009-2010 Scalable Solutions AB <http://scalablesolutions.se>
|
||||||
|
*/
|
||||||
|
|
||||||
|
package sample.ants
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
import scala.util.Random.{nextInt => randomInt}
|
||||||
|
import se.scalablesolutions.akka
|
||||||
|
import akka.actor.{ActorRef, Transactor, Scheduler}
|
||||||
|
import akka.actor.Actor.actorOf
|
||||||
|
import akka.stm.{Vector => _, _}
|
||||||
|
import akka.stm.Ref.Ref
|
||||||
|
import akka.stm.Transaction.Local._
|
||||||
|
|
||||||
|
object Config {
|
||||||
|
val Dim = 80 // dimensions of square world
|
||||||
|
val AntsSqrt = 7 // number of ants = AntsSqrt^2
|
||||||
|
val FoodPlaces = 35 // number of places with food
|
||||||
|
val FoodRange = 100 // range of amount of food at a place
|
||||||
|
val PherScale = 10 // scale factor for pheromone drawing
|
||||||
|
val AntMillis = 100 // how often an ant behaves (milliseconds)
|
||||||
|
val EvapMillis = 1000 // how often pheromone evaporation occurs (milliseconds)
|
||||||
|
val EvapRate = 0.99f // pheromone evaporation rate
|
||||||
|
val StartDelay = 1000 // delay before everything kicks off (milliseconds)
|
||||||
|
}
|
||||||
|
|
||||||
|
case class Ant(dir: Int, food: Boolean = false) {
|
||||||
|
def turn(i: Int) = copy(dir = Util.dirBound(dir + i))
|
||||||
|
def turnAround = turn(4)
|
||||||
|
def pickUp = copy(food = true)
|
||||||
|
def dropOff = copy(food = false)
|
||||||
|
}
|
||||||
|
|
||||||
|
case class Cell(food: Int = 0, pher: Float = 0, ant: Option[Ant] = None, home: Boolean = false) {
|
||||||
|
def addFood(i: Int) = copy(food = food + i)
|
||||||
|
def addPher(x: Float) = copy(pher = pher + x)
|
||||||
|
def alterPher(f: Float => Float) = copy(pher = f(pher))
|
||||||
|
def putAnt(antOpt: Option[Ant]) = copy(ant = antOpt)
|
||||||
|
def makeHome = copy(home = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
object EmptyCell extends Cell
|
||||||
|
|
||||||
|
class Place(initCell: Cell = EmptyCell) extends Ref(Some(initCell)) {
|
||||||
|
def cell: Cell = get.get
|
||||||
|
def food: Int = cell.food
|
||||||
|
def food(i: Int) = alter(_.addFood(i))
|
||||||
|
def hasFood = food > 0
|
||||||
|
def pher: Float = cell.pher
|
||||||
|
def pher(f: Float => Float) = alter(_.alterPher(f))
|
||||||
|
def trail = alter(_.addPher(1))
|
||||||
|
def ant: Option[Ant] = cell.ant
|
||||||
|
def ant(f: Ant => Ant): Cell = alter(_.putAnt(ant map f))
|
||||||
|
def enter(antOpt: Option[Ant]): Cell = alter(_.putAnt(antOpt))
|
||||||
|
def enter(ant: Ant): Cell = enter(Some(ant))
|
||||||
|
def leave = enter(None)
|
||||||
|
def occupied: Boolean = ant.isDefined
|
||||||
|
def makeHome = alter(_.makeHome)
|
||||||
|
def home: Boolean = cell.home
|
||||||
|
}
|
||||||
|
|
||||||
|
object World {
|
||||||
|
import Config._
|
||||||
|
|
||||||
|
val homeOff = Dim / 4
|
||||||
|
lazy val places = Vector.fill(Dim, Dim)(new Place)
|
||||||
|
lazy val ants = setup
|
||||||
|
lazy val evaporator = actorOf[Evaporator].start
|
||||||
|
|
||||||
|
def place(loc: (Int, Int)) = places(loc._1)(loc._2)
|
||||||
|
|
||||||
|
private def setup = atomic {
|
||||||
|
for (i <- 1 to FoodPlaces) {
|
||||||
|
place(randomInt(Dim), randomInt(Dim)) food (randomInt(FoodRange))
|
||||||
|
}
|
||||||
|
val homeRange = homeOff until (AntsSqrt + homeOff)
|
||||||
|
for (x <- homeRange; y <- homeRange) yield {
|
||||||
|
place(x, y).makeHome
|
||||||
|
place(x, y) enter Ant(randomInt(8))
|
||||||
|
actorOf(new AntActor(x, y)).start
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def start = {
|
||||||
|
ants foreach (pingEvery(_, AntMillis))
|
||||||
|
pingEvery(evaporator, EvapMillis)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def pingEvery(actor: ActorRef, millis: Long) =
|
||||||
|
Scheduler.schedule(actor, "ping", Config.StartDelay, millis, TimeUnit.MILLISECONDS)
|
||||||
|
}
|
||||||
|
|
||||||
|
object Util {
|
||||||
|
import Config._
|
||||||
|
|
||||||
|
def bound(b: Int, n: Int) = {
|
||||||
|
val x = n % b
|
||||||
|
if (x < 0) x + b else x
|
||||||
|
}
|
||||||
|
|
||||||
|
def dirBound(n: Int) = bound(8, n)
|
||||||
|
def dimBound(n: Int) = bound(Dim, n)
|
||||||
|
|
||||||
|
val dirDelta = Map(0 -> (0, -1), 1 -> (1, -1), 2 -> (1, 0), 3 -> (1, 1),
|
||||||
|
4 -> (0, 1), 5 -> (-1, 1), 6 -> (-1, 0), 7 -> (-1, -1))
|
||||||
|
def deltaLoc(x: Int, y: Int, dir: Int) = {
|
||||||
|
val (dx, dy) = dirDelta(dirBound(dir))
|
||||||
|
(dimBound(x + dx), dimBound(y + dy))
|
||||||
|
}
|
||||||
|
|
||||||
|
def rankBy[A, B: Ordering](xs: Seq[A], f: A => B) = Map(xs.sortBy(f).zip(Stream from 1): _*)
|
||||||
|
|
||||||
|
def roulette(slices: Seq[Int]) = {
|
||||||
|
val total = slices.sum
|
||||||
|
val r = randomInt(total)
|
||||||
|
var i, sum = 0
|
||||||
|
while ((sum + slices(i)) <= r) {
|
||||||
|
sum += slices(i)
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
trait WorldActor extends Transactor {
|
||||||
|
def act
|
||||||
|
def receive = { case "ping" => act }
|
||||||
|
}
|
||||||
|
|
||||||
|
class AntActor(initLoc: (Int, Int)) extends WorldActor {
|
||||||
|
import World._
|
||||||
|
import Util._
|
||||||
|
|
||||||
|
val locRef = Ref(initLoc)
|
||||||
|
val homing = (p: Place) => p.pher + (100 * (if (p.home) 0 else 1))
|
||||||
|
val foraging = (p: Place) => p.pher + p.food
|
||||||
|
|
||||||
|
def loc = locRef.get.getOrElse(initLoc)
|
||||||
|
def newLoc(l: (Int, Int)) = locRef swap l
|
||||||
|
|
||||||
|
def act = {
|
||||||
|
val (x, y) = loc
|
||||||
|
val current = place(x, y)
|
||||||
|
for (ant <- current.ant) {
|
||||||
|
val ahead = place(deltaLoc(x, y, ant.dir))
|
||||||
|
if (ant.food) { // homing
|
||||||
|
if (current.home) dropFood
|
||||||
|
else if (ahead.home && !ahead.occupied) move
|
||||||
|
else random(homing)
|
||||||
|
} else { // foraging
|
||||||
|
if (!current.home && current.hasFood) pickUpFood
|
||||||
|
else if (!ahead.home && ahead.hasFood && !ahead.occupied) move
|
||||||
|
else random(foraging)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def move = {
|
||||||
|
val (x, y) = loc
|
||||||
|
val from = place(x, y)
|
||||||
|
for (ant <- from.ant) {
|
||||||
|
val toLoc = deltaLoc(x, y, ant.dir)
|
||||||
|
val to = place(toLoc)
|
||||||
|
to enter ant
|
||||||
|
from.leave
|
||||||
|
if (!from.home) from.trail
|
||||||
|
newLoc(toLoc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def pickUpFood = {
|
||||||
|
val current = place(loc)
|
||||||
|
current food -1
|
||||||
|
current ant (_.pickUp.turnAround)
|
||||||
|
}
|
||||||
|
|
||||||
|
def dropFood = {
|
||||||
|
val current = place(loc)
|
||||||
|
current food +1
|
||||||
|
current ant (_.dropOff.turnAround)
|
||||||
|
}
|
||||||
|
|
||||||
|
def random[A: Ordering](ranking: Place => A) = {
|
||||||
|
val (x, y) = loc
|
||||||
|
val current = place(x, y)
|
||||||
|
for (ant <- current.ant) {
|
||||||
|
val delta = (turn: Int) => place(deltaLoc(x, y, ant.dir + turn))
|
||||||
|
val ahead = delta(0)
|
||||||
|
val aheadLeft = delta(-1)
|
||||||
|
val aheadRight = delta(+1)
|
||||||
|
val locations = Seq(ahead, aheadLeft, aheadRight)
|
||||||
|
val ranks = rankBy(locations, ranking)
|
||||||
|
val ranked = Seq(ranks(aheadLeft), (if (ahead.occupied) 0 else ranks(ahead)), ranks(aheadRight))
|
||||||
|
val dir = roulette(ranked) - 1
|
||||||
|
if (dir == 0) move
|
||||||
|
else current ant (_.turn(dir))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Evaporator extends WorldActor {
|
||||||
|
import Config._
|
||||||
|
import World._
|
||||||
|
val evaporate = (pher: Float) => pher * EvapRate
|
||||||
|
def act = for (x <- 0 until Dim; y <- 0 until Dim) place(x, y) pher evaporate
|
||||||
|
}
|
||||||
46
akka-samples/akka-sample-ants/src/main/spde/Ants.spde
Normal file
46
akka-samples/akka-sample-ants/src/main/spde/Ants.spde
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
import sample.ants._
|
||||||
|
import sample.ants.Config._
|
||||||
|
import se.scalablesolutions.akka.stm.Transaction.Local._
|
||||||
|
|
||||||
|
val scale = 5
|
||||||
|
|
||||||
|
size(Dim * scale, Dim * scale)
|
||||||
|
smooth()
|
||||||
|
|
||||||
|
override def setup() {
|
||||||
|
background(255)
|
||||||
|
World.start
|
||||||
|
}
|
||||||
|
|
||||||
|
def draw() {
|
||||||
|
for (x <- 0 until Dim; y <- 0 until Dim) {
|
||||||
|
val cell = atomic { World.place(x, y).cell }
|
||||||
|
val (rx, ry, rw, rh) = (x * scale, y * scale, scale, scale)
|
||||||
|
noStroke()
|
||||||
|
fill(255)
|
||||||
|
rect(rx, ry, rw, rh)
|
||||||
|
if (cell.pher > 0) fill(0, 255, 0, cell.pher * PherScale)
|
||||||
|
if (cell.food > 0) fill(255, 0, 0, 255 * (cell.food / FoodRange.floatValue))
|
||||||
|
rect(rx, ry, rw, rh)
|
||||||
|
for (ant <- cell.ant) {
|
||||||
|
if (ant.food) stroke(255, 0, 0) else stroke(0)
|
||||||
|
val (hx, hy, tx, ty) = antLine(ant.dir)
|
||||||
|
line(rx + hx, ry + hy, rx + tx, ry + ty)
|
||||||
|
}
|
||||||
|
stroke(0, 0, 255)
|
||||||
|
noFill()
|
||||||
|
val homeStart = World.homeOff * scale
|
||||||
|
val homeWidth = AntsSqrt * scale
|
||||||
|
rect(homeStart, homeStart, homeWidth, homeWidth)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val s = scale - 1
|
||||||
|
val m = s / 2
|
||||||
|
|
||||||
|
def antLine(dir: Int) = dir match {
|
||||||
|
case 0|4 => (m, 0, m, s)
|
||||||
|
case 1|5 => (s, 0, 0, s)
|
||||||
|
case 2|6 => (s, m, 0, m)
|
||||||
|
case _ => (s, s, 0, 0)
|
||||||
|
}
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
import sbt._
|
import sbt._
|
||||||
import sbt.CompileOrder._
|
import sbt.CompileOrder._
|
||||||
|
import spde._
|
||||||
|
|
||||||
import java.util.jar.Attributes
|
import java.util.jar.Attributes
|
||||||
import java.util.jar.Attributes.Name._
|
import java.util.jar.Attributes.Name._
|
||||||
|
|
@ -198,7 +199,7 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) {
|
||||||
}
|
}
|
||||||
|
|
||||||
class AkkaRedisProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) {
|
class AkkaRedisProject(info: ProjectInfo) extends AkkaDefaultProject(info, distPath) {
|
||||||
val redis = "com.redis" % "redisclient" % "2.8.0.RC2-1.4-SNAPSHOT" % "compile"
|
val redis = "com.redis" % "redisclient" % "2.8.0.Beta1-1.3" % "compile"
|
||||||
override def testOptions = TestFilter((name: String) => name.endsWith("Test")) :: Nil
|
override def testOptions = TestFilter((name: String) => name.endsWith("Test")) :: Nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -249,8 +250,9 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) {
|
||||||
//val atomikos_transactions_util = "com.atomikos" % "transactions-util" % "3.2.3" % "compile"
|
//val atomikos_transactions_util = "com.atomikos" % "transactions-util" % "3.2.3" % "compile"
|
||||||
val jta_spec = "org.apache.geronimo.specs" % "geronimo-jta_1.1_spec" % "1.1.1" % "compile"
|
val jta_spec = "org.apache.geronimo.specs" % "geronimo-jta_1.1_spec" % "1.1.1" % "compile"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ================= TESTS ==================
|
||||||
|
|
||||||
// examples
|
|
||||||
class AkkaFunTestProject(info: ProjectInfo) extends DefaultProject(info) {
|
class AkkaFunTestProject(info: ProjectInfo) extends DefaultProject(info) {
|
||||||
val jackson_core_asl = "org.codehaus.jackson" % "jackson-core-asl" % "1.2.1" % "compile"
|
val jackson_core_asl = "org.codehaus.jackson" % "jackson-core-asl" % "1.2.1" % "compile"
|
||||||
val stax_api = "javax.xml.stream" % "stax-api" % "1.0-2" % "compile"
|
val stax_api = "javax.xml.stream" % "stax-api" % "1.0-2" % "compile"
|
||||||
|
|
@ -264,7 +266,15 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) {
|
||||||
val jmock = "org.jmock" % "jmock" % "2.4.0" % "test"
|
val jmock = "org.jmock" % "jmock" % "2.4.0" % "test"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ================= EXAMPLES ==================
|
||||||
|
|
||||||
|
class AkkaSampleAntsProject(info: ProjectInfo) extends DefaultSpdeProject(info) {
|
||||||
|
val scalaToolsSnapshots = ScalaToolsSnapshots
|
||||||
|
override def spdeSourcePath = mainSourcePath / "spde"
|
||||||
|
}
|
||||||
|
|
||||||
class AkkaSampleChatProject(info: ProjectInfo) extends AkkaDefaultProject(info, deployPath)
|
class AkkaSampleChatProject(info: ProjectInfo) extends AkkaDefaultProject(info, deployPath)
|
||||||
|
|
||||||
class AkkaSamplePubSubProject(info: ProjectInfo) extends AkkaDefaultProject(info, deployPath)
|
class AkkaSamplePubSubProject(info: ProjectInfo) extends AkkaDefaultProject(info, deployPath)
|
||||||
|
|
||||||
class AkkaSampleLiftProject(info: ProjectInfo) extends AkkaDefaultProject(info, deployPath) {
|
class AkkaSampleLiftProject(info: ProjectInfo) extends AkkaDefaultProject(info, deployPath) {
|
||||||
|
|
@ -300,6 +310,8 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) {
|
||||||
}
|
}
|
||||||
|
|
||||||
class AkkaSamplesParentProject(info: ProjectInfo) extends ParentProject(info) {
|
class AkkaSamplesParentProject(info: ProjectInfo) extends ParentProject(info) {
|
||||||
|
lazy val akka_sample_ants = project("akka-sample-ants", "akka-sample-ants",
|
||||||
|
new AkkaSampleAntsProject(_), akka_core)
|
||||||
lazy val akka_sample_chat = project("akka-sample-chat", "akka-sample-chat",
|
lazy val akka_sample_chat = project("akka-sample-chat", "akka-sample-chat",
|
||||||
new AkkaSampleChatProject(_), akka_kernel)
|
new AkkaSampleChatProject(_), akka_kernel)
|
||||||
lazy val akka_sample_pubsub = project("akka-sample-pubsub", "akka-sample-pubsub",
|
lazy val akka_sample_pubsub = project("akka-sample-pubsub", "akka-sample-pubsub",
|
||||||
|
|
@ -351,14 +363,17 @@ class AkkaParent(info: ProjectInfo) extends DefaultProject(info) {
|
||||||
def deployPath: Path
|
def deployPath: Path
|
||||||
|
|
||||||
lazy val dist = distAction
|
lazy val dist = distAction
|
||||||
|
|
||||||
def distAction = deployTask(jarPath, packageDocsJar, packageSrcJar, deployPath, true, true, true) dependsOn(
|
def distAction = deployTask(jarPath, packageDocsJar, packageSrcJar, deployPath, true, true, true) dependsOn(
|
||||||
`package`, packageDocs, packageSrc) describedAs("Deploying")
|
`package`, packageDocs, packageSrc) describedAs("Deploying")
|
||||||
|
|
||||||
def deployTask(jar: Path, docs: Path, src: Path, toDir: Path,
|
def deployTask(jar: Path, docs: Path, src: Path, toDir: Path,
|
||||||
genJar: Boolean, genDocs: Boolean, genSource: Boolean) = task {
|
genJar: Boolean, genDocs: Boolean, genSource: Boolean) = task {
|
||||||
gen(jar, toDir, genJar, "Deploying bits") orElse
|
gen(jar, toDir, genJar, "Deploying bits") orElse
|
||||||
gen(docs, toDir, genDocs, "Deploying docs") orElse
|
gen(docs, toDir, genDocs, "Deploying docs") orElse
|
||||||
gen(src, toDir, genSource, "Deploying sources")
|
gen(src, toDir, genSource, "Deploying sources")
|
||||||
}
|
}
|
||||||
|
|
||||||
private def gen(jar: Path, toDir: Path, flag: Boolean, msg: String): Option[String] =
|
private def gen(jar: Path, toDir: Path, flag: Boolean, msg: String): Option[String] =
|
||||||
if (flag) {
|
if (flag) {
|
||||||
log.info(msg + " " + jar)
|
log.info(msg + " " + jar)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
import sbt._
|
import sbt._
|
||||||
|
|
||||||
class Plugins(info: ProjectInfo) extends PluginDefinition(info) {
|
class Plugins(info: ProjectInfo) extends PluginDefinition(info) {
|
||||||
|
val databinderRepo = "Databinder Repository" at "http://databinder.net/repo"
|
||||||
|
val spdeSbt = "us.technically.spde" % "spde-sbt-plugin" % "0.4.1"
|
||||||
// val repo = "GH-pages repo" at "http://mpeltonen.github.com/maven/"
|
// val repo = "GH-pages repo" at "http://mpeltonen.github.com/maven/"
|
||||||
// val idea = "com.github.mpeltonen" % "sbt-idea-plugin" % "0.1-SNAPSHOT"
|
// val idea = "com.github.mpeltonen" % "sbt-idea-plugin" % "0.1-SNAPSHOT"
|
||||||
}
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue