+ All Categories
Home > Technology > Mum, I want to be a Groovy full-stack developer

Mum, I want to be a Groovy full-stack developer

Date post: 16-Jan-2017
Category:
Upload: gr8conf
View: 261 times
Download: 1 times
Share this document with a friend
59
Iván López - @ilopmar Mum, I want to be a Groovy full-stack developer
Transcript
Page 1: Mum, I want to be a Groovy full-stack developer

Iván López - @ilopmar

Mum, I want to be a Groovy full-stack developer

Page 2: Mum, I want to be a Groovy full-stack developer

Hello!I am Iván López

@ilopmar

http://greachconf.com@madridgug

Page 3: Mum, I want to be a Groovy full-stack developer

Thank you very much!Q&A

Page 4: Mum, I want to be a Groovy full-stack developer

Just kidding!

Page 5: Mum, I want to be a Groovy full-stack developer
Page 6: Mum, I want to be a Groovy full-stack developer

What's a full-stack developer?

Page 7: Mum, I want to be a Groovy full-stack developer

Full-stack developer

Backend language

Javascript

HTML

Mobile App

Page 8: Mum, I want to be a Groovy full-stack developer
Page 9: Mum, I want to be a Groovy full-stack developer

Polaromatic

Page 10: Mum, I want to be a Groovy full-stack developer
Page 11: Mum, I want to be a Groovy full-stack developer

1.Demo

Page 12: Mum, I want to be a Groovy full-stack developer

2.Application flow

Page 13: Mum, I want to be a Groovy full-stack developer
Page 14: Mum, I want to be a Groovy full-stack developer

3.Backend

Page 15: Mum, I want to be a Groovy full-stack developer
Page 16: Mum, I want to be a Groovy full-stack developer

Polaromatic

▷ Spring Boot

▷ Core App

▷ Spring MVC

▷ Spring Integration Flow

Page 17: Mum, I want to be a Groovy full-stack developer

<file:inbound-channel-adapter directory="work" channel="incommingFilesChannel"/>

<chain input-channel="incommingFilesChannel"> <service-activator ref="fileService" method="preprocessFile"/> <service-activator ref="imageConverterService" method="applyEffect"/> <service-activator ref="browserPushService" method="pushToBrowser"/> <service-activator ref="metricsService" method="updateMetrics"/> <service-activator ref="fileService" method="deleteTempFiles"/></chain>

Spring Integration Flow

Page 18: Mum, I want to be a Groovy full-stack developer

<file:inbound-channel-adapter directory="work" channel="incommingFilesChannel"/>

<chain input-channel="incommingFilesChannel"> <service-activator ref="fileService" method="preprocessFile"/> <service-activator ref="imageConverterService" method="applyEffect"/> <service-activator ref="browserPushService" method="pushToBrowser"/> <service-activator ref="metricsService" method="updateMetrics"/> <service-activator ref="fileService" method="deleteTempFiles"/></chain>

Spring Integration Flow

Photo preprocessFile(File file) { def pr = new PolaroidRequest(file) this.preprocessFile(pr)}

File service

Page 19: Mum, I want to be a Groovy full-stack developer

<file:inbound-channel-adapter directory="work" channel="incommingFilesChannel"/>

<chain input-channel="incommingFilesChannel"> <service-activator ref="fileService" method="preprocessFile"/> <service-activator ref="imageConverterService" method="applyEffect"/> <service-activator ref="browserPushService" method="pushToBrowser"/> <service-activator ref="metricsService" method="updateMetrics"/> <service-activator ref="fileService" method="deleteTempFiles"/></chain>

Spring Integration Flow

File servicePhoto preprocessFile(File file) { def pr = new PolaroidRequest(file) this.preprocessFile(pr)} Photo preprocessFile(PolaroidRequest polaroidRequest) {

String outputFile = File.createTempFile("output", ".png").path

return new Photo(input: polaroidRequest.inputFile.absolutePath, output: outputFile, text: polaroidRequest.text)}

Page 20: Mum, I want to be a Groovy full-stack developer

class ImageConverterService {

private static final String DEFAULT_CAPTION = "#LearningSpringBoot with Polaromatic\\n"

Random rnd = new Random()

Photo applyEffect(Photo photo) { log.debug "Applying effect to file: ${photo.input}..."

def inputFile = photo.input def outputFile = photo.output

double polaroidRotation = rnd.nextInt(6).toDouble() String caption = photo.text ?: DEFAULT_CAPTION

def op = new IMOperation() op.addImage(inputFile) op.thumbnail(300, 300) .set("caption", caption) .gravity("center") .pointsize(20) .background("black") .polaroid(rnd.nextBoolean() ? polaroidRotation : -polaroidRotation) .addImage(outputFile)

def command = new ConvertCmd() command.run(op)

photo }}

