Hardscrabble 🍫

By Max Jacobson


19 Mar 2013

Note: When I was first learning Ruby in late 2012, I made a small web app called “beefsteak” to act as my blog. I wrote several posts on that blog while I was exploring and adding features. I’ve since migrated my personal blog away from beefsteak and to Octopress, and then Jekyll. I decided to collapse those posts into one post because I think they’re kind of confusing now that they’re on a Jekyll site. I’m writing this note on July 10th, 2016. By the time you’re reading this, who knows what the blog is running on…

testing this little devblog thing

Originally posted December 1, 2012

So it’s like 6am and I guess I just made my first mini markdown blog engine.

I’m almost certainly too quick to congratulate myself. It has approximately zero features but it’s succeeding at putting HTML on the screen and I call that a win.

This is pretty much the whole app:

require 'sinatra'
require 'kramdown'

get '/' do
  @title = "devblog - max jacobson"
  the_html = String.new
  the_html << "<h1>#{@title}</h1>\n\n<hr />"
  posts = Dir.entries("posts")
  posts.each do |filename|
    if filename =~ /.md/
      the_text = File.read("posts/" + filename)
      the_html << Kramdown::Document.new(the_text).to_html + "<hr />"
  erb the_html

get '/css/style.css' do
  scss :style

It just checks all the files in a directory called “posts”, checks all of the ones with the .md file extension, and then renders them all one after the other. That’s it!

I wrote this in just a minute or two so I’ll be sure to add more blog-like features to this. Maybe design it up a bit. That mention of sass is more or less for show.

I feel smart right now so I’m comfortable admitting that I’d love to use Jekyll but just can’t figure out how the hell it works.

This is not one of those smartly static sites. It generates each page as you load it. Probably pretty fast, but still, not smart-smart.

Already with the updates

Originally posted December 1, 2012

There are already updates wow I am good.

Things I’ve added in the last small amount of time:

  • a config.rb file for helper methods that get the blog title and subtitle
    • so nothing in the main app file, web.rb is hardcoded and theoretically someone else could use this
  • permalinks based on the file name
  • option to see the markdown source of any page with a little link at the bottom. (alternately just add the .md to any page)
  • some small amount of style, mainly to do with displaying codeblocks
    • incidentally, I’m treating markdown source as “code”

Here’s what the tree of my project directory looks like right now:

[~/Dropbox/Sites/devblog] [maxjacobson] [07:41 AM]
ϡ tree
├── Gemfile
├── Gemfile.lock
├── Procfile
├── config.rb
├── posts
│   ├── 2012-12-01-already-updates.md
│   ├── 2012-12-01-gem-anxiety.md
│   └── 2012-12-01-the-first-post.md
├── views
│   ├── layout.erb
│   └── style.scss
└── web.rb

2 directories, 10 files

Off topic, but: I learned how to customize my bash prompt earlier today and that’s what it looks like.

added sorting

Originally posted December 1, 2012

I have added sorting and so I believe the posts on the homepage will be in the proper order. Kewl.

did i just implement search in like 5 minutes?

Originally posted December 1, 2012

I’m not sure but I think I just implemented search like it was no big deal.

It works well and fast but only because there are so few posts, I assume. I’m not searching the whole internet here or anything, but I do kinda feel like I Am Google or something.

That’s a theme for this system: it will get worse the more I use it. I’m kinda excited to ruin it.

man, sorting is hard when you use the twelve hour clock

Originally posted December 1, 2012

I have a TextExpander snippet for quickly inserting the current time. I write ttime and it inserts, say, 10:43 PM. In that format. If it were an hour earlier, it would say 9:43 PM. No leading zero.

This is what was screwing up my sorting method.

I want posts to be sorted based on both date and time, so if there are multiple posts in a day they’ll show up in the right order instead of just alphabetically.

I could have just written those leading zeros manually or I could’ve just used a twenty-four hour clock, but I like my snippet how it is and want my code to come to me rather than the other way around.

So I’ll share my method because I’m sure in as little as a month I’ll find it embarrassing and that’s very fun for me, being embarrassed.

The argument for this method, to_sort, is an array of hashes that stores all of the posts.

Each hash looks like this:

  :text=>"the body of the post (in markdown) would go here, but it would be long so I'm leaving it out",
  :title=>"Introducing myself to the command line",
  :time=>"12:00 PM",
  :tags_array=>["command-line", "learning"],

And the method takes the info, specifically the date and time, and adds an additional parameter, :sorter, and then sorts the array based on that value of each hash, and returns the sorted array.

def sort_posts (to_sort)
  to_sort.each do |post|
    the_date = post[:date]
    the_time = post[:time]
    if the_time =~ /AM|am/
      if the_time =~ /^[0-9]:/ # aka ONE digit before the colon
        sorter = the_date.gsub(/-/,'') + ("0" + the_time).gsub(/:| |AM|am/,'')
        sorter = the_date.gsub(/-/,'') + the_time.gsub(/:| |AM|am/,'')
    if the_time =~ /PM|pm/
      if the_time =~ /12:/
        sorter = the_date.gsub(/-/,'') + ((the_time.gsub(/:| |PM|pm/,'')).to_i).to_s
        if the_time =~ /^[0-9]:/ # aka ONE digit before the colon
          sorter = the_date.gsub(/-/,'') + ((("0" + the_time).gsub(/:| |PM|pm/,'')).to_i + 1200).to_s
          sorter = the_date.gsub(/-/,'') + ((the_time.gsub(/:| |PM|pm/,'')).to_i + 1200).to_s
    post[:sorter] = sorter.to_i
  to_sort.sort! { |a,b| b[:sorter] <=> a[:sorter]}
  return to_sort

So that’s that.

Edit (2012-12-02, 10:58 AM): There’s a bug in there! posts at 12:15am would sort newer than posts at 1:15am becuase twelve is a higher number than one. So I added some conditioanls to treat 12 like zero. Updated code below:

def sort_posts (to_sort)
  to_sort.each do |post|
    the_date = post[:date]
    the_time = post[:time]
    if the_time =~ /AM|am/
      if the_time =~ /^[0-9]:/ # aka ONE digit before the colon
        sorter = the_date.gsub(/-/,'') + ("0" + the_time).gsub(/:| |AM|am/,'')
      elsif the_time =~ /^12:/
        sorter = the_date.gsub(/-/,'') + ("00" + the_time).gsub(/:| |AM|am|12/,'')
        sorter = the_date.gsub(/-/,'') + the_time.gsub(/:| |AM|am/,'')
    if the_time =~ /PM|pm/
      if the_time =~ /12:/
        sorter = the_date.gsub(/-/,'') + ((the_time.gsub(/:| |PM|pm/,'')).to_i).to_s
        if the_time =~ /^[0-9]:/ # aka ONE digit before the colon
          sorter = the_date.gsub(/-/,'') + ((("0" + the_time).gsub(/:| |PM|pm/,'')).to_i + 1200).to_s
          sorter = the_date.gsub(/-/,'') + ((the_time.gsub(/:| |PM|pm/,'')).to_i + 1200).to_s
    post[:sorter] = sorter.to_i

  to_sort.sort! { |a,b| b[:sorter] <=> a[:sorter]}

SEE?? Embarrassing. I love it. I’m sure there’s an easier way to do this. But it’s interesting, I think, that an idea (“hmm, i want to sort by both date and time, so why don’t I make a :sorter variable that takes those two strings and combines them into one an integer that represents when it was posted”) morphs and grows into this 28 line mess. I guess it’s time to refactor.

categories and tags

Originally posted December 1, 2012

I added some functionality for categories and tags. The difference between categories and tags, in my implementation, is that there can only be one category, but there can be a bunch of tags.

So be generous with your tags.

You can use spaces in your categories and tags and they’ll be converted to hyphens. They’re comma-delimited.

You can see the tags at the bottom of this post and you’ll see some hyphens. but if you look at the markdown source, you’ll see some spaces.

(You can see the markdown source by clicking the link at the bottom of each post or by simply replacing the / with .md at the end of a post url)

RSS is hard to implement and I’m not sure I’m doing it right

Originally posted December 1, 2012

I’m pretty sure I’m not doing this right but I’ve added an RSS feed so that’s fun, enjoy.

It’s available at /feed and ever time that “file” is queried, the sinatra app will generate an up-to-the-minute accurate feed, including all of the posts.

I don’t necessarily recommend subscribing because I might change where it’s located soon. Right now the blog is online at http://devblog.maxjacobson.net but I may move it to blog. or beef. or beefsteak. or something else entirely.

going open source

Originally posted December 2, 2012

Just for fun I’m sharing the source code for this blog in full on github. I don’t know if anyone else will or should use this but maybe they’ll want to and I won’t stop them.

It’s surprisingly easy having two remote destinations for this code (heroku, where my blog is hosted, and now github where the source code is hosted). I make changes, commit them, push to one, then push to the other. Easy peasy.

