Comment créer une application combinant une interface Web et une interface plein écran texte (TUI) avec rivo/tview.
Le code est sur https://github.com/fgm/twinui
Le site du livre est https://osinet.fr/go
Nouveautés JavaScript dans le monde Microsoftdavrous
Présentation delivrée le 19 mars 2015 lors du JavaScript Open Day: http://www.meetup.com/Javascript-Open-Day/events/220087351/
Au programme: les nouveautés du moteur du projet Spartan comme Web Audio ou ECMAScript 6
Bonnes pratiques pour apprivoiser le C++11 avec Visual C++Microsoft
Le C++ a vécu un tel renouveau ces dernières années avec la publication de C++11 (et l'arrivée imminente de C++14) que l'on peut dire qu'il y a une rupture, un avant et un après. Ce changement dans le style de programmation transparait dans chaque ligne de code et se reconnait du premier coup d’œil. À chaque nouvelle version, Visual C++ se rapproche un peu plus du support de la norme, permettant d'écrire du code plus lisible, plus robuste et plus rapide. Plus qu'un simple catalogue des fonctionnalités du langage prises en compte par Visual C++2013, cette présentation a pour ambition de vous montrer, par des exemples concrets, quels bénéfices vous pouvez tirer du C++ moderne. Au détour de ces exemples, on mettra en œuvre la sémantique de déplacement, les fonctions par défaut ou interdites, l'initialisation uniforme, les alias de types... en démasquant quelques pièges à éviter, mais surtout en présentant des pratiques sur lesquels s'appuyer pour apprivoiser ce langage moderne qu'est le C++. On fera enfin un petit point sur l'état d'avancement de Visual C++ par rapport à la norme, et sur la feuille de route présentée par les équipes de développement de Visual C++.
Speakers : Christophe Pichaud (Sogeti), Loïc Joly (Cast)
Nouveautés JavaScript dans le monde Microsoftdavrous
Présentation delivrée le 19 mars 2015 lors du JavaScript Open Day: http://www.meetup.com/Javascript-Open-Day/events/220087351/
Au programme: les nouveautés du moteur du projet Spartan comme Web Audio ou ECMAScript 6
Bonnes pratiques pour apprivoiser le C++11 avec Visual C++Microsoft
Le C++ a vécu un tel renouveau ces dernières années avec la publication de C++11 (et l'arrivée imminente de C++14) que l'on peut dire qu'il y a une rupture, un avant et un après. Ce changement dans le style de programmation transparait dans chaque ligne de code et se reconnait du premier coup d’œil. À chaque nouvelle version, Visual C++ se rapproche un peu plus du support de la norme, permettant d'écrire du code plus lisible, plus robuste et plus rapide. Plus qu'un simple catalogue des fonctionnalités du langage prises en compte par Visual C++2013, cette présentation a pour ambition de vous montrer, par des exemples concrets, quels bénéfices vous pouvez tirer du C++ moderne. Au détour de ces exemples, on mettra en œuvre la sémantique de déplacement, les fonctions par défaut ou interdites, l'initialisation uniforme, les alias de types... en démasquant quelques pièges à éviter, mais surtout en présentant des pratiques sur lesquels s'appuyer pour apprivoiser ce langage moderne qu'est le C++. On fera enfin un petit point sur l'état d'avancement de Visual C++ par rapport à la norme, et sur la feuille de route présentée par les équipes de développement de Visual C++.
Speakers : Christophe Pichaud (Sogeti), Loïc Joly (Cast)
Les applications web recourent de plus en plus au javascript, et on commence maintenant à recourir au javascript pour les parties serveur avec des outils comme Node.js TypeScript est un nouveau language permettant d'annoter et de structurer son code javascript, afin d'en faciliter la fiabilité et la maintenance. Dans cette session, nous vous présenterons les bases de TypeScript et comment tirer le meilleur parti de ce nouvel outil dans vos applications.
Une présentation du JavaScript en surface que j'ai faite durant ma première formation en JavaScript à l'Ecole Supérieure d'Informatique et de Mathématiques de Monastir.
Les bases de javascript. Introduction au langage javascript. Les boucles, les fonctions, scope local et global. Accès et modification des éléments du Dom (Document object model)
JavaScript est désormais omniprésent et rend possible l'écriture d'applications complexes et riches. Mais il est souvent mal connu des développeurs orientés objets classiques comme ceux pratiquant le C#, le Java ou le PHP. Cette session passera donc en revue les bases du langage JavaScript, ses spécificités comme les portées, les closures, le this différent de ce que vous pouvez connaître. Bref, vous verrez qu'il y a des pièges à éviter et qu'il ne faut pas négliger JavaScript. Il est très puissant mais potentiellement différent de ce vous connaissez déjà. Venez apprendre à le respecter avant de le maitriser!
Une révision des principaux concepts du langage JavaScript : variables, fonctions, types, fermetures (closures), objets et prototypes, format JSON. De nombreux exemples téléchargeables sur GitHub.
JavaScript est un langage de programmation permettant d’ajouter de l’interactivité aux pages web. C’est un langage facile à apprendre et adapté aux débutants.
Lors de cette formation, les participants verront les principes de base du langage et apprendront à faire réagir une page web aux actions de l’utilisateur.
Des connaissances de base en HTML sont requises.
Retour d’expérience technique de plus d’un an de mission chez TF1 sur #Go, #GraphQL, des micro-services contactés en #gRPC suivant un contrat d’interface avec #Protobuf.
J’ai aussi abordé les sujets de #WebPerf avec du cache applicatif et HTTP et enfin un 3ème volet sur le déploiement #Kubernetes, #Kustomize, le monitoring avec #Grafana et #Prometheus et la façon dont les développeurs travaillent en local avec tous ces micro-services.
La toute première présentation du Toulouse-JUG le 13 mai 2009 !
Le développement sur mobile en J2ME : contraintes, techniques, difficultés, outils et... une pléthore de téléphones pour les tests !
Big Data Viz (and much more!) with Apache ZeppelinBruno Bonnin
Slides du talk réalisé à Web2Day 2016 sur Apache Zeppelin (env. dédié à l'exploration des données, avec support de multiples langages, multiples backends)
Les applications web recourent de plus en plus au javascript, et on commence maintenant à recourir au javascript pour les parties serveur avec des outils comme Node.js TypeScript est un nouveau language permettant d'annoter et de structurer son code javascript, afin d'en faciliter la fiabilité et la maintenance. Dans cette session, nous vous présenterons les bases de TypeScript et comment tirer le meilleur parti de ce nouvel outil dans vos applications.
Une présentation du JavaScript en surface que j'ai faite durant ma première formation en JavaScript à l'Ecole Supérieure d'Informatique et de Mathématiques de Monastir.
Les bases de javascript. Introduction au langage javascript. Les boucles, les fonctions, scope local et global. Accès et modification des éléments du Dom (Document object model)
JavaScript est désormais omniprésent et rend possible l'écriture d'applications complexes et riches. Mais il est souvent mal connu des développeurs orientés objets classiques comme ceux pratiquant le C#, le Java ou le PHP. Cette session passera donc en revue les bases du langage JavaScript, ses spécificités comme les portées, les closures, le this différent de ce que vous pouvez connaître. Bref, vous verrez qu'il y a des pièges à éviter et qu'il ne faut pas négliger JavaScript. Il est très puissant mais potentiellement différent de ce vous connaissez déjà. Venez apprendre à le respecter avant de le maitriser!
Une révision des principaux concepts du langage JavaScript : variables, fonctions, types, fermetures (closures), objets et prototypes, format JSON. De nombreux exemples téléchargeables sur GitHub.
JavaScript est un langage de programmation permettant d’ajouter de l’interactivité aux pages web. C’est un langage facile à apprendre et adapté aux débutants.
Lors de cette formation, les participants verront les principes de base du langage et apprendront à faire réagir une page web aux actions de l’utilisateur.
Des connaissances de base en HTML sont requises.
Retour d’expérience technique de plus d’un an de mission chez TF1 sur #Go, #GraphQL, des micro-services contactés en #gRPC suivant un contrat d’interface avec #Protobuf.
J’ai aussi abordé les sujets de #WebPerf avec du cache applicatif et HTTP et enfin un 3ème volet sur le déploiement #Kubernetes, #Kustomize, le monitoring avec #Grafana et #Prometheus et la façon dont les développeurs travaillent en local avec tous ces micro-services.
La toute première présentation du Toulouse-JUG le 13 mai 2009 !
Le développement sur mobile en J2ME : contraintes, techniques, difficultés, outils et... une pléthore de téléphones pour les tests !
Big Data Viz (and much more!) with Apache ZeppelinBruno Bonnin
Slides du talk réalisé à Web2Day 2016 sur Apache Zeppelin (env. dédié à l'exploration des données, avec support de multiples langages, multiples backends)
Au coeur des applications Web riches, de HTML5 ou des applications Web mobiles, JavaScript est désormais incontournable. Sa communauté très dynamique a contribué à créer un écosystème complet pour répondre aux problématiques courantes de test, qualité du code ou intégration continue comme l'a fait avant elle la communauté Java il y a pas loin de dix ans... Parmi les projets les plus observés du moment, c'est curieusement côté serveur que JavaScript fait le plus parler de lui avec Node.js, un environnement de développement Web qui cultive sa différence.
Téléchargement du Coding Kata :
Panorama des tendances, nouvelles normes, conseils précieux aux développeurs… Entre front, back et design, le Blend Web Mix offre chaque année un cocktail très prisé de technologies et de savoir-faire.
Introduction au langage Go, avec comparaisons à Java
Présentation donnée dans différents meetups et conférences en 2015 et 2016.
Sources : https://github.com/swallez/golang-talks/
Pourquoi JavaScript?
Considéré comme « l’un des langages les plus universels », a déclaré Todd Anglin, vice-président de la gestion des produits et du marketing chez Telerik.
Quels sont les enjeux?
Exposer les APIs de la plateforme native à JavaScript
Produire une interface utilisateur native
Faire abstraction des interfaces utilisateurs natives
Talk made at meetup Mozilla Paris octobre 2017 by Christophe Villeneuve on "Introduction aux webExtensions".
La présentation a pour but d'aider à mieux appréhender ces nouvelles extensions, les impacts et les possibilités offertes dans le navigateur, les emplacements disponibles, le tout cadencé de cas exemples.
Comment développer un serveur métier en python/C++cppfrug
Quelles sont les problématiques d'interfaçage, les avantages/inconvénients des langages, la stratégie de code, etc. dans le développement d'un serveur métier ?
Scaling up and accelerating Drupal 8 with NoSQLOSInet
Drupal 8 can scale well and serve pages fast to many users, especially by offloading parts of the work load from the main SQL database to NoSQL solutions.
This presentation describes the strategies and technologies usable to achieve such gains, including specific configuration, contributed modules and custom coding strategies.
Face it: most Drupal intranets / extranets / back-offices feel sluggish, and that's because they do too much during the page cycle. Make them snappier by deferring work to a Queue worker.
Delayed operations with queues for website performanceOSInet
Delaying work and deferring it to a queue handled asynchronously is one of the most efficient ways to improve full-page performance on complex page structures typical of content-oriented sites built with Drupal and other CMSes. These are the slides of the talk I have at DrupalCon Barcelona 2015 with Yuriy Gerasimov on this topic : learn about deferred submits, anticipated content refresh, and other tricks to speed up your sites.
En introduction de la conférence Drupagora 2015, Marine Soroko et moi-même avons présenté les éléments de stratégie impliqués dans le lancement de projets Drupal 8 : un regard technique d'un côté, un regard gestion de projets de l'autre.
Cache speedup with Heisencache for Drupal 7 and Drupal 8OSInet
Most performance tuning stops at deploying caches to replace file or database access. The next step is to examine how these cached data fare, even in production under high loads : Heisencache provides such analysis tools, for Drupal 7 and Drupal 8.
Recueil des mauvaises pratiques constatées lors de l'audit de sites Drupal 7OSInet
En 3 ans d'audit de sites Drupal 7 pour identifier des problèmes de performance, qualité, ou sécurité OSInet a identifié les causes d'erreurs les plus fréquentes : en règle général, chaque site audit présente au moins l'une d'entre elles.
Votre site est-il affecté par ces erreurs ?
Le groupe PHP-FIG s'est formé pour favoriser l'interopérabilité des frameworks PHP.
Découvrez l'organisation et le fonctionnement du FIG, et les standards PHP PSR-0/PSR-4 pour l'autoloading, PSR-1/PSR-2 pour les normes de codage, PSR-3 pour le logging, les autres standards en cours d'élaboration: PSR-5 pour PHPdoc, PSR-6 pour le cache, et toutes les discussions en cours sur la standardisation PHP.
Présentation donnée au meetup AFUP du 02/04/2014.
Le système de blocs a été présent depuis les origines de Drupal sur drop.org jusqu'à aujourd'hui dans Drupal 8.
Cette présentation retrace l'historique de son développement, et plus largement celui de Drupal dans son ensemble.
Panorama des technologies NoSQL compatibles avec Drupal 7 et 6 à fin 2011: objectifs globaux, tâches fonctionnelles, techniques de mise en oeuvre, coûts, bonnes pratiques, compromis, modules disponibles.
Avec une bibliographie.
Slides from Frederic G. MARAND's "Developing to the Views 7.3 API" at the Drupal Dev Days in Brussels, 2011-02-05.
More details and code access on my blog.
5. Le besoin: pourquoi 2 interfaces ?
● L’interface Web est la référence pour
les consommateurs
● La ligne de commande est le
quotidien des développeurs
6. Le besoin: pourquoi 2 interfaces ?
● Lancer un programme graphique (IDE…)
● Chercher les options d’un programme
● Assembler un pipeline pour un script
● Utiliser vim ou emacs, atop/htop ...
● Administrer une instance en SSH
● Contrôler un programme en cours (docker)
7. Le besoin: pourquoi 2 interfaces ?
● Exemple: “un livre dont vous êtes le
héros” aka “choose your own adventure”
● L’original: un projet de Ion Calhoun sur
son site Gophercises
11. Choix techniques: côté Web - pour cette démo
● Pourquoi un framework web plutôt qu’un autre ?
● Aucun: bien pour un micro-service, rarement au-delà
○ Chemins dynamiques
○ Chargeurs / validateurs d’arguments
○ Filtres par méthode
○ Montages de sous-groupes
● Gorilla/mux:
○ minimal
○ popularité maximale
○ modèle des middlewares
○ utilisé pour l’exemple, facile à convertir
12. Choix techniques: côté Web - pour un projet lourd
● Gin https://gin-gonic.com/
○ plutôt orienté APIs
○ rapide, simple
● Beego https://beego.me/
○ inspiration Django,
○ simple, bien outillé (ORM, ligne de commande)
● Buffalo https://gobuffalo.io/fr/
○ pour les projets Web classiques
○ riche: adoption de composants tiers
○ ORM Pop, moteur de templates Plush, I18n…
○ documentation en français
● Revel http://revel.github.io/
○ Positionnement Buffalo mais depuis 2013
13. Choix techniques: côté texte - l’offre
● Préhistoire: https://github.com/gbin/goncurses
● nsf/termbox-go : ncurses repensé en Go. Très populaire. Abandonné.
● gdamore/tcell : le successeur. Vitesse, Unicode, couleurs, souris.
● gizak/termui : graphique pour tableaux de bord comme expvarmon
● JoelOtter/termloop : spécialisé pour les jeux plein écran texte
● jroimartin/gocui : gestion de “fenêtres” texte basique
● rivo/tview : widgets, fenêtres, application, layout (flex!), 256 couleurs,
souris, son, Unicode, basé sur tcell.
● VladimirMarkelov/clui: TurboVision TNG :-O
tview FTW !
17. TView: organisation du code
● Créer des Widgets: Button, Text, List …
● Les configurer
○ Configurables par leur champ promu Box: SetTitle, SetBackgroundColor ...
○ Et aussi avec leurs propres méthodes: Checkbox.SetChecked, Table.InsertRow …
● Leur ajouter des handlers d’événement s’ils composent FormItem
○ Soit à l’ajout d’enfants: List.AddItem(pri, sec, run, func()) *List
○ Soit directement: List.SetSelectedFunc(func(int, string, string, rune) *List)
● Créer une application avec NewApplication
● Définir quel widget est la racine de l’arborescence avec SetRoot
● Lui ajouter un handler d’événement par SetInputCapture
● Lancer la boucle événementielle avec la méthode Run.
18. TView: composants
package main
import "github.com/rivo/tview"
func main() {
tv := tview.NewButton("Hello, world!")
tview.NewApplication().SetRoot(tv,
true).Run()
}
20. TView: composants personnalisés
● Créer ses composants = implémenter TView.Primitive
○ Blur, Draw, Focus, GetFocusable, GetRect, InputHandler, MouseHandler, SetRect
○ Soit directement, soit en composant un Box comme font les autres
● Plus simplement:
○ Surcharger le Draw d’un composant approprié
○ Intercepter les rafraîchissements par les hooks Application.Draw:
SetBeforeDrawFunc(), SetAfterDrawFunc()
○ Intercepter les événements
■ Globalement: Application.SetInputCapture()
■ Sur une Primitive: Box.SetInputCapture()
22. Coder ses UI: web
Layout:
● arc.gohtml
● gopher.json
● main.go
● README.md
● story.go
● style.css
● web.go
Un même package main:
● Template
● Données
● Point d’entrée de l’application
● Documentation
● Modèle de données
● Styles
● Handlers web
23. Coder ses UI: web - main.go
package main
import (
"flag"
"html/template"
"log"
"net/http"
"os"
"strconv"
"github.com/gorilla/mux"
)
func main() {
path := flag.String("story", "gopher.json",
"The name of the file containing the data")
port := flag.Int("port", 8080, "TCP port")
flag.Parse()
story, err := loadStory(*path)
if err != nil {
log.Fatalln(err)
}
//...suite...
tpl, err := template.ParseFiles("arc.gohtml")
if err != nil {
log.Println("Failed parsing arc template", err)
os.Exit(2)
}
r := mux.NewRouter()
r.HandleFunc("/style.css", styleHandler)
r.HandleFunc("/arc/{arc}",
func(w http.ResponseWriter, r *http.Request) {
arcHandler(w, r, story, tpl)
})
r.HandleFunc("/",
func(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/arc/intro",
http.StatusMovedPermanently)
})
_ = http.ListenAndServe(":"+strconv.Itoa(*port), r)
}
24. Coder ses UI: web - arc.gohtml
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Choose Your Own Adventure: {{.Title}}</title>
<link rel="stylesheet" href="/style.css"/>
</head>
<body>
<section class="page">
<h1>{{ .Title }}</h1>
{{ range .Story }}
<p>{{ . }}</p>
{{ end }}
{{ if .Options }}
<ul>
{{ range .Options }}
<li><a href="/arc/{{ .ArcName }}">
{{ .Text }}</a></li>
{{ end }}
</ul>
{{ end }}
</section>
<footer>
This is a demo of an exercise from the
free course
<a href="https://gophercises.com">
Gophercises</a>. Check it out
if you are interested in learning /
practicing Go.
</footer>
</body>
</html>
25. Coder ses UI: web - story.go
package main
import (
"encoding/json"
"os"
)
// Option represents a choice at the end of an arc.
type Option struct {
Text string
ArcName string `json:"arc"`
}
// Arc represents an individual narrative structure.
type Arc struct {
Title string
Story []string
Options []Option
}
// Story is the graph of arcs.
type Story map[string]Arc
func loadStory(path string) (Story, error) {
story := Story{}
file, err := os.Open(path)
if err != nil {
return story, err
}
decoder := json.NewDecoder(file)
err = decoder.Decode(&story)
return story, err
}
26. Coder ses UI: web - web.go
func arcHandler(w http.ResponseWriter,
r *http.Request, story Story,
tpl *template.Template) {
name, ok := mux.Vars(r)["arc"]
if !ok {
log.Println("arcHandler called without an arc")
http.Redirect(w, r, "/",
http.StatusMovedPermanently)
return
}
arc, ok := story[name]
if !ok {
log.Printf("Incorrect arc requested: %sn", name)
http.NotFound(w, r)
return
}
err := tpl.Execute(w, arc)
if err != nil {
log.Printf("Arc template %#v: %vn", arc, err)
w.WriteHeader(http.StatusInternalServerError)
return
}
}
func styleHandler(w http.ResponseWriter,
r *http.Request) {
file, err := os.Open("style.css")
if err != nil {
log.Println(err)
http.NotFound(w, r)
return
}
defer file.Close()
w.Header().Set("Content-Type", "text/css")
_, err = io.Copy(w, file)
if err != nil {
log.Println("Failed sending CSS", err)
w.WriteHeader(
http.StatusInternalServerError)
}
}
29. Coder ses UI: terminal - layout
Layout:
● ../gophercises_cyoa/gopher.json
● main.go
● model.go
● ui.go
Un même package main:
● Données
● Point d’entrée de l’application
● Accès aux données
● Composants d’interface
30. Coder ses UI: terminal - main.go
func initUI(story *Story)
(*tview.Application, *View) {
view := NewView(story)
app := tview.NewApplication()
app.SetRoot(view.Grid, true).
EnableMouse(true).
SetInputCapture(func(event *tcell.EventKey)
*tcell.EventKey {
switch event.Key() {
case tcell.KeyEsc:
app.Stop()
case tcell.KeyRune:
u := view.URLFromKey(event)
switch {
case u == `quit`:
app.Stop()
case u != ``:
view.Handle(u)
}
}
return nil
})
return app, view
}
func initModel(path string) (*Story, error) {
story := make(Story)
err := story.Load(path)
if err != nil {
return nil, fmt.Errorf(`loading story: %w`, err)
}
return &story, nil
}
func main() {
path := flag.String(`story`, `./gophercises_cyoa/gopher.json`,
` "The name of the file containing the data`)
flag.Parse()
story, err := initModel(*path)
if err != nil {
log.Fatalf("Starting model: %vn", err)
}
app, view := initUI(story)
view.Handle(`intro`)
if err := app.Run(); err != nil {
log.Fatalf("Running app: %vn", err)
}
}
31. Coder ses UI: terminal - model.go
package main
import (
"encoding/json"
"fmt"
"os"
)
// Option represents a choice at the end of an arc.
type Option struct {
Label string `json:"text"`
URL string `json:"arc"`
}
// Arc represents an individual narrative structure.
type Arc struct {
Title string `json:"title"`
Body []string `json:"story"`
Options []Option `json:"options"`
}
// Story is the graph of arcs.
type Story map[string]Arc
// Load fetches the story data from disk.
func (s *Story) Load(path string) error {
file, err := os.Open(path)
if err != nil {
return fmt.Errorf(`opening %s: %w`, path,
err)
}
decoder := json.NewDecoder(file)
err = decoder.Decode(s)
return err
}
// Arc obtains an arc from its URL.
func (s *Story) Arc(url string) *Arc {
a, ok := (*s)[url]
if !ok {
return nil
}
return &a
}
32. Coder ses UI: terminal - ui.go
package main
import (
"fmt"
"log"
"github.com/gdamore/tcell"
"github.com/rivo/tview"
)
// View holds the structure of the application View:
type View struct {
// Heading is the top line.
Heading *tview.TextView
// Body is the main frame.
Body *tview.TextView
// Actions contains the action menu.
Actions *tview.List
// Grid is the container wrapping Heading, Body, Actions.
*tview.Grid
// Story is the model from which the View reads data.
*Story
}
func (v View) Handle(url string) {
arc := v.Story.Arc(url)
if arc == nil {
log.Printf("Path not found: %sn", url)
return
}
fmt.Fprint(v.Heading.Clear(), arc.Title)
b := v.Body.Clear()
for _, row := range arc.Body {
fmt.Fprintln(b, row + "n")
}
v.Actions.Clear()
if len(arc.Options) == 0 {
arc.Options = []Option{{
Label: `Leave story`,
URL: `quit`,
}}
}
for k, item := range arc.Options {
v.Actions.InsertItem(k, item.Label,
item.URL, rune('a' + k), nil)
}
}
33. Coder ses UI: terminal - ui.go (2)
func textView(title string) *tview.TextView {
tv := tview.NewTextView().
SetTextAlign(tview.AlignLeft).
SetTextColor(tcell.ColorBlack)
tv.SetBackgroundColor(tcell.ColorWhite).
SetBorderColor(tcell.ColorLightGray).
SetBorder(true)
tv.SetTitle(` ` + title + ` `).
SetTitleColor(tcell.ColorSlateGray).
SetTitleAlign(tview.AlignLeft)
return tv
}
func list(title string) *tview.List {
l := tview.NewList().
SetMainTextColor(tcell.ColorBlack).
ShowSecondaryText(false).
SetShortcutColor(tcell.ColorDarkGreen)
l.SetBackgroundColor(tcell.ColorWhite).
SetBorderColor(tcell.ColorLightGray).
SetBorder(true).
SetTitle(` ` + title + ` `).
SetTitleColor(tcell.ColorSlateGray).
SetTitleAlign(tview.AlignLeft)
return l
}
// NewView builds an initialized View.
func NewView(story *Story) *View {
v := &View{
Heading: textView("Scene"),
Body: textView("Description").SetScrollable(true),
Actions: list("Choose wisely"),
Grid: tview.NewGrid(),
Story: story,
}
v.Grid.
SetRows(3, 0, 5). // 1-row title, 3-row actions. Add 2
for their own borders.
SetBorders(false). // Use the view borders instead.
AddItem(v.Heading, 0, 0, 1, 1, 0, 0, false).
AddItem(v.Body, 1, 0, 1, 1, 0, 0, false).
AddItem(v.Actions, 2, 0, 1, 1, 0, 0, true)
return v
}
36. TwinUI: composer les 2 UIs
● 2 paquets main, chacun dans leur répertoire
● Problème: chacun a sa logique d’accès aux données
● Problème: chacun se termine par une fonction bloquante:
○ Web: _ = http.ListenAndServe(":"+strconv.Itoa(*port), r)
○ TView: _ = app.Run()
● Refactoring !
37. TwinUI: composer les 2 UIs
Layout:
● main.go
● model/
○ gopher.json
○ model.go
● tview/
○ ui.go
● web/
○ arc.gohtml
○ style.css
○ web.go
4 paquets (...mais…)
● Point d’entrée de l’application
● Source de données
○ Données
○ Accès aux données
● Interacteur: terminal
○ Composants texte
● Interacteur: web
○ Template
○ Styles
○ Composants web
38. TwinUI: composer les 2 UIs
Si ça vous rappelle quelque
chose, ce n’est pas un
hasard…
À l’échelle d’un projet réel, la
généralisation est souvent
représentée comme ceci et
appelée “architecture
hexagonale”.
Image: Blog Netflix
https://netflixtechblog.com/ready-for-
changes-with-hexagonal-architecture-
b315ec967749
39. TwinUI: le secret est dans le main
func main() {
path := flag.String(`story`, `./model/gopher.json`, `The name of the data file`)
port := flag.Int("port", 8080, "The TCP port on which to listen")
flag.Parse()
// Initialize model: without data, we can't proceed.
story, err := initModel(*path)
if err != nil { log.Fatalf("Starting model: %vn", err) }
defer story.Close()
// Initialize the twin UIs.
app := initTextUI(story)
router := initWebUI(story)
....
40. TwinUI: le secret est dans le main
….
// Run the twin UIs, exiting the app whenever either of them exits.
done := make(chan bool)
go func() {
if err := app.Run(); err != nil {
log.Fatalf("Running text app: %vn", err)
}
done <- true
}()
go func() {
if err := http.ListenAndServe(":"+strconv.Itoa(*port), router); err !=
nil {
log.Fatalf("Running web app: %vn", err)
}
done <- true
}()
<-done
41. TwinUI: one last thing...
● Et les erreurs ? log utilise la sortie d’erreur, donc le terminal
○ Mais nous avons déjà une UI plein écran en cours
42. TwinUI: one last thing...
● tview.TermView implémente io.Writer:
● log.SetOutput(&logger{app: app, Writer: view.Body})