Image converter

Page 21: Mum, I want to be a Groovy full-stack developer
Page 22: Mum, I want to be a Groovy full-stack developer

FlickrDownloader

▷ Spring Boot CLI

▷ Download Flickr Interesting pictures

▷ Jsoup, GPars

▷ 55 lines of Groovy code(microservice?)

Page 23: Mum, I want to be a Groovy full-stack developer

@Slf4j@EnableScheduling@Grab('org.jsoup:jsoup:1.8.1')@Grab('commons-io:commons-io:2.4')@Grab('org.codehaus.gpars:gpars:1.2.1')class FlickrDownloader {

static final String FLICKER_INTERESTING_URL = "https://www.flickr.com/explore/interesting/7days"

static final String WORK_DIR = "./work" final File workDir = new File(WORK_DIR)

@Scheduled(fixedRate = 30000L) void downloadFlickrInteresting() { def photos = extractPhotosFromFlickr()

withPool { photos.eachParallel { photoUrl -> log.info "Downloading photo ${photoUrl}" def tempFile = download(photoUrl)

FileUtils.moveFileToDirectory(tempFile, workDir, true) } } }

}

FlickrDownloader

Page 24: Mum, I want to be a Groovy full-stack developer

@Slf4j@EnableScheduling@Grab('org.jsoup:jsoup:1.8.1')@Grab('commons-io:commons-io:2.4')@Grab('org.codehaus.gpars:gpars:1.2.1')class FlickrDownloader {

static final String FLICKER_INTERESTING_URL = "https://www.flickr.com/explore/interesting/7days"

static final String WORK_DIR = "./work" final File workDir = new File(WORK_DIR)

@Scheduled(fixedRate = 30000L) void downloadFlickrInteresting() { def photos = extractPhotosFromFlickr()

withPool { photos.eachParallel { photoUrl -> log.info "Downloading photo ${photoUrl}" def tempFile = download(photoUrl)

FileUtils.moveFileToDirectory(tempFile, workDir, true) } } }

}

FlickrDownloader

Page 25: Mum, I want to be a Groovy full-stack developer

FlickrDownloader

@Slf4j@EnableScheduling@Grab('org.jsoup:jsoup:1.8.1')@Grab('commons-io:commons-io:2.4')@Grab('org.codehaus.gpars:gpars:1.2.1')class FlickrDownloader {

static final String FLICKER_INTERESTING_URL = "https://www.flickr.com/explore/interesting/7days"

static final String WORK_DIR = "./work" final File workDir = new File(WORK_DIR)

@Scheduled(fixedRate = 30000L) void downloadFlickrInteresting() { def photos = extractPhotosFromFlickr()

withPool { photos.eachParallel { photoUrl -> log.info "Downloading photo ${photoUrl}" def tempFile = download(photoUrl)

FileUtils.moveFileToDirectory(tempFile, workDir, true) } } }

}

private List extractPhotosFromFlickr() { Document doc = Jsoup.connect(FLICKER_INTERESTING_URL).get() Elements images = doc.select("img.pc_img")

def photos = images .listIterator() .collect { it.attr('src').replace('_m.jpg', '_b.jpg') }

photos}

Page 26: Mum, I want to be a Groovy full-stack developer

FlickrDownloader

@Slf4j@EnableScheduling@Grab('org.jsoup:jsoup:1.8.1')@Grab('commons-io:commons-io:2.4')@Grab('org.codehaus.gpars:gpars:1.2.1')class FlickrDownloader {

static final String FLICKER_INTERESTING_URL = "https://www.flickr.com/explore/interesting/7days"

static final String WORK_DIR = "./work" final File workDir = new File(WORK_DIR)

@Scheduled(fixedRate = 30000L) void downloadFlickrInteresting() { def photos = extractPhotosFromFlickr()

withPool { photos.eachParallel { photoUrl -> log.info "Downloading photo ${photoUrl}" def tempFile = download(photoUrl)

FileUtils.moveFileToDirectory(tempFile, workDir, true) } } }

}

