Confirmation dialog in EmberJs

I am fairly new to EmberJs and I needed to create a nice confirmation dialog when user clicks a delete button. I’ve came up with following solution. Use it or copy it as you wish (please let me know in comments) or propose me a better solution if you know one, I will be glad to improve 🙂

Scenario

We have a list of items displayed in a table. Each item has a name, ID and a delete button. When user clicks it, confirmation dialog should appear.

Solution

For modal dialog we will use Ember Modal component. It will be wrapped inside {{#if}} helper, like this

{{#if hasItemToDelete}}
    {{#modal-dialog close=(action 'closeConfirmationDialog') translucentOverlay=true}}
        <h2 class="ui header">{{t "confirmation.are-you-sure-to-delete-name-placeholder" name=itemToDelete.name}}</h2>
        
        <div class="ui divider"></div>
        <button type="button" {{action "doRemoveItem"}} class="ui button primary">{{t "delete"}}</button>
        <button type="button" {{action "closeConfirmationDialog"}} class="ui button right floated">{{t "cancel"}}</button>
        
    {{/modal-dialog}}
{{/if}}

The result should look like this

confirm_delete

 

You may notice that the {{modal-dialog}} block is wrapped in {{#if hasItemToDelete}}. When user clicks a delete button, the clicked item is stored in variable itemToDelete. Then I have a computed property hasItemToDelete which is true when itemToDelete is not null. Like this:

import Ember from 'ember';
const {computed} = Ember;

export default Ember.Component.extend({
    i18n: Ember.inject.service(),
    
    itemToDelete: null,
    
    hasItemToDelete: computed('itemToDelete', function(){
       return this.get('itemToDelete') != null; 
    }),
    
    _closeConfirmationDialog: function() {
      this.set('itemToDelete', null);
    },
    
    items: [
      {id: 1, name: 'first item'},
      {id: 2, name: 'second item'},
      {id: 3, name: 'third item'},
      {id: 4, name: 'fourth item'},
      {id: 5, name: 'fifth item'},
    ],

    actions: {
      removeItem(item) {
          this.set('itemToDelete', item);
      },
      
      /**
       * DO NOT CALL THIS METHOD DIRECTLY
       * BUT ALWAYS VIA CONFIRMATION DIALOG
       */
      doRemoveItem() {
          const itemToDelete = this.get('itemToDelete');
          
          if (!itemToDelete){
              return;
          }
          
          // normally you would call something like itemToDelete.destroyRecord()
          // but this just a simplified example ....
          // so ... you know what to do :)
      },
     }
}

Working example

Unfortunately Twiddle does not let me use {{modal-dialog}} helper, even when it is in the dependencies, so I had to omit it.

Sticky header menu with animation using Javascript

The problem

I needed to create animated menu, which will stay always sticked to the top and will collapse or expand as user scroll

How it should look

Click the image for a live version – Wisephora.com

animatedStickyMenu

What do you need to implement it?

You will need a little bit Javascript and CSS. There are several ways how to implement it, at first I wanted to do it purely by using CSS transitions, but I got whole bunch of weird bugs, so finally I implemented it using mostly Javascript.

The main idea is that menu switches between two states, “expanded” and “collapsed”. I have an eventListener for scroll event (needs to be debounced btw). It calculates how far the user has scrolled, and if some predefined threshold is reached, it switches the menu to “collapsed” state. Or wice wersa when user scrolls up.

The menu is wrapped in a wrapper div, which becomes the “sticky” part via position: fixed.

Do you a simplified example to show me?

Sure 🙂 I have created a JsBin – http://jsbin.com/bavireb/1/edit?html,css,js,output. Check it out, I hope it will save you some precious time. Let me know in comments what you think.  Happy coding!

Debounced actions in EmberJs

I was developing EmberJS component based on input field. It’s main purpose was auto-save when user stops typing. That is a typing use-case for debounce and should be easy and straightforward. Right?

Well, since I am new to Ember, it was not so easy for me. It was more like a riddle and it took me a considerable time to solve. I finally found a solution using debounce from the underscore library. It worked, yes, but obviously it was not ideal, since Ember has its own implementation in Ember.run.debounce. But it is not very much documented and I could not find example for my use case.

Luckilly, at Topmonks, we do code reviews, so a colleague of mine spotted my struggle and instructed me on how to propertly use the Ember.run.debounce.

The final solution is below. The Ember way how to do debounce. I hope this will save someone’s time 🙂 If so, please let me know in the comments or tweet about it.

export default Ember.Component.extend({
  actions: {    
        save: function(){
          // the target method has to be defined in the component object, 
          // not in the actions object
          Ember.run.debounce(this, this.doSave, 1000);
        }
    },
    
    doSave: function() {
      // perform the save logic here
    }
});

The handlebars template is straight forward

{{input value=myObject.myProperty key-up="save"}}

Ohlédnutí za konferencí Wisephora 2016

Děkuji Topmonks za super konferenci. Udělala mi radost 🙂

Nosné myšlenky, které jsem si odnesl

(Podle mých zápisků v notýsku, není to doslovný přepis, spíše zachycení poselství. Nebyl jsem na všech přednáškách, takže něco chybí… A něco mě třeba tolik nezaujalo 🙂 )

@steida – Člověk umře a Bůh se ho zeptá: “Tak co, jak bylo v ráji”?

@steida – Stát je to, co vznikne, když se lidé o stát nezajímají.

Celkově dost zajímavá přednáška. O anarcho-kapitalismu nevím skoro nic, nedokážu posoudit, nakolik je to utopie a nakolik by to mohlo fungovat v praxi. Stát chápu trochu jako protiváhu megakorporacím, protože je stát dokáže do jisté míry regulovat zákony. Otázka je, jestli v tom nejsem naivní 🙂

@vojtarocek – Každý obor, kde existují velké výdělky, není dostatečně optimalizovaný. Přijde nějaký disruptor, který celý obor překope. Typickým příkladem jsou velké banky.

@vojtarocek – Zmenšuje se nika pro chytré lidi. Pro většinu z nich už není možné zbohatnout.  Dříve někdo dokázal vydělat spoustu peněz, protože byl o něco chytřejší než ostatní a dokázal systém očůrat. To je dnes mnohem těžší. Algoritmy nás všechny srovnají…

@vojtarocek – Až naplno přijde virtuální realita a základní příjem, tak většina neproduktivní populace nebude mít důvod VR brýle sundat a postupně uhnijou ve virtuální realitě.

Tomáš Formánek – rozhovor s 95ti letým japonským kuchařem, který stále pracuje a roznáší lidem tofu ve své restauraci – http://www.madfeed.co/video/jiro-ono-and-rene-redzepi-have-a-cup-of-tea/

Ondřej Štefl – Základní stavba školních předmětů je z roku 1848.

Ondřej Štefl – Neptej se dítěte, co dostalo za známku, zeptej se ho, co mu udělalo ten den ve škole radost.

Ondřej Štefl – Ve Scio škole mají děti 4-5 týdenní projekty a mezi nimi je sabatický týden, kdy se rozhodují na jakém projektu chtějí dělat příště.

Petr Skondrojanis – Ve věku tlaku na zisk a efektivitu nemůžete očekávat, že zvítězí humanita nad ziskem.

Petr Skondrojanis – Home office není benefit, ale lidský přístup.

Petr Ludwig – Nachytej lidi při tom, že dělají něco dobře (a řekni jim to).

Petr Ludwig – Ikigai ikigai

Petr Ludwig – Vyhrál jsi? Prohrál jsi? To jsou špatné otázky. Zeptej se “udělal jsi, vše co jsi mohl?”

Petr LudwigNaučená bezmoc (odkaz na rozhovor)

Dělali výzkum s hlodavci a toho křečka dali do akvária bez vody a přikryli ho skleněným víkem. První den se snažil vyskočit a hlavou narážel do skleněného víka. Druhý den skákal méně, třetí ještě méně. Po pár dnech to víko sundali a ten křeček už se nikdy nepokusil vyskočit. Získal naučenou bezmoc, což potkává i nás lidi. Párkrát něco zkusíme, když to nejde, tak už se o to dále nepokoušíme. Na sloních farmách najdete slony přivázané tenkým provázkem. Oni vám řeknou, že tím tenkým provázkem přivázali ještě slůně, které se přesvědčilo o tom, že provázek nejde utrhnout. Poté vyrostlo v ohromného slona a už se o to nikdy nepokusilo, protože získalo tu naučenou bezmoc. Je to taková ta rezignovanost.

Tomáš Hrivnák – Máme atrofovanou schopnost prožívat vůči schopnosti programovat.

Tomáš Hrivnák – Hodně mluví o Viktoru E. Franklovi, rakousko-židovském psychologovi, který prošel koncentračním táborem. Frankl přinesl termín logoterapie – terapie smyslem. Spousta lidí dnes nemá (svůj) smysl života a nahrazuje ho mocí (stoupání po kariérním žebříčku, nadvláda nad lidmi) nebo slastí (peníze, drogy, zážitky). S panem Hrivnákem jsem pak ještě krátce mluvil na baru. (viz níže)

Hlavní myšlenka rozhovoru s p. Hrivnákem

Lidé, hlavně muži, mají svoji identitu a smysl života spjatý se svou prací. Muži potřebuji pracovat, aby se cítili užiteční a mužní. Co se s nimi stane, když většinu práce nahradí stroje? Co to udělá s jejich sebehodnotou, pracovitostí, pocitem, že někam patří a že jejich život má smysl a řád? Už dnes má spousta mužů problém definovat svoji mužnost. Stírají se rozdíly mezi muži a ženami, ženy už muže tolik nepotřebují… Tento trend bude sílit. K tomu se přidává virtualizace,  určitá odtrženost od reality a ticha.

Feedback

Celkově dost dobrá konference. Velké díky všem lidem z Topmonks za uspořádání! Hodně mě potěšil workshop Taiči. Kdysi jsem taiči cvičil a úplně jsem zapomněl, jaký to je skvělý pocit 🙂

Některé přednášky byly silnější, jiné slabší a opakovaly to, co už zaznělo jinde. Ale to se asi dalo čekat… pokud je něco moc nové a nikdy jste to neslyšeli, tak si nejspíš řeknete, že je to blbost. Takže ok.

DSC_0546
Credit – http://nataliyayashchuk.pixieset.com/wisephora2016/

Tip na témata na příští rok

Tohle je čistě subjektivní, někoho třeba osloví jiné věci. Možná moje tipy patří spíše na nějakou konferenci o osobním rozvoji 🙂

Hledání smyslu – částečně to zaznělo na Hrivnákově přednášce, ale je to hodně silné téma, o kterém se, myslím, mluví málo. Lidé potřebují ve svém životě vidět (vyšší) smysl, často ho mají spojený s prací, ale tradiční práce ubývá a rychle se mění. Co to udělá s hodnotami nastupující generace?

Rodina – základní prvek společnosti se také rychle mění. Přibývá rozvodů, singles, mingles, lidé odkládají početí dítěte, mají spoustu jiných možností, o které se narozením dítěte připraví. Jaké vztahy budou převažovat? Jak důležité je mít stabilní rodinu? Za jakých podmínek? Dříve spolu lidé často zůstávali z ekonomických důvodů, dnes už nemusí. Je to dobře nebo špatně, a proč?

Instantní informace, overload a informační dieta – nikdy dříve jsme nevygenerovali tolik dat o sobě. Nikdy dříve jsme neměli tolik informací k dispozici. Spousta lidí je přilepená na svůj mobil, facebook, twitter, sociální síť. Přibývá lidí závislých na internetu a sociálních sítích, přibývá depresí z toho, že se lidé srovnávají s “dokonalými statusy” svých přátel a známých na FB. Jsme zvyklí mít všechny informace hned. Umíme čekat? Být zticha? Dnes máme milion způsobů, jak šířit svoje myšlenky do světa, ale možná ztrácíme schopnost naslouchat a kriticky myslet.

Spánek – lidé dnes spí v průměru o hodinu až hodinu a půl méně. Mnoho lidi “zkontroluje” facebook a email než jde spát, nebo si otevře dříve než se nasnídá. Večer koukáme na televizi, tablet, mobil atd… ale modré světlo monitorů má vliv na náš spánkový cyklus a probouzí nás. Hůře se pak usíná….

Pracovní doba – opravdu potřebujeme pracovat 8 hodin denně? Odkud se to vlastně vzalo? Kolik hodin denně potřebuje člověk pracovat, aby se cítil užitečný a naplněný a zároveň ho práce uživila?

 

Jak sehnat super práci

Tento článek je možná trochu předčasný, protože reálně ještě novou práci nemám. A tu starou už nemám 🙂 Čekám, jestli se rozeběhne jeden zahraniční projekt pod hlavičkou X-Team. Pokud ne, tak budu pracovat v jiné super firmě 🙂 Ale jak takovou super firmu sehnat? Jak najít práci, která bude zároveň kreativní, bude obsahovat nové výzvy, bude dobře placená a kde bude člověk mít možnost dělat na zajímavých projektech? Je jasné, že obyčejný LinkedIn profil na to nestačí. Ten je dobrý tak možná na to, aby vám chodily copy-paste nabídky a neosobní pozvánky od head-hunterů s defaultním textem. Je potřeba vystoupit z davu. Není to zas tak náročné, ale zabere to nějaký čas.

Tento článek je primárně pro programátory. Netuším, jak sehnat super práci jako krejčí nebo kuchař, ale některé body budou asi společné 🙂

TLDR:

Hlavní body

  • aktivně udržovaný vlastní blog
  • veřejně dostupný kód
  • odpovídat na otázky  na Stackoverflow (nepovinné, ale pomáhá to)
  • správně vyplněný LinkedIn profil
  • chodit na meetupy
  • pořád se učit a mít touhu se posouvat a růst. Musí to z vás být cítit.
  • angličtina
  • vědět, co chci a nechci

A teď trochu podrobněji pro ty, které to zatím neodradilo 🙂

Blog

Blog je skvělý způsob, jak si procvičíte psaní. Možná se to nezdá, ale psaní “obyčejného textu” vám pomůže psát lepší kód. Naučíte se psát stručně a jasně. A takový by měl být i váš kód. (Přiznám se ale, že s tímhle ještě bojuji.) Druhá věc je, že blog vám pomáhá vytvořit si online identitu. I když píšete třeba jen jednou za měsíc, tak to tolik nevadí. Postupně se nahromadí (snad) kvalitní texty, které o vás dohromady tvoří nějaký obrázek. Zároveň můžete někoho inspirovat nebo ukázat řešení nějakého technického problému. V neposlední řadě se blog hodí i jako místo, kam si můžete zapisovat nějaké technické vychytávky, které byste jinak zapomněli. Taková externí paměť 🙂

Veřejně dostupný kód

Asi není třeba představovat Github. Co ale možná nevíte je, že ho používají i head-hunteři pro hledání talentů. Více o tom třeba zde a zde. Opravdu to funguje, už mi takhle pár lidí napsalo. A když vám někdo napíše, že si dal tu práci a prohlédl si váš github, linkedin, prostě si dohledal všechny online informace o vás a pak vám napsal, že vás chce do firmy, tak to má mnohem větší sílu, než copy-paste email na LinkedIn. Rád si pak poslechnu jejich nabídku, většinou je zajímavá. Samozřejmě byste měli sdílet jen takový kód, který je prezentovatelný 🙂

Stackoverflow

Opět netřeba představovat. Každý, kdo chvíli programoval, dřív nebo později na SO zavítal. Většina lidí, kteří mají na SO účet, si ho založí jen proto, aby položili jednu, dvě otázky a dál ho nepoužívají. Takže je celkem jednoduché se (alespoň ze začátku) posouvat nahoru. Někdy se stane, že strávíte s odpovědí třeba 10 minut a tázající pak vybere jako správnou odpověď tu od někoho jiného. Vyplatí se přečíst si odpovědi ostatních 🙂 Někdo také třeba používá SO jako učebnici. Cíleně vyhledává otázky, na které nezdá okamžitou odpověď a dá si práci s tím, tu odpověď najít.

LinkedIn

V současnosti asi nejpoužívanější síť pro hledání kandidátů. Bohužel je tam ale hodně spamu. Spam ignorujte, na ostatní nabídky je dobré odpovídat. I kdyby to mělo být jen “Děkuji za nabídku, nic teď nehledám” nebo “hledám spíše X a Y”. Buďte zdvořilí a slušní 🙂 Odpověď vám moc času nezabere a můžete si tak nechat dveře otevřené do budoucna. Je škoda nechat si utéct příležitost jen proto, že “linkedin je spam a tak všechno ignoruju”. Doporučuji také pročíst si pár článků o tom, jak si zlepšit LinkedIn rating (opravdu to funguje). Je dobré také sdílet na LN kvalitní technické články, které vás zaujaly. Podobně jako v případě blogu si tak budujete online vizitku a dáváte najevo zájem o svůj obor. Myslete na to, že je to často to jediné, co o vás firma ví ve chvíli, kdy zvažuje, jestli vám vůbec napsat. Je ve vašem zájmu udělat dobrý dojem 🙂

Programátorské meetupy

Sice vám nepomůžou najít práci snů, ale zvednou vám morálku 🙂 Je super potkat další zapálené lidi, kteří dělají v technologiích, o kterých jste možná v životě neslyšeli. Dozvíte se třeba o věcech, které se dějí mimo váš běžný záběr. Může to ve vás probudit zvědavost a zájem o obor. Potkáte nové lidi, seznámíte se. Dáte si spolu pivo 🙂

Pořád se učit

K tomu asi není co dodat. Jste jako ryba v rychlé řece. Ve chvíli, kdy přestanete plavat dopředu, tak vás proud bere zpátky. Musíte se posouvat, jinak zakrníte. IT obor se vyvíjí tak rychle, že je to neuvěřitelné. Pokud vás nebaví se učit nové věci, nebo vás to dokonce děsí, tak jste si možná spletli obor. Čtěte knihy, choďte na přednášky, předplaťte si Pluralsight…

Vědět, co chci a nechci

Každá firma píše v inzerátu seznam požadavků na kandidáta. Je dobré sepsat si podobný seznam vašich požadavků na firmu. Prací strávíme třetinu dne, tak ať je to dobře strávený čas! Pomůže mi firma v mém rozvoji? Nebo mě bude tlačit do splnění deadlinů a na nějaké učení se vlastně nezbyde nikdy čas? Budu pracovat s lidmi, kterých si vážím a od kterých se můžu něco naučit? Nebo budu pracovat v nepřátelském kolektivu, kde se přehazuje odpovědnost jako horký brambor? Budu pracovat na zajímavých projektech? Budu dělat na něčem, co mi přijde užitečné a svým způsobem krásné? Nebo budu jen vyměňovat svůj čas a energii za peníze? Jak často se ve firmě nestíhá? Jak často se dělají přesčasy? Jakým způsobem jsou vedené projekty? Jaký hardware budu mít? A v neposlední řadě: co z toho budu mít? Budu pracovat jen za peníze? (nebo za kupu peněz? :)) Budu mít třeba nějaký podíl na zisku? A co třeba masérka na pracovišti? (vážně jsem to v jednom inzerátu viděl a přišlo mi to super.)

Angličtina

Měli byste umět alespoň tolik, abyste dokázali přelouskat technickou dokumentaci 🙂 Pokud ale chcete fakt dobrou práci, tak se bez angličtiny neobejdete. Svět je velký a plný příležitostí. Ale ty příležitosti většinou nemluví česky 🙂 Nehledě na to, že kvalitních zdrojů o programování v češtině je málo.

Další čtení k tématu

3 hlavní věci, které firmy hledají u developera –  http://x-team.com/2015/08/3-traits-when-hiring-developers/

Příběh člověka, který strávil 30 dní odpovídáním otázek na Stackoverflow – https://www.airpair.com/stackoverflow/posts/30-days-of-answers-on-stack-overflow

Jak sehnat práci z domova a pracovat do zahraničí – https://plus.google.com/+MichalHantl/posts/GDWTAVUJaPm

Jak vyjednávat o penězích. V angličtině je na to kupa článků, vyplatí se pročíst si důkladně i komentáře pod články, bývají tam cenné informace.  Například tento článek How to negotiate your salary

Proč vývojáři odcházejí z IT firem – http://www.robertdresler.cz/2014/01/proc-vyvojari-odchazeji-z-it-firem.html

PS: Můj aktuální status

Od začátku ledna jsem měl pracovat pro X-Team, což je jedna hodně dobrá firma z Austrálie. Pak se projekt odložil na polovinu ledna. Teď je konec ledna a pořád ještě čekám. Pomalu mi ale dochází trpělivost, takže už odpovídám na další nabídky. Jsem zvědavý, kde nakonec skončím 🙂

My Bower/Gulp settings

This is my Gulp.js workflow settings based on examples shown in this PluralSight course (I really recommend you to watch it).

I am using these gulp plugins:

When you install some library via bower you should do so with the –save option, so that it automatically updates the bower.json file. Like this

bower install angular --save

That will add following line to bower.json file:

 "dependencies": {
    "angular": "~1.4.8"
  }

Why do you need it?

Because the main-bower-files plugin will then read this “dependencies” section and inject listed scripts into the index.html file. Below is the responsible part of gulp configuration.

var mainBowerFiles = require('main-bower-files');
....
gulp.task('copyVendorScripts', function () {
 // call to mainBowerFiles() returns list of scripts
 // something like this: ['angular', 'satellizer', 'jquery'] etc...
 return gulp.src(mainBowerFiles())
 .pipe(gulp.dest('path/to/destination/folder'));
})

So, you do not really “need” to use the main-bower-files, but then you need to manually list the scripts in the section above. This way is just a bit more convenient and automatic. But I guess there are other ways how to achieve this goal.

How does inject work?

It basically filles placeholders in the index.html file. The sample index.html file may look like this:

<!DOCTYPE html>
<html>
<head>
  <title>Module</title>
  <!-- inject:css -->
  ALL CSS FILES WILL BE INJECTED HERE
  <!-- endinject -->
</head>
<body>
  <h1>Module</h1>
  <!-- inject:js -->
  ALL JS FILES WILL BE INJECTED HERE
  <!-- endinject --> 
</body> 
</html>

Most likely you will want to group your scripts into named sections. For example “vendor” scripts will contain all 3rd party libraries, such as AngularJs or jQuery etc. The “app” scripts will contain all your application logic. In order to achieve that you can use named sections, like this.

  <!-- vendorInject:js -->
  ALL VENDOR JS FILES WILL BE INJECTED HERE
  <!-- endinject --> 

  <!-- appInject:js -->
  ONLY APP SPECIFIC JS FILES WILL BE INJECTED HERE
  <!-- endinject -->

Your gulpfile will then look like this:

gulp.task('vendors', function () {

 var tempVendors = gulp.src('path/to/vendor/scripts', {
 read: false
 });

 return gulp.src('path/to/index.html/file')
  .pipe(inject(tempVendors, {
     relative: true,
     // the name must correspond to that in index.html
     name: 'vendorInject'
   }))
   .pipe(gulp.dest('path/to/destination/folder'));
});

 

PS: The described workflow is not from my head, but based on Alexander Zanfir’s. I am quite new to Gulp/Grunt/Bower etc tools, so comments and suggestions for improvements are welcomed and appreciated.

If anyone is interested, I’ve also created Github repository based on the Pluralsight course.

Recover deleted files by VisualStudio

Image this:

You are working on a new functionality. You open up your IDE and start hacking away. You create several new files, invest your time and effort and then you accidentally delete the wrong file. No problem, that’s what we have Git for, right? But you forgot to add that now deleted file to version control. Aaarrrrggh. There is no way to retrieve that lost file. Bad, bad stupid little programmer.

There is one little hack to save you (if you are in Windows):

Locate the folder of deleted file and press Ctrl-Z. That will undo your deletion.

It has just saved my day 🙂

openfolder

 

quote-Mae-West-to-err-is-human-but-it-feels-104651

 

Difference between async loops in C#

What do you think will happen when you run these 2 snippets of code? They both looks quite similar at the first sight, don’t they.

This one

var results = new List<SomeClass>();
mylist.ForEach(async item =>
  {
      var result = await SomeDbQueryAsync(item);
      results.Add(result);
  });

versus this one

var results = new List<SomeClass>();
foreach(var item in mylist)
  {
      var result = await SomeDbQueryAsync(item);
      results.Add(result);
  });

The code looks remarkably similar, but the 1st sample will throw this exception “A second operation started on this context before a previous asynchronous operation completed. Use ‘await’ to ensure that any asynchronous operations have completed before calling another method on this context. Any instance members are not guaranteed to be thread safe

The reason is that the first approach should be used only for event handlers and never for DB query. As explained in this Stackoverflow answer. The first approach will start several asynchronous operations (one for each item in the list) and never wait for them to finish. (They will finish eventually, but the caller does not care).

There is but 1 case when the first approach will actually work. Switch to debug mode and insert a breakpoint somewhere inside the SomeDbQueryAsync() method. I am writing that because it bite me too 🙂

Happy coding!

tumblr_mqbs65pyML1r8lg7to1_500
http://programmerryangosling.tumblr.com/