読者です 読者をやめる 読者になる 読者になる

おさかな日誌

魚類がプログラミング

Swift: Class 外から Inner class な enum value にアクセスしたい時

ClassName.EnumType.EnumValue みたいに EnumType を経由した識別子でアクセスできる

import Foundation

class A {
    enum Color {
        case Red
        case Blue
    }
}


let color = A.Color.Red

// ダメ
//let color3 = .Red

// Color 型であることがわかる時はコンパイラが名前空間を考慮してくれる
switch color {
case .Red:
    print("red")
case .Blue:
    print("blue") // => "blue\n"
}

(要約記事) Varnish と Microservices: Zipnish の紹介

元記事: Varnish and Microservices: Introducing zipnish


Amedia が Microservices パターンを適用した時の事例を元に Zipnish というソフトウェアの紹介記事

元記事には無い前提情報

Microservices アーキテクチャを apply する時に Varnish を活用する手法があるようだ。

今まではそれぞれのサービス間はそれぞれ通信するので、接続先のサービスを探す "Service Discovery" の問題を解決しなければいけなかった。具体的には、各サービスのインスタンス情報の格納のために Service Direcotry (元記事では Service Catalog と呼んでいる)の運用、サービスのインスタンス情報の更新(health check も含む)など。

(画像は Microservices 2.0 から引用)

f:id:aladhi:20160127163302p:plain

Varnish を中央に設置して、サービス間の通信は全て中央の Varnish を通り、Serivce Discovery やキャッシングは Varnish に任せる手法。

f:id:aladhi:20160127163312p:plain

参考リンク: Microservices 2.0

(Microservices Varnish の手法の)良かったこと

  • Service Discovery の問題を Varnish で解決できる
    • サービスの接続先の管理や取得を Varnish がやるので他の仕組みを用意しなくて良い。
    • Service Direcotry を用意しなくて良い (Zookeeper や etcd を使った仕組み)。
  • それぞれのサービスをステートレスにしたことで中央で一括でキャッシュできた。
  • 中央で一括してキャッシングしてるので cache invalidation が簡単。それぞれのサービスがキャッシュレイヤーを持っている場合だと、キャッシュが散らばってるしレイヤーも複数あったりするので cache invalidation が難しい。

(2) については「どうやってステートレスにするんだ」という疑問があってよくわからない....

Zipnish の紹介

  • Zipkin が解決しようとしている Microservices の monitoring 問題がある。例えば、依存サービスのモニタリング、どのサービスに問題があるのか、どのリクエストが遅いのか、などを可視化したい。
  • Zipkin は JVM 前提で使いづらい。heavy。
  • Microservices Varnish の手法では中央の Varnish を必ず通るのでそこで Zipkin がするようなロギングを行うアイディア
    • Varnish Cache 4.0 から導入された logging API を使うっぽい(?)
  • 今はビュー周りは Zipkin を再利用している。Python で書かれたものに置き換える予定。

Rails request spec の header name の変遷

たまに request spec とかで「HTTP_ いるっけ?」となった時に思い出したいので自分宛てメモ


before Rails 4.0.0

get '/path', params, { 'X-Name' => 1 }

X-Name のままだった。

after Rails 4.0.0

get '/path', params, { 'X-Name' => 1 }

と書くと HTTP_X_NAME という header name に変換されるので、controller とかからアクセスできるようになる。

https://github.com/rails/rails/commit/9af59b2468e4ad6c3c2ca89f90968fdcaa417aba

「X がモナドである」はなにが嬉しいのか

この記事はぼくの考えを雑に文章化したものであって、不正確な可能性が特に高いので気をつけてくださいね。


ごくたまに「X はモナドだったんだ」のような発言をすることがある。これは自分が理解しようとしている対象 X が、自分がすでに理解している概念であるモナドの性質を持っていることがわかったことで対象 X への理解が進んだ時に主に発していると思う。これは別にモナドにかぎらず、「あ、Strategy パターンだ」とか他にもある。

さて、「X がモナドである」とわかった時になにが嬉しいのかいまいち言語化できてなかったのだけど、今日 関数型プログラマのための Rx 入門(後編) を読んでいて不意に Applicative Functor について調べた時に、自分が 「X がモナドである」とわかった時の嬉しさが明瞭になった。

インターネット上でもたまに「X がモナドである」という発言を目にすることがある。「X がモナドである」とわかるとなにが嬉しいのかわからない人がこの記事を読んで、なるほどと腑に落ちることを目指してこの記事を書いてみる。