private File download(String url) { def tempFile = File.createTempFile('flickr_downloader', '') tempFile << url.toURL().bytes

tempFile}

Page 27: Mum, I want to be a Groovy full-stack developer

FlickrDownloader

@Slf4j@EnableScheduling@Grab('org.jsoup:jsoup:1.8.1')@Grab('commons-io:commons-io:2.4')@Grab('org.codehaus.gpars:gpars:1.2.1')class FlickrDownloader {

static final String FLICKER_INTERESTING_URL = "https://www.flickr.com/explore/interesting/7days"

static final String WORK_DIR = "./work" final File workDir = new File(WORK_DIR)

@Scheduled(fixedRate = 30000L) void downloadFlickrInteresting() { def photos = extractPhotosFromFlickr()

withPool { photos.eachParallel { photoUrl -> log.info "Downloading photo ${photoUrl}" def tempFile = download(photoUrl)

FileUtils.moveFileToDirectory(tempFile, workDir, true) } } }

}

Page 28: Mum, I want to be a Groovy full-stack developer

FlickrDownloader

2016-05-27 21:56:11.139 INFO 16447 --- [111617-worker-1] polaromatic.FlickrDownloader2016-05-27 21:56:11.139 INFO 16447 --- [111617-worker-2] polaromatic.FlickrDownloader2016-05-27 21:56:11.139 INFO 16447 --- [111617-worker-3] polaromatic.FlickrDownloader2016-05-27 21:56:11.354 INFO 16447 --- [111617-worker-1] polaromatic.FlickrDownloader2016-05-27 21:56:11.375 INFO 16447 --- [111617-worker-2] polaromatic.FlickrDownloader2016-05-27 21:56:11.527 INFO 16447 --- [111617-worker-3] polaromatic.FlickrDownloader2016-05-27 21:56:11.537 INFO 16447 --- [111617-worker-2] polaromatic.FlickrDownloader2016-05-27 21:56:11.612 INFO 16447 --- [111617-worker-2] polaromatic.FlickrDownloader2016-05-27 21:56:11.693 INFO 16447 --- [111617-worker-2] polaromatic.FlickrDownloader

2016-05-27 22:02:17.019 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader2016-05-27 22:02:19.451 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader2016-05-27 22:02:21.661 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader2016-05-27 22:02:22.079 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader2016-05-27 22:02:22.877 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader2016-05-27 22:02:23.392 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader2016-05-27 22:02:23.749 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader2016-05-27 22:02:24.250 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader2016-05-27 22:02:24.695 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader

Page 29: Mum, I want to be a Groovy full-stack developer

FlickrDownloader

2016-05-27 21:56:11.139 INFO 16447 --- [111617-worker-1] polaromatic.FlickrDownloader2016-05-27 21:56:11.139 INFO 16447 --- [111617-worker-2] polaromatic.FlickrDownloader2016-05-27 21:56:11.139 INFO 16447 --- [111617-worker-3] polaromatic.FlickrDownloader2016-05-27 21:56:11.354 INFO 16447 --- [111617-worker-1] polaromatic.FlickrDownloader2016-05-27 21:56:11.375 INFO 16447 --- [111617-worker-2] polaromatic.FlickrDownloader2016-05-27 21:56:11.527 INFO 16447 --- [111617-worker-3] polaromatic.FlickrDownloader2016-05-27 21:56:11.537 INFO 16447 --- [111617-worker-2] polaromatic.FlickrDownloader2016-05-27 21:56:11.612 INFO 16447 --- [111617-worker-2] polaromatic.FlickrDownloader2016-05-27 21:56:11.693 INFO 16447 --- [111617-worker-2] polaromatic.FlickrDownloader

2016-05-27 22:02:17.019 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader2016-05-27 22:02:19.451 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader2016-05-27 22:02:21.661 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader2016-05-27 22:02:22.079 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader2016-05-27 22:02:22.877 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader2016-05-27 22:02:23.392 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader2016-05-27 22:02:23.749 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader2016-05-27 22:02:24.250 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader2016-05-27 22:02:24.695 INFO 9872 --- [pool-1-thread-1] polaromatic.FlickrDownloader

Page 30: Mum, I want to be a Groovy full-stack developer

4.Frontend

Page 31: Mum, I want to be a Groovy full-stack developer
Page 32: Mum, I want to be a Groovy full-stack developer

Frontend

▷ MarkupTemplateEngine (HTML)

▷ Websockets

▷ Grooscript

Page 33: Mum, I want to be a Groovy full-stack developer

HTML

yieldUnescaped '<!DOCTYPE html>'

html { head { title "Polaromatic"

link(rel: 'stylesheet', href: '/css/app.css') link(rel: 'stylesheet', href: '/css/gh-fork-ribbon.css')

['webjars/sockjs-client/0.3.4-1/sockjs.min.js', 'webjars/stomp-websocket/2.3.1-1/stomp.min.js', 'webjars/jquery/2.1.3/jquery.min.js', 'webjars/handlebars/2.0.0-1/handlebars.min.js', 'js/Connection.js'] .each { yieldUnescaped "<script src='$it'></script>" } }}

Page 34: Mum, I want to be a Groovy full-stack developer

HTMLbody { ...

div(id: 'header') { div(class: 'center') { a(href: 'https://github.com/ilopmar/contest', target: 'blank') { img(src: 'images/polaromatic-logo.png') } p('Polaromatic') span('Powered by Spring Boot') } } div(id: 'timeline', class: 'center')}

script(id: 'photo-template', type: 'text/x-handlebars-template') { div(class: 'photo-cover') { div(class: 'photo', style: 'visibility:hidden; height:0') { img(src: '{{image}}') } }}

yieldUnescaped "<script>Connection().start()</script>"}

Page 35: Mum, I want to be a Groovy full-stack developer

Websockets

@Configuration@EnableWebSocketMessageBrokerclass WebsocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

@Override void configureMessageBroker(MessageBrokerRegistry config) { config.enableSimpleBroker '/notifications' }

@Override void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint('/polaromatic').withSockJS() }}

Page 36: Mum, I want to be a Groovy full-stack developer

Websockets

class BrowserPushService {

@Autowired SimpMessagingTemplate template

Photo pushToBrowser(Photo photo) { log.debug "Pushing file to browser: ${photo.output}"

String imageB64 = new File(photo.output).bytes.encodeBase64().toString()

template.convertAndSend "/notifications/photo", imageB64

return photo }}

Page 37: Mum, I want to be a Groovy full-stack developer

Websockets

class BrowserPushService {

@Autowired SimpMessagingTemplate template

Photo pushToBrowser(Photo photo) { log.debug "Pushing file to browser: ${photo.output}"

String imageB64 = new File(photo.output).bytes.encodeBase64().toString()

template.convertAndSend "/notifications/photo", imageB64

return photo }}

<chain input-channel="incommingFilesChannel"> <service-activator ref="fileService" method="preprocessFile"/> <service-activator ref="imageConverterService" method="applyEffect"/> <service-activator ref="browserPushService" method="pushToBrowser"/> <service-activator ref="metricsService" method="updateMetrics"/> <service-activator ref="fileService" method="deleteTempFiles"/></chain>

Page 38: Mum, I want to be a Groovy full-stack developer

class Connection { @GsNative def initOn(source, path) {/* var socket = new SockJS(path); return [Handlebars.compile(source), Stomp.over(socket)]; */}

def start() { def source = $("#photo-template").html() def (template, client) = initOn(source, '/polaromatic') client.debug = null

client.connect(gs.toJavascript([:])) { -> client.subscribe('/notifications/photo') { message -> def context = [image: 'data:image/png;base64,' + message.body] def html = template(context) $("#timeline").prepend(html) $("#timeline .photo:first-child img").on("load") { $(this).parent().css(gs.toJavascript(display: 'none', visibility: 'visible', height: 'auto')) $(this).parent().slideDown() } } } }}

Grooscript (Javascript)

Page 39: Mum, I want to be a Groovy full-stack developer

5.Android

Page 40: Mum, I want to be a Groovy full-stack developer
Page 41: Mum, I want to be a Groovy full-stack developer

Android App

▷ Disclaimer: I'm not an Android developer

▷ Lazybones template (@marioggar)

▷ Traits, @CompileStatic

▷ SwissKnife

Page 42: Mum, I want to be a Groovy full-stack developer
Page 43: Mum, I want to be a Groovy full-stack developer

Android

trait Toastable { @OnUIThread void showToastMessage(String message) { Toast toast = Toast.makeText(applicationContext, message, Toast.LENGTH_SHORT) toast.show() }}

Page 44: Mum, I want to be a Groovy full-stack developer

Android

