Rails チュートリアル 1章 勉強メモ3

Rails チュートリアルの勉強メモ

railstutorial.jp

  • 今日も生きるために勉強していきましょう
  • 前回の続き見ていきます
    • いつまで rails new を追ってるんだ

1章続き rails new 何やってんの?

  • 前回は$HOME/.rbenv/versions/*.*.*/bin/rails を読んだ
  • $HOME/.rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/railties-5.1.6/exe/rails これを見る
    • バージョン伏せるの面倒だからやめた
#!/usr/bin/env ruby
  
git_path = File.expand_path("../../../.git", __FILE__)

if File.exist?(git_path)
  railties_path = File.expand_path("../../lib", __FILE__)
  $:.unshift(railties_path)
end
require "rails/cli"

短い

  • $HOME/.rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/.git があれば、$HOME/.rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/railties-5.1.6/lib の path を追加($:は path)
  • その後、"rails/cli" へ
  • $HOME/.rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/railties-5.1.6/lib/rails/cli.rb
require "rails/app_loader"
  
# If we are inside a Rails application this method performs an exec and thus
# the rest of this script is not run.
Rails::AppLoader.exec_app

require "rails/ruby_version_check"
Signal.trap("INT") { puts; exit(1) }

require "rails/command"

if ARGV.first == "plugin"
  ARGV.shift
  Rails::Command.invoke :plugin, ARGV
else
  Rails::Command.invoke :application, ARGV
end

こっちもあんまり長くないなぁ

  • $HOME/.rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/railties-5.1.6/lib/rails/app_loader.rb を読んでいる
require "pathname"
require "rails/version"

module Rails
  module AppLoader # :nodoc:
    extend self

    RUBY = Gem.ruby
    EXECUTABLES = ["bin/rails", "script/rails"]
    BUNDLER_WARNING = <<EOS

EOS

    def exec_app
      original_cwd = Dir.pwd

      loop do
        if exe = find_executable
          contents = File.read(exe)

          if contents =~ /(APP|ENGINE)_PATH/
            exec RUBY, exe, *ARGV
            break # non reachable, hack to be able to stub exec in the test suite
          elsif exe.end_with?("bin/rails") && contents.include?("This file was generated by Bundler")
            $stderr.puts(BUNDLER_WARNING)
            Object.const_set(:APP_PATH, File.expand_path("config/application", Dir.pwd))
            require File.expand_path("../boot", APP_PATH)
            require "rails/commands"
            break
          end
        end

        # If we exhaust the search there is no executable, this could be a
        # call to generate a new application, so restore the original cwd.
        Dir.chdir(original_cwd) && return if Pathname.new(Dir.pwd).root?

        # Otherwise keep moving upwards in search of an executable.
        Dir.chdir("..")
      end
    end

    def find_executable
      EXECUTABLES.find { |exe| File.file?(exe) }
    end
  end
end

おお、Rails モジュール!ついに出てきました

  • require "pathname"

class Pathname (Ruby 2.6.0)

  • パス名を操作するメソッドが色々あるようだ

  • require "rails/version"

    • $HOME/.rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/railties-5.1.6/lib/rails/version.rb 次はここ
require_relative "gem_version"
  
module Rails
  # Returns the version of the currently loaded Rails as a string.
  def self.version
    VERSION::STRING
  end
end
  • 短いからのっける
  • require_relativegem_version を読み込んでいる
  • require との違いは、
    • require_relative はこのファイルの位置から指定されたパスのファイルを読み込んでいる
  • gem_version.rb も見てみる
module Rails
  # Returns the version of the currently loaded Rails as a <tt>Gem::Version</tt>
  def self.gem_version
    Gem::Version.new VERSION::STRING
  end

  module VERSION
    MAJOR = 5
    MINOR = 1
    TINY  = 6
    PRE   = nil

    STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
  end
end
  • Rails::VERSION::STRING でバージョンが取れる。面白い
    • version.rbRails.version で取れるようにしている
  • Rails.gem_version で Gem::Version.new してくれる

  • extend self

    • extend は 指定したモジュールのメソッドを特異メソッドとして追加する

instance method Object#extend (Ruby 2.6.0)

  • ここでは self を渡しているので自分自身のインスタンスメソッドを特異メソッドに追加している
    • これをすることでクラスメソッドとして扱える?
      • いちいち self.exec_app とか書かなくてよくなるって感じだろうか?
  • rails/cli.rbrequire "rails/app_loader" の後早速 Rails::AppLoader.exec_app をしている
  • original_cwd には元々の rails コマンドを叩いた場所の path が入っている

  • find_executablebin/rails または script/rails を探している

  • 見つからなかったら一つ上の Dir へ行くを繰り返す
    • rails アプリを new していれば bin/rails があるはずなのでおそらくそれを探している
    • 今回はないのでそのまま続行
  • rails/ruby_version_check を呼んでいる。ruby のバージョンをチェックしてそう
if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("2.2.2") && RUBY_ENGINE == "ruby"
  desc = defined?(RUBY_DESCRIPTION) ? RUBY_DESCRIPTION : "ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE})"
  abort <<-end_message

    Rails 5 requires Ruby 2.2.2 or newer.

    You're running
      #{desc}

    Please upgrade to Ruby 2.2.2 or newer to continue.

  end_message
end
  • ruby のバージョンが 2.2.2 より低ければメッセージを出す
  • Signal.trap("INT") { puts; exit(1) }

    指定された割り込み signal に対するハンドラとして command を登録します。 指定したシグナルが捕捉された時には例外が発生せず、代わりに command が実行されます。 ブロックを指定した場合にはブロックをハンドラとして登録します。

    trap は前回の trap で設定したハンドラを返します。 文字列を登録していた場合はそれを、 ブロックを登録していたらそれを Proc オブジェクトに変換して返します。 また何も登録されていないときも nil を返します。 ruby の仕組みの外でシグナルハンドラが登録された場合 (例えば拡張ライブラリが独自に sigaction を呼んだ場合など) も nil を返します。

module Signal (Ruby 2.6.0)

  • require "rails/command" 次はここを読もうと思ったけど疲れたので一旦終わり