Scaling Images With HTML5 Canvas

Had intented to post this 8 months ago but it got lost in the sea of gists..

This is old news by now for most but I had quite a bit of fun implementing it for myself and figured I’d share my code and some learnings that came along with it. The basic idea is to use canvas to render an uploaded image and then utilize the toDataURL method on canvas to retrieve a Base64 encoded version of the image. In the example included here we will just direct link to the newly scaled image but you could imagine that we kick off an ajax request and actually process the image (in PHP base64_decode FTW). Without any more tangential delay let’s take a look at the code.

1
2
3
4
5
6
7
8
9
10
11
<input type="file" accept="image/*" id="imageFile" />
<table>
  <tr>
    <td>Width: <input type="text" id="width" value="200" style="width:30; margin-left: 20px;" /></td>
  </tr>
  <tr>
    <td>Height: <input type="text" id="height" value="200" style="width:30; margin-left: 20px;" /></td>
  </tr>
</table>
<canvas id="canvas" style="border: 1px solid black;" width="200" height="200"></canvas>
<button width="30" id="saveImage">Save Image</button>

The above HTML shouldn’t need any explanation but if it does feel free to open the attached JSFiddle to get a feel for it..

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
(function(){
  (function(){
      document.getElementById("imageFile").addEventListener("change", fileChanged, false);
    document.getElementById("width").addEventListener("keyup", sizeChanged, false);
    document.getElementById("height").addEventListener("keyup", sizeChanged, false);
    document.getElementById("saveImage").addEventListener("click", share, false);
  }());

  var currentImage,
    canvas = document.getElementById("canvas");

  function sizeChanged() {
    var dimension = this.id,
        value = this.value;
    canvas[dimension] = value;
    if(currentImage) { renderImage() }
  }

  function fileChanged() {
    var file = this.files[0],
        imageType = /^image\//;

        if (!imageType.test(file.type)) {
          console.error("not an image yo!");
        } else {
          var reader = new FileReader();
          reader.onload = function(e) {
            currentImage = e.target.result;
            renderImage();
          };
          reader.readAsDataURL(file);
        }
  }

  function renderImage() {
    var data = currentImage,
      image = document.createElement("img");
    image.src = data;
    image.onload = function() {
        context = canvas.getContext("2d");
      context.drawImage(this, 0, 0, canvas.width, canvas.height);
    }
  }
  function share() {
      document.location = canvas.toDataURL();
  }
}());

In order to bring the HTML to life we need to attach a few EventHandlers and define some basic functionality. The first things to tackle is the actual file upload.

