Last active
December 10, 2015 01:48
-
-
Save negator/4361424 to your computer and use it in GitHub Desktop.
General purpose Url class for silky smooth url string manipulation and sugary Play! WS integration. Enjoy responsibly.
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
import play.api.libs.ws._ | |
import play.api.libs.json._ | |
import scalaz._ | |
import Scalaz._ | |
import java.net.{ URL => JavaUrl } | |
/* This class makes url manipulation a piece of cake (dark chocolate). It also includes Play WS request generation. | |
Requires scalaz6 and Play 2.0.4 | |
Examples: | |
//Facebook url: "https://graph.facebook.com/12344/likes?token=abcd" | |
val facebookUrl = https("graph.facebook.com") / "1234" / "likes" +? ("token"->"abcd") | |
//Add 'limit' query param | |
val newUrl = facebookUrl +? ("limit"->10) | |
//Remove token param | |
val newUrl = facebookUrl -? ("token") | |
//Create WSRequest and invoke get | |
facebookUrl.toWsRequest.get | |
*/ | |
sealed abstract class Protocol(val name: String) | |
object Http extends Protocol("http") | |
object Https extends Protocol("https") | |
case class Url( | |
protocol: Protocol, | |
host: String, | |
port: Option[Int] = None, | |
paths: Seq[String] = Nil, | |
params: Seq[(String, String)] = Nil) { | |
/*Add path param*/ | |
def /(path: String): Url = copy(paths = paths ++ Seq(path)) | |
/*Add query param*/ | |
def +?(keyValue: (String, Any)): Url = copy(params = params ++ Seq(keyValue._1 -> keyValue._2.toString)) | |
/*Remove query param*/ | |
def -?(key: String): Url = copy(params = params.filterNot(_._1 == key)) | |
/*Update queryparam*/ | |
def ^?(keyValue: (String, Any)): Url = -?(keyValue._1) +? (keyValue) | |
/*Value of query param*/ | |
def ??(key: String): Option[String] = params.find(_._1 == key).map(_._2) | |
def %(args: String*) = format(args: _*) | |
def format(args: String*) = Url(toString format (args: _*)) | |
override lazy val toString = throughPaths + queryString | |
lazy val toJavaURL = new java.net.URL(toString) | |
lazy val toWsRequest = WS.url(throughPaths).withQueryString(params: _*) | |
private lazy val throughPaths: String = { | |
// Start with protocol. | |
val builder = new StringBuilder(protocol.name) | |
// Add host and port. | |
builder ++= "://" ++= host ++= (port.map(_.toString) | "") | |
// Add paths. | |
paths foreach { builder ++= "/" ++= _ } | |
// Get result. | |
builder.toString | |
} | |
private lazy val queryString: String = { | |
// Start with empty String. | |
val builder = new StringBuilder("") | |
// The sep is first "?" and becomes "&" after. | |
var sep = "?" | |
// Add query params starting with sep. | |
params foreach { | |
case (key, value) => | |
builder ++= sep ++= key ++= "=" ++= value | |
sep = "&" | |
} | |
// Get result. | |
builder.toString | |
} | |
} | |
object Url { | |
def apply(url: String): Url = { | |
val javaurl = new JavaUrl(url) | |
val protocol = javaurl.getProtocol match { | |
case "https" => Https | |
case _ => Http | |
} | |
val host = javaurl.getHost | |
val port = javaurl.getPort match { | |
case -1 => None //Java URL returns -1 when port not specified. | |
case p => Some(p) | |
} | |
val paths = javaurl.getPath.stripPrefix("/").split("/").toList | |
val params = Option(javaurl.getQuery) map { | |
_.split("&").toList map (_.split("=", 2)) collect { | |
case Array(key, value) => key -> value | |
} | |
} | |
Url(protocol, host, port, paths, params | Nil) | |
} | |
def http(host: String) = Url(Http, host) | |
def https(host: String) = Url(Https, host) | |
implicit val urlFormat: Format[Url] = new Format[Url] { | |
def writes(url: Url) = Json toJson url.toString | |
def reads(js: JsValue) = Url(Json.fromJson[String](js)) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment