-
Notifications
You must be signed in to change notification settings - Fork 33
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Ian Forsey
committed
Nov 17, 2012
0 parents
commit 34ca453
Showing
7 changed files
with
253 additions
and
0 deletions.
There are no files selected for viewing
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
target/ | ||
*.iml | ||
*.ipr | ||
*.iws | ||
.idea/ | ||
|
||
.project | ||
|
||
# OS X | ||
Icon | ||
Thumbs.db | ||
.DS_Store | ||
|
||
.history |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
# Scala scala-uri | ||
|
||
`scala-uri` is a small self contained Scala library that helps you work with URIs. It can be used outside a servlet environment as it has zero dependencies on the servlet spec or existing web frameworks. | ||
|
||
## Including in your project | ||
|
||
TODO: Put in repo. Add SBT config | ||
|
||
## Building URIs with the DSL | ||
|
||
import com.github.theon.uri.Uri._ | ||
val uri = "http://theon.github.com/scala-uri" ? ("param1" -> "1") & ("param2" -> "2") | ||
|
||
## Parsing URIs | ||
|
||
import com.github.theon.uri.Uri._ | ||
val uri = parseUri("http://theon.github.com/scala-uri?param1=1¶m2=2") |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
organization := "com.github.theon" | ||
|
||
version := "0.1-SNAPSHOT" | ||
|
||
scalaVersion := "2.9.2" | ||
|
||
scalacOptions := Seq("-Ydependent-method-types", "-unchecked", "-deprecation", "-encoding", "utf8") | ||
|
||
resolvers ++= Seq( | ||
"Sonatype OSS" at "http://oss.sonatype.org/content/groups/scala-tools" | ||
) | ||
|
||
libraryDependencies ++= Seq( | ||
"org.scalatest" %% "scalatest" % "1.8" % "test" | ||
) |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
package com.github.theon.uri | ||
|
||
import java.net.URI | ||
import com.github.theon.uri.Uri._ | ||
|
||
case class Uri(protocol:Option[String], host:Option[String], path:String, query:Querystring = Querystring()) { | ||
|
||
def param(kv:(String, Any)) = { | ||
val (k,v) = kv | ||
v match { | ||
case valueOpt:Option[_] => | ||
copy(query = query & (k, valueOpt)) | ||
case _ => | ||
copy(query = query & (k, Some(v))) | ||
} | ||
} | ||
|
||
def ?(kv:(String, Any)) = param(kv) | ||
def &(kv:(String, Any)) = param(kv) | ||
|
||
def replace(k:String, v:String) = { | ||
copy(query = query.replace(k, v)) | ||
} | ||
|
||
override def toString() = toString(true) | ||
def toStringRaw() = toString(false) | ||
|
||
def toString(enc:Boolean) = { | ||
val encPath = if(enc) uriEncode(path) else path | ||
|
||
protocol.map(_ + "://").getOrElse("") + | ||
host.getOrElse("") + | ||
encPath + | ||
query.toString("?", enc) | ||
} | ||
} | ||
|
||
case class Querystring(params:Map[String,List[String]] = Map()) { | ||
|
||
def replace(k:String, v:String) = { | ||
copy(params = params + (k -> List(v))) | ||
} | ||
|
||
def &(kv:(String, Option[Any])) = { | ||
val (k,vOpt) = kv | ||
vOpt match { | ||
case Some(v) => { | ||
val values = params.getOrElse(k, List()) | ||
copy(params = params + (k -> (v.toString :: values))) | ||
} | ||
case _ => this | ||
} | ||
} | ||
|
||
override def toString() = toString(true) | ||
def toStringRaw() = toString(false) | ||
|
||
def toString(prefix:String, enc:Boolean):String = { | ||
if(params.isEmpty) { | ||
"" | ||
} else { | ||
prefix + toString(enc) | ||
} | ||
} | ||
|
||
def toString(enc:Boolean) = { | ||
params.flatMap(kv => { | ||
val (k,v) = kv | ||
if(enc) { | ||
v.map(uriEncode(k) + "=" + uriEncode(_)) | ||
} else { | ||
v.map(k + "=" + _) | ||
} | ||
}).mkString("&") | ||
} | ||
} | ||
|
||
object Uri { | ||
implicit def stringToUri(s:String) = parseUri(s) | ||
implicit def uriToString(uri:Uri):String = uri.toString | ||
|
||
def parseUri(s:String) = { | ||
val uri = new URI(s) | ||
val q = parseQuery(Option(uri.getQuery)) | ||
Uri(Option(uri.getScheme), Option(uri.getAuthority), uri.getPath, q) | ||
} | ||
|
||
def parseQuery(qsOpt:Option[String]) = { | ||
qsOpt match { | ||
case None => Querystring() | ||
case Some(qs) => { | ||
val tuples = qs.split("&").map(pairStr => { | ||
val pair = pairStr.split("=") | ||
(pair(0), pair(1)) | ||
}).toList | ||
|
||
val map = tuples.groupBy(_._1).map(kv => { | ||
val (k,v) = kv | ||
(k,v.map(_._2)) | ||
}) | ||
|
||
Querystring(map) | ||
} | ||
} | ||
} | ||
|
||
def uriEncode(s:String) = new URI(null, s, null).toASCIIString | ||
def uriDecode(s:String) = URI.create(s).getPath | ||
|
||
def apply(protocol:String, host:String, path:String):Uri = | ||
Uri(Some(protocol), Some(host), path) | ||
|
||
def apply(protocol:String, host:String, path:String, query:Querystring):Uri = | ||
Uri(Some(protocol), Some(host), path, query) | ||
|
||
def apply(path:String):Uri = | ||
Uri(None, None, path) | ||
|
||
def apply(path:String, query:Querystring):Uri = | ||
Uri(None, None, path, query) | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package com.github.theon.urlutils | ||
|
||
import org.scalatest._ | ||
import org.scalatest.matchers._ | ||
import com.github.theon.uri.Uri | ||
import com.github.theon.uri.Uri._ | ||
|
||
class DslTests extends FlatSpec with ShouldMatchers { | ||
|
||
"A simple absolute URI" should "render correctly" in { | ||
val uri:Uri = "http://theon.github.com/uris-in-scala.html" | ||
uri.toString should equal ("http://theon.github.com/uris-in-scala.html") | ||
} | ||
|
||
"A simple relative URI" should "render correctly" in { | ||
val uri:Uri = "/uris-in-scala.html" | ||
uri.toString should equal ("/uris-in-scala.html") | ||
} | ||
|
||
"An absolute URI with querystring params" should "render correctly" in { | ||
val uri = "http://theon.github.com/uris-in-scala.html" ? ("testOne" -> "1") & ("testTwo" -> "2") | ||
uri.toString should equal ("http://theon.github.com/uris-in-scala.html?testOne=1&testTwo=2") | ||
} | ||
|
||
"A relative URI with querystring params" should "render correctly" in { | ||
val uri = "/uris-in-scala.html" ? ("testOne" -> "1") & ("testTwo" -> "2") | ||
uri.toString should equal ("/uris-in-scala.html?testOne=1&testTwo=2") | ||
} | ||
|
||
"Multiple querystring params with the same key" should "render correctly" in { | ||
val uri = "/uris-in-scala.html" ? ("testOne" -> "1") & ("testOne" -> "2") | ||
uri.toString should equal ("/uris-in-scala.html?testOne=2&testOne=1") | ||
} | ||
} |
19 changes: 19 additions & 0 deletions
19
src/test/scala/com/github/theon/urlutils/EncodingTests.scala
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package com.github.theon.urlutils | ||
|
||
import org.scalatest._ | ||
import org.scalatest.matchers._ | ||
import com.github.theon.uri.Uri | ||
import com.github.theon.uri.Uri._ | ||
|
||
class EncodingTests extends FlatSpec with ShouldMatchers { | ||
|
||
"URI paths" should "be percent encoded" in { | ||
val uri:Uri = "http://theon.github.com/üris-in-scàla.html" | ||
uri.toString should equal ("http://theon.github.com/%C3%BCris-in-sc%C3%A0la.html") | ||
} | ||
|
||
"Querystring parameters" should "be percent encoded" in { | ||
val uri = "http://theon.github.com/uris-in-scala.html" ? ("càsh" -> "£50") & ("©opyright" -> "false") | ||
uri.toString should equal ("http://theon.github.com/uris-in-scala.html?c%C3%A0sh=%C2%A350&%C2%A9opyright=false") | ||
} | ||
} |
33 changes: 33 additions & 0 deletions
33
src/test/scala/com/github/theon/urlutils/ParsingTests.scala
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package com.github.theon.urlutils | ||
|
||
import org.scalatest._ | ||
import org.scalatest.matchers._ | ||
import com.github.theon.uri.Uri | ||
import com.github.theon.uri.Uri._ | ||
|
||
class ParsingTests extends FlatSpec with ShouldMatchers { | ||
|
||
"Parsing an absolute URI" should "result in a valid Uri object" in { | ||
val uri = parseUri("http://theon.github.com/uris-in-scala.html") | ||
uri.protocol should equal (Some("http")) | ||
uri.host should equal (Some("theon.github.com")) | ||
uri.path should equal ("/uris-in-scala.html") | ||
} | ||
|
||
"Parsing a relative URI" should "result in a valid Uri object" in { | ||
val uri = parseUri("/uris-in-scala.html") | ||
uri.protocol should equal (None) | ||
uri.host should equal (None) | ||
uri.path should equal ("/uris-in-scala.html") | ||
} | ||
|
||
"Parsing a URI with querystring paramteres" should "result in a valid Uri object" in { | ||
val uri = parseUri("/uris-in-scala.html?query_param_one=hello&query_param_one=goodbye&query_param_two=false") | ||
uri.query.params should equal ( | ||
Map( | ||
("query_param_two" -> List("false")), | ||
("query_param_one" -> List("hello", "goodbye")) | ||
) | ||
) | ||
} | ||
} |