rcmdnk's blog

ライオン 歯科用 Check-Up standard 10本 歯磨き粉 120g チェックアップ スタンダード [ヘルスケア&ケア用品]

この前Octopressをアップデート して、 Jekyllがバージョン2になったり色々変更があったんですが、 過去のcodeblock関連の所が表示がおかしくなっていたので それを修正したのと、 エラーにならない予期しない動作を防げる様に 色々ポストの変換前のMarkdownファイルをチェックする様にした話。

見つかった問題

codeblockで書いてる次の様な部分:

次のコード:
{% codeblock %}
test
{% endcodeblock %}

が、ページ上で

次のコード:<div class=’bogus-wrapper’>
<div class=”highlight”><table><tr><td class=”gutter”><pre class=”line-numbers”>1 </pre></td><td class=’code’><pre>test code</pre></td></tr></table></div>

みたいにHTMLのソースがそのまま表示されていました。

問題はcodeblockのすぐ上の行が空白行ではないことです。

以前も、MarkdownのパーサーをKramdownへ変更 を行った際にインデントでのコード表示や 引用(>)の部分の前に空白行が無いときちんと表示されない問題が出ましたが、 KramdownかJekyllのアップデートでcodeblockの前もきちんと空けないといけなくなった模様 1

いずれにしろ、この辺の書き方で1行空けても他のパーサーでも問題ないわけで、 今後も空けて問題になることは無いと思うので空ける様に。

ただ、結構沢山該当箇所があって、調べるついでに Rakeタスクで自動的にチェックできる様な物も作りました。

チェック用タスク

Rakefileに以下の様な関数2つとタスクを1つ加えます。

Rakefile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
def ok_failed_stop(condition)
  if (condition)
    puts "OK"
  else
    raise "FAILD"
  end
end

def grep_check(word, grep_option, opt)
  vword=word
  if word.index("^") == 0
    vword=word.sub("^", ":")
  end
  if opt == 0
    grep_option = "-A1 -B1 #{grep_option}"
    comment = "An empty line is required around \\\"#{word}\\\"!"
  elsif opt == 1
    grep_option = "-B1 #{grep_option}"
    comment = "An empty line is required before \\\"#{word}\\\"!"
  elsif opt == 2
    grep_option = "-A1 #{grep_option}"
    comment = "An empty line is required after \\\"#{word}\\\"!"
  end
  puts "\nChecking \"#{word}\"...\n\n"
  ok_failed_stop system("\
      if grep -H -n -e \"#{word}\" #{grep_option}|\
          grep -v \"#{vword}\";then \
        printf \"\\\\n\\\\e[31m\";\
        echo #{comment};\
        printf \"\\\\e[m\\\\n\";\
        exit 1;\
      fi")
end

desc 'Check source'
task :check, :opt do |t, args|
  grep_files = "-r #{source_dir} --include \"*.#{new_post_ext}\""
  if args.opt == "new"
    grep_files = "#{source_dir}/#{posts_dir}/*.#{new_post_ext}"
  end
  puts "\n## Checking codes..."
  grep_option = "#{grep_files}|\
      grep -v \"^--$\"|\
      grep -v \"#{new_post_ext}-[0-9]\\\+-$\"|\
      grep -v \"{%\ *raw\ *%}$\""

  grep_check("^{%\ *codeblock", grep_option, 1)
  #grep_check("^>", grep_option, 0)
  #grep_check("^#", grep_option, 1)
  grep_check("^- - -$", grep_option, 1)
  #grep_check("^<hr>$", grep_option, 1)

  puts "\nChecking words to be avoided...\n\n"
  ok_failed_stop system("\
      if [ -f #{word_avoid} ];then \
        while read a;do \
          if ret=$(grep -i -q $a #{grep_files});then \
            echo \"A word $a is included, must be avoided!!!\";\
            echo $ret;\
            exit 1;\
          fi;\
        done < #{word_avoid};\
      fi")
