Skip to content
This repository has been archived by the owner on Nov 18, 2021. It is now read-only.

Commit

Permalink
adding support for mapping over arrays and returning values from read…
Browse files Browse the repository at this point in the history
…Object
  • Loading branch information
mmcbride committed Nov 20, 2011
1 parent 732d8db commit c807861
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 11 deletions.
2 changes: 1 addition & 1 deletion project/plugins/Plugins.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ import sbt._

class Plugins(info: ProjectInfo) extends PluginDefinition(info) {
val twitterMaven = "twitter.com" at "http://maven.twttr.com/"
val defaultProject = "com.twitter" % "standard-project" % "0.11.6-SNAPSHOT"
val defaultProject = "com.twitter" % "standard-project" % "1.0.0"
}
42 changes: 32 additions & 10 deletions src/main/scala/com/twitter/streamyj/Streamy.scala
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,12 @@ class Streamy(parser: JsonParser, val source: String) {
/**
* alias for readObject(fn)
*/
def \(fn: ObjectParseFunc) = readObject(fn)
def \[T](fn: PartialFunction[String, T]) = readObject(fn)

/**
* An alias for readArray.
*/
def arr(fn: ArrayParseFunc) = readArray(fn)
def arr[T](fn: Function[Int, T]) = readArray(fn)

/**
* Matches the start of an object value, but without reading the object
Expand Down Expand Up @@ -116,7 +116,7 @@ class Streamy(parser: JsonParser, val source: String) {
* read the corresponding value or skip it. Not doing so will leave
* the parser in an unpredictable state.
*/
def readObject(fn: ObjectParseFunc) {
def readObject[T](fn: PartialFunction[String, T]) = {
startObject()
readObjectBody(fn)
}
Expand All @@ -128,7 +128,7 @@ class Streamy(parser: JsonParser, val source: String) {
* read the corresponding value or skip it. Not doing so will leave
* the parser in an unpredictable state.
*/
def readObjectOption(fn: ObjectParseFunc): Boolean = {
def readObjectOption[T](fn: PartialFunction[String, T]): Boolean = {
startObjectOption() && { readObjectBody(fn); true }
}

Expand All @@ -140,15 +140,21 @@ class Streamy(parser: JsonParser, val source: String) {
* read the corresponding value or skip it. Not doing so will leave
* the parser in an unpredictable state.
*/
def readObjectBody(fn: ObjectParseFunc) {
def loop() {
def readObjectBody[T](fn: PartialFunction[String, T]): Option[T] = {
var resp: Option[T] = None
def loop(): Option[T] = {
next() match {
case EndObject => // done
case FieldName(name) =>
if (fn.isDefinedAt(name)) fn(name) else skipNext()
if (fn.isDefinedAt(name)) {
resp = Some(fn(name))
} else {
skipNext()
}
loop()
case token => unexpected(token, "field name")
}
resp
}
loop()
}
Expand Down Expand Up @@ -219,7 +225,7 @@ class Streamy(parser: JsonParser, val source: String) {
* The given function MUST either fully read the corresponding value
* or skip it. Not doing so will leave the parser in an unpredictable state.
*/
def readArray(fn: ArrayParseFunc) {
def readArray[T](fn: Function[Int, T]) = {
startArray()
readArrayBody(fn)
}
Expand All @@ -231,7 +237,7 @@ class Streamy(parser: JsonParser, val source: String) {
* The given function MUST either fully read the corresponding value
* or skip it. Not doing so will leave the parser in an unpredictable state.
*/
def readArrayOption(fn: ArrayParseFunc): Boolean = {
def readArrayOption[T](fn: Function[Int, T]): Boolean = {
startArrayOption() && { readArrayBody(fn); true }
}

Expand All @@ -240,7 +246,7 @@ class Streamy(parser: JsonParser, val source: String) {
* The given function MUST either fully read the corresponding value
* or skip it. Not doing so will leave the parser in an unpredictable state.
*/
def readArrayBody(fn: ArrayParseFunc) {
def readArrayBody[T](fn: Function[Int, T]) = {
def loop(index: Int) {
if (peek() == EndArray) {
next() // skip ]
Expand All @@ -252,6 +258,22 @@ class Streamy(parser: JsonParser, val source: String) {
loop(0)
}

def mapArray[T](fn: Function[Int, T]): Seq[T] = {
startArray()
mapArrayBody(fn)
}

def mapArrayBody[T](fn: Function[Int, T]): Seq[T] = {
def loop(index: Int): List[T] = {
if (peek() == EndArray) {
next() // skip ]
Nil
} else {
fn(index) :: loop(index + 1)
}
}
loop(0)
}
/**
* Reads an array using an accumulator.
* The given function MUST either fully read the corresponding value
Expand Down
14 changes: 14 additions & 0 deletions src/test/scala/com/twitter/streamyj/StreamySpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -248,5 +248,19 @@ object StreamySpec extends Specification {
baz must beSome(1L)
arr mustEqual List(List(1L), 2L, 3L, 4L)
}

"support returning values from function" in {
val s = Streamy("""{"bar":"testing"}""")
val text = s.readObject{ case "bar" => s.readString() }
text mustEqual Some("testing")
}

"support mapping values from arrays" in {
val s = Streamy("""{"bar":[1,2,3,4,5]}""")
val neg = s.readObject {
case "bar" => s.mapArray { case i => -1 * s.readInt() }
}
neg mustEqual Some(List(-1, -2, -3, -4, -5))
}
}
}

0 comments on commit c807861

Please sign in to comment.