How Websockets Work

Oh, websockets. 

I had trouble conceptualizing websockets and understanding how information is passed from the server to the client. 

Here's the analogy I finally settled on: websockets are a lot like tossing a ball with a friend at the park. 

Websockets and Balls 

Pretend you're the server. You send out a message asking if anyone wants to play.

Your friend (the client) happens to hear and sends back another message, "Hell yea, I'd love to go to the park!"

This conversation is essentially opening the connection between two sockets. Now you're both listening to each other and can receive any messages sent. 

But here's the deal: Because you're the server, you can play ball with multiple friends at the same time. Your friend, the client, isn't so powerful. He or she can only interact with you. 

Set Up Your App

Before we dive deep into websockets, let's set up a basic Node app to work with them.

If you haven't already, make a directory and `npm init`. Work through the initial setup and then install Express: `npm i express --save`.

Within your app, create a server.js file.

This just requires some of the dependencies we'll be working with to get this app off the ground.

Create a folder called `public` and create a file within it called `index.html`. This will be the file we send to the client when they visit http://localhost:3000. Why 3000? Well, that's what we're setting on line 12 above. We're telling this app to listen on port 3000. 

In your console, type `npm start`. Then visit http://localhost:3000 and return to the console. You should see a message: "Listening on port 3000."

Cool, huh?

Ok, now we'll get to the websockets. 

We've set up the server above and now we're going to pass that variable into socketIo. (Don't worry about line 22 for now — that's for another piece of functionality.)

Now open another tab in your browser and visit http://localhost:3000. Return to your console and you should see a new message: "A user has connected. 1"

When you visited the site as a client, the server recognized the connection and notified you. 

Can you guess the difference between line 27 and line 29?

`io.sockets.emit()` sends information to ALL the sockets listening on your connection. 

`socket.emit()` sends information to one specific socket. 

How to Work with Websockets in Real Life

Now let's build some extra features to pass information back and forth between the server and the client. 

Add the following to your index.html file. 

Create a client.js file in your public folder. Then add the following.

Now experiment with your connections by opening up a bunch of tabs in your browser. See how the connection count increases and decreases as you open and close tabs.

The awesome part of websockets is that they're LIVE. You don't have to refresh the page. The connection automatically updates the information based on what's happening in real time. 

Got something to add about websockets? Feel free to comment below.

Starting a Node Project

Getting started is usually the hardest part of any project. Here's what to do. 

  • If you haven't already, download Node
  • Make a new directory and cd into it: `mkdir myapp` and `cd myapp`
  • When you enter `npm init`, you'll be given a series of options for your app. Press enter to accept the default answer or enter your own. 
name: (myapp)
version: (1.0.0)
description: An amazing app
entry point: (index.js) server.js
test command: mocha
git repository: https://github.com/{your username}/myapp
keywords: node, javascript, app
author: Emily Dowdle
license: (ISC) MIT
  • I'm going to use express and mocha. To install these dependencies, type `npm i express --save` and `npm i mocha --save-dev`

Turing as a great tutorial for creating and unit testing an express app. I highly recommend you follow along to get some more experience.

 

Video Code Along (Part 1)

 

Video Code Along (Part 2)

I was interrupted in the middle of recording. Here's the final part. 

Real Time Crowdsourcing App

For the next week, I'll be writing a blog series on an app I'm writing: Real Time. It's a polling app and the responses can be viewed in, you guessed it, real time!

Here's everything you need to know:

Basic Requirements and Features:

Crowdsource

Steve is an instructor at a seven-month developer training program in Colorado. In the middle of a long rant about the merits of CoffeeScript, he wants to check for student understanding. He could pause for a moment and ask the room if they have any idea what he's talking about, but he suspects they'll just smile and nod like they always do. He knows that some students may not want to admit in front of the whole group that they neither understand what Steve is talking about, nor do they particularly care.

  • Steve decides to use Crowdsource to anonymously poll the room.
  • He goes to the site to generate a new poll and adds three potential responses: "This is old hat to me", "I have an okay understanding of this", "I have no idea what you're babbling about".
  • He gets back two links: a admin view that shows the poll and a voting page that shows the options.
  • He drops the link into Slack and his students vote.
  • Steve feels pleased when he sees that 100% of his students are absolute masters of everything he has ever taught.
  • He turns off the poll before a certain student that comes late every day arrives and messes up his perfect score.

