Help us understand the problem. What is going on with this article?

Rakefileの動きをRunbookで書く

はじめに

卒業研究の一環として,研究室で使われているPDFを作るためのRakefileを,Runbookの形式で書かく.
- Patrick Blesi, Github, runbook
ここでは書き換えの過程とできたものの使い方を書く.

実行環境

  • MacBook Air
  • MacOS High Sierra(バージョン 10.13.3)
  • Rubyバージョン 2.7.0

Runbookとは

手順を明確にし一つ一つのステップを踏みながら進んでいくプログラム.
RubyのDSLの一つとして段階的に自動化を書いていくことができる.
- Patrick Blesi, Runbook: A Ruby DSL for Gradual System Automation

なぜRakeじゃなくてRunbook?

私のように初心者でCUIを使うことにまだ慣れていない人間からすれば,Rakefileを実行すると勝手に処理が進み何をしているのかがわからなくなる.そこでRunbookを使うことにより手順が明確になり自分が何をしているのかがわかるようになる.
また,Rakefileでは実行途中でおかしく思っても止めることを最初からプログラムに書いていないと難しい,しかし,Runbookであれば細かく分かれた手順の中で毎回ユーザーに入力を求めて続けるかを選ぶことが出来る.

プログラムの概要

Runbookの基本

基本はタイトル,セクション,ステップからなるプログラムで,ステップの中にプログラムを書いていく.
Runbookのコマンドにスタートのための雛形があるのでそれをインストールしてRunbookの基本を確かめる.

> runbook generate runbook my_first_runbook

これによりディレクトリ内にmy_first_runbook.rbが生成される.

my_first_runbook.rb
require "runbook"

runbook = Runbook.book "My First Runbook" do
  description <<-DESC                                                           
This is a runbook that...                                                       
  DESC

  section "SECTION" do
    step "STEP" do
      # Add statements here                                                     
    end
  end
end

if __FILE__ == $0
  Runbook::Runner.new(runbook).run
else
  runbook
end

実行すると下図のように続けるかを聞かれるだけのものが実行される.

> runbook exec my_first_runbook.rb 
Executing My First Runbook...

Description:
This is a runbook that...

Section 1: SECTION

Step 1.1: STEP

Continue? (enter "h" for help) [c,s,j,P,e,h] 
> runbook exec my_first_runbook.rb 
Executing My First Runbook...

Description:
This is a runbook that...

Section 1: SECTION

Step 1.1: STEP

Continue? (enter "h" for help) [c,s,j,P,e,h] c
>> Continue to execute this step
> runbook exec my_first_runbook.rb 
Executing My First Runbook...

Description:
This is a runbook that...

Section 1: SECTION

Step 1.1: STEP

Continue? Continue to execute this step

RakefileをRunbookに移植

研究室使われているRakefileの構成は主に
- orgファイルの読み込み
- latexに変換
- report用の形式にlatexを変更
- platexを用いてPDFの作成

の四つなので,これらをステップとしてRunbookを書く.

make_pdf.rb
Runbook.book "Make PDF" do
  description <<-DESC                                                           
    This is a make PDF from org                                                 
  DESC

  section "Make pdf" do
    step "Load org file" 

    step "Make tex file" 

    step "Load and Convert tex file" 

    step "Make pdf" 
  end
end

実行結果

> runbook exec make_pdf.rb
Executing Make PDF...

Description:
    This is a make PDF from org

Section 1: Make pdf

Step 1.1: Load org file

Continue? Continue to execute this step
Step 1.2: Make tex file

Continue? Continue to execute this step
Step 1.3: Load and Convert tex file

Continue? Continue to execute this step
Step 1.4: Make pdf

雛形ができたのでこれに中身を足していく.

セクションをlatexを作るものとPDFに変換するものに分けたほうが止めやすいと思ったのでそのようにし,中身を足す.

make_pdf.rb
require "./convert"
require "colorize"

