LoginSignup
5
2

More than 5 years have passed since last update.

Rails 1.0のコードを読む(Webrick周り)

Posted at

はじめに

  • マネーフォワードさんのMF Rails勉強会 ~Rails 1.0のコードを読む~に参加させて頂きました。
  • コードリーディングをする上で幾つかお題が出され、読んでみたい分野のチームに参加する
  • Railsのroutingやconfig部分を読むチームに参加しました。(が、途中から逸れてwebrickの方に行ってしまいました・・・)

この記事のTopics

  • Rails v1.0.0時代はRackが無くて、webrickとlighttpdの2択だった
  • 当時はwebrickとlighttpd以外を使うときは、Rails自体に手を入れる必要があった
  • DHHは人生何周もしているに違いない(という噂)

Railsのコード準備

git pull https://github.com/rails/rails
cd rails
git checkout v1.0.0

コードリーディングする上でのお題

  • Railsの起動周り(routing周りとは初期化)
    • の予定だったが、途中から逸れてwebrickの方に行ってしまった

コードリーディング

  • Railsのコード単体でなく、Railsから生成したアプリのコードからも読み進めるとわかりやすいのでは?ということで、用意していただいたサンプルアプリのコードと一緒に読み進めました。(MFの担当者の方ありがとうございました!)
  • サンプルアプリは「hello_app」
  • 以下、本記事のどちらのソースコードを読んでいるかは、
    • hello_app/:サンプルアプリのコード
    • rails/:rails自体のコード

rails起動!(hello_app/script/server)

  • 皆さん一番実行されていると思う rails server で呼ばれる所
#!/usr/bin/env ruby
require File.dirname(__FILE__) + '/../config/boot'
require 'commands/server'
  • config/bootcommands/server の処理を行っている
  • 結果的に、 config/boot がroutingやconfigの処理だったようだが、いきなり飛ばしてしまった・・・
    • 最初で脱線ッ・・・・!!!

rails/railties/lib/commands/server.rb

...
(L9)
server = case ARGV.first
  when "lighttpd"
    ARGV.shift
  when "webrick"
    ARGV.shift
  else
    if RUBY_PLATFORM !~ /mswin/ && !silence_stderr { `lighttpd -version` }.blank? && defined?(FCGI)
      "lighttpd"
    else
      "webrick"
    end
end
...
  • いきなりのパワースポットがくる
  • Webサーバはlighttpdかwebrickの2択がべた書きされている
  • mswinってwindows server?らしき物もlighttpdで起動
...
(L28)
require "commands/servers/#{server}"
  • 上記で選択されたserverへ(ここではwebricに進みました)

rails/railties/lib/commands/servers/webrick.rb

...
(L4)
OPTIONS = {
  :port            => 3000,
  :ip              => "0.0.0.0",
  :environment     => (ENV['RAILS_ENV'] || "development").dup,
  :server_root     => File.expand_path(RAILS_ROOT + "/public/"),
  :server_type     => WEBrick::SimpleServer,
  :charset         => "UTF-8",
  :mime_types      => WEBrick::HTTPUtils::DefaultMimeTypes
}
  • webrickのオプション郡の指定
  • 起動メッセージの表示など
...
(L59)
DispatchServlet.dispatch(OPTIONS)
  • Servletってなんだ?Java?という雰囲気になった。
  • まあまずは読んでみよう。(脱線してたんだけど)

rails/railties/lib/webrick_server.rb

class DispatchServlet < WEBrick::HTTPServlet::AbstractServlet
...
  def self.dispatch(options = {})
    ...
    (L61)
    server = WEBrick::HTTPServer.new(params)
    server.mount('/', DispatchServlet, options)
    ...
  end
...
end
  • webrickのオプションをセットして、webrickのインスタンス生成している
  • '/' に自身?(DispatchServlet)をマウントしてする
  • DispatchServlet は何か?
class DispatchServlet < WEBrick::HTTPServlet::AbstractServlet
...
  (L72)
  def initialize(server, options) #:nodoc:
    @server_options = options
    @file_handler = WEBrick::HTTPServlet::FileHandler.new(server, options[:server_root])
    Dir.chdir(ABSOLUTE_RAILS_ROOT)
    super
  end
...
end
  • webrickのFileHandlerをセットしているっぽい。(この先はwebrickのソースなので読まなかった)
  • 結果として、 '/' にwebrickのFileHandlerをマウントしている
  • (時間が惜しかったので「そういうもの」として読み進む)