「X がモナドである」がわかると、X が次のような性質を持っていることがわかる(はず)

  1. X はふつう世界の型をラップするような型であること (Functor の性質)
  2. ふつうの世界の関数を X の世界の関数に写すことができること (Functor の性質)
  3. X の世界の関数を X の世界の値に適用できること (Applicative Functor の性質)
  4. X の世界の型をさらにラップした X(X) な世界の型を X の世界の型に写すことができる (Monad の性質)

ここでいう "世界" というぼうが勝手に作った一般的でない用語は、パラレルワールドものの世界線を超えて同時に存在(しているように見える)世界とかを想像してもらえるといいかもしれない。

「X がモナドである」ことがわかるとこれだけのことがわかって便利。もちろんこれだけしかわからないから「X がモナドである」ことがわかっても X についての理解はそこまで進んでない。それでも「X がモナドである」という発言が実際の理解以上の喜びを含んでいるのは、慣れ親しんだモナドをこれから学ぼうとする新しい領域にも見つけた喜びのせいに違いない、きっとそうだ。

Redis cluster

Redis 初心者が Redis 3.0 から追加された cluster 機能を使ってみました。勘違いや憶測が多分に含まれます、ご注意を><


セットアップ

現時点では 3.0 がパッケージマネージャでインストールできなかったので、Redis 公式ページから latest stable をダウンロードして、make, make test した。

大体 http://redis.io/topics/cluster-tutorial を見てセットアップする。今回はまずは 4 master node で動かしてみる。後でノードを追加したいので余分に設定を作っておく。

mkdir 7000 7001 7002 7003 7004 7005
❯ cat <<EOS > redis.conf
port 7000
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
EOSfor i in {0..5}; do cp -av redis.conf "700$i/"; sed -i -e "s/port 7000/port 700$i/g" "700$i/redis.conf"; done

cluster-config-file は redis を起動させたディレクトリにできる。この config ファイルは cluster enabled な Redis サーバーが自動で生成するもので各インスタンス固有にしなければならない。なので redis-server 起動時は各インスタンスの CWD が重ならないようにする。そこに気をつけてそれぞれのディレクトリに cd しながら各 config ファイルで4つの Redis インスタンスを起動する。redis-server redis.conf

原始的な方法だと、それぞれの Redis インスタンスを cluster enable モードで立ち上げておいて、redis/src/redis-trib.rb スクリプトを使ってクラスタを作成する。今回は replica なしで 4 master でセットアップした。(--replicate 0 を指定)

~/Downloads/redis-3.0.0/src
❯ ./redis-trib.rb create --replicas 0 \
  127.0.0.1:7000 \
  127.0.0.1:7001 \
  127.0.0.1:7002 \
  127.0.0.1:7003

クラスタを作成すると各インスタンスのログに 1925:M 04 Apr 15:04:55.489 # Cluster state changed: ok とか出た。

~/Downloads/redis-3.0.0/src
❯ redis-cli -p 7000 cluster nodes
f368ccf3ca85a5007a34e8e3b93f3e37797055f6 127.0.0.1:7003 master - 0 1428129180876 4 connected 12288-16383
2b4114a2b99ba696950ad919196223eb25c9ede2 127.0.0.1:7000 myself,master - 0 0 1 connected 0-4095
0e1301d83a174b598c6315167b868be93003e4c4 127.0.0.1:7002 master - 0 1428129182430 3 connected 8192-12287
968d6f7c12ae4c6c70239c59aafd89c302dfd427 127.0.0.1:7001 master - 0 1428129181397 2 connected 4096-8191

うん、動いている。

redis-cluster は hash slot という概念でシャーディングしている。キーの集合からなる hash slot があり、クラスタ内の1ノードが複数の hash slot を担当する方式だ。

  • Node A contains hash slots from 0 to 5500.
  • Node B contains hash slots from 5501 to 11000.
  • Node C contains hash slots from 11001 to 16384.

ある割合の hash slot をノードからノードへ移動することで、master ノードの追加や削除に対応する。

コマンドオプションから察せれるように、ノードの replication にも対応していて、3 master - 6 slave 構成とかを取ることもできるらしい。

ノード間の通信は次のことを通信している。(画像は link のスライドから)

f:id:aladhi:20150404165517p:plain

コマンド操作

redis-cli を使えば透過的にクラスタ内のノードにコマンドを発行できるが、試しにクラスタ機能に対応してなさそうなクライアントでコマンド発行してみた。

# gem i pry redis
# pry -r redis
[15] pry(main)> redis = Redis.new(port: 7000)
=> #<Redis client v3.2.1 for redis://127.0.0.1:7000/0>
[16] pry(main)> redis.get('my_key')
Redis::CommandError: MOVED 13711 127.0.0.1:7003
from /Users/taiki45/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/redis-3.2.1/lib/redis/client.rb:113:in `call'
[17] pry(main)> redis = Redis.new(port: 7003)
=> #<Redis client v3.2.1 for redis://127.0.0.1:7003/0>
[18] pry(main)> redis.get('my_key')
=> nil
[19] pry(main)> redis.set('my_key', 123)
=> "OK"
[20] pry(main)> redis.get('my_key')
=> "123"
[21] pry(main)> redis = Redis.new(port: 7000)
=> #<Redis client v3.2.1 for redis://127.0.0.1:7000/0>
[22] pry(main)> redis.get('my_key')
Redis::CommandError: MOVED 13711 127.0.0.1:7003
from /Users/taiki45/.rbenv/versions/2.2.1/lib/ruby/gems/2.2.0/gems/redis-3.2.1/lib/redis/client.rb:113:in `call'

キーによってクラスタ内のノードが決定されていて、キーから決定されるノードと違うノードにコマンドを発行すると MOVED とレスポンスされた。レスポンスされたノードへ繋ぐといつも通りにコマンド発行ができるっぽい。プロキシーを用意しない方式は初めてみたがシンプルでなんとなく Redis っぽいさを感じる。(画像は link のスライドから)

f:id:aladhi:20150404165748p:plain

クラスタ機能に対応する実装ではこのエラーレスポンスをパースして自動的に接続するノードを切り替えることで透過的な操作を可能にしている。

link

if errv[0] == "MOVED" || errv[0] == "ASK"
    (...)
    newslot = errv[1].to_i
    node_ip,node_port = errv[2].split(":")
    (...)
else

このあたりの話はまだ読んでない Redis Cluster Specification に書いてありそう。

ノードの追加と re-shard

試しに1台ノードを追加してみる。コマンドは add-node new_host:new_port existing_host:existing_port だ。

~/Downloads/redis-3.0.0/src
❯ ./redis-trib.rb add-node 127.0.0.1:7004 127.0.0.1:7000
>>> Adding node 127.0.0.1:7004 to cluster 127.0.0.1:7000
(...)
[OK] New node added correctly.

~/Downloads/redis-3.0.0/src
❯ redis-cli -p 7000 cluster nodes
2b4114a2b99ba696950ad919196223eb25c9ede2 127.0.0.1:7000 myself,master - 0 0 1 connected 0-4095
968d6f7c12ae4c6c70239c59aafd89c302dfd427 127.0.0.1:7001 master - 0 1428129717741 2 connected 4096-8191
f368ccf3ca85a5007a34e8e3b93f3e37797055f6 127.0.0.1:7003 master - 0 1428129718766 4 connected 12288-16383
48da16af4dc5a15b844f2977eefb490ea7b9720e 127.0.0.1:7004 master - 0 1428129719281 0 connected
0e1301d83a174b598c6315167b868be93003e4c4 127.0.0.1:7002 master - 0 1428129718766 3 connected 8192-12287

クラスタに参加できだ。クラスタに参加しただけだとまだ hash slot は割り当てられない。割り当てられないだけでクライアントからのリクエストには応答できるようだ。

[23] pry(main)> redis = Redis.new(port: 7004)
=> #<Redis client v3.2.1 for redis://127.0.0.1:7004/0>
[24] pry(main)> redis.get('my_key')
Redis::CommandError: MOVED 13711 127.0.0.1:7003

新しく追加した port 7004 で listen しているノードに hash slot を割り当てる。コマンドの出力が長いのと対話的なやりとりがあるのでログに直接コメントを書いた。# で始まる行が加工したコメントで、別途見やすいように改行を追加してある。

