Browsing the archives for the work safe category.

Always be Knolling – the joy of auto formatters

work safe

Knolling is the practice of aligning your tools in your work area so they are easy to grab, easy to place down, and out of the way. In many workshops where the focus is on creativity and the work, the phrase Always Be Knolling has become a mantra of working clean and fast with respect to your tools.

Small things matter. Until they don’t.

There is a pattern adopted by many self-considered elite thinkers. They wear a Personal Uniform instead of a wardrobe. Steve Jobs was iconic in his turtle neck, jeans, and New Balance sneakers. Likewise, Albert Einstein wore a Gray suit and white shirt every day. The modern productiva have latched on the the ‘Personal Wardrobe’ as both a productivity hack and as a personal brand. They reveled in the ability to reduce the ‘trivial’ choices that let them spend their time on more important things in life.

While I don’t find the same joy in sartorial pursuits as some folks do, this does feel like either the girl who only eats chicken nuggets or that one guy who build a personality around a band he liked at age 14. Why was it always Metallica? However, I do have one winter jacket. We each have things we care about and things we don’t find interesting.

And that’s why the number one code review comment I HATE and never want to see again is someone taking time out of their day to take time out of my day to tell me the indentation of a section of a code file is ‘a bit wonky. Could you be a sport and fix it?’, just because their tabs happen to be set differently to mine in an xml file we should be editing with a designer anyway, but it was just easier because designer tools often fall short.

The compiler don’t care, and some days neither do I.

In some ecosystems this is solved using a code formatter or linter. The Go language and Elm both have community wide standards they use with no configurability. This means the triviality of which line gets the bracket is never going to be argued over. Not once.

And it may not suit you perfectly, but having a house style is useful.

Long story short, all your code should be formatted before it’s committed. And you shouldn’t have to think about it. Either Format when Visual Studio Code saves, or Learn how to use git hooks to apply formatting.

It is always much easier to clean as you go. And the best cleaning is when you don’t even think of it was ‘work’.

Comments Off on Always be Knolling – the joy of auto formatters

Exploring Android Dependability

work safe

I’m currently trying to load a group of third-party Android Libraries in to a Xamarin application. The first thing we need to do is find out what are the dependent libraries. I had a Java Android project using the correct Gradle invocations to bring in the libraries. Using this bit of Gradle documentation for Viewing and Debugging Dependencies, I was able to build out a list of all the AAR and JAR files used in the example app.

$ gradle dependencies > dependencies.txt

Then I could cherry pick the AAR and JAR files out of the ~/.gradle/caches directory tree when creating my Xamarin Android Binding Libraries.

No Comments

Simple Parsers for Simple Problems

work safe

One minor detail I’ve noticed about working on the Advent of Code problems this year is the ubiquity of parsing. Every problem has an input string. It’s actually a lesson I learn every year as I build up my library of methods of processing text.

Some years, it’s fairly straight forward. I understand the text handling in C# fairly well. In others, it is more complex. Swift seems to want you to build an application were the input comes from a structured source like JSON or a database as it uses more complex APIs for splitting strings and such. Elm, while lovely, doesn’t have a clean mechanism for just reading in a static file.

Do I need a parser?

This year, I’m working in C#, but I still noticed there was an anti-pattern I saw that kept cropping up. I was reading input by counting lines or repeated splits. Look at the example data for the Day 5 problem.

0,9 -> 5,9
1,2 -> 1,6

Each line has a definition of a vent line. The vent lines are each pairs of points separated by the ” ” string. I was using string.Split(string n) to pry the data apart. Another option is using Regular Expressions to match the parts of each row. I wanted a more structured method of handling this.

Parser Combinator

In other languages, I’ve worked Monadic Parser Combinators. They are libraries that let you write small parsers that can be combined with other small parsers to make larger and more complex parsers.

What is a parser? It is just a function or object that turns a string in to a more structured or typed piece of data.

I found Sprache. Sprache is a parser combinator library that lets you build a definition of your expected string input and how those pieces get translated in to data.