If someone else wants to use it, they’ll have to delete my stuff and replace it with their stuff. I’m not giving them a blank slate. Because I’m not sure how to maintain two separate versions like that. But they’ll figure it out, I think.

I guess I’ve been planning to do this, because I’ve refrained from hardcoding my information (name, blog title, etc) into the code. Instead it’s in a separate config.rb file (which you can see on github).

I’m cold on this balcony in San Diego. I am proud that I made this in like two days. I will keep working on it. A small part of me just got a tingling urge to hurl my laptop down into the parking lot like it’s my punk rock guitar.

I’m worried that it will be a whole hassle if I ever want to change the address of this blog. I already kinda feel limited by “devblog”.

Right now my Top Level website, http://maxjacobson.net is mostly just a front for my Pinboard public links. maybe I’ll merge that idea into this one and put the whole thing at that address. I glanced over the Pinboard gem and I could probably whip something up myself instead of using the linkroll widget. That would give me some more flexibility to display multimedia stuff, Layabout-style and do some more color-coding without having to use jquery like I’m currently doing to emphasize posts with the max_jacobson tag.

I’m just daydreaming here.

some new features here

Originally posted December 13, 2012

Things I’ve added to beefsteak tonight:

more feeds

Like, for individual categories, tags, and search queries. Try clicking on a tag/cat or searching something, and you’ll see a link to the feed at the bottom.

A fun one might be: http://devblog.maxjacobson.net/tag/gush/feed or http://devblog.maxjacobson.net/search/confess/feed if you like me at my gushiest or most confessional.


My about page is now at http://devblog.maxjacobson.net/~about. I made another one mostly to test what it looked like with two. It doesn’t look great.

You can view the markdown source of pages the same as posts. Just add the .md suffix or click the link at the bottom. I don’t know why I offer this. It doesn’t work amazingly. In some posts with code, I see underscores disappear. It’s weird.

favicon and apple touch icon

I just threw in some all-black-errything squares for now. I figure if anyone else uses this they can replace with something they like.

Here’s what the tree for this site looks like right now:

(notice the new pages directory, mostly)

[~/Dropbox/Sites/devblog] [max] [08:23 PM]
 ϡ tree
├── config.rb
├── Gemfile
├── Gemfile.lock
├── helpers.rb
├── pages
│   ├── about.md
│   └── projects.md
├── posts
│   ├── 2011-03-12-un-americano.md
│   ├── 2012-02-26-introducing-myself-to-the-command-line.md
│   ├── 2012-08-26-favblogging.md
│   ├── 2012-12-01-added-sorting.md
│   ├── 2012-12-01-already-updates.md
│   ├── 2012-12-01-categories-and-tags.md
│   ├── 2012-12-01-gem-anxiety.md
│   ├── 2012-12-01-sleep.md
│   ├── 2012-12-01-sorting.md
│   ├── 2012-12-01-the-first-post.md
│   ├── 2012-12-02-erb-and-indendation.md
│   ├── 2012-12-02-on-an-airplane.md
│   ├── 2012-12-02-open-source.md
│   ├── 2012-12-02-RSS-is-hard.md
│   ├── 2012-12-02-search.md
│   ├── 2012-12-04-seal-attack.md
│   └── 2012-12-13-meetups.md
├── Procfile
├── public
│   └── img

│       ├── 2012-12-04-seal-1.jpg
│       ├── 2012-12-04-seal-2.jpg
│       ├── 2012-12-04-seal-3.jpg
│       ├── apple-touch-icon.png
│       └── favicon.ico
├── README.md
├── views
│   ├── 404.erb
│   ├── 500.erb
│   ├── layout.erb
│   └── style.scss
└── web.rb

5 directories, 35 files

fuzzy search

Originally posted December 14, 2012

I just made the search a little smarter on this blog. I think. I made it so that when you search a multi-word query, it’ll include posts that match any of the words, not just posts that match all of the words. They’re sorted chronologically, not based on relevance.

And you can put a query in quotes if you want it to match all of the words, in that order.

Here’s how I did that (with some stuff paraphrased):

