Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/natbusa/breakfast
How to prepare breakfast in a reactive, asynchronous way in scala
https://github.com/natbusa/breakfast
Last synced: about 2 months ago
JSON representation
How to prepare breakfast in a reactive, asynchronous way in scala
- Host: GitHub
- URL: https://github.com/natbusa/breakfast
- Owner: natbusa
- Created: 2014-01-19T20:31:47.000Z (almost 11 years ago)
- Default Branch: master
- Last Pushed: 2014-12-20T23:22:09.000Z (about 10 years ago)
- Last Synced: 2023-03-25T11:23:57.077Z (almost 2 years ago)
- Language: Scala
- Size: 367 KB
- Stars: 2
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
[![Codeship Status](https://codeship.com/projects/077d3840-6ac9-0132-6a14-123b90e6e43d/status?branch=master)](https://codeship.com/projects/53786)
Breakfast
=========How to prepare breakfast in a reactive, asynchronous way in scala
Motivations
============Preparing breakfast is an excellent exercise of practical real-life programming. It's a procedure which takes some input, namely your order, process the ingredients, and produce a result namely your desired breakfast.
Breakfast making has also some interesting properties.
1. Can be (partially) parallelized, for instance by cooking eggs and toasting bread at the same time.
2. It's constrained by the available resources, think of preparing two breakfasts with a single pan.
3. A breakfast is a recipe. Which means that it's compositional by nature.
It should be easy to adjust the recipe in order to serve a different breakfast.
4. A breakfast can go wrong. For instance, your can burn your toast. What about error recovery and termination?Preparing breakfast allows also to reason about the entities which take part of the process.
Think of the ingredients (eggs, bacon), the cooked artifacts (scrambled eggs, fried bacon), the actors (the cook), and the utensils/functionals (e.g. the pan)Also since this is a breakfast web API, this example allows you to reason about the concept of http routing, json (un-)marshalling, performance, monitoring. etc.
Enough motivations let's prepare some breakfast!Specs
============
1. It's an API call2. Inputs:
nr. of eggs, bacon stripes, toast slices, coffee mugs, and glasses of juices3. Output:
Json of the assemble breakfast with main, side dishes and drinks4. Timeout:
1 second to complete the breakfast.
Partially prepared breakfasts are not delivered.5. Composable
Easy to maintain and modify the recipe.6. Reactive and lazy
Make use of the resources as soon as possible, and only when necessary.7. Scalable (In, Up, and Out)
Providing more resources, should (linearly) scale the system (increase throuput)
Providing more resources, should parallelize the breakfast making algorithm (descrease latency)Reference Request/Response
============
Request:> GET /api/v1/breakfast?eggs=2&strips=4&slices=1&juices=1&coffee=2 HTTP/1.1
> User-Agent: curl/7.29.0
> Host: localhost:8888
> Accept: */*
> Content-Type: application/json
>Response:
< HTTP/1.1 200 OK
< Server: spray-can/1.2-RC2
< Date: Mon, 10 Feb 2014 19:36:43 GMT
< Content-Type: application/json; charset=UTF-8
< Content-Length: 247
<
{
"main": {
"eggs": {
"num": 2
},
"bacon": {
"strips": 4
}
},
"side": {
"toast": {
"slices": 1
}
},
"drink": {
"juice": {
"glasses": 1
},
"coffee": {
"mugs": 2
}
}
}First implementation
============The first implementation consists of four files. Two files define the objects of the breakfast (Elements.scala and JsonImplicit.scala), one defines the API and the last define the main http listener service.
The breakfast elements
```scala
package com.natalinobusa.breakfast.elementscase class ToastedBread(slices: Int)
case class FriedEggs(num: Int)
case class CrispyBacon(strips: Int)
case class OrangeJuice(glasses: Int)
case class HotCoffee(mugs: Int)case class MainDish(eggs: FriedEggs, bacon: CrispyBacon)
case class SideDish(toast: ToastedBread)
case class Drinks(juice: OrangeJuice, coffee: HotCoffee)case class Breakfast(main: MainDish, side: SideDish, drink: Drinks)
```The json (un-)marshalling
```scala
import spray.json.DefaultJsonProtocolobject JsonImplicits extends DefaultJsonProtocol {
//main dish
implicit val impCrispyBacon = jsonFormat1(CrispyBacon)
implicit val impFriedEggs = jsonFormat1(FriedEggs)
implicit val impMainDish = jsonFormat2(MainDish)//side dish
implicit val impToastedBread = jsonFormat1(ToastedBread)
implicit val impSideDish = jsonFormat1(SideDish)//drinks
implicit val impHotCoffee = jsonFormat1(HotCoffee)
implicit val impOrangeJuice = jsonFormat1(OrangeJuice)
implicit val impDrinks = jsonFormat2(Drinks)//breakfast
implicit val impBreakfast = jsonFormat3(Breakfast)
}
```Breakfast service:
```scala
package com.natalinobusa.breakfastimport spray.routing.HttpServiceActor
import spray.routing.directives.PathDirectives._// our breakfast elements
import elements._
// marshalling breakfast to json
import spray.httpx.SprayJsonSupport.sprayJsonMarshaller
import elements.JsonImplicits._// Routing embedded in the actor
class BreakfastApiService extends HttpServiceActor {val serviceRoute = {
pathPrefix("api" / "v1" / "breakfast") {
get {
parameters('eggs.as[Int], 'strips.as[Int], 'slices.as[Int], 'juices.as[Int], 'coffee.as[Int]) {
(friedeggs, baconstrips, breadslices, orangejuices, coffeemugs) =>complete {
Breakfast(
MainDish(
FriedEggs(friedeggs),
CrispyBacon(baconstrips)),
SideDish(
ToastedBread(breadslices)),
Drinks(
OrangeJuice(orangejuices),
HotCoffee(coffeemugs)))
}}
}
}
}// this actor only runs our route,
// but you could add other things here
def receive = runRoute(serviceRoute)}
```Main:
The main is quit straightforward, the akka actor system is defined.
The service is instantiated and thereafter the service is attached to the IO http listener.```scala
object Boot extends App {
implicit val system = ActorSystem()
// create and start our service actor
val service = system.actorOf(Props[BreakfastApiService], "breakfast-api-service")
// start a new HTTP server with our service actor as the handler
IO(Http) ! Http.Bind(service, "localhost", port = 8888)}
```