Ember + Electron

I started this week knowing nothing about Ember or Electron. So, naturally, I set out to make an app using both.

Ember

Ember is an open-source JavaScript framework developed in 2011 by Yehuda Katz and Tom Dale. It follows the MVC (Model, View, Controller) pattern of development for developers designing scalable single-page web apps.

The Ember stack includes:

  • Ember CLI: Build tools and command line utility. Like Rails, all you have to do is run `ember new app-name` to generate a new app with a standard file and directory structure, a testing framework, dependencies (managed by bower and npm), ES6 syntax support using Babel and asset management for minifying. 
  • Ember Data: A data-persistence library. Ember Data can save records via a RESTful JSON API without any additional configuration.
  • Ember Inspector: An extension for Firefox and Chrome to help developers debug Ember apps.
  • Fastboot: An Ember CLI addon to allow developers the ability run their apps in Node.js.
  • Liquid Fire: Animation support.

Electron

Electron is an open-source framework for desktop GUI applications. Built by Github, Electron used to be known as Atom Shell and is the framework Atom was built with. 

Electron uses Node.js, Chromium (the magic behind Chrome), HTML, CSS and JavaScript to build cross-platform apps — Electron apps can run on Mac, Windows and Linux.

Apps built on Electron include Slack, VisualStudio, Avocode, PopKey and GitKraken.

You can think of Electron as a mini Chrome browser, controlled by JavaScript. It uses Chromium to display web pages as its GUI.

There are two main processes: Main and Renderer. Main creates the web pages by using `BrowserWindow` instances. Renderer is the tool used to display web pages. But, unlike normal browsers, Electron apps have access to native resources. When a `BrowserWindow` instance is destroyed, the renderer process is also stopped. 

 

Development Philosophy

Ember's philosophy centers around the developer. Its creators used four ideas to drive the design:

  1. Full package: Unlike most JavaScript frameworks, Ember provides the full MVC framework for ambitious client-side applications.
  2. Productivity: Ember provides tools that make getting started fast. Ember CLI provides a standard architecture with countless options for enhancement.
  3. Stability: Ember's creators and maintainers recognize the importance of backward compatibility while continuing to innovate.
  4. Flexibility: Yehuda Katz is a member of the committee responsible for creating future versions of JavaScript and made sure Ember was an early adopter of ES6.

Ember adheres to the Don't Repeat Yourself (DRY) principle and prefers convention over configuration. It is opinionated but flexible, providing developers both freedom and guidance.

Electron is best thought of by its former name: it provides a shell that wraps around your JavaScript web app to provide it lower-level access to native processes and a desktop interface. It's a fast, lightweight way to build a desktop app for any platform. 

 

Why Ember + Electron?

Well, for one, it seems like it hasn't been done a lot before. That's kinda cool and leaves lots of room to learn through experimentation. (Or pull out my hair in frustration, you know, either way.)

Initially I thought Electron would be the most heavily used because the app would be a desktop app. But again, it's really just a wrapper. 

Ember does most of the heavy lifting. 

And, I'm sure there are some people who would argue I should have opted for a native app and not used a cross-platform tool like Electron. 

While I think there's a time and place for that, right now, I'm focused on learning. And I love that I'll be able to deploy it for any system. 

 

Challenges

The greatest immediate challenge was getting started. By the end of Monday, my understanding of Ember was about 15% and my knowledge of Electron was maybe 5% — and that's probably stretching it. 

I had no idea where to start. 

So, here's what I did:

(Warning, Steve Kinney makes a lot of appearances.)

After implementing edit and create on my own program, the flow of Ember started to make more sense. Ember follows an MVC philosophy, but in a very different way than Rails. I still felt unsure of how to pass information around.  

 

The Solution

One of the features we aimed to complete was a preview of the note (written in Markdown) in HTML, shown in a separate pane. 

So how do you get the note into another component?

Here's the code for the note:

