An Unexpected Coder

opera singer turned web developer

Don’t Fence Me in: How to Use Gulp With Shopify and Timber to Gain Control of Your Front End

For my first Shopify project, I decided to use the Timber framework to help with my theme development. I think it’s a great way to get your theme started fast with the right setup. However, I hated the lack of organization and control of your front end code. Everything was dumped in the ‘assets’ folder. Images and JS files and SCSS files all thrown in together? I think not! Plus I wasn’t a huge fan their grid system and wanted to use Bourbon Neat, which is my favorite grid system. But here’s what Timber has to say about imports and mixins and Bourbon Neat:

You cannot use @imports in a Sass liquid file. That means no dependence on Compass, Bourbon or similar Sass frameworks.

Don’t tell me what to do Timber! I’m a grown woman, and I’ll use Bourbon if I want to!

So, I set out to set up my shopify-theme to be able to precompile assets locally. There’s a bunch of documentation and blog posts on how to use Grunt to do this, but I like Gulp better…so I decided to figure that out.

TL;DR: I used Gulp, Browserify, and other tools to compile an external lib folder full of my organized JS, SCSS, and Images. The compiled files are added to Timber’s assets folder and automatically uploaded to Shopify (along with any other files that were changed).

Here’s the step-by-step version:

First, create your project. In your terminal: mkdir my-shopify-shop cd my-shopify-shop

Then bring in Timber: git clone https://github.com/Shopify/Timber.git

Now that we have all the Timber files, we need to upload those to Shopify. For this we’re going to use the shopify_theme gem.

cd Timber

Open the Gemfile inside the Timber folder and add gem 'shopify_theme'. Then run bundle install in your terminal. In the root of the Timber folder, you have a config-sample.yml file. Duplicate that file, rename it config.yml and add your Shopify API credentials to it. To get your API credentials, go into your Shopify admin panel and create a private app.

Then add config.yml to your .gitignore (the one in the root of your project, not the Timber folder).

Using the shopify_theme gem, we are going to run theme replace in your terminal. Make sure you are in the Timber folder while doing this so it has access to your Gemfile and your config.yml. This will replace all of Shopify’s default files, with Timber’s default files. You only have to do this once. From here on out, any file we save will automatically be uploaded to Shopify through another library.

Go back to the root of your project cd ...

Then make yourself a folder where you’ll put all your precompiled assets. I named mine ‘lib’: mkdir lib cd lib mkdir js scss images

Here’s what your file structure looks like so far:

1
2
3
4
5
6
7
my-shopify-shop
├── lib
│   └── images
│   └── js
│   └── scss
├── Timber
│   └── All the automatically installed Timber stuff (assets, config, layout, etc.)

Next let’s start our gulp configuration. We need it to do the following: 1. Compile our scss and js files 2. Optimize images 3. Watch for changes 4. Upload any changes to Shopify

One disclaimer before we start. Say good-bye to livereload right now. Shed some tears and move on, cuz it ain’t gonna work (at least that I’ve tried). This is because we’re working from Shopify’s servers and not our own local environment. Our changes have to be pushed up to Shopfiy, then you have to make a hard refresh to see the changes. I know. Life is hard.

Go back to the root cd .. and touch gulpfile.js

You’re also going to have to create a ‘package.json’ for all your node_modules. npm init and press enter through all the prompts until it’s created.

Let’s start with uploading changes to Shopify. npm install --global gulp npm install --save-dev gulp gulp-watch gulp-shopify-upload That’s going to install both gulp, , and gulp-shopify-upload

Create a file called config.json in your root and put your Shopify API credentials in there. It should look something like this (but obviously with all your credentials):

config.json

1
2
3
4
5
{
  "shopify_api_key": "whateveryourapikeyishere",
  "shopify_api_password": "whateveryourapipasswordishere",
  "shopify_url": "YOUR-STORE-NAME.myshopify.com"
}