Building Our Parser

Let’s start with a point parser that generates an instance of Point from the input string x,y.

static Parser<Point> _pointParser =
    from x in Parse.Decimal.Select(int.Parse)
  from _ in Parse.Char(',')
  from y in Parse.Decimal.Select(int.Parse))
  select new Point(x,y);

You see that we can use a number of built in helpers and now I have a parser that I can use for any point string to build a valid type.

Point point = _pointParser.Parse("5,4");
point.X.Should().Be(5);
point.Y.Should().Be(4);

Since Parsers can be combined, we can build out a vent line definition.

static Parser<VentLine> _lineParser =
    from p1 in _pointParser
  from _ in Parse.String(" -> ");
  from p2 in _pointParser
  select new VentLine(p1,p2);

Now each line can be parsed in to a vent line. Sweet!

But each file specifies a list of vent lines separated by new line characters

static Parser<IEnumerable<VentLine>> _listParser =
    (from ventLine in _lineParser
   from _ = Parse.String("\n").Maybe()
   select ventLine).Many();

This is all we need to read the file.

var vents = _listParser.Parse(File.ReadAllText(filename))
                    .ToArray();

Solving the problem is now an exercise left to the reader. But at least the parser was easy!

More Parsers to Check Out

One of my coworkers who checked this post out had a couple of things he wanted me to add about choosing your parser library in the .NET ecosystem. For C# users, he suggested Sprache might not be the most performant. Superpower is based on Sprache and focuses on better error messages and better performance. Pidgin is a different lineage of parser library focusing on speed. I’ve been told I should really check out Pidgin once I have some spare time. And as a final note, If I was using F#, I would definitely be looking at FParsec, a port of the Haskell Parsec library that defined the genre of Parser Combinators.

No Comments

Soundex

work safe

Let’s dip our toes in to computational English by exploring the classic algorithm, Soundex. Soundex was created in 1918 by Robert Russel and Margaret Odel King, but most people know it from work by Donald Knuth.

Soundex indexes words based on their ’sound’. It is one way to checking spelling based on similar sounds, so we should get similar scores for ‘been’ and ‘bean’ and, of course, ‘their’, ‘there’, and ‘they’re’.

Is it useful?

Probably not. There are newer, better phonetic algorithms like Metaphone and modern spell checkers are interested in keyboard distances between words. So it’s just for funsies. And that seems like a good enough reason for me!

Understanding the Algorithm

There is an interesting part of the wikipedia article where the editors re-order the steps to simplify some of the duplication detection. Great! Let’s use THAT version, and not necessarily the National Archives and Records Administration’s General Information Leaflet 55, “Using the Census Soundex”.

  1. Save the first letter, remove all occurrences of ‘h’ and ‘w’ except the first.
  2. Replace all consonants (including the first letter) with digits
  • bfpv 1
  • cgjkqsxz 2
  • dt 3
  • l 4
  • mn 5
  • r 6
  1. Replace all adjacent same digits with one digit
  2. Remove all vowels (including y), except the first letter
  3. If the first symbol is a digit, replace with the letter
  4. Pad with zeros or trim so it only has a letter and three digits

Some Intuition and Test Cases

Grate and great should be the same. Same and sim. In fact most homophones, like homafoen, should be the same.

The wikipedia article gives us some example values.

  • Robert ‘R163’
  • Rupert ‘R150’
  • Ashcroft ‘A261’
  • Tymczak ’T522’
  • Pfister ‘P236’

We can include a couple of hand picked cases, like ‘the’ and ‘a’ which are all zeros.

  • the ’T000’
  • a ‘A000’

The code for these examples is clear and readable.


module SoundexTests exposing (..)

import Expect
import Fuzz exposing (int, list, string, tuple)
import Soundex exposing (..)
import String
import Test exposing (..)


