(Accidentally) Implementing the World’s Worst Static Site Generator: Part 1
Part One
Dear Diary,
Today I decided to make a static site generator. It will be extremely simple and will just do its job. I will start with a posts directory and a ruby script. From there, I’m going to let my mind wander.
Version 1: Ruby a Gem, and a Directory,
If you want a static site generator, you will need to have some posts to convert to HTML from some markup language. I chose markdown and the gem rdiscount
for parsing them to HTML. The initial version is incredibly simple. I made a base page that had the roots of an HTML document, add the posts, then close the original tags.
It looked like this:
<DOCTYPE html><body><h1>hi underneath here is a blog</h1>
The Ruby code looks like this:
#!usr/bin/rubyrequire ‘rdiscount’def main base = File.read(“index.html”) html_docs = `ls posts`.split(“\n”).map do |file| markdown = File.read(“posts/#{file}”) RDiscount.new(markdown).to_htmlendputs base + html_docs + “</body></html>”main
You can make a Gemfile
or just make sure rdiscount
is installed. Either works, we’re winging it. From here we have the dead basics we need to get things started. We have a doc that if we paste it into a file or direct the STDOUT to a file from running this that will open in a browser and have our posts.
Version 2: An Object Oriented Ruby Solution
Thinking further, I wondered if I could make it a more OO setup.
I have decided that the page will have a couple sections added and a new feature, enabled by this. We want to have an about me that prints as a cool banner and to add a button that will make a spaghetti-filled grilled cheese sandwich become the background. I added a sandwich.jpg
file to my directory after finding it.
Let’s start with the about me since it will be on the top.
class AboutMePage attr_reader :html, :colors, :header_size def initialize(phrase, header_size = 2) # I know light blue isn’t a color to the browser in this string form, I just like it there @colors = [“red”, “light blue”, “orange”] @header_size = header_size words = phrase.split(“ “) headered_span_words = create_headered_span_words(words) about_me_page_that_needs_closing_html_and_body_tags = base_doc + buttons + “<div>” + headered_span_words + “</div>” # simple api, documenting by name. so clever. lol @html = about_me_page_that_needs_closing_html_and_body_tags end def create_headered_span_words(words) words.map do |word| chars = word.split(“”) spans = chars.map.with_index { |char, i| span_for_character(char, chars, i) }.join “<h#{header_size}>#{spans}</h#{header_size}>” end.join
end def span_for_character(char, chars, index) if index == chars.length — 1 “<span style=’color: #{colors.sample}’>#{char}</br></span>\n” else “<span style=’color: #{colors.sample}’>#{char}</span>\n” end end def base_doc “<!DOCTYPE html>\n<html>\n<head>\n<meta charset=\”UTF-8\”\n</head>” end def buttons “<button onclick=\”document.body.background = ‘’\”>Sandwich Off</button><button onclick=\”document.body.background = ‘sandwich.jpg’\”>Sandwich On</button>\n” endend
Let’s start with initialize
.
def initialize(phrase, header_size = 2) # I know light blue isn’t a color to the browser in this string form, I just like it there @colors = [“red”, “light blue”, “orange”] @header_size = header_size words = phrase.split(“ “) headered_span_words = create_headered_span_words(words) about_me_page_that_needs_closing_html_and_body_tags = base_doc + buttons + “<div>” + headered_span_words + “</div>”# simple api, documenting by name. so clever. lol @html = about_me_page_that_needs_closing_html_and_body_tagsend
@colors = [“red”, “light blue”, “orange”]
We alternate random colors for our banner.
This just sets us up for that.
However, it’s worth noting light blue
isn’t a color that will resolve to anything and those letters will be black.
Cuz why not?
@header_size = header_size
This is for us to decide what size of <h>
tag to use for our intro.
words = phrase.split(“ “)
Here we split out into words because we’re printing one word per line for our intro.
headered_span_words = create_headered_span_words(words)
This sets up our coloring and spacing for the words we made — more on it later.
about_me_page_that_needs_closing_html_and_body_tags = base_doc + buttons + “<div>” + headered_span_words + “</div>”
This is where we glue it all together, and on the next line set it to an instance var we can read out for whatever purpose we need.
Now let’s take a look at those other functions we call starting from the top.
def create_headered_span_words(words) words.map do |word| chars = word.split(“”)
spans = chars.map.with_index { |char, i| span_for_character(char, chars, i) }.join
“<h#{header_size}>#{spans}</h#{header_size}>”
end.join
end
Here, we map through the words and get their characters. The span_for_character
function is coloring them at random and making new tags. We then join them all together. In order to get our buttons for sandwich on/off functionality, we add this:
def buttons “<button onclick=\”document.body.background = ‘’\”>Sandwich Off</button><button onclick=\”document.body.background = ‘sandwich.jpg’\”>Sandwich On</button>\n” end
These are self-explanatory. They just shove in the tags.
Next let’s look at the class that we can use to orchestrate the posts along with the about me page.
class OnePageSite def initialize(posts_directory = “/posts”) @posts_directory = posts_directory end def generate post_html = `ls posts`.split(“\n”).map { |post| post_to_html(post) }.join(“#{‘</br>’ * 75}\n”) html_document = base + “#{‘</br>’ * 75}” + post_html + “</body>\n</html>” copy_to_clipboard(html_document) puts html_document html_document end def copy_to_clipboard(document) pbcopy document end def html_posts posts.map { |post| post_to_html(post) } end def post_to_html(post) RDiscount.new(File.read(“posts/#{post}”)).to_html end def posts `ls posts`.split(“\n”) end def base AboutMePage.new(“Hi I’m Bobby. I’m a programmer in New York”).html end def pbcopy(text) IO.popen(‘pbcopy’, ‘w’) { |f| f << text } endend
This class’s main logic is in its initialization. Let’s walk through it piece by piece.
def generate post_html = `ls posts`.split(“\n”).map { |post| post_to_html(post) }.join(“#{‘</br>’ * 75}\n”) html_document = base + “#{‘</br>’ * 75}” + post_html + “</body>\n</html>” copy_to_clipboard(html_document) puts html_document html_document end
Here is the first bit:
post_html = `ls posts`.split(“\n”).map { |post| post_to_html(post) }.join(“#{‘</br>’ * 75}\n”)
Here we convert our posts to markdown just like last time, but this time we add a bunch of line breaks between them.
html_document = base + “#{‘</br>’ * 75}” + post_html + “</body>\n</html>”
Now we get the base
, which is a function creating the basis of our HTML + About Me section, and combine it with more line breaks before posts, the posts themselves, then the closing body and html tags.
copy_to_clipboard(html_document) puts html_document html_document
Now, we copy the document to the clipboard, puts it out so it’s in STDOUT, and also return the string itself so that it can be manipulated if anyone needs. With this we have a functioning site that makes a page.
It looks like this:
But to summarize, so far we:
- generate HTML from ruby classes that may or may not be valid
- use a color code that doesn’t exist to keep the default letter color
- inlined button functionality to interact with the page, duplicating a lot of logic
- complicated what was initially a simple script that did its job
- inline every single style
- made it so that there’s a sick photo of a spaghetti and mozzarella grilled cheese on garlic bread that can appear
Some (but not all) of what we’ll end up after my mind wanders further *spoilers*
- build ruby from source
- a fully dockerized setup
- build elixir from source
- orchestrate with unix on top of docker
- horrible ideas that we can’t spoil let
Gotta jibboo — talk soon,
Bobdawg
Thanks for reading! Wanna work on a mission-driven team that loves farcical thought exercises and Phish? We’re hiring!
To learn more about Flatiron School, visit the website, follow us on Facebook and Twitter, and visit us at upcoming events near you.
Flatiron School is a proud member of the WeWork family. Check out our sister technology blogs WeWork Technology and Making Meetup.