Skip to content

Instantly share code, notes, and snippets.

@avdgaag
Created March 4, 2015 20:00
Show Gist options
  • Select an option

  • Save avdgaag/f40be301ef37a52b8c7b to your computer and use it in GitHub Desktop.

Select an option

Save avdgaag/f40be301ef37a52b8c7b to your computer and use it in GitHub Desktop.

Revisions

  1. avdgaag created this gist Mar 4, 2015.
    135 changes: 135 additions & 0 deletions literate.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,135 @@
    #!/usr/bin/env ruby

    require 'open3'
    require 'tmpdir'

    module Literate
    class Line
    INDENT = ''.freeze

    def self.indent(str)
    return str if str == ""
    str.gsub(/^/, INDENT)
    end

    def initialize(str)
    @str = str
    end

    def to_s
    @str
    end

    def cd?
    !path.nil?
    end

    def path
    @str[/^#{INDENT}%-? cd (.+)$/, 1]
    end

    def silent?
    @str =~ /^#{INDENT}%- /
    end

    def ignore?
    @str =~ /^#{INDENT}%% /
    end

    def command?
    @str =~ /^#{INDENT}% /
    end

    def plain?
    !silent? && !ignore? && !command?
    end

    def formatted_command
    "#{INDENT}% #{command}\n"
    end

    def command
    @command ||=
    begin
    @str[/^#{INDENT}%[-%]? (.+)$/, 1].gsub /\{\{(.+)\}\}/ do |match|
    Command.capture($1.strip).chomp
    end
    end
    end
    end

    def self.in_masked_tmpdir
    original_directory = Dir.pwd
    new_dir = File.join(Dir.pwd, 'tmp')
    Dir.chdir(new_dir)
    yield(new_dir).gsub(new_dir, "~/examples")
    ensure
    Dir.chdir original_directory
    end

    class LineReader
    include Enumerable

    def initialize(source)
    @source = source
    end

    def each
    @source.each_line do |line|
    yield Line.new(line)
    end
    end
    end

    class Command
    attr_reader :line

    def initialize(line)
    @line = line
    end

    def result
    if line.silent?
    run
    ''
    elsif line.ignore? || line.cd?
    line.formatted_command
    elsif line.command?
    line.formatted_command +
    Line.indent(run)
    else
    line.to_s
    end
    end

    def self.capture(cmd)
    input, output, wait_thr = Open3.popen2e(cmd)
    output.read
    ensure
    input.close
    output.close
    end

    private

    def run
    self.class.capture(line.command)
    end
    end

    def self.literalize(str)
    in_masked_tmpdir do |dir|
    original_directory = dir
    LineReader.new(str).each_with_object('') do |line, output|
    if line.cd?
    dir = File.join(dir, line.path)
    raise "Invalid path: #{dir}" unless dir.start_with?(original_directory)
    Dir.chdir(dir)
    end
    output << Command.new(line).result
    end
    end
    end
    end

    puts Literate.literalize(STDIN.read)