You can also add your theme id if you’re using one. Don’t forget to add config.json to your .gitignore!!!! You don’t want to be sharing your Shopify API credentials with the world.

Now let’s create a task for gulp-shopify-upload.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Gulp plugin setup
var gulp = require('gulp');
// Watches single files
var watch = require('gulp-watch');
var gulpShopify = require('gulp-shopify-upload');
// Grabs your API credentials
var config = require('./config.json');

gulp.task('shopifywatch', function() {
  var options = {
    "basePath": "./Timber/"
  };

  return watch('./Timber/+(assets|layout|config|snippets|templates|locales)/**')
  .pipe(gulpShopify(config.shopify_api_key, config.shopify_api_password, config.shopify_url, null, options));
});

// Default gulp action when gulp is run
gulp.task('default', [
  'shopifywatch'
]);

Notice a few things here. We changed the url for the watch command to go one level deeper since we’re changing the file structure from the example given in gulp-shopify-upload’s docs. And we have to add the option for basePath for the same reason. Also, we’ve moved all our API credentials into a secret config.json file that we’ll require at the top.

Now if you run gulp in your terminal, it’ll watch for any changes. Go ahead and make a change to one of the templates. You’ll see it say ‘Upload Complete’ in the terminal. But remember, you’ll have to do a hard refresh in order to see the change in your browser. Did a single tear just fall down your cheek? I know. Me too.

Next we’ll want to precompile our scss and spit the compiled file into Timber’s assets folder. Also, I’m going to include Bourbon Neat here along with some handy error handling.

npm install --save-dev gulp-sass gulp-autoprefixer gulp-notify node-bourbon node-neat

Add to your gulpfile:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// Compiles SCSS files
var sass         = require('gulp-sass');
var autoprefixer = require('gulp-autoprefixer');
// Notifies of errors
var notify = require("gulp-notify");
// Includes the Bourbon Neat libraries
var neat         = require('node-neat').includePaths;

function handleErrors() {
  var args = Array.prototype.slice.call(arguments);

  // Send error to notification center with gulp-notify
  notify.onError({
    title: "Compile Error",
    message: "<%= error %>"
  }).apply(this, args);

  // Keep gulp from hanging on this task
  this.emit('end');
}

gulp.task('sass', function () {
  gulp.src('./lib/scss/*.{sass,scss}')
    .pipe(sass({includePaths: neat}))
    .on('error', handleErrors)
    .pipe(autoprefixer({ browsers: ['last 2 version'] }))
    .pipe(gulp.dest('./Timber/assets'));
});

gulp.task('watch', function () {
  gulp.watch('./lib/scss/**/*.{sass,scss}', ['sass']);
});

And add it to the default task:

1
2
3
gulp.task('default', [
  'shopifywatch', 'watch'
]);

Change the stylesheet tag in Timber’s template to point to your compiled file instead of the default ‘timber.scss.css’. For me it’s on line 31 of layout/theme.liquid.

Now to compile our JavaScript files. For this we’re using browserify, watchify, and vinyl-source-stream. Keep in mind, this is a pretty simple gulpfile. You can really beef this up, especially when it comes to browserify.

npm install --save-dev browserify watchify vinyl-source-stream

Add to your gulpfile:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Concats your JS files
var browserify = require('browserify');
var watchify = require('watchify');
var source = require('vinyl-source-stream');

gulp.task('browserify', function() {
  return browserify('./lib/js/app.js')
      .bundle()
      .on('error', handleErrors)
      //Pass desired output filename to vinyl-source-stream
      .pipe(source('bundle.js'))
      // Start piping stream to tasks!
      .pipe(gulp.dest('./Timber/assets/'));
});

And add this to your watch task:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
gulp.task('watch', function () {
  gulp.watch('./lib/scss/**/*.{sass,scss}', ['sass']);
  gulp.watch('./lib/js/**/*.js', ['browserify']);

  var watcher = watchify(browserify({
    // Specify the entry point of your app
    entries: ['./lib/js/app.js'],
    debug: true,
    cache: {}, packageCache: {}, fullPaths: true
  }));

  return watcher.on('update', function() {
    watcher.bundle()
      .pipe(source('bundle.js'))
      .pipe(gulp.dest('./Timber/assets/'))
  })
});