trait Toastable { @OnUIThread void showToastMessage(String message) { Toast toast = Toast.makeText(applicationContext, message, Toast.LENGTH_SHORT) toast.show() }}

@CompileStaticpublic class ShareActivity extends Activity implements Toastable { ...

showToastMessage(getString(R.string.share_ok_msg))

...

}

Page 45: Mum, I want to be a Groovy full-stack developer

6.Tests

Page 46: Mum, I want to be a Groovy full-stack developer

Tests

▷ Spock Framework

▷ Version 1.0 (more than 2 years now)

▷ JUnit compatible (but way better)

Page 47: Mum, I want to be a Groovy full-stack developer

Spockclass BrowserPushServiceSpec extends Specification {

void 'should push a converted photo to the browser'() { given: 'a photo' def output = File.createTempFile("output", "") def photo = new Photo(output: output.path)

and: 'a mocked SimpMessagingTemplate' def mockSimpMessagingTemplate = Mock(SimpMessagingTemplate)

and: 'the push service' def browserPushService = new BrowserPushService(template: mockSimpMessagingTemplate)

when: 'pushing the photo to the browser' browserPushService.pushToBrowser(photo)

then: 'the photo is pushed' 1 * mockSimpMessagingTemplate.convertAndSend('/notifications/photo', "") }}

Page 48: Mum, I want to be a Groovy full-stack developer

7.Build tool

Page 49: Mum, I want to be a Groovy full-stack developer

Build tool

▷ Gradle

▷ Multiproject to build backend, documentation and android

Page 50: Mum, I want to be a Groovy full-stack developer

Gradle

subprojects { buildscript { repositories { jcenter()

} }

repositories { jcenter() }}

task wrapper(type: Wrapper) { gradleVersion = '2.2.1'}

include 'polaromatic-back'include 'polaromatic-groid'include 'polaromatic-docs'

build.gradle settings.gradle

Page 51: Mum, I want to be a Groovy full-stack developer

8.Documentation

Page 52: Mum, I want to be a Groovy full-stack developer

Documentation

▷ Asciidoctor (FTW!)

▷ Gradle plugin

▷ Backends: html, epub, pdf,...

Page 53: Mum, I want to be a Groovy full-stack developer

Asciidoctor

buildscript { dependencies { classpath 'org.asciidoctor:asciidoctor-gradle-plugin:1.5.2' }}

apply plugin: 'org.asciidoctor.convert'

asciidoctor { sourceDir 'src/docs' outputDir "${buildDir}/docs"

attributes 'source-highlighter': 'coderay', toc : 'left', icons : 'font'}

Page 54: Mum, I want to be a Groovy full-stack developer

Asciidoctor[source,xml,indent=0].src/main/resources/resources.xml----include::{polaromaticBackResources}/resources.xml[tags=appFlow]----<1> Define the integration with the file system<2> Preprocess the file received<3> Apply the Polaroid effect<4> Send the new photo to the browser using Websockets<5> Update the metrics<6> Delete all temporary files

Page 55: Mum, I want to be a Groovy full-stack developer

Asciidoctor[source,xml,indent=0].src/main/resources/resources.xml----include::{polaromaticBackResources}/resources.xml[tags=appFlow]----<1> Define the integration with the file system<2> Preprocess the file received<3> Apply the Polaroid effect<4> Send the new photo to the browser using Websockets<5> Update the metrics<6> Delete all temporary files

<!-- tag::appFlow[] --><file:inbound-channel-adapter directory="work" channel="incommingFilesChannel"/> <!--1-->

<chain input-channel="incommingFilesChannel"> <service-activator ref="fileService" method="preprocessFile"/> <!--2--> <service-activator ref="imageConverterService" method="applyEffect"/> <!--3--> <service-activator ref="browserPushService" method="pushToBrowser"/> <!--4--> <service-activator ref="metricsService" method="updateMetrics"/> <!--5--> <service-activator ref="fileService" method="deleteTempFiles"/> <!--6--></chain><!-- end::appFlow[]-->

Page 56: Mum, I want to be a Groovy full-stack developer

Asciidoctor

Page 57: Mum, I want to be a Groovy full-stack developer

9.Summary

Page 58: Mum, I want to be a Groovy full-stack developer

“Groovy, groovy everywhere...

Page 59: Mum, I want to be a Groovy full-stack developer

Thanks!Any questions?

@ilopmar

[email protected]

https://github.com/ilopmar

Iván López

http://bit.ly/fullstack-groovy


Recommended