The File API has been added to the DOM since HTML5 and will be used here to open the uploaded file from <input type="file"> on the "change" event. Inside of the change event there are 2 things that we want to do; (1) confirm the file type, and (2) render the file onto the canvas. The confirm the file type we can use the MIME type given to use from file.type and do a simple regex test (/^image\//) before attempting to render the unknown file (Even though we’ve added accept="image/*" inside the input that can be easily modified to attempt to upload any file). Once we are convinced that the user has uploaded an image it’s time to read the file and send it off to the canvas to render. FileReader’s readDataAsURL will allow us to process the file asyncronously and allows for an onload callback that gives us the ability to set the newly read image and ask the canvas to draw.

Additional Reading

Playing the Twitter-game

I am not a marketer not do I have any real prior experience managing PR/social media for a company of any size. This is just a write-up of some of my learnings while out in the wild

By all accounts I am a Twitter novice; I joined a few years ago but don’t really keep up with it (rage tweet a politician or media personality from time to time but not much more). From other business ventures I’ve learned that having a strong presence on Twitter+Facebook can be a great way to drive traffic to your site and keep folks updated with any updates but I had never invested any time in growing the profiles (outside of word of mouth and the usual social links in the footer of a site). For my most recent project I decided to take an active role in the growth of my Twitter account and to attempt to use a few of these automation tools / API to make my life a little easier.

I started the journey by trying out a product called ManageFlitter.com; I had gone all in and decided to buy the smallest plan they offered to maximize my “strategic follows”. After about 2 days of bulk requesting it becamse obvious that the “snazzy dashboard” views were nothing more than a facade.. I was hitting rate limits and unable to process a single follow / request with the access grant I had enabled for the site.

At this point I started angrily emailing support to figure out why I was being blocked without hitting any of the actual API / request limits listed in the twitter guidelines. Here is the initial diagnosis I recieved (steps to fix are omitted since they were useless..)

Thank you for writing into ManageFlitter support on this. Unfortunately Twitter seems to have revoked some 3rd party app tokens over the last few weeks. This causes “invalid or expired token” in the notifications page and keeps your account from being able to process actions, post tweets into Twitter, or from allowing your ManageFlitter account to properly sync with Twitter.

Hmm so at this point I was frustrated because there is no way my token should have been revoked! Obviously they were using keys so that they could make these “analytic queries” on the twitter user base and had messed something up on their end that had made it impossible to proceed. I pressed along that line of thinking and received the following “helpful” response.

I am sorry to hear this. There seem to be a small group of Twitter users currently having this issue with ManageFlitter and some have noted that once their account is revoked, it is far easier for their account to continue to be revoked afterwards.

Some users have suggested that waiting 24 hours helps reset the account. Others have noted that the amount of actions they perform in sequence also matters greatly to avoid the revoked access. Some have noted that after they perform 100 clicks, if they wait 20-30 seconds (usually enough time to go to the Notifications page and see if Twitter is revoking access), then continuing to perform more clicks.

There is no particular reason why some accounts are being affected and other are not. We have been in touch with Twitter and unfortunately Twitter haven’t offered much in the way of remedy for us or our users.

TLDR; I was told to wait for the problem to fix itself..

This threw a massive wrench in my plans to bulk follow people inside the industry and hope that some percentage of them would follow me back. After a few more angry emails I was told to just wait it out.. At that moment I pulled out the classic "I will have to proceed with a claim to Paypal / the better business bureau" argument to get my money back and move on with another option.

After getting my money back I decided to ride the free train with Tweepi which had no problems for the first week of usage so I decided to buy a month of the lowest tier to use some of the analytics / follow tools that were being offered. With 2 weeks on the platform I can say that I’m very happy with what I paid for and will continue to use it in the future (until my follower count levels out a bit)

So why am I writing this article if I am just using a service to accomplish the task for me? While Tweepi does a lot for me it still imposes artificial limits on follow / unfollow in a 24 hour period. (see pic below)

You can see that the service has some limitations. The main one being that I can follow more people than I can unfollow in a given day. While that makes sense with Twitter’s policies my goal is a raw numbers game where I’d like to follow as many people as possible in hopes they follow me back. Whether they follow me back or not I am content to unfollow and continue with my following numbers game.

Through this process I was able to drive my followed count up quite a bit (considering my actual follower count)

but still had this problem of the unblanaced follow:followers that I wanted to correct. If I was active on Tweepi there was no way for me to drive this down without having to completely stop following people for a period while I unfollowed the max every day.

So today I decided to have a little fun inside the browser and see what I could do. :grin:

Since twitter has a web + mobile application I could obviously sit and click through each of the people I was following to reduce the number but..

So let’s see how well formatted the twitter follower page is (and since it’s Twitter we know its going to be well organized). When arriving at the page we see the trusty un/follow button for each of the followers

we also notice that twitter has some infinite scroll magic going on to continuous load the 1000s of people we follow. With that knowledge in our hands it’s time to craft some Jquery flavored code to do the clicking for us

1
2
3
$.each($('.user-actions-follow-button'), function(value,index) {
  $(index).click();
});

Pretty easy to click through each of the buttons on the page but that’s only going to account for the ones we have manually scrolled through.. Not sufficient since we have >4000 but <20 buttons on the page. So let’s handle that damned auto-scrolling

1
2
3
4
5
6
7
8
9
var count = 0;
function st(){
  $("html, body").animate({ scrollTop: $(document).height() }, "fast");
  if(count < 2000) {
    count += 1;
    setTimeout(st, 500);
  }
}
st()

You might be thinking; why not just for loop this shit?! The scroll animation needs a bit of time to allow for the page load; if you call it too fast the entire page will bug out and the “click button” code wont work as expected. So we just use setTimeout and let that sucker run (good time to take a stretch or make some coffee). When you come back you should hopefully be at the bottom of the screen (wait for GridTimeline-footer to show up and you know you are done) :D

Run the click code and patiently wait for your browser to slow down drastically and eventually unfollow your entire list. The result should look something like this

The 1 follower there through me off since when I clicked on the link for my followers there wasn’t anyone listed. At this point I was suspicious that I may have set off one of the limits that would have deactivated my accounts. I checked my emails and didn’t see any warnings or notifications from Twitter but did start seeing this whenever I tried to follow someone on their website. (Learn more here)

At this point I was thinking I just fucked myself and got my account banned or locked in some way. During this time of panic I decided to refresh my browser and saw some funky behavior on my profile page….

No “Following” count at all?! And I cant follow anyone because of some unknown policy breach..

After writing a short sob story to Twitter about how I had “accidentally unfollowed everyone”(cover my ass) I thought about the locking problem a bit more..Hmmm what about that Tweepi token I was using before? Who would have guessed that it would work and allow me to follow people again!

So with a little bit of crafted Javascript I was able to drop that Following count down without having to fight any artificial limits imposed on me by some third party. I’m incredibly happy with the results (as I am not banned and my account is working as expected) and plan to reproduce with another client in the future.

It’s always a good feeling when adding a new tool to the utility belt.

Replacing SimpleCov

After fighting with simplecov for a little longer that I would like to admit; was attempting to get it to start analyzing a group of files that were the meat and potatoes of my application(Golaith application). Unfortunately none of the default configs (Simplecov.start 'rails', etc) nor the filters were allowing my files to be tracked and printed in the handy coverage html file. Because of all this struggling I decided to go ahead and create my own crude coverage module; I’ll be using this post to discuss my learnings and share an early working iteration.

To get started I wanted to have the invocation of coverage be exactly the same as simplecov; so let’s start with the goal of adding CrudeCov.start inside of our spec_helper.rb to keep track of the files we care about.

Before diving into the code I did a little research on how Simplecov.start worked. I was mainly looking for information on how it was able to keep track of files with only a single invokation inside of the spec_helper. Inside of lib/simplecov.rb we find a definition of the start method; which checks to see if the water is friendly (SimpleCov.usable?) and then starts the tracking with a call to Coverage.start. At this point during my investigation I was pretty sure that Coverage was a Class/Module defined within the simplecov source; after some grepping within the repo I only found one other reference to Coverage inside of lib/simplecov/jruby_fix.rb. Unfortunately that reference is just as the name implies, a jruby specific fix for the Coverage module that overrides the result method. When I was that in the only reference to the module I ran off to google and was incredibly pleased to find that Coverage is a Ruby module! According to the Ruby 2.0 Coverage doc

Coverage provides coverage measurement feature for Ruby. This feature is experimental, so these APIs may be changed in future.

With that note about this being an experimental feature let’s be flexible and see what we can do (simplecov uses it and it’s a pretty successful gem). The usage note in the doc also looks fairly promising:

  1. require “coverage.so”

  2. do ::start

  3. require or load Ruby source file

  4. ::result will return a hash that contains filename as key and coverage array as value. A coverage array gives, for each line, the number of line execution by the interpreter. A nil value means coverage is disabled for this line (lines like else and end).

So we don’t have to worry about #1 (will be loaded by Ruby) and can start with #2 and call Coverage#start, load all the files that matter, and then use Coverage.result (which Returns a hash that contains filename as key and coverage array as value and disables coverage measurement.) to see how well the files have been covered.

As a note Coverage will pickup any file that has been required after do ::start so it’s a good idea to have a way to selectively find the files that you want to get the coverage results on (e.g. Array of keys Dir['./app/apis/*rb'] to grab the coverage results you want)

Since we don’t have any intention of supporting JRuby we should be able to use Coverage as is for our CrudeCov example. Let’s start off with the #start and #print_result(used after our test suite finishes)

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
module CrudeCov
  class << self
    def start
      @filelist = []
      Coverage.start
    end

    def print_result
      cov_results = Coverage.result

      root = File.dirname(__FILE__)[0..-6]
      filelist = [
        "./app/apis/untested_endpoint.rb",
        "./app/apis/covered_endpoint.rb"
      ]

      filelist.each do |file|
        # process file results
        # coverage results returns Array([1,0,..,nil,3] where val = # of times line was hit & size = # of lines)
        # this makes for easy matching when creating the pretty html result file
        file_results = cov_results[file]
        results = file_results.compact.sort # remove all nil entries & sort to help with calculations

        puts "Results for: #{file}"
        total_lines = (results.length*1.00).to_f
        covered_lines = total_lines-results.find_index(1)
        percentage = (covered_lines/total_lines).round(2)*100
        puts "#{percentage}% Covered (#{covered_lines} of #{total_lines} Lines Covered)"
      end

      # create html for easy viewing outside of shell
    end
  end
end

Our CrudeCov module above is pretty straightforward and covers our basic needs of (1)Having a one-line call to add to our spec_helper, and (2) a print method that we can call after our suite is finished running (ideally the module would figure out which test framework is being used and ensure that the hook is made to print results at the end of the suite). With the example above we will have to explicityly ensure that the print_result method is called.

Assuming that we are testing with RSpec our spec_helper will look something like this

1
2
3
4
5
6
7
8
9
10
11
require 'crudecov'

CrudeCov.start
# require project files..

Rspec.configure do |config|
  # your other config..
  config.after(:suite) do
    CrudeCov.print_result
  end
end

With that basic setup you will get a print out of the coverage percentages for all files that have been included in the filelist. In less than 30 lines of code we were able to have an incredibly simple coverage module that we could use in a project to sanity check a file that may potentially lacking coverage or confirm proper testing. From that simple example you can start to see how a project like simplecov would come into being and how something as simple as CrudeCov could become a full ruby coverage suite.

With the legitimate need to get data on the effectiveness of your tests; SaSS solutions like Coveralls (which did not recognize a Goliath application) + gems like simplecov, rcov and cover_me have all become relied upon staples for the TDD community.

What’s the point of even doing TDD if you aren’t covering new lines of code that could result in bugs down the road? For that reason alone I’d say it’s worthwhile to implement some sort of coverage tool when all the rest have failed.

Requirements for a Text Editor

These are my minimum requirements for a competent text editor. The list is meant to serve as a quick litmus test for true understanding of tooling in the craft of writing software.

  1. Navigate with ease; jumping to line number, move to top/bottom of file, selection/deletion helpers (inside of quotes, block, function, etc)
  2. Fuzzy file finder + jump to mentioned files / methods
    • I use ctrl-p to fuzzy find in vim (one of my must have plugins)

    • For part of my day rails.vim provides handy helpers to jump from model to controller using custom commands like :Emodel, and :Econtroller. When not working with Rails I can typically rely on ctags to do the trick and allow me to gf around projects at will (hard to beat Command+Click inspection in RubyMine)
  3. Run spec(s) without having to Alt+Tab
    • I use vim-dispatch to run tasks in the background and have the results returned in the vim quickfix window
    • When running a single test or named context I can use a custom hotkey to select the test name under the cursor and create the dispatch command to run
  4. Find Regex pattern within file (replace, count, etc)
    • I’m lucky enough that my editor has %s. Allows for a ton of options with the same simple DSL
  5. Search entire project (git grep or Ag inside the editor)
    • Results should be returned in easy to parse format with jump-to-file capabilities
  6. Organize code into tabs / windows / panes
    • This should come with all editors now but please know how to vsplit if you claim to be a vi user..
  7. View git changes within file (+/- on line numbers) and basic git workflow integration (git blame, commit/push without leaving editor)
    • I use vim-gitgutter to track changes to the file. This has become a staple of my vim config and I couldn’t imagine working without it.

    • For the rest of my git functionatlity I use Tim Pope’s `vim-fugitive’, serves as a wonderful git wrapper for the majority of git commands.
  8. Customizability
    • This is the whole reason I am a vim user. I love the ability to change almost all facets of the editor; from basic config options in .vimrc to adding autocmds to change functionality for events within the editor (file open, file save, etc). In my mind an editor should allow the user to tailor the tool exactly how they want it.

It’s important for anyone writing software to respect the craft and maximize the effectiveness of the tools we use to create. Since the editor is such an important tool in our toolbelt we should always strive to optimize and improve our daily usage.

Replacing Heroku

For anyone still on Heroku and throwing addons at your application please stop and seriously reconsider what you are doing. Across the board things are getting a bit ridiculous for the average hobbyist / fincially conscious company. I used to be quite the Heroku fanboy but after my recent experience with Hosted Graphite I have changed my tone completely.

I was recently looking to add some Graphite metrics to a Heroku application that I’m working on and stumbled across a solution called Hosted Graphite. There was of course a starter package which shows off the beauty of Grafana and a few metrics to give you an idea of the potential. The current pricing can be found here (this prices do fluctuate quite regularly depending on the service); I’ve also gone ahead and added the current prices as of writing this below:

Each price point adds a few key features to the mix + increases your metric limit (this is a bullshit number stored by whatever service you are using so feel free to push it without too much fear. I’ll do a follow-up post on my experience with a MongoDB solution that had a document “limit”). The paid features for Hosted Graphite are as follows:

  • Daily Backups (>Tiny)
  • Data Retention (>Tiny)
  • Hosted StatsD (>Small)
  • Account Sharing (>Medium)

For most folks that are looking to use Graphite in their application they are probably looking to use some StatsD wrapper to report metrics. If you only have a few data points that you care about and can deal with the low retention rate / backup policy then maybe you could add an aditional worker to handle the task and not have to deal with a “hosted StatsD” solution. Even with that stretch of the imagination you are looking at a $19 monthly solution but most likely going to be suckered into a higher cost if you seriously start to add metrics.

Rather than paying for a plugable addon I’d highly recommend spinning up your own Digitalocean server for $10 bucks and get as much value as the Small package (this is being very generous..) being offered on Heroku. If you don’t know how to spin up a Graphite+StatsD server there is no excuse with the resources provided by DO alone:

Assuming that you can get through a basic set of steps then you should have a working Graphite+StatsD service at your disposal with ½ the monthly cost. The interface will be exactly the same (API key/namespace + service endpoint) and you will have the freedom to manage the server as you please. And when you are measuring things you sure as hell don’t want things to stop reporting because a reporting mechanism fails and you have no way to tell..

This is another major gripe that I have with Hosted Graphite; why would I pay for a service like “hosted statsD” when it will fall over and I have no debug info into why that might have happened. When testing with a Small instance of Hosted Graphite the StatsD endpoint would become unresponsive for minutes at a time while UDP/TCP were working as expected. On the other hand with a droplet you have the freedom of config (and Graphite, Carbon, and StatsD have a lot of configuration).

Configuration brings me to the final point I want to make. “Features” like Data retention length are nothing more than configuration that can be managed when setting up your Graphite instance. By configuring carbon you can set custom retention policies on different data points (regex matching FTW) and ensure that you have the policy in place that makes sense for the data being stored. This gives you the freedom of full flexibility inside of a problem-set that you are actively engaged in when adding measurements (naming new metrics). As a best practice you should set a reasonable default and add new metrics into the appropriate policies as needed.

Deploying Jekyll Site on Ubuntu VPS

Over the past few months Heroku has become an increasingly expensive option for projects that are starting to scale in any way (Addon price hikes, Dyno Sleeping and Charging, etc.). For ease of use and speed to spin-up up a prototype and have it running Heroku is still the leader and will continue to be my goto in times of need. But as soon as Dyno Charging becomes an issue I think it’s time to capify that app and use one of the many VPS hosting options out there.

For this project I wanted to migrate my simple static site to a $5 droplet on DigitalOcean. I’m sure you could get a better price on AWS but am fine paying the little bit extra for ease of use on DO.. After going through the initial server setup (groups, perms) it’s time to go though the actual server preperation.

Server Preperation

sudo apt-get update
sudo apt-get install nginx

Ensure that nginx is properly installed by visiting the public ip from the following command

ip addr show eth0 | grep inet | awk '{ print $2; }' | sed 's/\/.*$//'

If everything went correctly you should see the following nginx welcome screen (nginx will automatically start when installing on Ubuntu so there is no need to start the service before running the test):

For basic nginx service management use the following commands:

sudo service nginx restart
sudo service nginx stop
sudo service nginx start

Now that we have nginx ready to go on our server let’s install the final pieces to bring our Jekyll site to life.

sudo apt-get install git-core
curl -L https://get.rvm.io | bash -s stable --ruby=2.1.2

After installing rvm there will be some information about the install process and some next steps that may be required to start developing. If you do not have access to rvm on the command line after install you may have to run the following or add to your ~/bash_profile:

source /usr/local/rvm/scripts/rvm

Once Ruby is running correctly go ahead and install jekyll and all it’s glorious dependencies.

gem install jekyll

Server Setup Compete

Now it’s time to set up your jekyll site for deploy. For this example we are going to use Capistrano 2.x

gem install capistrano
capify .

This will create a few config files but the one we want to concern ourselves with is config/deploy.rb

set :application, "blog"
set :repository, 'public'
set :scm, :none
set :deploy_via, :copy
set :copy_compression, :gzip
set :use_sudo, false

set :user, "deployer"

# the path to deploy to on your VPS
set :deploy_to, "/home/#{user}/blog"

# the ip address of your VPS
role :web, "XX.XX.XX.XXX"

before 'deploy:update', 'deploy:update_jekyll'

namespace :deploy do
  [:start, :stop, :restart, :finalize_update].each do |t|
    desc "#{t} task is a no-op with jekyll"
    task t, :roles => :app do ; end
  end

  desc 'Run jekyll to update site before uploading'
  task :update_jekyll do
    # clear existing _site
    # build site using jekyll
    # remove Capistrano stuff from build
     %x(rm -rf public/* && rake generate)
  end
end

Modify a few lines to suit your needs and you should be ready to cap deploy. To verify your new Capistrano deploy run the following; if both commands succeed without Warnings or Errors you will ready to deploy.

cap deploy:setup
cap deploy:check

Enjoy your Jekyll VPS

Matchadd vs Match

The target of this post is to clear up any confusion about the subtle differences between matchadd and match/2match/3match. This is not meant for the novice vim user but folks that have some experience with vimL. I’ll try to keep it as simple as possible for anyone interested in some of vim’s inner workings.

From :help match we get the following information:

:mat[ch] {group} /{pattern}/
        Define a pattern to highlight in the current window.  It will
        be highlighted with {group}.  Example:
            :highlight MyGroup ctermbg=green guibg=green
            :match MyGroup /TODO/
        Instead of // any character can be used to mark the start and
        end of the {pattern}.  Watch out for using special characters,
        such as '"' and '|'.

        {group} must exist at the moment this command is executed.

        The {group} highlighting still applies when a character is
        to be highlighted for 'hlsearch', as the highlighting for
        matches is given higher priority than that of 'hlsearch'.
        Syntax highlighting (see 'syntax') is also overruled by
        matches.

From the definition it should be pretty clear that match is responsible for applying defined highlights to the current window. The {group} argument is simply the name of the highlight group definition you would like to use on a given {pattern}.

To visualize a list of all available highlight groups you can use :highlight. This is really handy when programatically trying to add hightlight groups for a plugin. As you can see from the screenshot below our colorscheme has redefined the majority of the groups and :hi does a great job showing all of the possibile combinations.

Using that information we could use any one of the groups and apply a match in our current buffer. Let’s take a look at using the ErrorMsg highlight group to match the entire contents of a line.

:match ErrorMsg /\%13l/

If you have not used match before take a moment now to play around with it in your own vim session and get a feel for how vim reacts to all different types of scenarios. Which match takes priority when running consecutive matches on the same pattern?

One of the main limitations with using match is that you cannot continuously apply new matches and have the previous ones stick. Because of this it is not the most suitable option for creating any sort of full fledged highlighting plugin. If you read the help section below match you will encounter :2match and :3match which is meant to provide a way to manage 3 seperate highlight groups. Unfortunately these functions suffer from the same shortcoming we saw with the original match.

In order to compensate for the limitation on match you can create more complex patterns to include multiple line selections (/patern1\&pattern2/) or whatever else you would normally do inside of the typical vim search regex (remember a touch of magic is only a \M away with this type of searching). If you’d like to see evidence of the hacks that are possible while using check out the first iteration of vim-poi (fresh-start branch is ready for testing and this logic will all be replaced with the smarter and better matchadd pattern)

1
2
3
4
5
6
7
8
9
10
11
12
13
  for i in b:poi_lines{a:group}
    let c += 1
    if c == 1
      let s:build_string = s:build_string.'/\%'.string(i["line_num"]).'l\&\M'.i["content"]
    else
      let s:build_string = s:build_string.'\%'.string(i["line_num"]).'l\&\M'.i["content"]
    endif
    if c == len(b:poi_lines{a:group})
      let s:build_string = s:build_string.'/'
    else
      let s:build_string = s:build_string.'\|'
    endif
  endfor

Stop Being Lazy and Learn Haml or Another Templating Engine

The desire to write familiar view code, similar to the same html you were writing to support your shitty LAMP apps is completely understandable but it’s time to move on from the glory days..

There seems to be this idea that using erb is “good enough” in all scenarios because so many of web devs are accustomed to seeing their old friend html.. As someone who knowingly opted out of using Haml because HTML always felt like option that would give me the most flexibility. Since the actual engine driving the browser has a simple set of responsibilites (MASSIVE OVERSIMPLIFICATION: fetch a document from a URL and render the contents of that document onto the page) the document that we are going to be providing to the browser’s engine must adhere to standards that allow many browsers to function with their various implementations. Because of this fact debugging/learning a templating engine is actually a walk in the park if your HTML skills are as sharp as they should be..

To be fair; when researching Haml it may not be clear what you are getting yourself into.

1
2
3
4
5
6
7
<section class=”container”>
  <h1><%= post.title %></h1>
  <h2><%= post.subtitle %></h2>
  <div class=”content”>
    <%= post.content %>
  </div>
</section>

To be honest the above code doesn’t look to be that bad to me but I’ve never really had an “eye for design”. As an additional bonus I know exactly what the browser output is going to be (assuming no CSS + JS messing with things). But let’s take a look at some haml code..

1
2
3
4
5
%section.container
  %h1= post.title
  %h2= post.subtitle
    .content
      = post.content

The raw number of keystrokes should be striking to get the same browser output. The declarative simplicity that Haml provides is unbelievable when you consider all the bullshit we deal with when writing HTML. Anyone who written web apps can tell you at least one tail of a missing closed tag.

I was hesitant about writing something that wasn’t what I knew and used for a long time. The notion of “what works for me” is not a good enough reason to write code that is sub par. Since most devs are bright enough folk I don’t think learning Haml is more than a 1 micro-project away from being your new favorite way to write your views.

For more information on Haml check out the Haml Homepage. All links to get started should be easily accessible from there.

The beauty of using haml in every day development is that your code shape will be forced to be much nicer and cleaner. Consider writing a container div with a header, body, and footer. One would hope for clear structure that tells a “foreign eye” exactly what is going on in the given view.

Just imagine the skeleton that would evolve in the html version of this as a clean slate to start from.

1
2
3
4
5
6
7
8
9
10
<div class="container">
    <div class="header"></div>

    <div class="content">
    ...
    </div> <!-- end of content -->

    <div class="footer"><%= render "footer" %></div>

</div> <!-- end container -->

As anything gets added to this skeleton you hope that the structure stays readable. I think that the forced structure prevents you from being your own worst enemy . Because of this simple fact I am turning into a Haml guy and think a weekend using it will convert any non-believers.

Thoughts on Heroku

After deploying over 10 applications with Heroku I think that I can finally make a fair assessment on the general developer / Heroku relationship.

After watching an excellent talk by Matz at Waza (link at the bottom) I was taken aback by how much he seemed to respect Heroku’s ability to support the developer that wants to build. Heroku’s use of AWS to anyone to standup a system to run their code at no cost is an absolutely stunning accomplishment .

My skepticism with this idealistic view is that at the end of the day Heroku is a business that needs to make money. But they also need to keep their image and offer the “playground” to build and prototype without needing a budget.

During the talk Matz mentions this ability to spin up a prototype at no cost which as being something fundamentally new and exciting. Having this ability eliminates a the large risk of money puts people in a new age; one where there is no barrier of hardware costs preventing concepts to come to life.

Heroku as a company proves that hardware is cheap and that it is feasible to remove the hardware barrier that used to exist less than 10 years ago(someone tell me if I’m wrong, I was 13 and it was too expensive for me). For developers that like to build things from the ground up Heroku is a wonderful tool that should be utilized to its fullest for as long as you can use it for free.

I am not sure about the exact price model of Heroku’s production ready systems are but I think that it becomes clear pretty quickly that if you are building to grow and scale Heroku may not be the best option.

I will use a real world example of a small sinatra app that is currently hosted on Heroku for the steep price of $0.

I have 1 Dyno with 512 MB of RAM that likes to shut off when it becomes inactive for a certain period of time (smart on Heroku’s part for sure to keep costs down), 1 MongoHQ instance with 512 MB of storage (they call it sandbox mode so this is an expectedly low amount of storage).

When upgrading from the Standard (free) dyno it would cost me $34.50 to essentially double my computer power and have a cycling set of dynos that will work together as a failsafe mechanism. I have just recently started crashing dynos and am thinking that it would be nice to have a backup that is reliable without having to do any system work to setup proper restarting and recovery (no god debugging for me sounds pretty sweet).

For any additional storage I would be paying around $18 per GB of SSD storage hosted through MongoHQ (of course an Amazon service wrapped in an add-on pricing model). I am not sure what the going rate for a mongoDB hosted solution but that seems a bit pricey to me if you are going to do large lazy data storage like I have been enjoying. But to be fair that could easily be moved to another database since in reality it’s just a minor detail.

When laying out the costs to double the performance of my small application it would be ~$52 a month. TBH that isn’t a horrible price for no extra work scaling. I am curious what the minimal cost of this setup would be on AWS directly (I do know that if you want to run tests on postgres instances it will cost you a pretty penny at the end of the month).

References

Matz at Heroku’s Waza (2013)