Add another script tag below the one calling for timber.js (for me it’s at line 379) to call for your bundled js bundle.js in layout/theme.liquid.

Now let’s opmtimize our images. For that we use gulp-imagemin and gulp-changed.

npm install --save-dev gulp-imagemin gulp-changed

Add this to your gulpfile:

1
2
3
4
5
6
7
8
9
var imagemin   = require('gulp-imagemin');
var changed    = require('gulp-changed');

gulp.task('images', function() {
  return gulp.src('./lib/images/**')
    .pipe(changed('./Timber/assets/')) // Ignore unchanged files
    .pipe(imagemin()) // Optimize
    .pipe(gulp.dest('./Timber/assets/'))
});

And add gulp.watch('lib/images/*.{jpg,jpeg,png,gif,svg}', ['images']); to your gulp watch task.

If you’re using local font files, you’ll also want to add a fonts taks that will throw your font files in Timber’s asset folder as well.

You can check out a finished example of this code here.

Slide Slide Slippity Slide

How to Create a Slide Presentation with JavaScript

Recently a client needed a website that could be used both as a presentation tool (like a Keynote or PowerPoint presentation) and as a stand-alone website to be sent to possible investors to scroll through.

There are a lot of great libraries out there that help you mimic the functionality of a Keynote presentation. I took a look at flowtime, reveal.js, and impress.js. But, these libraries are big and seemed like overkill for what I needed to do. Also, I really wanted to see if I could build this on my own.

(If you want to jump ahead to the finished product, here’s an example of what I built along with the code on github.)

First I made my panels. I broke up the site into sections (literally — using <section></section> tags) and made each section height = 100vh. Actually, I had to make room for my fixed nav up top, so it was really height = calc(100vh - 102). Now that I had things looking the way they should, I needed to make them act the way they should.

I soon realized that my biggest challenge was to designate which section was the current panel so we could find the previous and next panels. I found this stackoverflow answer that seemed like it could be a solution. It used the jquery-visible plugin to tell if a section was in view and then found next and previous based on that. However, after the better part of a day going down that path, I realized it wouldn’t work for my needs. Before giving up, I asked another engineer if he had any idea how I could make this work (shout out, Kevin!). He said, “Why don’t you try using waypoints?” YES! Why didn’t I think of that?

Waypoints is a great library that lets you trigger a function on scroll. I could make each section have its own waypoint where it gets the class ‘currentPanel’ as soon as it’s almost to the top of the screen.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
getCurrentPanel: function() {
  var navHeight = $('header').outerHeight() - 1;

  $('section').each(function(){
    var index = $(this).index('section'),
        currentSection = $('section').get(index),
        nextSection = $(currentSection).next(),
        prevSection = $(currentSection).prev();
        currentPanelClass = 'current-panel';

    var waypointsDown = new Waypoint({
      element: currentSection,
      handler: function(direction) {
        if (direction === 'down') {
          $(currentSection).addClass(currentPanelClass);
          if (prevSection.length > 0) {
            prevSection.removeClass(currentPanelClass);
          }
        }
      },
      offset: 200
    });
    var waypointsUp = new Waypoint({
      element: currentSection,
      handler: function(direction) {
        if (direction === 'up') {
          $(currentSection).addClass(currentPanelClass);
          if ($(currentSection).length > 0) {
            nextSection.removeClass(currentPanelClass);
          }
        }
      },
      offset: navHeight
    });
  });
}

In that function, I’m looping through all the sections and assigning a waypoint to each based on its index. As you’re scrolling down, a section will get the class ‘currentPanel’ when it’s 200px from the top. On the way back up, a section would get that class as soon as it hits the nav. I’m also making sure that as the ‘currentPanel’ class is added to one section, it is removed from the section that just had it.