Runbook.book "Make PDF" do
  description <<-DESC
    This is a make PDF from org
  DESC

  section "Make latex" do
    $t_file = "report"
    step "Load org file" do
      note "Load org file"
      ruby_command do
        $file = Dir.glob("*.org")[0].match(/(.*).org/)[1]
        puts "your org file is " + $file.red + "."
      end
    end
    step "Make tex file" do
      note "Make tex file"
      ruby_command do
        system "emacs #{$file}.org --batch -f org-latex-export-to-latex --kill"
      end
    end
    step "Load and Convert tex file" do
      ruby_command do
        $lines = File.readlines("#{$file}.tex")
        $lines = convert_thesis($lines)

        File.open("#{$t_file}.tex", "w") do |f|
        $lines.each { |line| f.print line }
      end
    end
  end

  section "Make PDF" do
    step "Make pdf" do
      note "Make pdf"
      ruby_command do
        commands = ["platex #{$t_file}.tex",
                    "bibtex #{$t_file}.tex",
                    "platex #{$t_file}.tex",
                    "dvipdfmx #{$t_file}.dvi"]
        commands.each { |com| system com }
      end
    end
  end
end

これで,"Load org file" でディレクトリ内のorgファイルを読み込み,latexに変換しPDFを作るRunbookのプログラムができた.

  • convert.rb は"Load and Convert tex file"内で使っている,convert_thesisの関数を使うためのプログラム.内容はorgから作成したlatexをレポート用の雛形に変換するものである.
convert.rb
def convert_thesis(lines)
  head = <<'EOS'
  \documentclass[a4j,twocolumn]{jsarticle}
  \usepackage[dvipdfmx]{graphicx}
  \usepackage{url}

  \setlength{\textheight}{275mm}
  \headheight 5mm
  \topmargin -30mm
  \textwidth 185mm
  \oddsidemargin -15mm
  \evensidemargin -15mm
  \pagestyle{empty}


  \begin{document} 

  \title{}
  \author{学科 \hspace{5mm} 学籍番号 \hspace{5mm} your name}
  \date{}

  \maketitle
EOS
  head2 = <<'EOS'
  {\small\setlength\baselineskip{15pt}  % 参考文献は小さめの文字で行間を詰めてある
  \begin{thebibliography}{9}
  \bibitem{} 
  \end{thebibliography}
  }
  \end{document}
EOS
  new_line = [head]
  lines[31..-1].each do |line|
    new_line << line
  end

  new_line.each do |line|
    line.gsub!('\end{document}', head2)
    line.gsub!('\tableofcontents', "")
  end
  return new_line
end

指摘

研究室メンバーからの指摘

  • orgファイルが複数ある場合選べるようにしたい.
  • ディレクトリ内にできたファイル(.auxや.div)が自分で変更することがないのに残っていて見にくいので消したり,まとめたりして欲しい.

orgの選択を"Load org file"のなかで出来るようにする.

make_pdf.rb
require "./convert"
require "colorize"

Runbook.book "Make PDF" do

...

$t_file = "report"
    step "Load org file" do
      note "Load org file"
      ruby_command do
        str = Dir.glob("*.org")
        str.each do |name|
          puts "your org file is " + name.red + " ? (y or n)"
          res = $stdin.gets.chomp

          if res == "y"
            $file = name.match(/(.*).org/)[1]
            break
          elsif res == "n"
            if name == str[str.size - 1]
              puts "This directory not have the objective file".red
              exit
            end
          end
        end
      end
    end

...

end

orgファイルの名前ををメッセージとしてターミナル上に表示し,"y"か"n"を入力することで選べるようにした.全て”n”なら強制終了する.

次に一番最後に"Move report"とし,できたレポートをreportというディレクトリを作り移動させるようにした.

make_pdf.rb
require "./convert"
require "colorize"

Runbook.book "Make PDF" do

...

section "Make PDF" do
    step "Make pdf" do

...

    step "Move report" do
      note "Move report"
      ruby_command do
        commands = ["mkdir report",
                    "mv -f #{$t_file}.* ./report",
                    "open ./report/#{$t_file}.pdf"]
        commands.each { |com| system com }
      end
    end
  end
end

教授からの指摘

  • 入力をせずにノンストップで全てを実行できる方法が無いのかどうかを調べて確認しろとの指摘.

gem RunbookのGithubに載っているマニュアルに記述があった.

> runbook exec --auto my_runbook.rb

これで実行すると入力なしに最後まで実行できる.

使い方

Runbookの導入

gemでインストールする.gem install runbook

実行方法

runbook exec runbook.rbとするとrunbook.rbが実行される.
- runbook.rbは自分のプログラム.
- プログラムのある場所のパスが分かればどのディレクトリでも実行可能

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした