paris scala user group #43 - spray (magnet pattern) + rxscala / elasticsearch
DESCRIPTION
Retour d'experience sur l'utilisation de spray dans le cadre de Mogobiz. Explication de la technique du "Magnet Pattern" et présentation des bénéfices de l'utilisation de Rx. http://j.mp/psug43XebiaTRANSCRIPT
Spray / RxScala / ElasticSearch
Paris Scala User Group #43 !!!!
Contexte
Mogobiz
http://www.jahia.com/home/products/ecommerce-factory.html
SPRAY ?
Ensemble de bibliothèques pour la construc>on
d’API RESTful basé sur Akka
spray-http
spray-can
IO STACK
spray-routing
spray-routing
spray-routing
spray-routing
hAp://spray.io/blog/2012-‐12-‐13-‐the-‐magnet-‐paAern
Place au code
ObjecLf: • DSL composé de la foncLon host • Masquer la complexité de host
ObjecLf: • DSL composé de la foncLon host • Masquer la complexité de host
Notre méthode host prend comme
paramètre plusieurs types en entré.
ObjecLf: • DSL composé de la foncLon host • Masquer la complexité de host
Notre méthode host prend comme paramètre
en entrée plusieurs types en entré.
Il faut le converLr implicitement
en Int (logique applicaLve pour
notre exemple)
=> l'evidence ev
ObjecLf: • DSL composé de la foncLon host • Masquer la complexité de host
Notre méthode host prend comme paramètre
en entrée plusieurs types en entré.
Il faut le converLr implicitement
en Int (logique applicaLve pour
notre exemple)
=> l'evidence ev
ObjecLf: • DSL composé de la foncLon host • Masquer la complexité de host
Notre méthode host prend comme paramètre
en entrée plusieurs types en entré.
Il faut le converLr implicitement
en Int (logique applicaLve pour
notre exemple)
=> l'evidence ev
!<console>:24: error: type mismatch; found : SprayTestsNoMagnet.Route required: String => Int new Route {} ^
!<console>:24: error: type mismatch; found : SprayTestsNoMagnet.Route required: String => Int new Route {} ^
val route = host("SprayTestsNoMagnet") ( new Route {} ) =
val route = host("SprayTestsNoMagnet") ( new Route {} ) =
Solu>on => Appel intermédiaire
scala> import SprayTestsNoMagnet._
import SprayTestsNoMagnet._
!scala> tmpHost
res1: SprayTestsNoMagnet.Route => SprayTestsNoMagnet.Route = <funcLon1>
!tmpHost (new Route {})
res2: SprayTestsNoMagnet.Route = $anon$1@6f5c0b7b
!host("SprayTestsNoMagnet") (stringToInt) (new Route {})
res4: SprayTestsNoMagnet.Route = $anon$1@521352d8
Nous ne pouvons pas nous contenter de ce/e solu1on si nous é1ons entrain d'écrire un DSL
==> Magnet « paMern » qui résout entre autre ceMe probléma>que
Nous ne pouvons pas nous contenter de ce/e solu1on si nous é1ons entrain d'écrire un DSL
// Notre nouveau Type HostMagnet est bien sensé renvoyer la même foncLon Route => Route
// Notre nouveau Type HostMagnet est bien sensé renvoyer la même foncLon Route => Route
// Le premier implicit est une conversion implicite qui nous permet d'obtenir // une instance HostMagnet à parLr d'une string , en l'occurrence "mogobiz.io"
// Notre nouveau Type HostMagnet est bien sensé renvoyer la même foncLon Route => Route
// Le premier implicit est une conversion implicite qui nous permet d'obtenir // une instance HostMagnet à parLr d'une string , en l'occurrence "mogobiz.io"
// host prend un paramètre de type HostMagnet // et retourne une instance de HostMagnet
// magnet.apply()
scala> import SprayTestsWithMagnet._ import SprayTestsWithMagnet._ !scala> val route = host("mogobiz.io") { | new Route {} | } route: SprayTestsWithMagnet.Route = $anon$1@51f347a !scala> val x = host("mogobiz.io") x: SprayTestsWithMagnet.Route => SprayTestsWithMagnet.Route = <function1>
• Finaliser une requête HTTP
• DSL ==> « complete »
• Plusieurs manière de finaliser une requête HTTP ==> Surcharger « complete »
Type erasure
LimitaLon pour les types paramètrés
Nice, but show me…
… some Magnet PaMern in Spray-‐rou>ng
CeAe direcLve est uLlisée au sein d'une val route qui est de Type Route (spray-‐rouLng).
CeAe direcLve est uLlisée au sein d'une val route qui est de Type Route (spray-‐rouLng).
?
pathPrefix est une foncLon qui prend en paramètre un PathMatcher et qui nous renvoi un objet de type Directive. !(cf. spray-‐rouLng/src/main/scala/spray/rouLng/direcLves/PathDirecLves.scala)
pathPrefix est une foncLon qui prend en paramètre un PathMatcher et qui nous renvoi un objet de type Directive. !(cf. spray-‐rouLng/src/main/scala/spray/rouLng/direcLves/PathDirecLves.scala)
PathMatcher défini un implicite qui converL un String en PathMatcher (conversion d’ "acount" en une instance de PathMatcher) (cf. spray-‐rouLng/src/main/scala/spray/rouLng/PathMatcher.scala)
pathPrefix est une direcLve, qui prend en paramètre une closure de type Route, Et retourne une Route.
pathPrefix est une direcLve, qui prend en paramètre une closure de type Route, Et retourne une Route.
==> Conver1r implicitement la Directive en une fonc1on qui prend un paramètre de type Route et renvoi un résultat de Type Route (Route => Route)
(cf. spray-‐rouLng/src/main/scala/spray/rouLng/DirecLve.scala)
Il prend en paramètre une DirecLve ET un Converter en implicite. !L’ ApplyConverter en implicite est un type paramétré, donc il dépend du type de DirecLve en entré.
(cf. spray-‐rouLng/src/main/scala/spray/rouLng/DirecLve.scala)
(cf. spray-‐rouLng/src/main/scala/spray/rouLng/ApplyConverter.scala)
l'ApplyConverter est l'équivalent de notre évidence de l'exemple précédant qui va converLr la DirecLve[L] en Route
(cf. spray-‐rouLng/src/main/scala/spray/rouLng/DirecLve.scala)
(cf. spray-‐rouLng/src/main/scala/spray/rouLng/ApplyConverter.scala)
ApplyConverter va renvoyer un In qui est en fait une Route. Le pimpApply renvoi une foncLon hac.In => Route !=> Nous avons donc notre Type de retour Route => Route
• Notre Objet Magnet est la Directive
• pathPrefix est exécuté une seule fois; lors du chargement de
l'applicaLon (Spray) transformé en Directive
• La Directive est transformée en une foncLon qui prend en
paramètre; la Route entre accolade
• Tout ce qui produit une Directive est exécuté au chargement de
spray (get, path, pathPrefix, completed, )
• Spray va stocker ceAe informaLon et ne va pas l'évaluer à chaque fois.
• Par contre, les définiLons des closures seront chargés mais non
exécuté.
RxJava-Sclala
spray-client
spray-client• non adapté dans certain cas d’uLlisaLon (Mogobiz)
• Appel de plusieurs Index ElasLcSearch en parallèle
• Appliquer des traitement (Suppression des langues, calcul du prix)
• ComposiLon + dépendance des Futures
==> Complexité d’implémentaLon
==> Nous perdons la simplicité du DSL spray-‐client
RxJava• ImplémentaLon par Nexlix du Projet Rx (ReacLve Extensions) hAp://codeplex.com/ de Microsoy
• Bibliothèque permeAant de composer des programmes événemen>els asynchrone, via l’uLlisaLon de séquences observables.
RxJava
Resources• Ben Christensen. “FuncLonal ReacLve Programming in the Nexlix API.” hAp://fr.slideshare.net/InfoQ/funcLonal-‐reacLve-‐programming-‐in-‐the-‐nexlix-‐api
• Mathias Doenitz. “Spray: REST on Akka (Scala Days).” hAp://fr.slideshare.net/sirthias/spray-‐rest-‐on-‐akka-‐12616908
• “Spray | Blog » The Magnet PaAern.” hAp://spray.io/blog/2012-‐12-‐13-‐the-‐magnet-‐paAern/
Resources• “Spray | DocumentaLon.” hAp://spray.io/documentaLon/
• “Implicit Parameters -‐ Scala DocumentaLon.” hAp://docs.scala-‐lang.org/tutorials/tour/implicit-‐parameters.html
• “Scala IO 2013 => Spray : REST on Akka -‐ Blog.roddet.com.” hAp://blog.roddet.com/2013/10/scalaio-‐2013-‐spray-‐rest-‐on-‐akka/
Medias• “Sciences Naturelles: Horseshoe-‐Magnet-‐Red-‐Silver-‐Iron-‐Filings-‐Highres-‐.”hAp://mirror-‐us-‐ga1.gallery.hd.org/_c/natural-‐science/_more2008/_more12/horseshoe-‐magnet-‐red-‐silver-‐iron-‐filings-‐highres-‐AHD.jpg.html
• “Willy Wonka” hAp://memegenerator.net/Willywonka/capLon
• “API Safe and Easy Aquarium Spray” hAp://www.amazon.com/API-‐Safe-‐Aquarium-‐Spray-‐8-‐Ounce/dp/B001D728VI
Merci
Q&A