この記事はアピリッツの技術ブログ「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です。