if query =~ /^".+"$/
  query_array = [query.gsub!(/"/,'')]
  query_array = query.split(' ')
# ... each post |the_text|
query_array.each do |q|
  if the_text =~ Regexp.new(q, true)
    # then include the post

So, first I check if the query is in quotes (I’m coming to really love using regular expressions), and if so I remove the quotes and put that whole string thru. If it’s not in quotes, I split the string into an array of strings, using a space as the delimeter. Then I go through each post and check it against each item in the array. If the query was in quotes, the array only has one item in it. Once there’s a match, I push it thru. The break makes it so once a post matches the query, we move on to the next post rather than keep checking other words in the query against it.

I wonder if I should allow single quotes too. Note to self, allow single quotes.

I wonder what new bugs I just introduced.

heave ho

Originally posted December 22, 2012

So I am happy enough with beefsteak that I am moving this to my proper homepage, http://www.maxjacobson.net.

That space was mainly occupied by my pinboard public links, and I’ve integrated those into beefsteak now.

I considered integrating a pinboard gem but for now I’m sticking with the same old linkroll widget I’ve been using. It’s not super flexible but it has an important feature the main pinboard gem lacks: a distinction between private and public links. It’s not that I have such private stuff in there, it’s just that I have so much stuff in there. As previously described I use IFTTT to send all kinds of stuff automatically, and then I occasionally make a small minority of those links public. And maybe there’s some private stuff in there.

Who is sleepier than I? No one is.

The project is still mostly useable by other people, I think. The only thing is the small jquery script that modifies the DOM so that links with the tag max_jacobson are made red. I tweaked it so it lets you specify what that tag will be, but it’s not working. I’m going to fix that later, maybe.

I create links just to kill them at this point. Old feeds and links have a short shelf life.

beefsteak v2

Originally posted March 19, 2013

This version of beefsteak is brought to you by Justin Timberlake.

I put his new album on repeat eleven or twelve hours ago and starting working.

I realized people might be looking at my GitHub and realized I was embarrassed of the way this blog worked. In the few months from the last update, I learned a lot. I hope I’ll be embarrassed of this in a few months.

Heck I already kind of am.1

The first several posts I wrote for this blog were, naturally, about this blog and the process of building it and excitement of discovery. A lot of it is really dumb but I’m leaving it.

So here’s what’s new:

  • syntax highlighting for code blocks, using my fav colorscheme, cobalt
  • switched from erb to haml – now writing far less HTML by hand
  • appending .md to posts or pages takes you to a text file instead of embedding within a page, which displayed poorly (missing underscores for some reason)
  • some (goofy?) animations on page loads. It picks randomly from a list
  • oh, and when you click to see a footnote, the right one will wiggle. see? 2
  • much better config file; in case anyone besides me uses this system, it should be relatively easy for them to plug in their info and start blogging
  1. or will be in the morning, anyway 

  2. and now I’ve just made it so the return button makes the source from within the blog wiggle, too. I want to learn to write my own CSS3 animations. Right now I’m leaning heavily on Dan Eden

click to ugh

21 Jan 2013

It’s time for You’re Doing it Wrong, my sometimes series where I complain about blogs whose HTML or CSS bugs me!

Today’s offending blog is The AV Club, which I generally really like and read all the time. Here’s an example post: Jonathan Coulton says Glee ripped off his cover of “Baby God Back”.

How the heck did they come up with this embed code for a YouTube video:

<embed width="425" height="344" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" src="http://www.youtube.com/v/Yww4BLjReEk%26hl=en%26fs=1%26rel=0">

Had they copied the embed code from beneath the YouTube video, they would have gotten this:

<iframe width="560" height="315" src="http://www.youtube.com/embed/Yww4BLjReEk" frameborder="0" allowfullscreen></iframe>

I don’t know where The AV Club got their embed code, but exhibit A for it’s outdatedness is the macromedia reference. Try going to that page. Because Macromedia doesn’t exist anymore, you’ll be redirected to Flash’s new owner, Adobe. This post is from three days ago; Adobe acquired Macromedia seven years ago.

Also: every video they embed is given the dimensions 425x344, without regard to the video’s aspect ratio. YouTube’s provided embed code is dimensions-aware and doesn’t introduce unnecessary letterboxing.

So why do I care? The videos still play and it’s pretty much fine I guess. But it screws up ClickToPlugin.

ClickToPlugin is an awesome Safari Extension that blocks plugins like Flash from running until you click on them. It also, when it can, replaces embedded media with a vanilla HTML5 video player. When I typically encounter a YouTube or Vimeo video embedded on a blog, I don’t see their custom Flash player, I see this:

beyonce video

This example is from an AbsolutePunk.net post, Beyoncé Performs Star Spangled Banner [Video]. It behaves much like a regular, Flash YouTube embed – it doesn’t play until you click the triangle although you have preferences like autoplay or auto-preload – it uses Safari’s implementation of the HTML5 video player. Among the benefits: YouTube hasn’t yet figured out how to include ads in the HTML5 videos, it feels faster, and on youtube.com, videos don’t autoplay so I can open six tabs of Taylor Swift videos without causing a terrible cacophany.

But when I visit that AV Club article, I see this:

avclub doesn't have a still frame

Which is fine, I guess. It’s what I signed up for, using this extension. But c’mon, don’t overthink things and get all fancy, stuff’ll get broken.

(AbsolutePunk is doing something custom too. Its code looks like this:

<div align="center"><object width="472" height="389" bgcolor="#f7f7f7"><param name="movie" value="http://www.youtube.com/v/Z-DSFrGnQrk&amp;fs=1"></param><param name="allowFullScreen" value="true"></param><param name="wmode" value="transparent"></param><param name="allowscriptaccess" value="always"></param><embed src="http://www.youtube.com/v/Z-DSFrGnQrk&amp;fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="472" height="389" bgcolor="#f7f7f7" wmode="transparent"></embed></object></div>

I don’t really understand that either, but I don’t need to, because it behaves the way I expect it to. It looks like all AbsolutePunk video embeds use the same aspect ratio (472x389) but at least it works with ClickToPlugin.)

On Layabout, my web app which is embedding videos all day every day, I see this:

here there is a still frame

Look at that aspect ratio – no black bars! And this is all without manually copying and pasting the embed code from YouTube (or Vimeo in that case). These sites support oEmbed, a protocol for taking a URL and supplying an embed code that works.

Unfortunatley it’s not as well-documented as it could be. Vimeo’s oEmbed API is better in that regard and supports the most important feature: maxwidth. I suspect this is at the root of the hardcoded aspect ratios; designers don’t want videos bursting out of their layouts. Vimeo’s page is very useful and clear. YouTube, as far as I can tell, has only provided one brief blog post on the subject.

Maybe that’s the problem?

using mobile web inspector

15 Jan 2013

I’ve just figured out why infinite scrolling wasn’t working on Layabout on my iPod Touch.

I’m using jQuery to detect when the user has reached the bottom of the page. There’s no hardcoded event for that, so you have to do some math using the methods that are given. It goes something like this:

If the user is scrolling, check if the current window height plus the amount of stuff above it is greater than or equal to the total height of the document. This works on the desktop (in the browsers I tested anyway) and on the iPad, but not on my iPod Touch.

$(window).scroll(function() {
  if($(window).scrollTop() + $(window).height() >= $(document).height()) {
    // ...

On the web, a nice and easy way to figure out problems like these is to use the console and log some variables. So I found myself adding in some stuff like this:

var scroll_top = $(window).scrollTop();
var window_height = $(window).height();
var document_height = $(document).height();
var current_position = scroll_top + window_height;
console.log("scroll top: " + scroll_top);
console.log("window height: " + window_height);
console.log("current position: " + current_position);
console.log("document height: " + document_height);

But it’s not very easy to access the console on an iOS device, so these messages were getting logged to nowhere.

Long story short I followed these instructions and managed to get access to the info I needed. Here’s what the page logged at the bottom of a page:

scroll top: 41628
window height: 1360
current position: 42988
document height: 43173

Huh! No wonder further pages weren’t loading, we were never (by this logic) truly reaching the end of the page!

So, to fix it, I just changed the logic to add 200 pixels of wiggle room, so now it triggers when you get near the bottom of the page rather than hitting it exactly.

So it works and I’m happy. But before we wrap things up too tightly, let’s try to figure out what’s going on here.

Let’s look at some numbers:

thing pixels amount
5th gen iPod Touch screen height 1136
Window height as logged by Mobile Safari ($(window).height()) 1360
The difference between those two 224

What’s the deal with that? Maybe it’s a points-and-pixels thing beyond my comprehension, but this feels kinda wrong to me. Is it possibly a bug in Mobile Safari? Why else would the window height be bigger than the total height of the screen? I expected the opposite; the window height should be less than the screen height, considering that the window doesn’t include browser chrome like the navigation bar along the bottom and sometimes (at the top of pages) the title and address bar.

Let’s revisit the numbers from the earlier log example:

(Note: other than window height, these values would be different on a longer or shorter page)

thing value
scroll top 41628
window height 1360
current position 42988
document height 43173
difference between those last two 185

So if I’m right that Mobile Safari is mis-reporting its height by 224 pixels, how come my earlier logic was falling short by 185 pixels? I think that difference (39 pixels) must be the height of the navigation toolbar?