# クラスタ内のノード。左がノードID。一番右が担当している hash slot 番号。
~/Downloads/redis-3.0.0/src
❯ redis-cli -p 7000 cluster nodes
2b4114a2b99ba696950ad919196223eb25c9ede2 127.0.0.1:7000 myself,master - 0 0 1 connected 0-4095
968d6f7c12ae4c6c70239c59aafd89c302dfd427 127.0.0.1:7001 master - 0 1428131195055 2 connected 4096-8191
f368ccf3ca85a5007a34e8e3b93f3e37797055f6 127.0.0.1:7003 master - 0 1428131194539 4 connected 12288-16383
48da16af4dc5a15b844f2977eefb490ea7b9720e 127.0.0.1:7004 master - 0 1428131193501 0 connected
0e1301d83a174b598c6315167b868be93003e4c4 127.0.0.1:7002 master - 0 1428131195561 3 connected 8192-12287

~/Downloads/redis-3.0.0/src
❯ ./redis-trib.rb reshard 127.0.0.1:7004

# (1) クラスタ内ノードのチェック
Connecting to node 127.0.0.1:7004: OK
Connecting to node 127.0.0.1:7003: OK
Connecting to node 127.0.0.1:7000: OK
Connecting to node 127.0.0.1:7002: OK
Connecting to node 127.0.0.1:7001: OK
>>> Performing Cluster Check (using node 127.0.0.1:7004)
M: 48da16af4dc5a15b844f2977eefb490ea7b9720e 127.0.0.1:7004
   slots: (0 slots) master
   0 additional replica(s)
M: f368ccf3ca85a5007a34e8e3b93f3e37797055f6 127.0.0.1:7003
   slots:12288-16383 (4096 slots) master
   0 additional replica(s)
M: 2b4114a2b99ba696950ad919196223eb25c9ede2 127.0.0.1:7000
   slots:0-4095 (4096 slots) master
   0 additional replica(s)
M: 0e1301d83a174b598c6315167b868be93003e4c4 127.0.0.1:7002
   slots:8192-12287 (4096 slots) master
   0 additional replica(s)
M: 968d6f7c12ae4c6c70239c59aafd89c302dfd427 127.0.0.1:7001
   slots:4096-8191 (4096 slots) master
   0 additional replica(s)
[OK] All nodes agree about slots configuration.

# (2) いくつ hash slot 動かすか質問される。
# re-shard 前は 16384 slot が各ノードへ4096ずつ割り当てられていた。
# 16384 / 5 がだいたい 3276 なので3276個を動かすことにする。
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
How many slots do you want to move (from 1 to 16384)? 3276

# (3) どのノードが今回動かす hash slot を受け取るか質問される。
# 新ノードのIDを指定する。
What is the receiving node ID? 48da16af4dc5a15b844f2977eefb490ea7b9720e

# (4) どのノードから hash slot を動かすか質問される。
# 今回はすべてのノードから平等に新ノードへ hash slot を動かしたいので `all` を指定する。
# ノードごとの性能が違ったりして、あるノードの負担を減らしたいときなどに個別指定が便利なのかもしれない。
Please enter all the source node IDs.
  Type 'all' to use all the nodes as source nodes for the hash slots.
  Type 'done' once you entered all the source nodes IDs.
Source node #1:all

# (5) Resharding plan が表示されるので眺めてから同意する。
eady to move 3276 slots.
  Source nodes:
    M: f368ccf3ca85a5007a34e8e3b93f3e37797055f6 127.0.0.1:7003
   slots:12288-16383 (4096 slots) master
   0 additional replica(s)
    M: 2b4114a2b99ba696950ad919196223eb25c9ede2 127.0.0.1:7000
   slots:0-4095 (4096 slots) master
   0 additional replica(s)
    M: 0e1301d83a174b598c6315167b868be93003e4c4 127.0.0.1:7002
   slots:8192-12287 (4096 slots) master
   0 additional replica(s)
    M: 968d6f7c12ae4c6c70239c59aafd89c302dfd427 127.0.0.1:7001
   slots:4096-8191 (4096 slots) master
   0 additional replica(s)
  Destination node:
    M: 48da16af4dc5a15b844f2977eefb490ea7b9720e 127.0.0.1:7004
   slots: (0 slots) master
   0 additional replica(s)
  Resharding plan:
    Moving slot 12288 from f368ccf3ca85a5007a34e8e3b93f3e37797055f6
    Moving slot 12289 from f368ccf3ca85a5007a34e8e3b93f3e37797055f6
(...)
Do you want to proceed with the proposed reshard plan (yes/no)? yes

# (6) あとはひたすら Resharding の様子が眺められる。

特に計測してないが、MBP (13, Early 2015) のマシンで30秒くらいで Resharding が終わった模様。実環境だとどれくらいだろうか...

cluster コマンドで各ノードを確認してみる。