Now that I could find the current panel, I needed to designate next and previous panels and animate the scroll.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
findNext: function() {
  var that = this,
      currentPanel = $('.current-panel'),
      nextPanel = currentPanel.next('section');
  if (nextPanel.length > 0) {
    that.scrollToElement(nextPanel);
  }
},

findPrev: function() {
  var that = this,
      currentPanel = $('.current-panel'),
      prevPanel = currentPanel.prev('section');
  if (prevPanel.length > 0) {
    that.scrollToElement(prevPanel);
  }
},

scrollToElement: function(panel) {
  var navHeight = $('header').outerHeight();
  $('html, body').animate({
    scrollTop: panel.offset().top - navHeight
  }, 200);
}

Now I needed that scroll to happen in response to a presentation clicker. We had this one in the office, so I connected it to my computer and console logged the keyCodes to find which it simulated. It was using 33 and 34 (page up and page down). I also added a few more keyCodes in case the client forgot his clicker and needed to use the keyboard to move through the site.

1
2
3
4
5
6
7
8
9
10
11
12
13
slider: function(e) {
  var that = this;
  $(document).on('keydown', function(e){
    var $currentPanel = $('.currentPanel');
    if (e.keyCode === 40 || e.keyCode === 32 || e.keyCode === 13  || e.keyCode === 34 || e.keyCode === 39) {
      e.preventDefault();
      that.findNext();
    } else if (e.keyCode === 38 || e.keyCode === 33 || e.keyCode === 37) {
      e.preventDefault();
      that.findPrev();
    }
  });
}

Thanks to my ‘getCurrentPanel’ function, I knew that the panel with the class ‘currentPanel’ was my current panel, and could fire my ‘findNext’ or ‘findPrev’ functions based on whether the keyCodes called for up or down movement. Yay!

On to what I call ‘fragmented’ panels. By this I mean sections that call for a halt in the movement up and down and instead move through the content inside the panel itself. We’re also looking out for a fragment-subnav if it has one. All the fragmented examples in my demo site have subnavs. They give a great visual cue that there are other fragments and lets the user easily navigate them. But a subnav is not required.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
fragmentedPanel: function(movement) {
  var that = this,
      currentFragmentedPanel = $('.current-panel.fragmented'),
      currentFragmentedParts = currentFragmentedPanel.find('.fragmented-part.active');
  currentFragmentedParts.each(function(){
    var fragmentIndex = $(this).index('.current-panel .fragmented-part'),
        fragment = $('.current-panel .fragmented-part').get(fragmentIndex),
        nextFragment = $(fragment).next(),
        prevFragment = $(fragment).prev(),
        checkForLast = nextFragment.next(),
        checkForFirst = fragmentIndex - 2,
        hasSubnav = currentFragmentedPanel.find('.fragment-subnav').length;
    if (hasSubnav) {
      var activeSubnav = $('.current-panel .fragment-subnav.active'),
          nextSubnav = $('.current-panel .fragment-subnav').get(fragmentIndex + 1),
          prevSubnav = $('.current-panel .fragment-subnav').get(fragmentIndex - 1);
    }
    if (movement === 'down' && nextFragment.length > 0) {
      if (checkForLast.length > 0) {
        currentFragmentedParts.removeClass('active');
        nextFragment.addClass('active');
        currentFragmentedPanel.removeClass('first-fragment');
        currentFragmentedPanel.removeClass('last-fragment');
        if (hasSubnav) {
          activeSubnav.removeClass('active');
          $(nextSubnav).addClass('active');
        }
      } else {
        currentFragmentedParts.removeClass('active');
        nextFragment.addClass('active');
        currentFragmentedPanel.removeClass('first-fragment');
        currentFragmentedPanel.addClass('last-fragment');
        if (hasSubnav) {
          activeSubnav.removeClass('active');
          $(nextSubnav).addClass('active');
        }
      }
    } else if (movement === 'up' && prevFragment.length > 0) {
      if (checkForFirst >= 0) {
        currentFragmentedParts.removeClass('active');
        prevFragment.addClass('active');
        currentFragmentedPanel.removeClass('first-fragment').removeClass('last-fragment');
        if (hasSubnav) {
          activeSubnav.removeClass('active');
          $(prevSubnav).addClass('active');
        }
      } else {
        currentFragmentedParts.removeClass('active');
        prevFragment.addClass('active');
        currentFragmentedPanel.removeClass('last-fragment').addClass('first-fragment');
        if (hasSubnav) {
          activeSubnav.removeClass('active');
          $(prevSubnav).addClass('active');
        }
      }
    }
  });
}