end
  • ok_failed_stop(condition)Octopressのpublic/_deployディレクトリの変更 でも書いた、引数がtruならただOKと表示してそうでなければそこで raiseするだけの関数。
  • grep_checksoruce内のファイルを見て、wordの周りをチェックする関数。 grep_optionには対象となるファイルとかそれ以降の処理を入れます。 optでは0で上下、1で上だけ、2で下だけチェックする様にします。
  • task :check, :opt do |t, args|がメインのタスク。
    • 引数がnewの場合は#{source_dir}/#{posts_dir}/内にあるファイルだけを対象に、 それ以外の場合は#{source_dir}内にある全てのMarkdownファイルを対象にします。
      • 古い記事を#{source_dir}/#{posts_dir}/y_2014等年ごとのディレクトリに 退避するようにしてるのでnewが有効です。
      • Octopress(Jekyll)では#{posts_dir}以下にサブディレクトリがあっても再帰的に ポストのファイルを探して#{posts_dir}直下にあるファイルと同等の 扱いで変換してくれます。
    • grep_optionでは該当行の前後を見て、空白やrawの行が無いことを見る処理。
      • rawを使ってcodeblock内の1行目に書くことがあるので。
    • 普段チェックするのはcodeblock- - -、それに加えて Octopressのpublic/_deployディレクトリの変更 でもやった禁止ワードチェック。
      • MarkdownのパーサーをKramdownへ変更 のところには<hr>も空けないとだめ、と書いてますが今試した所大丈夫なので チェックから外してあります。
      • >(引用)については下側は空けないと>を付けないでも引き続き引用になります。 のでそうやって書いてあるところもあったのですが分かりやすいように 2行目以降にも>を付けるように変更しておきました。 ただ、>という文字は、diffで使われて、codeblock内の 途中に現れる事もあって、それをきっちり回避するのが面倒だったので、 普段はコメントアウトしておいて、必要なら外してチェックして 問題がありそうなところを探す、というふうにしました。
      • #については、章のタイトル部分でこれも上側を空けないと行けないわけですが、 これをコメントとして扱うプログラム言語が多いので それらがかなり多くヒットしてしまってこのままでは使い物にならないので、 普段はコメントアウト。
      • 上に上げたcodbelock次のコード:の例もcodeblockで書くと これによってエラーが出てしまいますが、通常、例でも避けた方が良いと思うので、 上の例では字下げブロックでエラーを回避しています。。

これを毎回rake generateする時に呼ぶように、

Rakefile
1
2
3
4
5
6
7
8
9
 desc "Generate jekyll site"
 task :generate do
   raise "### You haven't set anything up yet. First run `rake install` to set up an Octopress theme." unless File.directory?(source_dir)
+  Rake::Task[:check].inovoke("new")
   puts "## Generating Site with Jekyll"
   system "compass compile --css-dir #{source_dir}/stylesheets"
   system "jekyll build"
   system "rm -f .integrated"
 end

と、generateのタスクの中でjekyllを実行する前に入れておきます。

前に作ったgenerate_only 2 の方にも同様に入れておきます 3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
## usage rake generate_only[my-post]
  desc "Generate only specified post (much faster)"
  task :generate_only, :filename do |t, args|
    raise "### You haven't set anything up yet. First run `rake install` to set up an Octopress theme." unless File.directory?(source_dir)
    if args.filename
      filename = args.filename
    else
      filename = Dir.glob("#{source_dir}/#{posts_dir}/*.#{new_post_ext}").sort_by{|f| File.mtime(f)}.reverse[0]
    end
    puts ""
    puts "## Test build for #{filename}"
    puts ""
    puts "## Stashing other posts"
    Rake::Task[:isolate].invoke(filename)
    begin
+     Rake::Task[:check].invoke("new")
      puts "## Generating Site with Jekyll"
      system "compass compile --css-dir #{source_dir}/stylesheets"
      system({"OCTOPRESS_ENV"=>"preview"},"jekyll build")
      puts "## Restoring stashed posts"
      Rake::Task[:integrate].execute
    rescue
      puts $!
      Rake::Task[:integrate].execute
      exit 1
    end
  end

その他覚書

putsの最後の改行

putsは通常最後に改行が入るが、最後に\nを入れるとそれは無視される。 (puts自体が改行を1つ出すので、\n無しの場合と同じように次の行に移るだけ。) ので、

