DEV Community

Discussion on: One thing led to another and I built my own static site generator today

Collapse
 
andrewbrown profile image
Andrew Brown 🇨🇦 • Edited

This is mine. I decided to use erb since its not really much different than using handlebars.
Best part is if you don't use any ruby's gems other than standard easy to put on a lambda and use CodeBuild and CodePipeline to automatically deploy changes to S3 Static Website Hosting.

For my use case I have to compile at least a thousand pages and doing it this way is under a 1 minute

require 'erb'
require 'json'
require 'fileutils'

class Namespace
  def initialize(hash)
    hash.each do |key, value|
      singleton_class.send(:define_method, key) { value }
    end
  end

  def get_binding
    binding
  end
end

class Generator
  def self.render src_path, data={}
    html = ERB.new File.read(src_path), nil, '-'
    ns = Namespace.new data
    html.result(ns.get_binding)
  end

  def self.render_file src_path, build_path, data={}
    html = ERB.new File.read(src_path), nil, '-'
    ns = Namespace.new data
    File.open build_path, 'w' do |f|
      f.write html.result(ns.get_binding)
    end
  end

  def self.src_path name
    cwd = File.dirname __FILE__
    File.join cwd, 'src', "erb/#{name}.html.erb"
  end

  def self.build_path name
    puts "+ build/#{name}.html"
    cwd = File.dirname __FILE__
    path = File.join cwd, 'build', "#{name}.html"
    if File.exists?(path)
      File.delete path
    else
      FileUtils.mkdir_p File.dirname(path)
    end
    path
  end

  def self.json_data name
    cwd = File.dirname __FILE__
    path = File.join cwd, 'data', "#{name}.json"
    json = File.read(path)
    JSON.parse(json)
  end

  # when dealing with a file where each line is json
  def self.json_array_data name
    cwd = File.dirname __FILE__
    path = File.join cwd, 'data', "#{name}.json"
    data = []
    File.foreach(path).with_index do |line, line_num|
      data << JSON.parse(line)
    end
    data
  end
end

class PrepAnywhereGenerator < Generator
  def self.head
    src_path   = self.src_path('head')
    data       = {}
    self.render src_path, data
  end

  def self.header
    src_path   = self.src_path('header')
    data       = {}
    self.render src_path, data
  end

  def self.footer
    src_path   = self.src_path('footer')
    data       = {}
    self.render src_path, data
  end

  # /
  def self.homepage
    src_path   = self.src_path('homepage')
    build_path = self.build_path('index')
    data       = {
      head: self.head,
      header: self.header,
      footer: self.footer,
      data: self.json_data('homepage')
    }
    self.render_file src_path, build_path, data
  end

  # /textbooks
  # /textbooks/us
  # /textbooks/canada
  def self.textbooks
    src_path = self.src_path('textbooks')

    textbooks_all   = self.json_data('textbooks')
    textbooks_ca    = self.json_data('textbooks-ca')
    textbooks_us    = self.json_data('textbooks-us')

    # Canada Books
    build_path_all = self.build_path('textbooks/index')
    build_path_ca  = self.build_path('textbooks/ca')
    build_path_us  = self.build_path('textbooks/us')
    data = {
      head: self.head,
      header: self.header,
      footer: self.footer,
    }

    self.render_file src_path, build_path_all, data.merge({body_class: 'textbooks-all', data: textbooks_all})
    self.render_file src_path, build_path_us , data.merge({body_class: 'textbooks-us', data: textbooks_us })
    self.render_file src_path, build_path_ca , data.merge({body_class: 'textbooks-ca', data: textbooks_ca })
  end

  # /textbooks/:book
  def self.textbook data_key='textbook'
    src_path = self.src_path('textbook')
    books = self.json_array_data(data_key)
    books.each do |book|
      path = [
        'textbooks',
        book['permalink'],
        'index'
      ].join('/')
      build_path = self.build_path path
      data = {
        head: self.head,
        header: self.header,
        footer: self.footer,
        book: book
      }
      self.render_file src_path, build_path, data
    end
  end

  # /textbooks/:book/chapters/:chapter/materials/:material
  def self.material data_key='material'
    src_path = self.src_path('material')
    materials = self.json_array_data(data_key)
    materials.each do |material|
      build_path = self.build_path(self.material_path(material))
      data = {
        head: self.head,
        header: self.header,
        footer: self.footer,
        material: material
      }
      self.render_file src_path, build_path, data
    end
  end

  def self.material_path material
    [
      'textbooks',
      material['textbook_permalink'],
      'chapters',
      material['chapter_permalink'],
      'materials',
      material['permalink'],
      'index'
    ].join('/')
  end

  # /textbooks/:book/chapters/:chapter/materials/:material/videos/:video
  def self.video data_key='video'
    src_path = self.src_path('video')
    videos = self.json_array_data(data_key)
    videos.each do |video|
      build_path = self.build_path(self.video_path(video))
      data = {
        head: self.head,
        header: self.header,
        footer: self.footer,
        video: video
      }
      self.render_file src_path, build_path, data
    end
  end


  def self.video_path video
    [
      'textbooks',
      video['textbook_permalink'],
      'chapters',
      video['chapter_permalink'],
      'materials',
      video['material_permalink'],
      'videos',
      video['permalink'],
    ].join('/')
  end
end