For the code in your fragmented sections to work, you need to set up your HTML with certain classes. The ‘section’ will need to get the classes ‘fragmented’ and ‘first-fragment’. The fragments within your fragmented section need to get the class ‘fragmented-part’. The subnav items should get the class ‘fragment-subnav’. And the first fragment item and first subnav item should always get the ‘active’ class. Here’s an example of how to set up the HTML:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<section class="fragmented first-fragment">
  <!-- this is a slide with fragments -->
  <div class="fragmented-part active"></div>
  <div class="fragmented-part"></div>
  <div class="fragmented-part"></div>
</section>

<section class="fragmented first-fragment">
  <!-- this is a slide with fragments and a subnav-->
  <div class="fragment-subnav active"></div>
  <div class="fragment-subnav"></div>
  <div class="fragment-subnav"></div>

  <div class="fragmented-part active"></div>
  <div class="fragmented-part"></div>
  <div class="fragmented-part"></div>
</section>

A big thing in the fragmentedPanel function is to know when you’re reaching the end of the fragmented-parts so you can move on to the next slide. That’s why I have the ‘checkForLast’ and ‘checkForFirst’ variables. With every movement, I’m checking to see if an element exists in the next slot.

Great, now we need to update our slider function to halt movement if we see a ‘fragmented’ section.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
slider: function(e) {
  var that = this;
  $(document).on('keydown', function(e){
    var $currentPanel = $('.currentPanel');
    if (e.keyCode === 40 || e.keyCode === 32 || e.keyCode === 13  || e.keyCode === 34 || e.keyCode === 39) {
      e.preventDefault();
      if (currentPanel.hasClass('fragmented') && !(currentPanel.hasClass('last-fragment'))) {
        that.fragmentedPanel('down');
      } else {
        that.findNext();
      }
    } else if (e.keyCode === 38 || e.keyCode === 33 || e.keyCode === 37) {
      e.preventDefault();
      if ( currentPanel.hasClass('fragmented') && !(currentPanel.hasClass('first-fragment')) ) {
        that.fragmentedPanel('up');
      } else {
        that.findPrev();
      }
    }
  });
}

That’s pretty much it. I hope this makes some kind of sense to you. You can take a look at the full code here and a demo here.

And in honor of this blog post’s title. I leave you with a little Coolio circa 1994.

Boot It Up, Baby

How to Start a Simple Local Server

Sometimes when building a simple website, it’s tempting to check your changes by viewing your file(s) in a browser. But have you ever gotten stuck when some of your more complicated code isn’t working even though you KNOW IT SHOULD?!?!?!

Maybe you’re getting errors in the console like “XMLHttpRequest cannot load file:… Cross origin requests are only supported for HTTP”?

Well, the problem might not be you. It might just be your not running a local server. This happened to me while working with AngularJS’s custom directives and to a friend while working with popcorn.js.

“But I’m not using Rails or Sinatra,” you say?

“But I can’t type ‘shotgun’ or ‘rails s’ into my terminal,” you declare?

Want to know a trick?

1
brew install python

I know, it feels like you’re cheating on Ruby. But, she’ll never know, I swear.

1
python -m SimpleHTTPServer 8000

