2
\$\begingroup\$

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.

\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

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)

}
\$\endgroup\$
1
  • \$\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
    Dec 14, 2013 at 22:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.