I'm trying to make a scala interface to the spotify API. I'm using it to learn Scala. I come from mostly a python background.
import dispatch._, Defaults._
trait Request {
val name: String
val endpoint: String
}
case class Track(name: String) extends Request {
override val endpoint = "track.json"
}
case class Album(name: String) extends Request {
override val endpoint = "album.json"
}
case class Artist(name: String) extends Request {
override val endpoint = "artist.json"
}
object Spotify {
def getResponse(req: Request) = {
val params = Map("q" -> req.name)
val spotifyUrl = host("ws.spotify.com")
spotifyUrl / "search"/ "1" / req.endpoint <<? params
}
val foo = Album("foo")
val songs = Http(getResponse(foo) OK as.String)
}
I feel like getResponse
should be in the Request trait, or something since it's generic enough to only need the overridden endpoint
- but not sure.
Any general recommendations on structuring this sort of thing would be helpful.
1 Answer 1
Decouple everything.
I am certain that Scala veterans will have more to say than I do and possibly comment over my comments.
Here are some changes and respective comments to your code.
import dispatch._, Defaults._
// decouple from any "master" class.
case class Track(name: String)
case class Album(name: String)
case class Artist(name: String)
// This is called a structural type. We are using a def
// because a val restricts extensibility.
type Named = {def name: String}
// Separate out the endpoints into their own category
object endpointResolver {
// Use a partial function which you can re-use in order parts
// of your program. You may use this in your testing as well.
val endPoints: PartialFunction[Named, String] = {
case _: Track => "track.json"
case _: Album => "album.json"
case _: Artist => "artist.json"
}
// use an implicit class. This allows you to add methods
// to existing classes seamlessly.
implicit class nrEndpoint(namedRequest: Named) {
def endpoint: String =
endPoints apply namedRequest
}
}
object Spotify {
// Specify the return type. I was not sure what it was initially.
def getResponse(req: Named): Req = {
val params = Map("q" -> req.name)
val spotifyUrl = host("ws.spotify.com")
// import and utilise this implicit class
import endpointResolver.nrEndpoint
spotifyUrl / "search"/ "1" / req.endpoint <<? params
}
val foo = Album("foo")
// OK is another example of an implicit being used. Use an IDE
// such as IntelliJ IDEA to navigate through the vast depths
// of Scala source.
val songs = Http(getResponse(foo) OK as.String)
}
-
\$\begingroup\$ Very interesting, thanks for the code (and more for the comments). Lots of stuff to play with here. I'm not going to accept for a bit uncase like you say a veteran comes in, but will if that doesn't happen. \$\endgroup\$tshauck– tshauck2013年12月14日 22:35:04 +00:00Commented Dec 14, 2013 at 22:35