Then, just open up http://localhost:8000/ in your browser, and you’ll be on your way!

If your code still doesn’t work while on this local server…it might just be your code. So…there’s that.

Zip It, and Zip It Good

Sometimes scraping can be a complete pain when the site you’re dealing with is poorly structured. While trying to scrape composers and their respective musical pieces from a site, we ran into a problem because a composer’s pieces were not nested within that composer. Enter the ZIP method, the coolest method I had never heard of that I found out about today.

Here’s an example of the html structure we were dealing with:

1
2
3
4
5
6
7
8
9
10
11
12
13
<div lang="en" dir="ltr" class="mw-content-ltr">
  <p><b>Alejandre Prada, Manuel</b></p>

  <dl>
    <dd>Suite, Op.44</dd>
  </dl>

  <p><b>Barthe, Adrien</b></p>

  <dl>
    <dd>Aubade</dd>
  </dl>
</div>

It was easy to grab the composers and pieces seperately:

1
2
3
4
composers = @doc.css('div.mw-content-ltr p')
  #=>[#<Nokogiri::XML::Element:0x816eb2a8 name="p" children=[#<Nokogiri::XML::Element:0x816eb0c8 name="b" children=[#<Nokogiri::XML::Text:0x816eaee8 "Alejandre Prada, Manuel">]>, #<Nokogiri::XML::Text:0x816ead44 "\n">]>, #<Nokogiri::XML::Element:0x816ef894 name="p" children=[#<Nokogiri::XML::Element:0x816ef6b4 name="b" children=[#<Nokogiri::XML::Text:0x816ef4d4 "Barthe, Adrien">]
pieces = @doc.css('div.mw-content-ltr dl')
  #=>[#<Nokogiri::XML::Element:0x816eaaec name="dl" children=[#<Nokogiri::XML::Element:0x816ea90c name="dd" children=[#<Nokogiri::XML::Text:0x816ea72c " ">, #<Nokogiri::XML::Element:0x816ea678 name="a" attributes=[#<Nokogiri::XML::Attr:0x816ea614 name="href" value="/wiki/Suite,_Op.44_(Alejandre_Prada,_Manuel)">, #<Nokogiri::XML::Attr:0x816ea600 name="title" value="Suite, Op.44 (Alejandre Prada, Manuel)">, #<Nokogiri::XML::Attr:0x816ea5ec name="class" value="mw-redirect">] children=[#<Nokogiri::XML::Text:0x816efd80 "Suite, Op.44">]>, #<Nokogiri::XML::Text:0x816efbdc "\n">]>]>, #<Nokogiri::XML::Element:0x816ef0d8 name="dl" children=[#<Nokogiri::XML::Element:0x816eeef8 name="dd" children=[#<Nokogiri::XML::Text:0x816eed18 " ">, #<Nokogiri::XML::Element:0x816eec64 name="a" attributes=[#<Nokogiri::XML::Attr:0x816eec00 name="href" value="/wiki/Aubade_(Barthe,_Adrien)">, #<Nokogiri::XML::Attr:0x816eebec name="title" value="Aubade (Barthe, Adrien)">] children=[#<Nokogiri::XML::Text:0x816ee5ac "Aubade">]>, #<Nokogiri::XML::Text:0x816ee408 "\n">]>]>,

And iterate through them, putting the text into arrays:

1
2
3
4
composers_names = composers.collect {|e|e.text.gsub("\n", "")}
  #=>["Alejandre Prada, Manuel", "Barthe, Adrien"]
pieces_names = pieces.collect {|e|e.text}
  #=>[" Suite, Op.44", " Aubade"]

Then, we had to put these two arrays back together! So…we zipped ‘em!

1
2
composers_pieces = composers_names.zip(pieces_names)
  #=>[["Alejandre Prada, Manuel", " Suite, Op.44"], ["Barthe, Adrien", " Aubade"]]

