middleman を読む

middleman を読む手順

結構複雑で、難しいです。

  • rack / middleware
  • module, extend など ruby の詳細
  • hook の動き

などについての知識が必要になります。

根幹となる機能も “core-extensions” と拡張機能扱いで実装されているので、
追っていくのがなかなか大変です。

手順として、

  • まず console で触る
  • サンプルスクリプトを書き、簡単な拡張機能を書いてみる
  • middlemanのソース一式を手元に落とし、pry や printf デバッグなどでひとつずつ追っていく

という方法を取ります。

console で中身を見る

まずは middleman console で中身を見ながら追っていきます。

インストールと下準備

シンプルなシステム構成で init します。pry はソースを追うのに必要なのでいれておきます。

1
2
3
4
5
6
7
% middleman init mm-tets & cd mm-test
% vi Gemfile
gem 'pry-byebug'
gem 'middleman-pry'
gem 'rb-readline'
gem 'therubyracer'
% bundle

app (Middleman::Application)

console を起動し、self を見ます。

1
2
3
4
5
% bundle exec middleman console --verbose
[1] pry(#<Middleman::Application::MiddlemanApplication1>)> self
=> #<Middleman::Application:0x70265526584800>
[2] pry(#<Middleman::Application::MiddlemanApplication1>)> self.class
=> Middleman::Application::MiddlemanApplication1

Middleman::Application というのがアプリケーション用のクラス。箱。
これに後述の拡張機能を登録しフックを仕込み、順次駆動させていく形になります。
このクラスにextend バリバリつかって module を組み込んでいくため、
他のリクエスト、プロセスとの混乱しないよう、
リクエストごとに Middleman::Application::MiddlemanApplication1というように番号を振った派生クラスを作り、
それを利用していくことになります。

sitemap (Middleman::Sitemap::Store), resources[] (Middleman::Sitemap::Resource)

次に sitemap と sitemap.resources を見ます:

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
[3] pry(#<Middleman::Application::MiddlemanApplication1>)> sitemap
=> #<Middleman::Sitemap::Store:0x007fcfef180878
@_cached_metadata={},
@_lookup_by_destination_path=
{"images/background.png"=>
#<Middleman::Sitemap::Resource:0x007fcff0db7410
@app=#<Middleman::Application:0x70265526584800>,
@destination_path="images/background.png",
@local_metadata={:options=>{}, :locals=>{}, :page=>{}, :blocks=>[]},
@path="images/background.png",
@source_file="/vagrant/source/mm-test/source/images/background.png",
@store=#<Middleman::Sitemap::Store:0x007fcfef180878 ...>>,
"images/middleman.png"=>
#<Middleman::Sitemap::Resource:0x007fcff0db6bf0
@app=#<Middleman::Application:0x70265526584800>,
...
[4] pry(#<Middleman::Application::MiddlemanApplication1>)> sitempap.resources
=> [#<Middleman::Sitemap::Resource:0x007fcff0db7410
@app=#<Middleman::Application:0x70265526584800>,
@destination_path="images/background.png",
@local_metadata={:options=>{}, :locals=>{}, :page=>{}, :blocks=>[]},
@path="images/background.png",
@source_file="/vagrant/source/mm-test/source/images/background.png",
@store=
#<Middleman::Sitemap::Store:0x007fcfef180878
@_cached_metadata={},
@_lookup_by_destination_path=
{"images/background.png"=>
#<Middleman::Sitemap::Resource:0x007fcff0db7410 ...>,
"images/middleman.png"=>
  • Middleman::Sitemap::Store:source/ 以下のデータを管理するコンテナクラス、
  • Middleman::Sitemap::Resource:それぞれのファイルなどのリソースのコンポーネントクラス。

この2つがキモとなります。

特定のリソースを取り出すには、sitemap.find_resource_by_path() を使います:

1
2
3
4
5
6
7
8
9
10
11
12
[5] pry(#<Middleman::Application::MiddlemanApplication1>)> page = sitemap.find_resource_by_path("index.html")
=> #<Middleman::Sitemap::Resource:0x007f2bd08ecda0
@app=#<Middleman::Application:0x69913064777960>,
@destination_path="index.html",
@local_metadata={:options=>{}, :locals=>{}, :page=>{}, :blocks=>[]},
@path="index.html",
@source_file="/vagrant/source/mm-test/source/index.html.erb",
@store=
#<Middleman::Sitemap::Store:0x007f2bceda42c8
@_cached_metadata={},
@_lookup_by_destination_path=
...

これで page で特定のリソース(“/index.html”)のデータやパスなどの情報にアクセスできます。また render でテンプレートに従い加工されたテキストが返ります。

1
2
3
4
5
6
7
8
[6] pry(#<Middleman::Application::MiddlemanApplication1>)> page.data
=> {"title"=>"Welcome to Middleman"}
[7] pry(#<Middleman::Application::MiddlemanApplication1>)> page.path
=> "index.html"
[8] pry(#<Middleman::Application::MiddlemanApplication1>)> page.source_file
=> "/vagrant/source/mm-test/source/index.html.erb"
[17] pry(#<Middleman::Application::MiddlemanApplication1>)> page.render
=> "<!doctype html>\n<html>\n <head>\n <meta charset=\"utf-8\">\n \n <!-- Always force latest IE rendering engine or request Chrome Frame -->\n...

Middleman::Application クラス

Middleman::Application クラスを見てみます。

define hooks

37行目あたり にhook の定義がされています。

1
2
3
4
5
6
7
8
9
10
11
# Before request hook
define_hook :before
# Ready (all loading and parsing of extensions complete) hook
define_hook :ready
# Runs before the build is started
define_hook :before_build
# Runs after the build is finished
define_hook :after_build

ここでは4つのみですが、後ほどもっと出てきます。

core-extensions の register

142行目あたり から、いよいよ extensions の register が行われていきます。

まず、”include Middleman::CoreExtensions::Extensions” してますが、このモジュールには register()関数が定義されてて、これを使うことにより拡張機能(extension)を登録(register)することができます。詳しくは後述します。

このregister 関数を使って Middleman::CoreExtensions::Request, ShowException, FileWatcher… などの「コアな」extension をどんどん登録してきます。

拡張機能の仕組み

主な機能としては、

  • ヘルパー関数の提供
  • フックの仕込み

があります。

簡単な拡張機能を作りながら、動きを追っていきます。

ヘルパー関数

helpers/hello.rb

簡単なのは、helpers/ 以下のファイルに規則にそって定義することです:

1
2
3
4
5
6
% vi helpers/hello_helpers.rb
module HelloHelpers
def hello
"hello world"
end
end

こうすると、Middleman::CoreExtensions::ExternalHelpers で require され
Middleman::Extensionのhelpers
を使って module し app 内に組み込まれます(app.class.send(:include, m))。

簡単な extension

次に extension でやってみます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
% vi extensions/hello/middleman-hello.rb
module Middleman
module Hello
module Helpers
def hello
"hello world"
end
end
class Extension < Middleman::Extension
helpers do
include Helpers
end
end
end
end
Middleman::Extensions.register(:hello) do
Middleman::Hello::Extension
end
% vi config.rb
require 'extensions/middleman-hello'
activate :hello

Middleman::Extension から派生させたクラスを作り、同様に helper 関数を組み込みます。

config.rb でそれを require し activate (後述)させれば、hello() 関数が使えるようになります。

手を加える

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
module Middleman
module Hello
class << self
def registered(app)
$stderr.puts("- middleman-hello registered")
app.send :include, InstanceMethods
end
end
module InstanceMethods
def hello
"hello world"
end
end
end
end
Middleman::Extensions.register(:hello) do
Middleman::Hello
end

middleman を読む

サンプルスクリプトを書く

path: “/index.html” リクエストで source/index.html.md を HTML に変換するスクリプトを書いてみます:

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
% vi m.rb
require 'middleman'
require 'middleman-core/logger'
require 'pry-byebug'
logger = Middleman::Logger.singleton(Logger::DEBUG)
logger.debug '- define MyApp'
class MyApp < Middleman::Application
end
logger.debug '- create app: MyApp'
app = MyApp.new
logger.debug '- run_hook :ready'
app.run_hook :ready
logger.debug '- find index.html'
path = "/index.html"
page = app.sitemap.find_resource_by_path(path)
logger.debug '- render'
puts page.render
logger.debug '- all done.'

実行します:

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
% bundle exec ruby m.rb
- define MyApp
- create app: MyApp
== Activating: sprockets
== Reading: Local config
Loaded extensions:
== Extension: sprockets
== Extension: frontmatter
- run_hook :ready
== File Change: config.rb
== File Change: m.rb
== File Change: m.txt
== File Change: source/images/background.png
== File Change: source/images/middleman.png
== File Change: source/index.html.erb
== File Change: source/javascripts/all.js
== File Change: source/layouts/layout.erb
== File Change: source/stylesheets/all.css
== File Change: source/stylesheets/normalize.css
== Rebuilding resource list
- find index.html
- render
<!doctype html>
<html>
<head>
<meta charset="utf-8">
....
- all done.

ポイントは、

  • Middleman::Application の派生クラスのインスタンスを生成すると、extensions の読み込み、activate や local config の読み込みが行われる
  • :ready フックを run することで実際の resources の読み込みが行われる
    • このフックを起動しないとリソースの読み込みは完全に行われない

extensions の仕組みや、resources の読み込みの過程を洗ってみます。

参考サイト

https://github.com/yterajima/middleman-tansu
Wiki-like automatic linking in Middleman

MiddlemanのExtensionやTemplateを色々と試しました
Middlemanでwikiっぽいことができたら便利だなと思ったからmiddleman-tansu v0.1.0をリリースした | e2esound.com業務日誌

gem

middleman-pry

  • カスタマイズ
    • CSS: style.css

template-aks の構成と説明

  • layout
  • helpers
  • extentions

middlelamn-core を読み解く

  • Middleman::Application
    • define_hook する::before, :ready, :before_build, :after_build
    • include Middleman::Coreextensions::Extensions
      • => register() が使えるように成る
    • register Middleman::CoreExtensions::Request, FileWacher,Data., Middleman::Sitemap
      • => class なら extension.new、module などなら extension.registered(self) を呼ぶ
  • Middleman::Application.new
    • build cmd なら Middleman::BuildAction.new() で base.class.shared_instance で new される
      • リクエスト毎に Middleman::Application から派生されたクラスを作り、そこから new する
    • register したときに、Middleman::CoreExtensions::*:registered で InstanceMethod module 内の関数(initialize()含む)を app に include するよう send してるため、それぞれの initialize が呼ばれる
      • initialized() を持ってるのはData, Extensions のみ
      • 各種 extensions で hook などを設定
      • Extensions.new
        • initialized, before_configuration hook を呼ぶ
        • “config.rb” を実行する
          • activate する
            • extensions に登録
        • after_configuration hook を呼ぶ
  • require middleman
  • require middleman-core

  • application.rb

conf.rb

core-extensions/data
InstanseMethod
initialize: load config.rb

register
initialize
activate
after_configulation

sitemap を読み解く

middleman-blog を読み解く

  • BlogData
  • BlogArticle

カスタマイズ

変換の仕組み

基本的には、source/ 以下のファイルが拡張子に従って変換され build/ 以下に出力あるいは http リクエストに従いサーバーが http で返します。

例えば source/profile.html.md というファイルがあれば、入力を markdown (拡張子 .md に対応)として変換し、 build/profile.html に出力します。
source/profile.html.md.erb であれば erb をまず通し、その後 markdown として処理し、最終的に同じく build/profile.html に出力されます。

拡張子として登録されていない場合は、そのまま変換せずに出力されます。画像ファイル(.jpg や .png など)、スタイルシート(.css)、スクリプト(.js)などですね。

ここらへんの処理は、tilt を使ってやってるようですが、よく分かりません。詳しくはソースを参照のこと。

更新とアップロードの流れ

  • source/ 以下の元ファイルに書く
  • local サーバーで確認
  • 外部サーバへアップロード

至極当然の流れです。

ページと記事

で、中身を更新していきます。

シンプルなもの
blog拡張

スリーターンの陸上イメトレ

スリーターン陸上イメトレのやり方

フローリングの上で滑りやすい靴下はいてやるとよいです

  1. まず足を軽く揃えて普通に立ちます。
  2. 両手を平行にあげてそれぞれ斜め45度に開きます。
  3. 両腕の角度を保ったまま左右に軽く振ります。片方の手が前方をちょい超えるくらいまで。その時、腰はそのまま。動いちゃだめ。肩から腕が左右に動いて、腰はそのまま。この捻りがじゅーよー。
  4. 一旦もとに戻して片足で立ちます。あげたフリーレッグの方はエッジをスケーティングレッグのふくらはぎにつけるイメージで。膝は軽く開き気味で。この状態で目をつぶって10秒程度は保てるように
  5. 片足で同じように上体を左右に振ります。今度は振った側の腕が真後ろにくるくらいまで。同じく腰はそのまま。(多少上げた足のつま先で支えてもいいかも)
  6. 左に回して左手が真後ろにいった状態で、上体を止めます。するとあら不思議、足の拇指球あたりを支点にして腰から下がクルっと戻ります。この戻った時に、ターンが行われる。よく言ってる、「捻って、戻す」ってやつね。

今のは反動を使ってしまったけど、今度は反動に頼らず、純粋な捻りと、軽いアップと呼吸でやってみます。

  1. 片足立ち両腕を前に開いた状態を取ります。鼻で呼吸をします。吸うときに軽く上に伸びるようなイメージで。腕も若干それに乗じて上に動くかも。あくまで自然に。指揮者の振り始めのような。
    上に伸びる時は、踵を気持ちもちあげて拇指球で立つように
  2. 一旦両腕を逆(右側)に振って、息を吸いながら軽く戻し、左手が真後ろに来たあたりで吸いきり、軽く伸びることで、腰が戻される。(ここらへんのタイミングは人それぞれ微妙なので、リラックスした状態から捻って、自然に戻るところを探したほうがよいかも。)

補足とか

  • 右足立ちT字から左に踏み出してターン、今度は右足に踏み出して、とするとアウトスリーのパラグラフのイメトレになる。
  • 膝の上下も伸びに合わせて加えるとやりやすくなるかも
  • 特に「アウトに乗って」と意識しなくても、左足で立って左側にひねると、自然にアウト側に乗ってる状態になる。
  • フォアインスリーの場合は、右足で立って右に捻る(CCW)。足を曲げてあげないとやりにくい。上げた足を少し開き足の甲を立ってる足のふくらはぎの裏側につけてあげるとやりやすい。
  • 氷上でなぜターンが出来ないかというと、この捻った状態をキープできないからなんね。陸上でやってもわかると思うけど、ひねった状態だと腰、ふともも、そして足元に相応の付加がかかる。

独断と偏見によるルートと場所によるジャンプの見分け方

トゥジャンプはリンクを横切るように進んでフェンス手前で降りるイメージ。特にルッツをファーストジャンプに持ってくる場合は、ぐるっと外周バッククロスで回って斜めに横切ってジャンプ、ってよくあるよね。

エッジ系のは、リンクを中心とした大きな円上のどこかで飛ぶかんじ。アクセルは向正面に平行から、サルコウはジャッジに向かって、ループは向正面に向かって、というケースが多いような。

フィギュア撮影:憂、ミク

あたしが「フィギュア」と言ったら(略というわけで憂

バストアップ

medwidth

全体像

medheight

ちと背景が濃かったかもしれん。あとお顔にもうちょい光当てた方が。

左斜め後ろからクリップオンストロボ、上からRIFAでフルフィル、右に白レフ、顔にスポット、という定番ライティング。レンズは60マクロ、絞りはF11、現像はちと露出と明度いじっただけ。手持ち撮影。

あえて背中からメインあてて、朝日を後ろの窓から浴びて髪をまとめる的な。
ストロボだと手持ちでいろいろ角度返られるからよい。

ミクさん

続いてミクさん。まずは天井の室内灯のみ。お顔が暗いです。

smallheight

左斜め後ろからストロボ。これだけだとちょっと怖いですね。

smallheight

ディフューザ(半透明ポリ板に不織布を挟んだもの)をかましてみます。少し影はやわらかくなりました。

smallheight

右側に白レフ版をあてます。少し光が回りました。

smallheight

小さめの銀レフを右下あたりからあてます。

smallheight

上からRIFAでフルフィルをあてます。全体的に明るくなりました。

smallheight

少しRIFAを前目からあてて、顔にスポットをあてました。結構近づけたんですが、まだ弱いですね

smallheight

ちょっと色カブリしてたので、少しホワイトバランスを調整、あとコントラストをちょい引き上げました。

medheight

撮影状況

撮影状況はこんなかんじ。ストロボは見切れてるけど左側から。スポットライトはクリップライトにアルミ巻きつけたもの。実際には手持ちで近付けてる。

smallwidth

ストロボは、100きんでかったなんかの台にアームクリップで取り付け、椅子の上に置く。高さは箱がCD重ねて。

smallwidth

撮影台は無印のちゃぶ台に箱乗せてコルク板をしく。背景のはでかい二枚の色紙を折ってブックスタンドに洗濯バサミで固定。白布をRつけて敷いてその上に背景紙を置く。グラデ画像をPCで作ってセブンイレブンでA3印刷。

レフはダイソーのカラーボード。これもブックスタンドにテープか洗濯バサミで固定。ディフューザもダイソーで売ってた半透明のなんかの板。不織布はスーツカバー切ったの。

スマイルプロジェクトの問題点と贈呈状況・会計報告についてのフォローアップ

概要と現状

さて、白熱のソチオリンピックから一年が経ちましたが、その結果を受けて発足され、いろいろ問題を指摘されながらも進められた、とあるプロジェクトについてフォローアップしてみます。あまり楽しい話でもないので興味のない方はスルーして下さい。

概要としては、「ソチで金メダルをとれなかった真央ちゃんにゴールドを越えるプラチナメダルを贈ろう!募金よろ」、というものです。メッセージ集や自作プロトコル()なども一緒につけたり、他の選手にも一応クリスタルメダルを贈ろうとかもありますが、それらは後付けのオマケです

発足の経緯と当時の反対コメントは、以前shortnote にまとめた以下をご覧ください:

問題点

贈呈品が高価な貴金属であること

ファンが選手にメッセージや(常識的な範囲での)プレゼントを贈ること自体はよくあることですが、まず問題点としては、高価な貴金属であるプラチナ(実際総費用として約180万円かかっており、かなりの部分がプラチナメダル制作費にかかってると推測できる)を贈ろうとしてること。

連盟でも選手へのプレゼントは受け付けているが、貴金属などは対象外としており、社会通念上でも身近な人間でもない「ファン」がアマチュアの選手に対してのそのような高価な貴金属を贈るのは非常識とされる。

これは私見ですが、ゴールドより価値が高い貴金属であるプラチナでつくられたメダルを、ゴールドを実際の大会で取れなかった選手に送りつけるのは、ある意味選手に対する侮辱であり、オリンピックのメダルの価値を毀損しようとするものでもある。甥っ子(3歳)の手作りメダルならともかく。

「名義貸し」の募金振込先口座

寄付金の振り込み先として、氏のつきあいのある出版社の口座を借りてるが、これは立派な「名義貸し」のはず。マネロン的にどうなのか、その出版社のコンプライアンスが心配になってくる。業務外の入出金を無償で社の人間が行うのも、業務外の事項で手数料を取るのも問題のはず。通帳貸してたらマジヤバ

浅田選手以外への格差

浅田選手にプラチナ、その他の選手には(実際のゴールドメダリストにさえ)格の落ちるクリスタル、というのは非常に失礼。当初は浅田選手にのみ贈る予定だったのが「議論」の末他の選手にも、という流れになり、ついで感がハンパない。
「自作プロトコル()」に関しては、()としか。

(発起人の問題)

(発起人に問題があるという最大の問題点はあえて問題点として列挙しないでおくという問題)

贈呈状況

、、、といろいろ問題のあるプロジェクトですが、「スタッフ」の方がいろいろご苦労なされたようで、「贈呈」と「会計報告」まで漕ぎ着いたようです。

贈呈に関しては、ざっとまとめると以下の通り:

選手 贈呈時期 贈呈先
リードペア、高橋木原ペア  5月  木下工務店
高橋  5月  関西大学
町田  6月?  臨海スポーツ
鈴木  7月?  ある信頼できる方
浅田、村上  ?  関係のある、とある方
羽生  ?  代理人の弁護士

{: .table}

下の方の怪しさが何ともいえない風情ですね。曲がりなりにも「渡した」といえるのは高橋選手か町田選手までで、その下の「(自称)関係者」へ渡すのは「贈呈」といえるのか不明です。但し村上選手はツイッターに写真を上げていたので(即刻削除)、渡ってはいるようです。

肝心の浅田選手については、「 勝手ながら寄付者の方へのみ詳細をメールにてお伝えし、ブログ上では贈呈を行ったご報告のみに留めさせていただきました」とのことですが、ちゃんと渡せたのならそんなコソコソする必要もないので、いろいろ大変なのでしょう(棒)。送ったメールも返って来ちゃって大変

あ、あたしは当然寄付してないのでメールは来てませんよ?(タレコミ歓迎w

会計報告

入金総額は620万。正直者これにはおどろきました。支出はおよそ: 

  • 費用180万
  • 寄付440万
    • 所属大学220万
    • リンク160万
    • 基金60万

さすがにこれは嘘ではないでしょうし、金に色はついていないので、関連施設などに寄付が行われたこと自体は喜ばしいことです。ただプロジェクトの目的としては、あくまで浅田選手への贈呈が第一で、各所への寄付は剰余金からという扱いのはず。金額的にも達成度的にも主従が逆転してしまっています。

所感

実際に浅田選手に渡ったのかは不明ですが、高価な貴金属をまともに受け取るとは考えにくく、選手側としては、メッセージ集のみ受け取り、メダルは「お気持ちのみで、丁重にお断り」、プロジェクト側としては「関係者に贈呈」でケリをつけよう、というところでしょうか。

ファンとして何かを贈りたい、という気持ちは尊重されるべきですが、常識外の品物、借り口座、段取りのまずさ、(発起人略)などの問題を露呈してしまったのは否めません。メッセージ自体は好ましいものですし、それと手作り的な贈り物程度を添えて贈る。郵送代実費をアマゾンギフトかなにかでカンパよろ、程度なら問題にならないかと思います。

ランダムオーダー:スコアシートでのジャッジの表示順は選手ごとに異なるか?

選手ごとのジャッジの表示の並び順

GPシリーズ、オリンピック、ワールドなどでは、「ランダムオーダー」が採択され、
どのジャッジがどのスコア(GOE, PCS)を付与したかが明らかにされない。
具体的には、オフィシャル発表でのジャッジ並び順と、スコアシートでの並び順は異なる。

それでは、選手のスコアシートごとにジャッジに表示順は異なるのだろうか?あるいは縦軸は同一なのだろうか?

関連:「ジャッジの匿名性」についてのまとめ(まとめ中) - 小高あたるの日記

ランダムオーダーの定義

Special Regulatins & Technical Rules

Rule 353.4.c)

1
For ISU Championships, Olympic Winter Games, Senior Grand Prix of Figure Skating Events and Final, the Judges’ marks are listed in a random sequence without any reference to specific Judges’ names (anonymity).

あくまで「ランダム順にリストされ、ジャッジ名とヒモ付られない」とされているだけで、
選手ごとに入れ替えること、とまでは規定されていない。
が、同時に縦軸を揃えることとも規定されていないため、

  • 仕様:縦軸が同一であるとは保証されない
  • 実装:選手毎にシャッフル

ということか。

実例

thx to @winter026, @verona_london

Skate America 2009

http://www.isuresults.com/results/gpusa09/gpusa09_Men_FS_Scores.pdf

欠席ジャッジの表示位置が選手ごと異なる。かなり決定的。

JGPF 2014

http://www.isuresults.com/results/gpf1415/gpf1415_JuniorMen_SP_Scores.pdf

あくまで推量:TR/INに異常に低い点を付けるジャッジが同一の縦軸にきていない:

  • 一番右:Rank1#10 3.75/4.00
  • 左から二番目: Rank4#2: 3.75/4.25

結論

おそらく選手ごとシャッフルされてる。