aboutSoundex : Test
aboutSoundex =
    describe "Soundex.soundex"
        [ describe "Pure Examples"
            [ test "Robert = R163" <|
                \() -> Expect.equal "R163" <| soundex "Robert"
            , test "Rupert = R163" <|
                \() -> Expect.equal "R163" <| soundex "Rupert"
            , test "Rubin = R150" <|
                \() -> Expect.equal "R150" <| soundex "Rubin"
            , test "Ashcroft = Ashcraft" <|
                \() -> Expect.equal (soundex "Ashcroft") (soundex "Ashcraft")
            , test "Ashcroft = A261" <|
                \() -> Expect.equal "A261" <| soundex "Ashcroft"
            , test "Tymczak = T522" <|
                \() -> Expect.equal "T522" <| soundex "Tymczak"
            , test "Pfister = P236" <|
                \() -> Expect.equal "P236" <| soundex "Pfister"
            , test "a = A000" <|
                \() -> Expect.equal "A000" <| soundex "a"
            , test "the = T000" <|
                \() -> Expect.equal "T000" <| soundex "the"
            ]
        , describe "Starbucks Names?"
            [ test "Brian & brain" <|
                \() -> Expect.equal (soundex "Brian") (soundex "brain")
            ]
        ]

But the Implementation!?

The first function pretty much describes the algorithm using the steps I listed above. I use types to help guide what comes next and focus on each piece as I can. I made the initial implementations of each step as dumb as possible, mostly passing from one stage to the next. As I filled out each step, I could see what was happening to my examples. I could see the error cases coalesce toward correctness. The output words show missing letters. Then numeric substitutions. And duplicates dropped. Then vowels go missing. Until it finally worked!

module Soundex exposing (Soundex, soundex)

import Regex


type alias Soundex =
    String


soundex : String -> Soundex
soundex word =
    ( String.toUpper <| String.left 1 word, String.toLower word )
        |> removeHandW
        |> consonantsToDigits
        |> removeAdjacentDigits
        |> removeVowelsAfterFirst
        |> setFirstCharacter
        |> paddOrTrimToThreeDigits


removeHandW : ( String, String ) -> ( String, String )
removeHandW =
    Tuple.mapSecond (Regex.replace Regex.All (Regex.regex "[hw]") (\_ -> ""))


consonantToDigit : Char -> Char
consonantToDigit a =
    if List.member a [ 'b', 'f', 'p', 'v' ] then
        '1'
    else if List.member a [ 'c', 'g', 'j', 'k', 'q', 's', 'x', 'z' ] then
        '2'
    else if List.member a [ 'd', 't' ] then
        '3'
    else if 'l' == a then
        '4'
    else if List.member a [ 'm', 'n' ] then
        '5'
    else if 'r' == a then
        '6'
    else
        a


consonantsToDigits : ( String, String ) -> ( String, String )
consonantsToDigits =
    Tuple.mapSecond (\body -> body |> String.toList |> List.map consonantToDigit |> String.fromList)


removeAdjacentDigits : ( String, String ) -> ( String, String )
removeAdjacentDigits =
    let
        rdcr : Char -> ( Char, List Char ) -> ( Char, List Char )
        rdcr current ( prev, acc ) =
            if prev /= current then
                ( current, current :: acc )
            else
                ( prev, acc )
    in
    Tuple.mapSecond
        (\body ->
            body
                |> String.toList
                |> List.foldr rdcr ( ' ', [] )
                |> Tuple.second
                |> String.fromList
        )


removeVowelsAfterFirst : ( String, String ) -> ( String, String )
removeVowelsAfterFirst =
    Tuple.mapSecond
        (Regex.replace Regex.All
            (Regex.regex "[aeiouy]")
            (\match ->
                if match.index == 0 then
                    match.match
                else
                    ""
            )
        )


setFirstCharacter : ( String, String ) -> String
setFirstCharacter ( lead, a ) =
    lead ++ String.dropLeft 1 a


paddOrTrimToThreeDigits : String -> String
paddOrTrimToThreeDigits a =
    a
        ++ "000"
        |> String.left 4

Reductio ad absurdum