Hooray! We now have paired the correct piece with the correct composer. All we had to do then was iterate through our arrays to create hashes: assigning array[0] as the key and array[1..-1] as the value. We needed [1..-1] because sometimes a composer has multiple pieces.

1
2
composers_pieces.collect {|array| {array[0] => array[1..-1]}}
  #=> [{"Alejandre Prada, Manuel"=>[" Suite, Op.44"]}, {"Barthe, Adrien"=>[" Aubade"]}]

From there it was easy to save the key as the composer and the value as that composer’s piece. Thank you to my team members for their help with this today. Shout out to Rebecca Greenblatt and Will Lowry!

Premature Ajaxulation

Don’t worry, it happens to a lot of functions

Recently, while building an app that used a lot of ajax calls, we kept on running into the problem of an ajax call firing before the function above it finished. Given that the function above provided variables that were necessary in our ajax call, this broke everything.

My team and I scoured the internet (i.e. stack overflow) for an answer, but came up with nothing. We finally asked a Flatiron School TA, and she gave a us a great solution.

We were building an app that calculates the half-way point of a given route. Then, we used the latitude and longitude of that point to search the Yelp API. The ajax call helps us get our javascript variables to the ruby method that directly deals with the API. And, here’s what it looks like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
var check_done = "not done"

function getStopPoint(response, percentage) {
  var totalDist = response.routes[0].legs[0].distance.value,
      totalTime = response.routes[0].legs[0].duration.value,
      distance = (percentage/100) * totalDist,
      time = ((percentage/100) * totalTime/60).toFixed(2),
      polyline = new google.maps.Polyline({
        path: [],
        strokeColor: '#FF0000',
        strokeWeight: 3
      });
      var bounds = new google.maps.LatLngBounds();
      var steps = response.routes[0].legs[0].steps;
      for (j=0; j<steps.length; j++) {
        var nextSegment = steps[j].path;
        for (k=0; k<nextSegment.length; k++) {
          polyline.getPath().push(nextSegment[k]);
          bounds.extend(nextSegment[k]);
        }
      }
  stopPointLatLonObject = polyline.GetPointAtDistance(distance);
  placeMarker(stopPointLatLonObject);
  stopPointLat = stopPointLatLonObject["d"];
  stopPointLon = stopPointLatLonObject["e"];
  check_done = "done";
}

function check(){
  if (check_done === "done"){
    $.ajax({
       url:'/restaurants/yelp_search',
       type: 'POST',
       data:(
         'lat=' + stopPointLat + '&' +
         'lon=' + stopPointLon + '&' +
         'type=' + $("#type").val() + '&' +
         'sort=' + $("#sort").val() + '&' +
         'mtd=' + $("#mtd").val()
       )
    });
  } else {
    setTimeout(check, 1000);
  }
}

The getStopPoint function goes through the polyline (we’re dealing with the lovely Google Maps API here) and finds the stopping point. The most important part of the getStopPoint function is where we define our stopPointLat and stopPointLon variables. Those are the lat/lng coordinates we’re going to send to the Yelp API via the ajax call. But, alas, alack! All is going to break if the ajax train leaves the station without its lat/lng coordinates!

How we went about fixing this was by using a check_done variable that is set to “not done” when it is defined. It only changes to “done” once stopPointLat and stopPointLon have their values. We use an if/else statement inside the check function to make sure that check_done === “done” before we send off our ajax call. Else, we setTimeout and run the check function all over again.

I hope that helps anyone out there who might have this problem!

Iterators Schmiterators

Choosing Between Each, Map, Collect, Select, or Detect

Sometimes it can be confusing for a ruby newbie to know what method to use when iterating over an array or hash. Most tutorials focus on the each method like a prized first child and then gloss over the others. That can leave a lot of newbies writing extra code trying to make the each method work when they could use a different iterator with more ease.

The easiest way to decide which iterator to use is to ask yourself a few questions about the block of code you are passing in your iteration.

Iterator Tree