~/Downloads/redis-3.0.0/src
❯ redis-cli -p 7000 cluster nodes
2b4114a2b99ba696950ad919196223eb25c9ede2 127.0.0.1:7000 myself,master - 0 0 1 connected 819-4095
968d6f7c12ae4c6c70239c59aafd89c302dfd427 127.0.0.1:7001 master - 0 1428131461381 2 connected 4915-8191
f368ccf3ca85a5007a34e8e3b93f3e37797055f6 127.0.0.1:7003 master - 0 1428131463435 4 connected 13107-16383
48da16af4dc5a15b844f2977eefb490ea7b9720e 127.0.0.1:7004 master - 0 1428131462415 5 connected 0-818 4096-4914 8192-9010 12288-13106
0e1301d83a174b598c6315167b868be93003e4c4 127.0.0.1:7002 master - 0 1428131462415 3 connected 9011-12287

後から追加した ID 48da16af4dc5a15b844f2977eefb490ea7b9720e のノードに無事新しく hash slot が割り当てられた。

まとめ

  • Redis cluster についてざっくり説明
  • クラスタのセットアップ
  • No proxies, redirection を確かめた
  • ノードの追加と Resharding を試した

次は master-slave 構成にしてみてノードのダウン時にどうするかやってみたい。

参考にしたリスト

vimfiler 使いはじめた & 設定キメた

NERD Tree を一時期使ってたのだけど、なんかちがうなぁ... と思ってずっと unite.vim の file を使ってファイルを開いてたのだけどやっぱツリー構造で表示したいなぁって思ってて、MBP の環境構築に合わせて vim 周りをちょっといじったので vimfiler を導入してみた。これがよかった。

vimfiler 自体はデフォルトで起動すると vim 標準の filer + α なイメージで、コマンドオプションを使うと NERD Tree みたいな IDE のファイルツリーみたいなのも表示できる、みたいなやつです。

f:id:aladhi:20150328172336p:plain

vimfiler のなにがよかったか考えてみたんだけど、デフォルトの key binding が自分の思考に合ってたからな気がする。なにも見ずにとりあえず o 押した時にツリーがパカって開いたので「おっよさそう」って思った。他の binding も気に入ってる。あと help が読みやすかった。

:h vimfiler とググってでてきた記事いくつかを参考にして設定キメた。

  • autocmd FileType vimfiler nmap <buffer> <CR> <Plug>(vimfiler_expand_or_edit)
    • デフォルトだと Enter でディレクトリに入ってしまって好みと合わないので、Enter は単にツリーを開くだけにした。hl で "親ディレクトリに移動" と "子ディレクトリに移動" がデフォルトでできて対称性あるので、こっちの設定のが好み。
  • let g:vimfiler_as_default_explorer = 1
    • vim 標準のファイラを置き換える。
  • noremap <C-X><C-T> :VimFiler -split -simple -winwidth=45 -no-quit<ENTER>
    • C-X C-TIDE みたいなファイルツリーを開く。width の値を適当に変えると大きさが変わる。
  • autocmd VimEnter * VimFilerExplorer
    • vim 開いたら vimfiler が表示される。けど、git commit message でもでてきて、たぶん filetype とかで分岐すればいいんだろうけど、とりあえず無効にしている。気が向いたら filetype 判定やりたい。

参考にしたリスト

設定ファイル

taiki45/dotfiles · GitHub

NeoBundle 'Shougo/vimfiler'

" Use vimfiler instead of default filer.
let g:vimfiler_as_default_explorer = 1
" Open filer
noremap <silent> :tree :VimFiler -split -simple -winwidth=45 -no-quit
noremap <C-X><C-T> :VimFiler -split -simple -winwidth=45 -no-quit<ENTER>
" Don't let <CR> enter the directory but let it open the directory
autocmd FileType vimfiler nmap <buffer> <CR> <Plug>(vimfiler_expand_or_edit)
" Automatically open vimfiler on start
"autocmd VimEnter * VimFilerExplorer
" If no files are specified, open vimfiler
"autocmd VimEnter * if !argc() | VimFiler | endif

Rails application の起動と実行メモ