Soundex is for matching things that ‘sound like’ other things. Like when my five year old mishears a Taylor Swift lyric to hilarious results. Or when the well-intentioned hard worker on the other side of the counter has to listen over the bustle of other customers at busy shop while standing next to HVAC and loud brewing equipment to hear you speak in your best indoor voice and has to make a best guess as to what your name might be.

Tobias has a 'Starbucks Name' of Tubes

My “Starbucks Name” Generator in action.

I’ve written a simple web toy that uses an open-sourced word list to attempt to find an English word that sounds like your name. Just like a barista might mishear or simplify your name.

  1. Generate a Soundex hash of all the words
  2. Generate the Soundex of the name
  3. Look up an array of matched words and pick a random one
  4. If misses, return the name because you have an unmistakable unique name.

For fun I added a list of US census first names to search for likely misses. The majority of which (6 out of 10) are spelling variants of ‘Yolanda

Give my Starbucks Name Generator a try, but you need to keep a few things in mind. It takes a while (10 – 20 seconds) to load all the words in to a Soundex lookup before you see anything. I could do this preloading in a web worker, but that would take me longer because the Elm doesn’t yet have a robust backgrounding story and the easy path would be to rewrite the algorithm in JavaScript, and that would defeat the point of using Elm. That said, after the loading, it’s pretty fast! Also, feel free to check out the elm-soundex code on GitHub.

#blog/draft

No Comments

I Want to be the One in Control

work safe

It’s all about time. SEP, like most organizations, lives and dies by time sheets. Our time sheets are two weeks long. Each entry has a start time, a project code, a description, and a duration. Easy enough, but what does that have to do with EmberJS?

To Quote Miss Jackson, “I Want to be the One in Control”.

Each timesheet is an array of such entries. If we use an array controller to wrap the array model, we can leverage a lot of assumptions about controlling a number of items.

OK, Computed Properties

Controllers can compute properties, such as the total duration of time logged on a time sheet. In addition, we can validate the timesheet, checking for overlaps and showing the billable time.

App.EntryCollectionController = Ember.ArrayController.extend({
 totalDuration: function(){
   return this.reduce(function(sum,entry){ return entry.duration + sum; },
 0);
 }.property('@each.duration'),
 isValid: function(){
   var otherEntries = this.get('[]');
   this.reduce(function(isValid, entry, index){
     // TODO: the impl of doesntOverlapAfter is left as an exercise to the reader
     return isValid && doesntOverlapAfter(entry, otherEntries, index);
 })
 }.property('@each.startTime', '@each.duration')
});

And we can specify the name of the controller used to wrap each element in the model’s array. In our case, we will use it to calculate the end time from the duration.

App.EntryCollectionController.reopen({
   itemController: 'entry'
});

Objectifying Things for Fun and Profit

Those elements are likely an object controller.

App.EntryController = Ember.ObjectController.extend({
 endTime : function(){
    var time = this.get('startTime').clone();
    time.setMinutes(time.getMinutes() + this.get('duration') * 60);
    return time;
 }.property('startTime', 'duration')
})

All Things are Related

So the collection of entries needs to relate to another controller. In our case, it is the timesheet itself.

App.EntryCollectionController.reopen({
   needs: ['timesheet']
});
1 Comment

A Template for Lunches

work safe

If you want to show anything on your page, you’re going to have to write a template. A template is the html to display on each page view. And if you use resources, they nest! Ember, by default, uses the Handlebars template engine.

Lunchtime!

It’s a Friday and I want ribs for lunch. But I don’t want to go alone. I’ll just write an intranet app that lets me invite a few coworkers to lunch. Because email is hard.

Begin at the beginning.

The default template is the application template. you can tell it’s the application because it doesn’t have a name. The application template is used to lay out the main page structure and all the site branding. all other templates are nested inside and show up where the {{outlet}} tag is used.

<br/>
<script type="text/x-handlebars">  
<h1>Lunch-o-Matic!</h1>  
<div class="subtext">Meet your coworkers for lunch</div>  
{{outlet}}  
</script><br/>

But, that alone isn’t very interesting, is it?

