Skip to content
Open
110 changes: 39 additions & 71 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,92 +2,60 @@ plugins {
id "com.github.maiflai.scalatest" version "0.6-5-g9065d91"
}

apply plugin: "scala"
apply plugin: "eclipse"
apply plugin: "maven"
apply plugin: "signing"

apply from: "gradle/credentials.gradle"

group = "com.github.vergenzt"
version = "0.1.4"

repositories {
mavenCentral()
}
subprojects {
apply plugin: "scala"
apply plugin: "eclipse"

dependencies {
compile "org.scala-lang:scala-library:2.11.5"
repositories {
mavenCentral()
}

compile "org.scala-lang.modules:scala-xml_2.11:1.0.3"
compile "org.scalaj:scalaj-http_2.11:1.1.4"
compile "com.github.nscala-time:nscala-time_2.11:1.8.0"
configurations {
scalaCompilerPlugin
}

testCompile "org.scalatest:scalatest_2.11:2.2.4"
testCompile "org.mockito:mockito-core:1.10.19"
testCompile "com.squareup.okhttp:mockwebserver:2.3.0"
dependencies {
compile "org.scala-lang:scala-library:2.11.7"
compile "org.scala-lang:scala-compiler:2.11.7"

// for scalatest gradle plugin
testRuntime "org.pegdown:pegdown:1.1.0"
}
compile "org.scala-lang.modules:scala-xml_2.11:1.0.3"
compile "org.scalaj:scalaj-http_2.11:1.1.4"
compile "com.github.nscala-time:nscala-time_2.11:1.8.0"

task sourcesJar(type: Jar) {
classifier = "sources"
from sourceSets.main.allSource
}
scalaCompilerPlugin "org.scalamacros:paradise_2.11.7:2.1.0"

task scaladocJar(type: Jar, dependsOn: scaladoc) {
classifier = "javadoc"
from scaladoc.destinationDir
}
testCompile "org.scalatest:scalatest_2.11:2.2.4"
testCompile "org.mockito:mockito-core:1.10.19"
testCompile "com.squareup.okhttp:mockwebserver:2.3.0"

artifacts {
archives jar
archives sourcesJar
archives scaladocJar
}
// for scalatest gradle plugin
testRuntime "org.pegdown:pegdown:1.1.0"
}

signing {
sign configurations.archives
tasks.withType(ScalaCompile) {
scalaCompileOptions.additionalParameters = [
"-Xplugin:" + configurations.scalaCompilerPlugin.asPath,
"-Ymacro-debug-lite"
]
options.compilerArgs = ["-Xdebug", "-agentlib:jdwp=transport=dt_socket,server=n,suspend=y,address=9999"]
}
}

uploadArchives {
repositories.mavenDeployer {
beforeDeployment { deployment -> signing.signPom(deployment) }

repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") {
authentication(userName: "", password: "") // set in credentials.gradle
}
project(":rtm-scala") {
apply from: "$rootProject.rootDir/gradle/publishing.gradle"

pom.project {
name "rtm-scala_2.11"
packaging "jar"
description "rtm-scala is a Scala wrapper for the Remember the Milk API."
url "https://github.com/vergenzt/rtm-scala"

scm {
url "scm:git@github.com:vergenzt/rtm-scala.git"
connection "scm:git@github.com:vergenzt/rtm-scala.git"
developerConnection "scm:git@github.com:vergenzt/rtm-scala.git"
}

licenses {
license {
name "MIT License"
url "http://opensource.org/licenses/MIT"
distribution "repo"
}
}
dependencies {
compile project(":rtm-scala-lib")
compile project(":rtm-scala-meta")
}
}

developers {
developer {
id "vergenzt"
name "Tim Vergenz"
email "vergenzt@gmail.com"
url "https://github.com/vergenzt"
}
}
}
project(":rtm-scala-meta") {
dependencies {
compile project(":rtm-scala-lib")
}
}

64 changes: 64 additions & 0 deletions gradle/publishing.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
apply plugin: "maven"
apply plugin: "signing"
apply from: "$rootProject.rootDir/gradle/credentials.gradle"

task sourcesJar(type: Jar) {
classifier = "sources"
from sourceSets.main.allSource
}

task scaladocJar(type: Jar, dependsOn: scaladoc) {
classifier = "javadoc"
from scaladoc.destinationDir
}

artifacts {
archives jar
archives sourcesJar
archives scaladocJar
}

signing {
sign configurations.archives
}

uploadArchives {
repositories.mavenDeployer {
beforeDeployment { deployment -> signing.signPom(deployment) }

repository(url: "https://oss.sonatype.org/service/local/staging/deploy/maven2/") {
authentication(userName: "", password: "") // set in credentials.gradle
}

pom.project {
name "rtm-scala_2.11"
packaging "jar"
description "rtm-scala is a Scala wrapper for the Remember the Milk API."
url "https://github.com/vergenzt/rtm-scala"

scm {
url "scm:git@github.com:vergenzt/rtm-scala.git"
connection "scm:git@github.com:vergenzt/rtm-scala.git"
developerConnection "scm:git@github.com:vergenzt/rtm-scala.git"
}

licenses {
license {
name "MIT License"
url "http://opensource.org/licenses/MIT"
distribution "repo"
}
}

developers {
developer {
id "vergenzt"
name "Tim Vergenz"
email "vergenzt@gmail.com"
url "https://github.com/vergenzt"
}
}
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package com.github.vergenzt.rtmscala

import scalaj.http.Http
import scalaj.http.HttpRequest
import util._
import util.ParamConversions._
import util.XmlConversions._

object RtmApiBase extends RtmApiBase

/**
* Base trait for the Remember the Milk API.
*
* @define authToken @param authToken An authentication token.
* @define creds @param creds Your API credentials.
* @define method @param method The API method to call, without the prefix "rtm."
* (e.g."rtm.auth.getFrob" would be passed as "auth.getFrob")
* @define params @param params The method-specific parameters.
* @define timeline @param timeline A timeline obtained from `rtm.timelines.create`
*/
trait RtmApiBase {

val BASE_URL = "http://api.rememberthemilk.com/services"
val AUTH_URL = BASE_URL + "/auth/"

// for testing
private[rtmscala] var REST_URL = BASE_URL + "/rest/"

/**
* Construct an RTM API request.
* @param url The url to request.
* $params
* $creds
*/
protected[rtmscala]
def baseRequest(url: String, params: (String, String)*)
(implicit creds: ApiCreds): HttpRequest = {
Http(url).param("api_key", creds.apiKey).params(params)
}

/**
* Construct an unsigned RTM API request.
* $method
* $params
* $creds
*/
protected[rtmscala]
def unsignedRequest(method: String, params: (String, String)*)
(implicit creds: ApiCreds): HttpRequest = {
baseRequest(REST_URL, ("method" -> ("rtm." + method)) :: params.toList: _*)
}

/**
* Construct a signed RTM API request.
* $method
* $params
* $creds
*/
protected[rtmscala]
def request(method: String, params: (String, String)*)
(implicit creds: ApiCreds): HttpRequest = {
unsignedRequest(method, params: _*).signed
}

/**
* Construct an authenticated RTM API request.
* $method
* $params
* $creds
* $authToken
*/
protected[rtmscala]
def authedRequest(method: String, params: (String, String)*)
(implicit creds: ApiCreds, authToken: AuthToken) = {
request(method, ("auth_token" -> authToken.token) :: params.toList: _*)
}

/**
* Construct an authenticated RTM API request with a timeline.
* $method
* $params
* $creds
* $authToken
* $timeline
*/
protected[rtmscala]
def timelinedRequest(method: String, params: (String, String)*)
(implicit creds: ApiCreds, authToken: AuthToken, timeline: Timeline) = {
authedRequest(method, ("timeline" -> timeline.id) :: params.toList: _*)
}

/* Reflection methods */

object reflection {
def getMethodNames()(implicit creds: ApiCreds) =
request("reflection.getMethods").as[Seq[String]]

def getMethod(methodName: String)(implicit creds: ApiCreds) =
request("reflection.getMethodInfo", "method_name" -> methodName).as[MethodDesc]

def getMethods()(implicit creds: ApiCreds) =
getMethodNames().map(getMethod)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ object Permission {
case object Read extends Permission("read", 1)
case object Write extends Permission("write", 2)
case object Delete extends Permission("delete", 3)

val ALL = Seq(None, Read, Write, Delete)
def fromInt(value: Int) = ALL.find(_.value == value)
}

case class User(id: String, username: String, fullname: Option[String])
Expand Down Expand Up @@ -118,6 +121,33 @@ case class Transaction(
request: HttpRequest
)

/* Reflection */

case class MethodDesc(
fullName: String,
needsLogin: Boolean,
needsSigning: Boolean,
requiredPerms: Permission,
description: String,
exampleResponse: String,
arguments: Seq[ArgumentDesc],
errors: Seq[ErrorDesc]
) {
val Array("rtm", group, name) = fullName.split("\\.", 3)
}

case class ArgumentDesc(
name: String,
optional: Boolean,
description: String
)

case class ErrorDesc(
code: Int,
message: String,
description: String
)

/* Exceptions */

case class RtmException(val message: String, val code: Int) extends Exception {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,4 +160,37 @@ object XmlConversions {
implicit def xml2Timeline(xml: NodeSeq): Timeline = xml match {
case Seq(Node("timeline", _, id)) => Timeline(id.text)
}

/* Reflection */

implicit def xml2MethodList(xml: NodeSeq): Seq[String] = {
for {
method <- xml \\ "method"
} yield method.text
}

implicit def xml2MethodDesc(xml: NodeSeq): MethodDesc = xml match {
case Seq(method @ Node("method", _, body @ _*)) => MethodDesc(
fullName = method \@ "name",
needsLogin = method \@ "needslogin",
needsSigning = method \@ "needssigning",
requiredPerms = Permission.fromInt((method \@ "requiredperms").toInt).get,
description = (method \ "description").text,
exampleResponse = (method \ "response").text,
arguments = (method \\ "argument") map { arg =>
ArgumentDesc(
name = arg \@ "name",
optional = arg \@ "optional",
description = arg.text
)
},
errors = (method \\ "error") map { err =>
ErrorDesc(
code = (err \@ "code").toInt,
message = err \@ "message",
description = err.text
)
}
)
}
}
Loading