puts "\nChecking \"#{word}\"...\n\n"

となってる部分は下に一行空ける、になる様にしているだけです。 (最後の\nがダミーみたいな物)

Pygmentsのキャッシュ

色々いじってる最中に

$ rake generate_only

## Test build for source/_posts/2014-07-25-blog-octopress.md

## Stashing other posts
## Generating Site with Jekyll
identical source/stylesheets/screen.css
Configuration file: /octopress/_config.yml
            Source: source
       Destination: ~/tmp/octopress/public
      Generating...
  Liquid Exception: Tag '{% ' was not properly terminated with regexp: /\%\}/ in _posts/2014-07-25-blog-octopress.md.tmp/#excerpt
jekyll 2.1.0 | Error:  Tag '{% ' was not properly terminated with regexp: /\%\}/
## Restoring stashed posts
$

みたいなエラーが出て、以後、2014-07-25-blog-octopress.md の内容をすべて消しても同じエラーが出て止まらなくなりました。

同じファイルを違う名前にして試してみると通る状態。

これはPygmentsのキャッシュが残っていてそれがうまく処理できなかった様で、 rake clean4/octopress/.pygments-cacheディレクトリの中身を掃除したら 同じ名前の空ファイルで大丈夫になりました。

この時の問題は上のRakefile内のコードの記述で、 これらにはraw 5 が必要でした。

rake generate(jekyll build)時に使われるファイル

rake generateの際には#{source_dir}/#{posts_dir}/ 以下のファイルが使われるわけですが、 どうもファイルの拡張子とかは全く関係がなさそう。

使われるファイルは

  • 日付フォーマットで始まる: yyyy-mm-dd-name.md
  • 最低1ワードはファイル名がある: yyyy-mm-dd-name.md
  • 拡張子がある: yyyy-md-dd-name.md

を満たすもの(かつ、中身のyamlブロックでpublishedfalse出ないもの) になる模様。

拡張子がmdだろうとmarkdownだろうと関係ないし (Rakefileの中で設定してるのはrake new_post時に作るファイル名のためだけ)、 txtとかでも使われます。

さらに、1つ以上なら.はいくつもあって良いみたいで 2014-07-25-test.md.tmp等、最後に余計な物を付けても使われます。

なので、一時的に今のファイルを.tmpファイルとして退避させて、 みたいな事をやろうにも、中でpublished: falseにしておかないと 使われてしまいます。

さらに、Rakefile内のisolateというタスクでは 移動させないファイルをチェックするのにincludeを使っているので、 この様な.tmp付きのファイルは残ってしまいます。

これだとgenerate_onlyとか使ってもこの問題を回避できません。

includeを使っておけば同じ名前の一部を持つファイル群、みたいな指定の仕方も 出来るわけですが、実際にはそんな使い方しないのでend_withに変えておきます。

Rakefile
1
2
3
4
5
6
7
8
9
10
 # usage rake isolate[my-post]
 desc "Move all other posts than the one currently being worked on to a temporary stash location (stash) so regenerating the site happens much more quickly."
 task :isolate, :filename do |t, args|
   FileUtils.mkdir(full_stash_dir) unless File.exist?(full_stash_dir)
   Dir.glob("#{source_dir}/#{posts_dir}/*") do |post|
-    FileUtils.mv post, full_stash_dir unless post.include?(args.filename)
+    FileUtils.mv post, full_stash_dir unless post.end_with?(args.filename)
   end
   system "touch .isolated"
 end

テーマ

上の変更したRakefile等は下のテーマの中にも入っています。

Sponsored Links
  1. ちゃんと調べてないので適当。。。

  2. 前のコードに加えて、途中で失敗した時にintegrateし直す様にbegin~rescueが 予め入れてあります。

  3. rake cleanはデフォルトのRakefileのものだとうまく動かないので ちょっと変更してあります。

    GistのURLが変わった

  4. Octopressでのコードの表示やコメントのあれこれ

Sponsored Links

« VimをIDEっぽく(主にC++/Java用)整える FirefoxでGitHub等のページでブックマークレットを有効にする »

}