ホーム DoRuby Scaffoldはどこからくるの? 後編

Scaffoldはどこからくるの? 後編

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

こんにちは。
KBMJのエンジニアの高瀬です。

大分間が空いてしまいましたが、前回の続きを書く事にします。
前回は、http://doruby.kbmj.com/zenpou_on_rails/20071227/Scaffold___

前回はscaffoldを作って以下の様なファイルが出来ました。
     create  app/views/members
      create  app/views/members/index.html.erb
      create  app/views/members/show.html.erb
      create  app/views/members/new.html.erb
      create  app/views/members/edit.html.erb
      create  app/views/layouts/members.html.erb
      create  public/stylesheets/scaffold.css
      create    app/models/member.rb
      create    test/unit/member_test.rb
      create    test/fixtures/members.yml
      create    db/migrate
      create    db/migrate/001_create_members.rb
      create  app/controllers/members_controller.rb
      create  test/functional/members_controller_test.rb
      create  app/helpers/members_helper.rb

こちら、どこから来るのかを調べて行こうと思います。

結論を先に述べると、以下のパスにテンプレートが存在します。
{railsのインストール先}/lib/rails_generator/generators/components/scaffold/
これがどのような仕組みになっているかを追いかけて行きます。

前回、scaffoldは以下のコマンドによって生成されました。

      ruby script/generate scaffold Member name:string discription:text birthday:datetime
rubyの構文はruby –helpによって出してみたところ
    Usage: ruby [switches] [–] [programfile] [arguments]

      -0[octal]       specify record separator (\0, if no argument)
      -a              autosplit mode with -n or -p (splits $_ into $F)
      -c              check syntax only
      -Cdirectory     cd to directory, before executing your script
      -d              set debugging flags (set $DEBUG to true)
      -e ‘command’    one line of script. Several -e’s allowed. Omit [programfile]
      -Fpattern       split() pattern for autosplit (-a)
      -i[extension]   edit ARGV files in place (make backup if extension supplied)
      -Idirectory     specify $LOAD_PATH directory (may be used more than once)
      -Kkcode         specifies KANJI (Japanese) code-set
      -l              enable line ending processing
      -n              assume ‘while gets(); … end’ loop around your script
      -p              assume loop like -n but print line also like sed
      -rlibrary       require the library, before executing your script
      -s              enable some switch parsing for switches after script name
      -S              look for the script using PATH environment variable
      -T[level]       turn on tainting checks
      -v              print version number, then turn on verbose mode
      -w              turn warnings on for your script
      -W[level]       set warning level; 0=silence, 1=medium, 2=verbose (default)
      -x[directory]   strip off text before #!ruby line and perhaps cd to directory
      –copyright     print the copyright
      –version       print the version


となっています。
今回の場合、      “script/generate”[

がprogramfileで      “scaffold Member name:string discription:text birthday:datetime”
がargumentsになります。


というわけで、プログラムファイルの内容を見て見ましょう。
script/generateの中身はこんな内容になっています。      #!/usr/bin/env ruby
      require File.dirname(__FILE__) + ‘/../config/boot’
      require ‘commands/generate’


require は、ライブラリの呼び出しを行います。
ここで、      require File.dirname(__FILE__) + ‘/../config/boot’


は、プロジェクトのrailsの呼び出しを行っているRailsの基本的な所ですので、      require ‘commands/generate’


を見てみる事にします。
{Railsのインストール先}/lib/commands/generate.rb
を参照しましょう。      require “#{RAILS_ROOT}/config/environment”
      require ‘rails_generator’
      require ‘rails_generator/scripts/generate’

      ARGV.shift if [‘–help’, ‘-h’].include?(ARGV[0])
      Rails::Generator::Scripts::Generate.new.run(ARGV)


Railsのenvironment(環境の設定等)を呼び出し、rails_generatorとrails_generator/scripts/generateを呼び出して、
います。

ちょっと文章がソースばかりになるので、省略しますが、      require ‘rails_generator’


のrails_generator.rbにて、      require ‘rails_generator/base’
      require ‘rails_generator/lookup’
      require ‘rails_generator/commands’
      require ‘rails_generator/simple_logger’


など、generatorのファイルを呼び出しています。
そして、lookupによって下記のフォルダにあるファイルをgeneratorのコマンドに取り込んでいます。      #{::RAILS_ROOT}/lib/generators
      #{::RAILS_ROOT}/vendor/generators
      #{::RAILS_ROOT}/vendor/plugins/*/**/generators
      #{::RAILS_ROOT}/vendor/plugins/*/**/rails_generators
      #{Dir.user_home}/.rails/generators
      #{File.dirname(__FILE__)}/generators/components


そして実行段階。/lib/commands/generate.rbの中で実行されていたrunコマンドを見てみます。      Rails::Generator::Scripts::Generate.new.run(ARGV)


クラス名Rails::Generator::Scripts::Generateなのでrails_generator/scripts/generate.rbを見に行きます。
/lib/commands/generate.rbで3行目でrequireされてましたね。
見てみると      require File.dirname(__FILE__) + ‘/../scripts’

      module Rails::Generator::Scripts
        class Generate < Base
          mandatory_options :command => :create
        end
      end

というコードになっています。
run呼び出してるのにrunがない……。
Rails::Generator::Scriptsを拡張してるみたいです。
いったりきたりで大変ですね。

というわけでrails_generator/scripts.rbにいきます。        def run(args = [], runtime_options = {})
          begin
            parse!(args.dup, runtime_options)
          rescue OptionParser::InvalidOption => e
            # Don’t cry, script. Generators want what you think is invalid.
          end

          # Generator name is the only required option.
          unless options[:generator]
            usage if args.empty?
            options[:generator] ||= args.shift
          end

          # Look up generator instance and invoke command on it.
          Rails::Generator::Base.instance(options[:generator], args, options).command(options[:command]).invoke!
        rescue => e
          puts e
          puts ”  #{e.backtrace.join(“\n  “)}\n” if options[:backtrace]
          raise SystemExit
        end
長いのでrunのコードだけ採用しています。
Rails::Generator::Base.instance(options[:generator], args, options).command(options[:command]).invoke!
にてジェネレータを見つけ、lookupでロードしたgenaratorの manifestを呼び出しています。
ここに関するコマンドはlib/rails_generator/commands.rbなどに定義されているのでそこから掘っていってください。

というわけで、送った”scaffold Member name:string discription:text birthday:datetime”の最初のscaffoldの指定により、
scaffoldを呼び出します。

ということで、lookupの中のソースコード一覧の中で、scaffoldのソースコードが存在するのは
{railsのインストール先}/lib/rails_generator/generators/components/scaffold/
でしたので、scafolldはここから来ている事がわかりました。

今回、
Rails::Generator::Base.instance(options[:generator], args, options).command(options[:command]).invoke!
を検索している際、ヒットした
http://d.hatena.ne.jp/elm200/20070718/1184736852を途中から参考にさせていただきました。

というか、既にこちらのエントリがあったという事で、今回のエントリは二番煎じだったかな……と思いましたが、

とりあえず後悔公開CHUです。

記事を共有

最近人気な記事