カマカマの雑草ブログ

個人の日記です

scaffoldから学ぶあるコードの間にgenerateしたコードを注入する考え方

目的

自動生成したコードを既存のコードの末尾に追記ではなく、ある一定の判断基準をもって既存のコードの間に注入したいときにどういう考え方でやるのかが知りたい。

rails generate scaffoldから見る

railsだとgenerate系が充実しててtutorialではまずこれを使ってmodelを作ってみましょうという流れがある。

$ rails generate scaffold Micropost content:string user_id:integer
      invoke  active_record
      create    db/migrate/20160401165016_create_microposts.rb
      create    app/models/micropost.rb
      invoke    test_unit
      create      test/models/micropost_test.rb
      create      test/fixtures/microposts.yml
      invoke  resource_route
       route    resources :microposts
      invoke  scaffold_controller
      create    app/controllers/microposts_controller.rb
      invoke    erb
      create      app/views/microposts
      create      app/views/microposts/index.html.erb
      create      app/views/microposts/edit.html.erb
      create      app/views/microposts/show.html.erb
      create      app/views/microposts/new.html.erb
      create      app/views/microposts/_form.html.erb
      invoke    test_unit
      create      test/controllers/microposts_controller_test.rb
      invoke    helper
      create      app/helpers/microposts_helper.rb
      invoke      test_unit
      invoke    jbuilder
      create      app/views/microposts/index.json.jbuilder
      create      app/views/microposts/show.json.jbuilder
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/microposts.coffee
      invoke    scss
      create      app/assets/stylesheets/microposts.scss
      invoke  scss
   identical    app/assets/stylesheets/scaffolds.scss

ここのinvoke resource_routeのroute resources: microposts実行されると 以下のようにconfig/routes.rbのイイカンジの所に自動的にinjectされる。

Rails.application.routes.draw do
  resources :microposts  # <- inject at this point
  
  # Other codes
end

というわけで、どういう考え方でこれをやってるのかが知りたくなった。

code reading

invoke resource_routeの瞬間に実行されているのは以下
https://github.com/rails/rails/blob/master/railties/lib/rails/generators/rails/resource_route/resource_route_generator.rb#L15-L35

ここで生成したroute_stringから不要なスペースや改行コードを弾いてrouteへ渡している。此処が肝っぽい
https://github.com/rails/rails/blob/master/railties/lib/rails/generators/actions.rb#L236-L246

基準地点を正規表現としてルーチンの中で保持しておいて(ここでは/.routes.draw do\s*\n/m)、その基準地点の後のところにrouting_codeを差し込んでいる。 この実際の処理をしているinject_into_fileはThorというself-documenting command line utilities向けのライブラリの機能で、与えられたパスのファイルに対してdataをconfigのルールに従って注入できる。 似た機能で正規表現置換をするgsub_fileがあるらしいけど、そっちと違ってこれは元に戻せる仕組みらしい。
Method: Thor::Actions#inject_into_file — Documentation for wycats/thor (master)