The first question is, “Are you ‘puts’ing elements?” For that, you would want to use ‘each’ so that you can simply puts without altering the original array.

1
2
3
4
5
6
array = [1,2,3,4]
array.each { |n| puts "The number is #{n}."}
  #=> The number is 1.
  #   The number is 2.
  #   The number is 3.
  #   The number is 4.

If you aren’t ‘puts’ing elements, you need to ask, “Do you want to pass each element through the block and then return a new array or hash?” The key words here are ‘return a new array or hash’. The ‘each’ method won’t do that. It will just return the old array or hash no matter what you do with the elements in the block. If you know you want to create a new array or hash, you have to then ask, “Are you only evaluating truthiness?” If you are, ‘select’ or ‘detect’ are the way to go.

1
2
3
4
array.select { |n| n.odd? }
  #=> [1,3]
array.detect { |n| n.odd? }
  #=> 1

‘Select’ will return an array of all the elements that evaluate to true, while ‘detect’ will return only the first element that evaluates to true.

If you want to create a new array or hash AND you are doing something other than evaluating truthiness, you’ll want to use ‘map’ or ‘collect’. These two do the same thing. The only differences being: you have four less characters to type with ‘map’, but ‘collect’ describes a bit better what you are actually doing. Choose as you will.

1
2
array.collect { |n| n*10 }
  #=> [10, 20, 30, 40] 

One piece of advice: beware of sandwich coding. This happens if you are trying to use ‘each’ where you want to use ‘map’/‘collect’.

1
2
3
4
new_array = []
array.each { |n| new_array << n*10 }
new_array
  #=> [10, 20, 30, 40]

You see how I had to add two extra lines to set up the variable as an empty array and then call the variable after the code was passed? Those lines are the bread to the ‘each’ meat. Avoid this. Even if you love sandwiches. I love sandwiches.

The Surprising Accessibility of Coding

If you had come up to me a year ago and told me that I would become a web developer, I would have laughed in your face…hard. But here I am, after having a career as a professional opera singer, learning Ruby, a back-end web programming language used to develop some pretty complex web applications. And I’m actually liking it.

Like many, when I thought of programming, a stereo-type came to mind: one of a socially awkward loner whose brain works at lightning speed and is most likely a genius at math. And when I decided to switch careers at 30, I never thought of coding as a possibility. I was after all, coming from the world of opera. Opera singer turned coder? Ha! But then, at the urging of multiple friends, I decided to give it a chance.

What I discovered was that I was completely wrong about coding. It isn’t just for rocket-scientists and math geniuses. Not to say it isn’t hard. It is. I’m working my ass off. But I found it isn’t this unachievable thing that only people very far-removed from me can accomplish.

I quickly discovered, that I already had skills that are applicable to coding. Programming languages are just that, languages. And more and more, these languages are being developed to mimic the english language. Ruby is a great example of this. Want the word “Hello” to appear on your computer screen? Code in “puts ‘Hello’”. That makes a lot more sense than you thought it would, doesn’t it? Of course, it gets way more complicated than that. But it’s a great example of how learning a programming language is just learning new vocabulary within a new syntax. Anyone who has spent time learning a foreign language, already has experience with this.

I was also surprised by the creativity involved in coding. It isn’t just writing a bunch of zeros and ones that will sit on a server all alone like a chubby girl at a school dance. It’s building something that will speak to the world and could even change someone’s life…or just let them buy shoes, which is also cool. There are a million different ways, especially in Ruby, that allow you to code the same thing. You have a lot more freedom than you think and this is where you can let your creativity flow.

In the future, most of my blog posts will be more technical, focusing on specific aspects of Ruby and coding in general. But I thought it was so important to address this huge misconception the majority of humanity has when it comes to this profession. I urge you take a stab at it. Try your hand at HTML at code.org or codecademy. Or, if you are an experienced coder who wants to help make coding more accessible to more people, volunteer for such non-profits as Girls Who Code or CoderDojo NYC and help spread the code.