Octopressでfor文を使ってその中でタグを使った時に 全てが同じ出力の様になったり 同じ文字が追加され続けて出力が肥大化していったりしてしまいました。
ちょっとfor文について理解が間違ってた所があったのでそれについて。
Liquidタグでのfor文
よく使うのが
<ul>
{% for post in site.posts %}
<li><a href="{{post.url}}">post.title</a></li>
{% endfor %}
</ul>
みたいな感じで現在ブログにあるポストに関してループを回して 色々出力させる、と言う事。
また、一定数だけ回したい時は
{% for i in (1..10) %}
...
{% endfor %}
みたいな風にするとi=1から10までで回すことが出来ます 1。
for文の中でのタグの利用
次の様なテスト用タグを用意してみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
単に入力したタグ名とマークアップを取り入れて
さらにval_count
に関しては一度render
の方でインクリメントしています。
これを出力するだけのものです。
これを
{% for i in (1..10) %}
{% for_test test %}
{% endfor %}
としてみると
"Initialize: name = for_test, markup = test0 , count = 1, self = #<Jekyll::ForTest:0x007fe904db5778>"
"count: name = for_test, markup = test , count = 2, self = #<Jekyll::ForTest:0x007fe904db5778>"
"count: name = for_test, markup = test , count = 3, self = #<Jekyll::ForTest:0x007fe904db5778>"
"count: name = for_test, markup = test , count = 4, self = #<Jekyll::ForTest:0x007fe904db5778>"
"count: name = for_test, markup = test , count = 5, self = #<Jekyll::ForTest:0x007fe904db5778>"
"count: name = for_test, markup = test , count = 6, self = #<Jekyll::ForTest:0x007fe904db5778>"
"count: name = for_test, markup = test , count = 7, self = #<Jekyll::ForTest:0x007fe904db5778>"
"count: name = for_test, markup = test , count = 8, self = #<Jekyll::ForTest:0x007fe904db5778>"
"count: name = for_test, markup = test , count = 9, self = #<Jekyll::ForTest:0x007fe904db5778>"
"count: name = for_test, markup = test , count = 10, self = #<Jekyll::ForTest:0x007fe904db5778>"
"count: name = for_test, markup = test , count = 11, self = #<Jekyll::ForTest:0x007fe904db5778>"
と言った標準出力が出てきますが、ここで注目は
まずInitialize
が1回しかありません。
さらに全てのrender
からのself
の出力を見ると同じものを使っています。
つまり、for文で繰り返し同じタグを使った場合でも、 書かれた物が同じであれば同じオブジェクトとして扱われます。
Liquidタグではmarkupの部分に変数を与える事は通常出来ないので、 結果的にfor文の中は基本的に全て同じになるので これで問題が起こることは余り無いのかもしれません。
ですが、以前、変数をなんとか渡したくて
ここに書かれた方法でsite.config
に含まれる値を抜き出して来る様な事を出来るようにしていて、
これが問題を起こしました。
この様な設定をしてるとfor文の中でも別々の値を使う事が出来、
それぞれinitialize
で設定される値が変わってきます。
ですが、実際にはinitialize
は1回しか行われず、更に意図と違った形でしか実行されません。
ただ、これを回避する方法があって、
それはfor文を別のファイルに入れてinclude
タグで読み込むことです。
Liquidタグのinitialize
やrender
が実際いつ実行されてるのか
よく分かってなかった部分がありましたが、
initialize
はファイルが読み込まれた時、に実行されます。
この読み込まれた時と言うのはinclude
で読み込まれた時もその時点で行われます。
なので、あるページでタグを使っていて
さらにそこからinclude
した先でも別のタグを使っているとすると、
まず、最初のページを読み込んだ時点でそのページにあるタグがinitialize
され、
タグが書いてある部分まで来たら実行し、
また、include
タグがあった場合、その時点で
その読み込むファイルの中にあるタグをinitialize
し
タグまで辿り着いたら実行し。。。みたいな事を行います。
また、include
に関しては、for文の中にあったとしても
それぞれ別の物として扱われるので
for文の中でもinclude
が呼ばれる度、その中にあるタグがinitialize
されます。
試しに以下みたいなのを記事に書いておいて
{{"For loop test!"|debug: true}}
{% for_test test0 %}
{% for_test test1 %}
{% for i in (1..3) %}
{{i | debug}}
{% for_test test2 %}
{% include include_test.html %}
{% endfor %}
さらにsource/_include/include_test.htmlを
{% for_test test3 %}
と言う内容で用意します。
最初のFor loop test!
の所で使ってるdebug
というフィルターは
jekyll-bootstrapというJekyllのフレームワークの中に入ってる
プラグインで
1 2 3 4 |
|
みたいな定義になっています。
jekyll-bootstrap/debug.rb at master · plusjade/jekyll-bootstrap
記事の上にクラス名を含めて内容を表示させたり、
上の様にtrue
を与えればjekyll
のコマンドの標準出力として
表示させることも出来ます。
あると色々便利です。
で、これをgenerate
してみると
"Initialize: name = for_test, markup = test0 , count = 1, self = #<Jekyll::ForTest:0x007fe904db5778>"
"Initialize: name = for_test, markup = test1 , count = 1, self = #<Jekyll::ForTest:0x007fe904db54d0>"
"Initialize: name = for_test, markup = test2 , count = 1, self = #<Jekyll::ForTest:0x007fe904db4918>"
"For loop test!"
"count: name = for_test, markup = test0 , count = 2, self = #<Jekyll::ForTest:0x007fe904db5778>"
"count: name = for_test, markup = test1 , count = 2, self = #<Jekyll::ForTest:0x007fe904db54d0>"
1
"count: name = for_test, markup = test2 , count = 2, self = #<Jekyll::ForTest:0x007fe904db4918>"
"Initialize: name = for_test, markup = test3 , count = 1, self = #<Jekyll::ForTest:0x007fe904dcdb20>"
"count: name = for_test, markup = test3 , count = 2, self = #<Jekyll::ForTest:0x007fe904dcdb20>"
2
"count: name = for_test, markup = test2 , count = 3, self = #<Jekyll::ForTest:0x007fe904db4918>"
"Initialize: name = for_test, markup = test3 , count = 1, self = #<Jekyll::ForTest:0x007fe904dd7c38>"
"count: name = for_test, markup = test3 , count = 2, self = #<Jekyll::ForTest:0x007fe904dd7c38>"
3
"count: name = for_test, markup = test2 , count = 4, self = #<Jekyll::ForTest:0x007fe904db4918>"
"Initialize: name = for_test, markup = test3 , count = 1, self = #<Jekyll::ForTest:0x007fe904dd5960>"
"count: name = for_test, markup = test3 , count = 2, self = #<Jekyll::ForTest:0x007fe904dd5960>"
こんな感じの出力になります。
まず、記事中に書かれたtest0, test1, test2についてinitialize
が行われています。
その後、普通に書かれたtest0, test1を実行。 これらは別々のオブジェクトです。
ですが、for文内にあるtest2
については1つしかありません。
実際、その後のfor
文の中でもcount
の数が実行するたびに増えていってるのが分かりますし、
右のself
の値も全て同じです。
一方、include
して呼んでるtest3
については、test2の出力から分かる通り、
実際その場に来て初めて読み込まれて中のタグをinitialize
しています。
で、実行し、
さらに次に呼び出された時のも再びinitialize
し別のオブジェクトになって
count
の数やself
を見ても違うものだと確認出来ます。
ということでfor文の中でタグを読み込んで何か書きたいような時は注意が必要で、
必要であればinclude
を使って別ファイルにする、と言う対処が必要だ、ということです。