rails コマンド

  • bin/rails
  • require "rails/cli"
  • rails/cli: require 'rails/app_rails_loader'
  • Rails::AppRailsLoader.exec_app_rails:
    • bundler のチェック
    • APP_PATH = Dir.pwd + config/application.rb
    • require File.expand_path('../boot', APP_PATH)
    • require 'rails/commands'
  • rails/commands: ARGVの操作と alias の設定
  • require 'rails/commands/commands_tasks'
    • help message の定義
    • command の dispatch
    • require APP_PATH: gem の require, Rails.application
  • Rails::CommandsTasks.new(ARGV).run_command!(command): runner とかの実体を呼んで実行する
  • exec_app_rails: 正常に command が終了したら rails/cli まで戻る
  • if ARGV.first == 'plugin': require 'rails/commands/plugin' - plugin generate
  • else: require 'rails/commands/application' - application generate

Rails.application#call は実際にどうやってなにが呼ばれるのか

ActionController::Base#action(action).call(env) が呼ばれるまでを追ってみた。

雑にまとめると、通常の routing では

  • Rails::Application#call
  • ActionDispatch::MiddlewareStack#call
  • ActionDispatch::Routing::RouteSet#cal
  • ActionDispatch::Journey::Router#call
  • ActionDispatch::Jouney::Route#app.call
  • Routing::RouteSet::Dispatcher#call
  • SomeController.action(action).call

となる。以下乱雑なメモ

  • Rails::Application#call: super
  • Rails::Engine#call: #app.call(env)
  • Rails::Engine#app:
    • config.middleware = config.middleware.merge_into(default_middleware_stack)
    • config.middleware.build(endpoint)
  • #config: Application::Configuration.new(find_root_with_flag("config.ru", Dir.pwd)) & config= もある
  • Rails::Application#config: #app_middleware
  • Rails:::Railtie#app_middleware: @@app_middleware ||= Rails::Configuration::MiddlewareStackProxy.new
  • MiddlewareStackProxy#merge_into: #default_middleware_stack
  • Rails::Application#default_middleware_stack: DefaultMiddlewareStack.new, #buidl_stack
    • #build_stack: ActionDispatch::MiddlewareStack.new.tap {}
  • ActionDispatch::MiddlewareStack#build(endpoint):
    • Rails::Engine#endpoint: self.class.endpoint || routes
    • Rails::Engine#routes: ActionDispatch::Routing::RouteSet.new
    • #build(app = ActionDispatch::Routing::RouteSet.new) no block: middlewares.freeze.reverse.inject(app) { |a, e| e.build(a) }
    • ActionDispatch::MiddlewareStack::Middleware#build: klass.new(app, *args, &block)
    • Middleware#klass: 各 middleware で .new(app, *args, &block) する。args, block は use or insert とかの args, block が来る。
    • 返り値は全ての rack middleware をチェーンした ActionDispatch::Routing::RouteSet (= Rails::Application.endpoint)。endpoint を任意の rack interface 互換のものにどこかで差し替えれば Raisl の routing を差し替えれるかもしれない。
  • ActionDispatch::Routing::RouteSet#call: @router.call(env)
    • @router: Journey::Router.new(Journey::Routes.new)
  • Journey::Router#call: find_routes(env).each {|route| status, headers, body = route.app.call(env) }
    • route: filter_routes
    • Journey::Router#filter_routes: routes#simulator
    • Journey::Routes#simulator: GTG::Simulator.new(GTG::Builder.new(ast).transition_table)
    • Simulater.new(string):
      • string: Gtg#transition_table
    • Simulater#simulate: state
      • state: TODO
  • ActionDispatch::Jouney::Route#app.call(env):
    • #app: Set by Mapper
      • Mapper#app: Constraints.new(endpoint, blocks, @set.request_class)
      • endpoint: (:to option (get '/', to: 'users#index') which responsd_to? :call) or (dispatcher)
      • Mapping#dispatcher: Routing::RouteSet::Dispatcher.new(:defaults => defaults)
        • defaults: defaults: { format: 'jpg' } otion
  • Routing::RouteSet::Dispatcher#call(env): rack app とかが指定されない場合これが呼ばれる
    • params: env['action_dispatch.request.path_parameters']
    • params['controller], params['action']: すでにセットされている
  • Dispatcher#dispatch(controller):
    • controller: controller_reference(controller_params)
      • const_name: @controller_class_names[controller_param] ||= "#{controller_param.camelize}Controller"
      • ActiveSupport::Dependencies.constantize(const_name)
  • controller#action(action).call(env)

journey、やらなきゃいけないことが複雑でコードも複雑でめっちゃ疲れた追い切れない... JourneyHackingGuides ほしい...

次は named routes がどこの routes がどう呼ばれてるのか調べたい...