diff options
| author | Jérémy Zurcher <jeremy@asynk.ch> | 2018-05-21 16:59:13 +0200 | 
|---|---|---|
| committer | Jérémy Zurcher <jeremy@asynk.ch> | 2018-05-21 16:59:13 +0200 | 
| commit | 314f7a56dfffeacfa6c5466a067e2aa1417d1cb3 (patch) | |
| tree | f3b59f7ffa53a48b687bb462aa698eb8b9a8a911 | |
| parent | 08c1464bb2c588348b887e5aa6d11a447727d39a (diff) | |
| download | rules-vault-314f7a56dfffeacfa6c5466a067e2aa1417d1cb3.zip rules-vault-314f7a56dfffeacfa6c5466a067e2aa1417d1cb3.tar.gz | |
add ruby script
| -rwxr-xr-x | bin/ImANerdRules.rb | 209 | 
1 files changed, 209 insertions, 0 deletions
| diff --git a/bin/ImANerdRules.rb b/bin/ImANerdRules.rb new file mode 100755 index 0000000..2443f51 --- /dev/null +++ b/bin/ImANerdRules.rb @@ -0,0 +1,209 @@ +#! /usr/bin/env ruby +# -*- coding: UTF-8 -*- + +require 'redcarpet' + +module ImANerdRules + +    class CustomRender < Redcarpet::Render::HTML +      def header(text, header_level) +          text =~ /([\d+\.?]+)/ +          "<h#{header_level} id=\"hdr#{$1}\">#{text}</h#{header_level}>" +      end +    end + +    class Node +        include Enumerable +        @@glossary = {} +        # @@line_num = 0 +        attr_reader :name, :parent, :content +        def self.glossary +            @@glossary +        end +        def initialize name, lvl, parent +            @name = name +            @lvl = lvl +            @parent = parent +            @children = nil +        end +        def add name +            @children ||= [] +            @children << Node.new( name, @lvl + 1, self) +            @children[-1] +        end +        def lvl o=nil, r='' +            if not (o.nil? or @children.nil?) +                r = (@children.index(o) + 1).to_s + '.'*(r.length > 0 ? 1 : 0) + r +            end +            return r if @parent.nil? +            @parent.lvl(self, r)    # tail recursive +        end +        def each &block +            yield self +            @children.each { |n| n.each &block } unless @children.nil? +        end +        def to_s r='' +            r += "\n#{lvl}" + ' ' * (10-lvl.length) + ' ' + @name +            return r if @children.nil? +            @children.inject(r) { |r,c| c.to_s(r) } +        end +        def set_meta name, value +            instance_variable_set("@#{name}", value) +            if not self.class.method_defined? name.to_sym +                self.class.define_method(name.to_sym) { instance_variable_get "@#{name}" } +            end +        end +        def load dirname +            out = false +            meta = false +            @content = '' +            File.read(File.join(dirname, @name+'.md')).each_line { |l| +                if l =~ /^---\s*/ +                    meta = !meta +                    out = true if not meta +                    next +                end +                if meta +                    if l =~ /(\w+)\s*:\s*(.+)/ +                        set_meta $1, $2 +                    end +                end +                Node.glossary.each { |k,v| +                    r = /#{k}/ +                    if l =~ r +                        # @@line_num += 1 +                        # l.gsub! r, "<span id='ref#{@@line_num}'> [#{k}](#glos#{v[0]}) </span>" +                        l.gsub! r, "[#{k}](#glos#{v[0]})" +                        r = v[-1] +                        if r.empty? or r[-1] != self +                            r <<  self +                        end +                    end +                } +                @content += l if out +            } +            # FIXME +            # @content += "[toc](#ptoc)" +        end +        def href +            "#hdr#{lvl}" +        end +        def title_md +            "#{'#' * (@lvl+1)}  #{lvl} #{title}" +        end +        def anchor +            "<div id='#{@lvl == 0 ? 'ptoc' : 'p'+lvl}'>" +        end +        def toc +            return '' if @lvl == 0 +            "#{'  ' * @lvl} #{'*' * (@lvl > 0 ? 1 : 0)} #{lvl} [#{@name}](#{href})\n" +        end +    end + +    class Doc < Node +        def initialize dirname +            @dirname = dirname +            super nil, 0, nil +            @silent = false +        end +        def error line, line_num, why +            puts "#{line_num} : >#{line}< : #{why}" +            exit 1 +        end +        def say what +            puts what unless @silent +        end +        def read_toc filename +            level = 0 +            node = self +            last = nil +            File.foreach(File.join @dirname, filename).with_index do |line, line_num| +                line.chomp! +                if line =~ /^\s*(-*)\s*(\w+)$/ +                    l = $1.length +                    entry = $2 +                else +                    error line, line_num, "line not matching" +                end +                if l > (level + 1) +                    error line, line_num, "too deep in one step (from #{level} to #{l})" +                elsif l == (level + 1) +                    level = l +                    node = last +                elsif l < level +                    while level > l +                        node = node.parent +                        level -= 1 +                    end +                end +                last = node.add entry +            end +        end +        def read_glossary filename +            g=1 +            File.foreach(File.join @dirname, filename).with_index do |line, line_num| +                line.chomp! +                if line =~ /^\s*(.+)\s:\s(.+)\s:\s(.+)\s*$/ +                    Node.glossary[$1] = [g, $2, $3, []] +                    g += 1 +                else +                    error line, line_num, "line not matching" +                end +            end +        end +        def print_toc +            each { |n| puts "#{n.lvl}#{' ' * (10-n.lvl.length)} #{n.name}" } +        end +        def load dirname +        end +        def load_content header +            @header = header +            each { |n| n.load @dirname } +        end +        def content +            inject('') { |a,n| a+= n.toc } +        end +        def glossary +            c='<h2>Glossary</h2><dl>' +            Node.glossary.keys.sort.each { |k| +                v = Node.glossary[k] +                n = select { |n| n.name == v[2]} +                error "--", 0, "glossary '#{k} does not link to a known chapter" if n.size != 1 +                n = n[0] +                c += "<dt id='glos#{v[0]}'>#{k}</dt><dd>#{v[1]}</br>\n" +                c += "<a href='#{n.href}'>#{n.name}</a>\n" +                o = v[-1].select{|e| e!=n }.collect{ |e| "<a href='#{e.href}'>#{e.name}</a>" }.join(',') +                c += ' + ' + o unless o.empty? +                c += "</dd></br>" +            } +            c + '</dl>' +        end +        def gen_html outf, html_header, html_footer +            md = Redcarpet::Markdown.new(CustomRender, {}) +            File.open(outf,'w') { |f| +                f.write File.read(html_header) +                f.write md.render(File.read(File.join @dirname, @header)) +                each { |n| +                    f.write md.render(n.title_md) + "\n#{n.anchor}\n" +                    f.write md.render(n.content) + "\n</div>\n" +                } +                f.write glossary +                f.write File.read(html_footer) +            } +            say "#{outf} generated" +        end +    end +end + +if __FILE__ == $0 + +    doc = ImANerdRules::Doc.new './TankOnTank' +    doc.read_toc '_toc' +    doc.read_glossary '_glossary' +    doc.load_content 'header.md' +    doc.gen_html 'TankOnTank.html', 'header', 'footer' + +    exit 0 +end + +# EOF | 