class DispatchServlet < WEBrick::HTTPServlet::AbstractServlet
...
  def self.dispatch(options = {})
    ...
    (L67)
    require "dispatcher"

    server.start
    ...
  end
...
end
  • dispacher をrequire
  • その後にサーバをスタートしているっぽい

rails/railties/lib/dispatcher.rb

class Dispatcher
  class << self
    (L34)
    ...
    def dispatch(cgi = nil, session_options = ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS, output = $stdout)
      if cgi ||= new_cgi(output)
        request, response = ActionController::CgiRequest.new(cgi, session_options), ActionController::CgiResponse.new(cgi)
        prepare_application
        ActionController::Routing::Routes.recognize!(request).process(request, response).out(output)
      end
      ...
    end
    ...  
  end
  ...
end
  • ここが一番重かった
  • ActionControllerのrequestクラスとresponseクラスを使っている
  • prepare_application については先をざっくり読んだがわからなかったので飛ばす
ActionController::Routing::Routes.recognize!(request).process(request, response).out(output)
  • ActionController::Routing::Routes
    • おそらくroutes.rb関連のオブジェクトでしょう!という仮説で進む。

rails/actionpack/lib/action_controller/routing.rb(.recognize!(request)部分)

module ActionController
  module Routing #:nodoc:
    ...
    class RouteSet #:nodoc:
    ...
    (L452)
    def recognize(request)
      ...
      (L461)
      controller = hash['controller']  
      hash['controller'] = controller.controller_path  
      request.path_parameters = hash  
      controller.new 
    end
    alias :recognize! :recognize
    ...
  end
  ...
end
  • recognize! のエイリアス
  • ここも詳細に読み込むことができず、「おそらくcontroller関連のオブジェクトがhashに入っており、それをnewする」と推測

rails/actionpack/lib/action_controller/base.rb(.process(request, response) 部分)

module ActionController #:nodoc:
  ...
  class Base
  ...
    class << self
      ...
      (L361)
      def process(request, response, method = :perform_action, *arguments) #:nodoc:
        initialize_template_class(response)
        assign_shortcuts(request, response)
        initialize_current_url
        @action_name = params['action'] || 'index'
        @variables_added = nil

        log_processing if logger
        send(method, *arguments)
        @response
      ensure
        close_session
      end
      ...
      (L851)
      def perform_action
        if self.class.action_methods.include?(action_name) || self.class.action_methods.include?('method_missing')
          send(action_name)
          render unless performed?
        elsif template_exists? && template_public?
          render
        else
          raise UnknownAction, "No action responded to #{action_name}", caller
        end
      end

    end
    ...
  end
  ...
end
  • 詳細の読み込むことができなかったが、どこからかactionのmethodを取得してきて実行。
  • ざっと見た感じ、render_textとかを@responseにセット

rails/actionpack/lib/action_controller/cgi_process.rb(.out(output)部分)

    def out(output = $stdout)
      (L163)
      convert_content_type!(@headers)
      ...
    end
  • headerをセットしている・・・ぽい?

終了

  • 終盤に「あれ・・・routingとかconfigとか出てこなくね?」ってなって、一番最初で読む方向を間違ったことに気づく

感想

  • これを20代で作り上げるとかすごすぎだろ・・・DHHェ・・・
  • 全部を読む時間がなくて、仮説で読み飛ばしてしまったところが多く、あとでじっくり読み直したいと思った。
  • webrickにすごい依存した書き方だった
    • rackが来るとどう変わるのか興味深い
  • これが現在の5系になるまでどのように変わっていくか知りたいと思った
  • 1時間で読みましたが、時間もっと欲しいね!

最後に

  • 生のRubyコミッタに会えて「ラピュタは本当に存在したんだ!」という気持ちになりました
  • とても良い経験をさせていただいたマネーフォワード様に感謝!
  • 「v2のソースを読む」とかぜひやってほしい・・・・(最新まで!)
  • a_matsudaさんと握手できてよかった!!!

(おまけ)懇親会で聞けたこと

  • Railsのバージョンごとの進化について
    • v1からv2への大きな変化はREST
    • RackはRails v2.2〜2.3あたりで入った
    • v2からv3も大きな変更があった(Merbとの結合)
  • DHHについて
    • 25歳くらいでほぼ一人でrails作り上げたDHHすごい。
    • DHHは人生何周もしているに違いない(という噂)
  • その他
    • Rubyはどんどん早くなっているが、Railsは機能が増えていって重くなっていっている。
5
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
2