<div class="note-content pane">
  <h3>{{model.id}}</h3>
  <hr>

  <p class="note-content">{{model.content}}</p>

  {{textarea value=model.content}}

  <br>
  {{#if model.hasDirtyAttributes}}<button {{action 'saveNote' on='click'}}>Save Note</button>{{/if}}
  <button {{action 'deleteNote' on='click'}}>Delete</button>
</div>

{{markdown-renderer note=model}}

 

That last line is the key: here we're passing an object to the markdown-renderer component of our app. "Model" is what it's referred to in the note template. "Note" is what it will be referred to in markdown-renderer. 

Starting to make a little more sense?

Here's the code from markdown-renderer:

{{markdown-to-html markdown=note.content}}

{{yield}}

 

Pretty simple, huh? Almost too simple. One of my complaints about Ember is it seems like there's a lot of magic and I'm still finding it hard to parse out exactly what's happening under the hood. 

 

Next Time

I think it was overambitious of me to take on an Ember + Electron app when I'm unfamiliar with both. Next time, I'll choose one and dive deep. 

I continually struggled to figure out what docs I needed to read: Node, Ember or Electron? 

I'd like to see more apps use both technologies because I think they can work together quite well. I'm just not there yet. Maybe soon.

 

Resources

Interested in learning either Ember or Electron? Here's where to start.

 

I'm going to do something crazy

And share some really heinous code with you.

My first assignment in Turing was to build Mastermind — a simple game a user can play within his or her console. 

It seemed straightforward enough, except that I didn't know anything. I mean nothing

They hadn't even started the teaching part. We were just thrown in, like a bunch of babies at a Mommy & Me Swim Class. 

Needless to say, the original was pretty rough. 

The Refactor

I'm all for growth mindset, but I'm going to lead with the refactored version because — frankly — I'm embarrassed by the original. 

The refactor took me about two hours to complete, following the original rubric

I've been coding for three months. And I'm already horrified by my old code. What am I going to think in a year, five years, ten years?! 

The Original

Are you seriously prepared for your eyes to bleed? 

Here's my first version of Mastermind. Um, yes, that is a 130-line loop. 

def prompt(message)
  puts "=> #{message}"
end

def create_secret_code(code, chars)
  chars.each { |e| code << chars.shuffle.sample }
end

def valid_guess?(input)
  input.length == 4 && input.all? { |e| e == "B" || e == "R" || e == "G" || e == "Y" }
end

def fill_feedback(user_guess, secret_code, feedback)
  user_guess.each_with_index do |e, i|
    if e == secret_code[i]
      feedback[i] = e
    end
  end
end

### game
loop do

  prompt("Welcome to MASTERMIND")
  prompt("Would you like to (p)lay, read the (i)nstructions or (q)uit?")
  initial_user_choice = gets.chomp.upcase

  if initial_user_choice == "I"
    prompt("INSTRUCTIONS: The computer has created a secret code. To win, crack the code by guessing the correct colors and positions. But don't delay! The clock is ticking. Ready?")
  elsif initial_user_choice == "Q"
    prompt("Goodbye!")
    break
  else
  end

  loop do

    prompt("I have generated a beginner sequence with four elements made up of: (r)ed, (g)reen, (b)lue, and (y)ellow. Use (q)uit at any time to end the game.")

    code_possibilities = ["R", "G", "B", "Y"]
    secret_code = []

    create_secret_code(secret_code, code_possibilities)

    prompt("LET'S GET STARTED!")
    puts " ----- ----- ----- ----- "
    puts "|     |     |     |     |"
    puts " ----- ----- ----- ----- "

    start_time = Time.new

    feedback = ["?", "?", "?", "?"]
    correct_colors = 0
    correct_positions = 0
    num_of_guesses = 0

    loop do

      prompt("What's your guess?")
      user_guess = gets.chomp.to_s.upcase.split(//)

      if user_guess == ["Q"]
        break
      elsif user_guess == ["C"]
        prompt("The secret code is: #{secret_code.join("")}")
        break
      else
        loop do
          case
          when valid_guess?(user_guess)
            break
          when user_guess.length < 4
            prompt("That guess is too short.")
            prompt("What's your guess?")
            user_guess = gets.chomp.upcase.split(//)
          when user_guess.length > 4
            prompt("That guess is too long.")
            prompt("What's your guess?")
            user_guess = gets.chomp.upcase.split(//)
          elsif user_guess.to_s != user_guess
            prompt("Those look like numbers. Try B, R, Y or G.")
            prompt("What's your guess?")
            user_guess = gets.chomp.upcase.split(//)
          end
        end
      end

      fill_feedback(user_guess, secret_code, feedback)

      puts feedback.join

      puts " ----- ----- ----- ----- "
      puts "|  #{feedback[0]}  |  #{feedback[1]}  |  #{feedback[2]}  |  #{feedback[3]}  |"
      puts " ----- ----- ----- ----- "

      correct_colors = 0
      secret_code_clone = secret_code.clone

      user_guess.each do |e|
        location = secret_code_clone.index(e)
        if location != nil
          secret_code_clone.delete_at(location)
          correct_colors += 1
        end
      end

      correct_positions = 0
      feedback.each do |e|
        if e != "?"
          correct_positions += 1
        end
      end

      num_of_guesses += 1
      guess_syntax = "guess"
        if num_of_guesses > 1
          guess_syntax = "guesses"
        end

      if feedback.include?("?") == false
        finish_time = Time.new
        time_score = (finish_time - start_time).to_i # duration in seconds
        seconds = (time_score % 60)
        minutes = ((time_score / 60) % 60)

        unless minutes == 1
          minutes_syntax = "minutes"
        else minutes_syntax = "minute"
        end

        unless seconds == 1
          seconds_syntax = "seconds"
        else seconds_syntax = "second"
        end

        prompt("Congratulations! You guessed the sequence #{secret_code.join} in #{num_of_guesses} #{guess_syntax} over #{minutes} #{minutes_syntax}, #{seconds} #{seconds_syntax}.")
        break
      end

      prompt("#{user_guess.join} has #{correct_colors} of the correct elements with #{correct_positions} in the correct positions. You've taken #{num_of_guesses} #{guess_syntax}.")

    end

    prompt("Would you like to (p)lay again? Or (q)uit?")
    play_again = gets.chomp.upcase
    if play_again == "Q"
      break
    end

  end

  prompt("Goodbye!")
  break

end

Practical Approaches To Testing

Testing is a lot like going to the gym. We all know we should do it. But when the alarm clock sounds after a long night of coding, most of us hit the snooze button. 

Here’s the thing: tests should work for you, not against you. Testing may be hard, but it doesn’t have to be a nightmare. 

Mike Cohn, of Mountain Goat Software, created this testing pyramid.

He emphasizes abundant unit tests to ensure code works at a granular level, a healthy amount of integration tests to verify cross-functional behavior and a thin layer of end-to-end tests to test the user interface.

Mike’s testing pyramid matches up well with Google’s approach, which recommends your test suite be made up of 70% unit tests, 20% integration tests and 10% end-to-end tests.

Justin Searls, of Test Double, takes an entirely different approach. He recommends creating two types of tests: high-level end-to-end tests and low-level unit tests. (He also has a great talk on testing, given at RubyConf 2015.)

 

See all that grey space where integration tests would normally go? You can skip those. Searls believes your high-level tests will cover base expectations for user experience and ensure the app works. 

This design is sometimes referred to an hourglass pattern of testing where a test suite is made up of verbose end-to-end tests and a generous amount of unit tests with few, if any, mid-level integration tests. 

So why do some people emphasize end-to-end tests and others minimize them?

Well, there’s a bit of a debate. 

End-To-End Testing

There are a number of advantages to end-to-end tests. Managers love them because it eliminates the change of them receiving hateful twitter messages when the app breaks in production. 

And some developers — not you of course — like them because it reduces the pressure to write thorough unit tests.

But there are major challenges too. 

End-to-end tests are slow. To truly verify customer behavior, you have to spin up a browser for every test. The more code you write, the more expensive it is to run your test suite. 

Some end-to-end tests may mask issues that should have been caught in a unit test. On top of all that, some end-to-end tests are brittle to code changes. 

What’s the right answer? It depends. Small apps tend to benefit from thorough end-to-end testing while larger apps often require more dedication to unit testing. 

There’s no single “right” methodology. Your approach will depend heavily on your team and your unique circumstances. 

We’re all unicorns and snowflakes, aren’t we? 

Recursively Replicating Ruby's Flatten Method

You can view the full .flatten method challenge here.

Flatten Challenge

The flatten method is a handy tool to compress nested arrays into one, flat array without losing any of the data. It works like this:

> [[1, 3], [5, [7, 9]]].flatten
=> [1, 3, 5, 7, 9]

Instead of relying on the .flatten method in Ruby, we'll build our own recursively. 

TDD

I'll start by building a test.

require 'minitest'
require 'minitest/autorun'
require 'minitest/pride'
require '../flatten'

class FlattenTest < Minitest::Test

  def test_combine_nested_arrays
    all = [1, 1, 2, 3, 4, 5]
    start_array = [1, [1,2],[3,[4,5]]]

    assert_equal all, CustomArray.new.combine_nested_arrays(start_array)
  end

I'm always skeptical of tests that run correctly the first time, so I prefer to run each test, expecting it to fail, before I proceed. 

Now it's time to start building the method. 

My first thought is that I'll need to iterate through each nested array and flatten it before proceeding to the next nested array.

Let's set up the class file.

  require 'pry'

  class CustomFlattenMethod
    def need_a_method
    end
  end

The most efficient way I can think to do this would be to pull the data out of the nested array and shovel it into another array. 

I'll call the flatten method `combine_nested_arrays`.

The Method

I'll walk through it step-by-step below. 

  attr_reader :all

  def combine_nested_arrays(arr, all=[])
    arr.each do |item|
      if item.class != Array
        all << item
      else
        combine_nested_arrays(item, all)
      end
    end
    all
  end

I set the variable `all` to default to an empty array. That way the method will be able to receive one or two variables, both arrays. One to flatten and another (the default `all`) to push our data into. 

  def combine_nested_arrays(arr, all=[])

Then we want to iterate through the array containing the nested arrays and pull out the data from each nested array.

  arr.each do |item|

However, what happens if one of the items in the array isn't an array?

For example, what if our array looked like this:

> [2, [1, 3], [5, [7, 9]]]

The first number, 2, isn't an array, it's just an integer. I'll set up an if/then statement to manage this situation. 

  arr.each do |item|
    if item.class != Array
      all << item
    else
      combine_nested_arrays(item, all)
    end
  end

Now if the item being altered within our iteration isn't an array, it will automatically be shoveled into our new array, `all`. It doesn't matter if it's an integer, symbol or string.

Recursive Magic

If the item is an array, magic happens. 

Not really, but we do recursively solve the problem. An array is sent back into our method, `combine_nested_arrays`. 

A nested array will be sent back through the method until the item being item in the iteration is no longer an array. 

If the first item in the original array was an array, the parameter `all` will continue to be it's default `all = []` when it's sent through the method again.

However, if the first item is not an array, and `all` is no longer an empty array, that array will be passed into the method along with `arr`.

More Tests

Because I'm a fan of tests, I wrote some more, along with a few edge cases, to confirm my assumptions were correct. 

Below is my full test suite. 

  require 'minitest'
  require 'minitest/autorun'
  require 'minitest/pride'
  require '../flatten'

  class FlattenTest < Minitest::Test

    def test_combine_nested_arrays
      all = [1, 1, 2, 3, 4, 5]
      start_array = [1, [1,2],[3,[4,5]]]

      assert_equal all, CustomArray.new.combine_nested_arrays(start_array)
    end

    def test_combine_nested_arrays_start_with_array
      all = [1, 2, 3, 4, 5]
      start_array = [[1,2],[3,[4,5]]]

      assert_equal all, CustomArray.new.combine_nested_arrays(start_array)
    end

    def test_combine_nested_arrays_start_with_array_nested_nested
      all = [1, 2, 3, 4, 3, 4, 5]
      start_array = [[1, 2, [3, 4]],[3,[4,5]]]

      assert_equal all, CustomArray.new.combine_nested_arrays(start_array)
    end

    def test_combine_nested_arrays_differet_type_data
      all = [1, "2", 3, 4, 3, 4, 5]
      start_array = [[1, "2", [3, 4]],[3,[4,5]]]

      assert_equal all, CustomArray.new.combine_nested_arrays(start_array)
    end

  end

Want to play with the code? View this repository on Github.

Recursion + Factorials

Quick shoutout to Natasha the Robot for her excellent writeup.

Recursion is one of the most elegant things I don’t fully understand.
 

Recursion is beautiful. It's a bit like the movie Inception, in which everyone is in a dream within a dream.

In other words, a recursive method calls itself with new variables and continues to do so until the problem is solved, the method is told to break or your computer crashes. 

It's important to note that while a loop also continues to do something until it's forced to stop, loops are iterators and are not recursive in nature. 

Factorials

One of the problems best solved through a recursive method is finding the factorial of a number, which is denoted as the number, followed by an exclamation point (e.g. 5!). 

Factorial Iteration vs. Recursion

When I first approached the problem, I kept fighting the urge to iterate over the numbers. I blame Ruby's excellent flexibility. 

While you can find the factorial of a number, it's more elegantly coded as a recursive method. 

Nevertheless, here's an example of how you could iterate to find the factorial of a number:

You can read more about the #inject method in the Ruby docs.

 

Potential Problems

Negative numbers

Factorials of negative numbers are undefined. (I'm not a mathematician, but you can read more about there here.)

When I create a recursive method that takes input from the user, I'll have to make sure they submit a positive integer or natural number (in this case, I'm including zero as a natural number). 

The iteration currently doesn't account for this. It simply returns nil.

ZERO

The factorial of zero is one (0! = 1). If that's confusing to you, as it was to me, here's a brief explanation. 

1! = 1

2! = 2

3! = 6

4! = 24

If you're thinking, "Well, duh, get to the point," bear with me. 

Let's take a different approach. 

3! = 4!/4

In other words, 3! = 24/4, which can be further simplified to 3! = 6. 

OK, awesome. Let's do the rest.

2! = 3!/3 = 2

1! = 2!/2 = 1

Here's the kicker:

0! = 1!/1 = 1

Neat, huh? So, if the user inputs zero, I'll have to ensure the method returns one. The iteration below doesn't account for this.

Getting Started

I wrote some pseudocode of what I wanted to accomplish before I started coding. 

START program

GET input from user, convert string to integer

IF input == 0, RETURN 1

LOOP

IF input < 0, ask user for another input

END LOOP

RETURN factorial of input

END program
 

User prompt method

I created a simple method to add "=> " in front of any message. It will allow the user to more easily see messages and prevent me from having a hundred `puts` in my program. 

Welcome to Factorial!

I started the program off with a simple welcome message and asked the user to input a positive integer. 

User Input + Verification

I had to define the variable outside of the loop, so I set `user_input` to zero. 

When the user inputs a number, I convert it to an integer and check if it's a number greater than zero using the `is_valid?` method. 

If the number is negative, `is_valid?` returns a false and the loop tells the user it's not a valid number. 

stack level too deep

If you allowed the user to input a negative number using this recursive method, it wouldn't return nil. 

Instead, it would return an error: stack level too deep (SystemStackError)

Though it returns the error within a second, the computer can't process the amount of information you've put on top of the memory stack.

Basically, you've asked it to compute too much information and it's politely saying no. 

Recursive Method

And now the fun part. First, the `factorial` method determines if `user_input` is equal to zero. If it is, it returns a one. 

If it's not, it begins recursively problem-solving. 

As you can see above, the recursive method takes a number and multiplies it by the recursive method minus one. 

Visualizing Recursion

Returning the factorial

The final step is to let the user know the factorial of the value they inputed. 

 

The Full Program

See room for improvement? You can grab this factorial program on GitHub.

Or, shoot me an email. I'd love your feedback!

 

Trash Command Line Tool

I'm going to be honest here. 

`rm -rf` scares the poo out of me. I'm convinced that at some point I'm going to delete my entire computer. As if in one moment I'll be working diligently and the next everything I hold dear will be sucked into a permanent black hole. 

It's scary. Be honest. It scares you too. 

So I don't use `rm` unless I absolutely have to. 

Instead, I use this awesome little Trash command line tool for OS X.  

You can install it using Homebrew.

Install Trash

In your terminal, type `brew install trash`. 

Boom. It's just that easy. 

How To Use Trash

When you have a file or directory you'd like to remove, simply type `trash <your_file_name>`. Type `ls` in your current directory to double-check it's gone (it will be) and then click on your trash bin to find it.

If it turns out you didn't want to delete that file permanently the second after you hit return, have no fear. All your files are 100% retrievable.