Okay, lets start off by managing some expectations. I’ll cover Controllers next week, but I’ll be mentioning them often. About 1/2 of the cool things about templates have some counterpart in the controller supporting them. That said, I’m getting hungry. Let’s gather up the team and head out for Barbecue!

I Decide who Leaves and who Dines!

I need to display a list of my coworkers so I can pick a couple of them.

<br/>
<script type="text/x-handlebars" data-template-name="lunchPicker">  
<h2>Who's in?</h2>  
{{input value=lunchType placeholder="What's for Lunch"}}  
<table><tr><th>invite?</th><th>name</th><th>email</th></tr>  
{{#each employee in employees}}  
  {{#if employee.isAvailable}}  
    <tr><td>{{input type="checkbox" checked=employee.isSelected}}</td>  
        <td>{{employee.fullName}} ({{employee.initials}})</td>  
        <td>{{employee.email}}</td>  
    </tr>  
  {{/if}}  
{{/each}}  
</script><br/>

A few things to note in the above example. I’m using an {{input}} block to make a text field that binds to the controller property lunchType. That will be used to let everyone know what kind of food we’re going to eat! Next, I’m looping over the a list of employees using an {{#each}} block. The collection comes from another controller property. Each element is then scoped as ‘employee’ inside the each block. I can conditionally skip displaying an employee if they are not available. more correctly, I can use an {{#if}} block to show only the available employees. I display a checkbox using a built in helper, {{input}}. It binds the checked state of the input control to the isSelected property of the employee. The last three are simple data bindings to text. They just forward properties from the model to html.

The Web Without Links isn’t the True Web.

Let’s tweak the employee listing. I want to make the name link to their info page. I can use the {{link-to}} block.

<br/>
...
<br/>
 <tr><td>{{input type='checkbox' checked=employee.isSelected}}</td><br/>
 <td>{{link-to 'employee' employee.employee_id}}<br/>
 {{employee.fullName}}<br/>
 {{/link-to}}<br/>
 ({{employee.initials}})</td><br/>
 <td>{{employee.email}}</td><br/>
...<br/>

But we need to DO something!

Wonderful! Things are moving right along. Now, how do I submit my selections? With an action! An action is a function in the controller. I promise, we will get to that later. Just trust me when I say our bindings put all the information in the model, so we don’t need to pass anything to the function. We just need to bind the action to a button, right after the end of our {{#each}} block.

<br/>
...
<br/>
<button {{action 'pickThem'}}>Choose these Coworkers</button><br/>

We can pass parameters in to the function, but we don’t need to at this time. In addition, we don’t have to worry about controlling event propagation or target a different controller. All of these are possible, just not necessary for us.

Your Bill Comes with Mints

There are a few things we can do to help us clean up our code from here.

Helper isn’t just for Hamburgers

A helper is a function that gets called like a block. It returns text to be displayed in the Html. If you need to format dates, for example, or want to display an employee’s spark-line for their time on the tread mill in the past two weeks.

Partials

Partials are templates that can be reused. They bind to what ever model or controller is in the context, just incase your data doesn’t look like you expected because you named something slightly wrong.

Views

A view can pick it’s own template. It has a bit more logic, but I’ wouldn’t go crazy building complex controls with them. In a couple weeks, we’ll cover Components and then we’ll go nuts.

1 Comment

A Picture is Worth 1000 Lies

work safe

Last week, I wrote about Routing in EmberJS. And I kinda lied a bit. I laid out the Pokédex route like this …

<br/>
App.router.map(function(){<br/>
 this.route("pokedex", {path: '/pokedex/:poke_id'});<br/>
});<br/>

That’s probably the wrong choice, but I made it for Pedagogical reasons. I regret nothing, but we are going to play with the routes of an example application to think through some design decisions and pick up a small detail of the router I overlooked. Resources.

A Picture is worth 1000 words

I’m a Father, Husband, etc. That means we are generating Gigabytes of pictures. Some are even in focus. She’s two, and fast. So, we are going to build a photo sharing site! Let’s give it a classy name. I’m thinking Phlickr. Um, legal informs me that’s a bit too similar some other upstart.

Introducing PhotoAlbum

PhotoAlbum is the latest in picture sharing technology. Some of our neat features are:

  • Friends
  • Albums
  • Tags
  • Favorites
  • Popular Pictures
  • Searching
  • Dashboard

Laying the Groundwork

I’m going to start by giving some examples of pages and their Urls.

  • Index – ‘/’
  • Login – ‘/login’
  • Dashboard – ‘/dashboard’
  • Popular Pictures – ‘/popular’
  • My Friends – ‘/friends’
  • Search – ‘/search/’
  • Tags – ‘/tags/’
  • Tag – ‘/tag/Sunny%20Days’
  • My Page – ‘/U/Brian’
  • Viewing My Album – ‘/U/Brian/album/sketchnotes’
  • My Albums – ‘/U/Brian/albums’
  • Creating a new album – ‘/U/Brian/albums/new’
  • My Friends Albums – ‘/U/Matt/albums’
  • A Photo – ‘/U/Brian/photo/24153’
  • My Photos – ‘/U/Brian/photos’
  • Uploading a photo – ‘/U/Brian/photos/new’

Cartography in Code (Making a Map)

Router.map(function() {
 this.route('login');
 this.route('dashboard');
 this.route('popular');
 this.route('friends');
 this.route('search');
 this.route('tags');
 this.route('tag', {path: '/tag/:tagname'});
   this.resource('posters', {path: '/U'}, function(){
     this.resource('user', {path: '/:username'}, function(){
       this.route('album', {path: '/album/:albumname'});
       this.resource('albums', function(){
         this.route('new');
       });
       this.route('photo', {path: '/photo/:photo_id'});
       this.resource('photos', function(){
         this.route('new');
       });
     });
   });
});

What’s the Difference?

Routes are for individual pages. Routes cannot be nested. If we want nested pages, we use resources. And resources give us a few new defaults.

Defaults

By default, the router gives us the route named ‘index’. And that in turn builds the controller, route, and loads the template named ‘index’. A resource is like a mini router in that way. If we name a resource ‘posters’, then all the child routes are prefixed with the name, including a new default ‘index’. The tetrad is listed out below. When deeply nested, only the resource and route matter for naming as opposed to the parent resources. Example, the template for the new albums is ‘albumsNew’ despite the albums resource being a child of the user resource.

  • RouteName – postersIndex
  • Controller – PostersIndexController
  • Route – PostersIndexRoute
  • template – postersIndex

Chains

The defaults are not the only things. They nest, just like applications. The posters resource looks for a template named ‘posters’ and then the child template. While I won’t dwell too much templates now, remember when the application template had an {{outlet}}? Well, the ‘posters’ template can have an {{outlet}} as well. this allow the templates to nest.

But it’s not just the templates, the controllers nest as well. So the model and properties of a parent controller can be accessed by a child. And the routes nest. Each parent finishing before the child runs.

A Time to Choose

Of course we can use routes to describe everything here. I’m fairly sure. But we will have all kinds of issues with reuse. the nesting of controllers and routes gives us a sensible place to hang some code reuse. The really interesting thing is I chose my routes and resources strictly based on what Urls made sense, what parts belong to other pieces of information. For example, there is only one dashboard, yours. The login is also independent of other data. However, each photo album belongs to a user. Matt’s album Dinners I Made is totally different from my album Dinners I Made. And while a picture has a tag, the collection of tags is only useful when it belongs to the system as a whole, allowing me to find all the pictures tagged sunrise regardless of who posted it. Once again, it all comes down to following the Urls and letting them tell you all you need to know.

No Comments

Getting from Here to There

work safe

Where do we go from here? We begin with the Url. The history of the web is based around the URL and our ability to transition from one url to another. Ember is built around the Url.

You are a Web Developer if you build apps with Urls

– Tom Dale

Every view we want to represent in our application will be represented as a different Url. When we want to show a specific piece of data, we use a different Url. Want to modify how we are showing the screen, then we add data to the Ulr. This will allow us to Deep Link and share specific views and app state with colleagues and coworkers. This is how the Web has worked since the web began. And Below, we see how the Url communicates with Ember to make choices on what to show the browser.

Ember Url Structure

Ember’s Url Structure taken from the presentation mentioned above.

The Router

The router dissects the Url and chooses which Route, Controller, Model, and Template to compose, execute, and use the results for displaying to the screen. By default, you get an ‘index’ route for free. As if I went to http://example.com/, that would be the default index.

Baby-steps to Customization

I can specify just a route named ‘about’ and that will show an ‘about’ template that will use the path http://example.com/about without specifying that path.

App.Router.map(function(){
    this.route("about");
});

A Route With a Different Name, Would It Navigate as Sweet?

Lets say I have a dumb legacy Url (http://example.com/meet-the-team) I need to keep working. Because Urls are important. But I want my internal code to use clear names (about). I can specify the path used in the route mapping.

App.Router.map(function(){
    this.route("about", {path: '/meet-the-team' });
});

My Pokémans, let me show you them.

One more mapping to cover for now, before we start talking about the magic a route actually does. What if we want to show the same page, but vary the data based on part of the path? I have a super awesome collection of Pokémans, let me show you my favorite Squirtle. I keep him at http://www.example.com/pokedex/poke_1. I can specify part of the path to be a dynamic segment. The name of the dynamic segment will show up as a param in the Route code.

App.router.map(function(){
    this.route("pokedex", {path: "/pokedex/:poke_id"});
});

Spent All That Time Defining

It’s neat and all that I’ve just created a route with a dynamic segment. But I need to use that to actually do work. I can create a PokedexRoute and override it’s model creation!

App.PokedexRoute = Ember.Route.extend({
    model: function(params){
        return Ember.$.getJson('api/pokemon/' + params.poke_id);
    }
});

What’s a lifecycle?

There are so many hooks in the route that I can override. I can override the controller creation. I can specify a non-standard template. Oh, that might be fun! This is the BEST pokemans, so he needs a custom template.

App.PokedexRoute = Ember.Route.extend({
    renderTemplate: function(controller, model){
      if(model.name === "Squirtiepie") {
         this.render("pokedex_squirtle");
      } else {
        this.render('pokedex');
      }
    }
});

Odds and Ends

If your code is slow, because for example not tied to anything real and not to name names or anything you query exchange, you can create a template called ‘loading’. Or you an overload the loading behavior.

The same thing with handling errors.

Customizing the Base

You may notice that the Url ember gives you has a hash (#). You can pick the location strategy as one of the following three. If you don’t like the hash, you can use the ‘location’. If you’re running an app out of a page, you can use ‘none’. The ‘hash’ strategy is the default.

App.Router.reopen({
    location: 'history'
});

Transition Overrides

When you edit the Pokedex, you don’t want to update the data until the Pokemaster is finished with her entry and presses the submit button. But what if she clicks on a link to another page? We can intercept the transition and quit!

App.PokedexEditRoute = Ember.Route.extend({
    action: {
	    willTransition: function(transition){
			if(this.controller.get('formHasEdits') &&  confirm("Are you sure you want to throw away your changes?")){
				transition.abort();
			} else {
				return true;
			}
		}
	}
}	
});

We could also validate the input for correctness and prevent a transition if we found it necessary.

Redirecting

Let’s say you load a model and need to decide to redirect because any Pikachu needs to redirected to a list of rats. That jerk gets all the attention anyway.

App.PokedexRoute = Ember.Route.extend({
   afterModel: function(model, transition){
      if(model.get('type') ==='Pikachu'){
        this.transitionTo('rats');
      }
   }
});

You Are Here

The router is all about defining the transitions between your views. In all honesty, the next time I write a desktop application, I may have to write something like this. It’s super easy to manage and override just about any transition. It’s useful, flexible, and the heart and soul of your application.

2 Comments

Rise of the Tetrad

work safe

Ember has about six concepts that once you understand a bit will help you unlock the documentation and figure out how to develop your site. I have written before about not needing to fully understand ember before working on a project, but once you start getting your feet under you and need to branch out. My hope is that by the time you are done reading, you will have a roadmap to the Ember Guides and the Ember APIs.

Rise of the Tetrad

Ember uses naming conventions to tie together the four moving parts of an ember application; the Route Name, the Route, the Controller, and the Template.

The Route Name

When the route is defined like I did below, it is given a name. By default, the path mapped to the route is the same as the route. For example, the route below defines a route named “level1” and maps the url “/level1” to the route of the same name.

Route.map(function(){
 this.route("level1");
 ...
});

The Route

By default we are generating a route named App.Level1Route. If we want to change the behaviors of the route, which we will cover in more depth in a different post, we need to define our Route and provide it with some overrides. A common override is to provide some model data, like below

App.Level1Route = Ember.Route.extend({
 model: function(){
 return Ember.$.getJSON("http://mygame.com/api/level1.json");
 }
});

The Controller

Again by default, we have a controller called App.Level1Controller created for us. But if we want to override its behavior, we just make a new one with the correct name! For example, we can add an action to save the current state back to the server.

App.Level1Controller = Ember.Controller.extend({
 actions: {
 save: function(){
 Ember.$.postJSON("http://mygame.com/api/pause/", model);
 }
 }
});

The Template

We need to show screens for our level. This is done by showing a template. The default template is, of course, named ‘level1’. The template is written in handlebars.

<script type="text/x-handlebars" data-template-name="level1">
  <button {{action 'save'}}>save</button>
  <canvas name="game-level" width="500" height="400">
  </canvas>
</script>

Numbers are tricky. You said there were six, told us about four, but it looks more like only three.

Right. The Route, Controller, and Template are all based off of the Route’s Name. So, four for the price of three. The Other Fourth is the model. I’m just going to skip it for now. The three Ember apps I have written have not yet needed the complexity of an Ember Data model.

The other pieces you will come across are the Components, the views, and Router.

The Router

The router is the part that wires all the components together. We used it to set up our route mapping.

Views

Views let you add behavior to an html tag. You can add behaviors and event handlers to the tag.

Components

Components are an implementation of the W3C Custom Element Spec. They are far more functional than overriding an html tag, they are custom tags backed by a template and code that allow you to build custom controls. If you need a calendar control, start here. Need a tabbed page control? Check out Ember-Components.com for some examples.

Next Steps

Now that we know our way around the major concepts of the Ember framework, we can start to understand them one at a time. Maybe next time we will start with the routes.

No Comments

A quick note on EmberJs and ReactiveJS

work safe

I tweeted yesterday that I was having trouble with EmberJS and ReactiveJS integration.

Well, I figured it out! With some help from the fine ReactiveX folks.

The long story short is RxJs is smart about Ember, and there are consequences.

Yeah, it boiled down the the difference between mousedown and mouseDown. Did you catch the capitalization of the D? I never even thought to look…

So, what’s it look like in the end?

App.CanvasView = Ember.View.extend({
    tagName: 'canvas',
    didInsertElement: function(){
        this.subscription =
          Rx.Observable.fromEvent(this, 'mouseDown')
                       .subscribe(function(e){
                           console.log("x="+e.offsetX + " y="+e.offsetY);
                        });
    },
    willDestroyElement: function(){
        this.subscription.dispose();
    }
});

And now to start the magic of using RxJs.

COMPOSE ALL THE EVENTS

update

I still think there is one thing left. I’ll try and test it out and add a Pr in a bit, but it seems to be important for my situation.

If I look at line 124 and 125 of fromEvent.js, I see an unused argument. I passed that through to the ember listener calls.

    if (ember) {
      return fromEventPattern(
        function (h) { Ember.addListener(element, eventName, h); },
        function (h) { Ember.removeListener(element, eventName, h); },
        selector);
    }  

update 2

I submitted a pull request. I hope I’m right about this, or I’ll be beet-red.

1 Comment
« Older Posts