Ruby on Rails4/ファイル生成周りを読む/rails new時に行われる処理
をテンプレートにして作成
[
トップ
] [
新規
|
一覧
|
単語検索
|
最終更新
|
ヘルプ
]
開始行:
[[Ruby on Rails4を読む]]
#contents
*はじめに [#y72fe653]
Rails4では以下のようにアプリケーションのベースとなるファ...
C:\Sites>rails new foo
C:\Sites>cd foo
C:\Sites\foo>rails generate scaffold user name:string em...
アプリケーション名やらなんやらはRails入れるときに[[参考に...
さて、以前に読んだときはファイル生成周りを読んでませんが...
*PATH上にあるrailsコマンド [#h348e558]
まずはエントリーポイント、railsコマンドを見ていきます。
#code(Ruby){{
require 'rubygems'
version = ">= 0"
# 引数でバージョン指定があったらversionを更新するコード
gem 'railties', version
load Gem.bin_path('railties', 'rails', version)
}}
というわけでrailtiesのrailsに進みます。「.gitがあったら」...
#code(Ruby){{
require "rails/cli"
}}
*railties/lib/rails/cli.rb [#ieb0f093]
rails/cli.rb。
#code(Ruby){{
require 'rails/app_rails_loader'
# If we are inside a Rails application this method perfor...
# the rest of this script is not run.
Rails::AppRailsLoader.exec_app_rails
require 'rails/ruby_version_check'
Signal.trap("INT") { puts; exit(1) }
if ARGV.first == 'plugin'
ARGV.shift
require 'rails/commands/plugin'
else
require 'rails/commands/application'
end
}}
妙なことが書いてありますがいったん無視して下の方に注目し...
*railties/lib/rails/commands/application.rb [#ja65951d]
rails/commands/application.rb。
#code(Ruby){{
require 'rails/generators'
require 'rails/generators/rails/app/app_generator'
(中略)
args = Rails::Generators::ARGVScrubber.new(ARGV).prepare!
Rails::Generators::AppGenerator.start args
}}
ARGVScrubberはapp_generator.rbの後半に書かれています。い...
-変なコマンドが指定されてたら--helpが指定されてたというこ...
-railsrcを読み込んでオプションに加える
さて、AppGenerator.startですがこれを追っかけるのはなかな...
rails/generators/rails/app/app_generator.rb
#code(Ruby){{
require 'rails/generators/app_base'
module Rails
module Generators
class AppGenerator < AppBase # :nodoc:
}}
rails/generators/app_base.rb
#code(Ruby){{
require 'rails/generators'
module Rails
module Generators
class AppBase < Base # :nodoc:
}}
base.rbはrequireしていない。しかし、generators.rbで以下の...
rails/generators.rb
#code(Ruby){{
module Rails
module Generators
autoload :Base, 'rails/generators/base'
}}
rails/generators/base.rb
#code(Ruby){{
require 'thor/group'
module Rails
module Generators
class Base < Thor::Group
}}
というわけで、gemが変わってthorに移動します。
thor/group.rb
#code(C){{
require "thor/base"
class Thor::Group # rubocop:disable ClassLength
include Thor::Base
}}
thor/base.rb
#code(Ruby){{
class Thor
module Base
class << self
def included(base) #:nodoc:
base.extend ClassMethods
base.send :include, Invocation
base.send :include, Shell
end
module ClassMethods
def start(given_args = ARGV, config = {})
config[:shell] ||= Thor::Base.shell.new
dispatch(nil, given_args.dup, nil, config)
end
}}
というわけでようやくstartはけ〜んです。
*Thor::Group.dispatch (thor/lib/thor/group.rb) [#v72b37fc]
さてというわけでdispatchというなんかコマンド実行してそう...
#code(Ruby){{
class Thor::Group # rubocop:disable ClassLength
class << self
def dispatch(command, given_args, given_opts, config)...
if Thor::HELP_MAPPINGS.include?(given_args.first)
help(config[:shell])
return
end
args, opts = Thor::Options.split(given_args)
opts = given_opts || opts
instance = new(args, opts, config)
yield instance if block_given?
if command
instance.invoke_command(all_commands[command])
else
instance.invoke_all
end
end
}}
オプションは付けてないので飛ばします。大したことはやって...
で、newされてます。やや見慣れない感じですが、selfはクラス...
railties/lib/rails/generators/rails/app/app_generator.rb
#code(Ruby){{
class AppGenerator < AppBase # :nodoc:
def initialize(*args)
super
unless app_path
raise Error, "Application name should be provided i...
end
if !options[:skip_active_record] && !DATABASES.includ...
raise Error, "Invalid value for --database option. ...
end
end
}}
「app_pathってなに〜!?」って心の叫びを今から解決します...
thor/lib/thor/base.rb
#code(Ruby){{
def initialize(args = [], local_options = {}, config = {}...
# オプションの処理。さくっと省略します
# Add the remaining arguments from the options parser t...
# arguments passed in to initialize. Then remove any po...
# arguments declared using #argument (this is primarily...
# by Thor::Group). Tis will leave us with the remaining
# positional arguments.
to_parse = args
to_parse += opts.remaining unless self.class.strict_arg...
thor_args = Thor::Arguments.new(self.class.arguments)
thor_args.parse(to_parse).each { |k, v| __send__("#{k}=...
@args = thor_args.remaining
end
}}
また謎来ました。見た感じ、渡された引数を定義されている引...
ところで・・・、新しくアプリケーション作るときって、「rai...
railties/lib/rails/generators/rails/app/app_generator.rb
#code(Ruby){{
def prepare!
handle_version_request!(@argv.first)
handle_invalid_command!(@argv.first, @argv) do
handle_rails_rc!(@argv.drop(1))
end
end
def handle_invalid_command!(argument, argv)
if argument == "new"
yield
else
['--help'] + argv.drop(1)
end
end
def handle_rails_rc!(argv)
if argv.find { |arg| arg == '--no-rc' }
argv.reject { |arg| arg == '--no-rc' }
else
railsrc(argv) { |rc_argv, rc| insert_railsrc_into_arg...
end
end
def railsrc(argv)
if (customrc = argv.index{ |x| x.include?("--rc=") })
fname = File.expand_path(argv[customrc].gsub(/--rc=/,...
yield(argv.take(customrc) + argv.drop(customrc + 1), ...
else
yield argv, self.class.default_rc_file
end
end
def insert_railsrc_into_argv!(argv, railsrc)
return argv unless File.exist?(railsrc)
extra_args = read_rc_file railsrc
argv.take(1) + extra_args + argv.drop(1)
end
}}
渡された引数配列をdrop(1)しているのでしれっとnewが除けら...
話を戻してargumentです。AppGeneratorにはapp_pathの定義は...
railties/lib/rails/generators/app_base.rb
#code(Ruby){{
class AppBase < Base # :nodoc:
argument :app_path, type: :string
}}
argumentメソッドを貼るのは省略します。attr_accessorでsett...
*Thor::Invocation::invoke_all (thor/lib/thor/invocation.r...
さて。ここまででやっとインスタンス生成が終わりました。よ...
#code(Ruby){{
class Thor
module Invocation
def invoke_all #:nodoc:
self.class.all_commands.map { |_, command| invoke_c...
end
}}
またまた謎のall_commandsが登場です。こちらはbase.rbに書か...
thor/lib/thor.base.rb
#code(Ruby){{
def all_commands
@all_commands ||= from_superclass(:all_commands, Thor::...
@all_commands.merge(commands)
end
def commands
@commands ||= Thor::CoreExt::OrderedHash.new
end
}}
まあ想像通りかなと思います。OrderedHashってのはまあOrdere...
で・・・、commandsはいつ設定されるの?と先ほどのargument...
railties/lib/rails/generators/rails/app/app_generator.rb
#code(Ruby){{
module Rails
module Generators
class AppGenerator < AppBase # :nodoc:
def create_root_files
build(:readme)
build(:rakefile)
build(:configru)
build(:gitignore) unless options[:skip_git]
build(:gemfile) unless options[:skip_gemfile]
end
}}
明らかにcreate_root_files実行されてる(commandsに入れられ...
thor/lib/thor/base.rb
#code(Ruby){{
def method_added(meth)
meth = meth.to_s
if meth == "initialize"
initialize_added
return
end
# Return if it's not a public instance method
return unless public_method_defined?(meth.to_sym)
@no_commands ||= false
return if @no_commands || !create_command(meth)
is_thor_reserved_word?(meth, :command)
Thor::Base.register_klass_file(self)
end
}}
というわけで、publicメソッドはcommandsとして登録されるよ...
ここまで来たら後は淡々とテンプレートのファイルをコピーす...
*おわりに [#ce896bb4]
今回はRailsアプリケーションのスタート地点、rails newコマ...
rails generateまで読む予定でしたが長くなるので今回はここ...
そういえばbundle install忘れてた。けど、長くなるのでやめ...
終了行:
[[Ruby on Rails4を読む]]
#contents
*はじめに [#y72fe653]
Rails4では以下のようにアプリケーションのベースとなるファ...
C:\Sites>rails new foo
C:\Sites>cd foo
C:\Sites\foo>rails generate scaffold user name:string em...
アプリケーション名やらなんやらはRails入れるときに[[参考に...
さて、以前に読んだときはファイル生成周りを読んでませんが...
*PATH上にあるrailsコマンド [#h348e558]
まずはエントリーポイント、railsコマンドを見ていきます。
#code(Ruby){{
require 'rubygems'
version = ">= 0"
# 引数でバージョン指定があったらversionを更新するコード
gem 'railties', version
load Gem.bin_path('railties', 'rails', version)
}}
というわけでrailtiesのrailsに進みます。「.gitがあったら」...
#code(Ruby){{
require "rails/cli"
}}
*railties/lib/rails/cli.rb [#ieb0f093]
rails/cli.rb。
#code(Ruby){{
require 'rails/app_rails_loader'
# If we are inside a Rails application this method perfor...
# the rest of this script is not run.
Rails::AppRailsLoader.exec_app_rails
require 'rails/ruby_version_check'
Signal.trap("INT") { puts; exit(1) }
if ARGV.first == 'plugin'
ARGV.shift
require 'rails/commands/plugin'
else
require 'rails/commands/application'
end
}}
妙なことが書いてありますがいったん無視して下の方に注目し...
*railties/lib/rails/commands/application.rb [#ja65951d]
rails/commands/application.rb。
#code(Ruby){{
require 'rails/generators'
require 'rails/generators/rails/app/app_generator'
(中略)
args = Rails::Generators::ARGVScrubber.new(ARGV).prepare!
Rails::Generators::AppGenerator.start args
}}
ARGVScrubberはapp_generator.rbの後半に書かれています。い...
-変なコマンドが指定されてたら--helpが指定されてたというこ...
-railsrcを読み込んでオプションに加える
さて、AppGenerator.startですがこれを追っかけるのはなかな...
rails/generators/rails/app/app_generator.rb
#code(Ruby){{
require 'rails/generators/app_base'
module Rails
module Generators
class AppGenerator < AppBase # :nodoc:
}}
rails/generators/app_base.rb
#code(Ruby){{
require 'rails/generators'
module Rails
module Generators
class AppBase < Base # :nodoc:
}}
base.rbはrequireしていない。しかし、generators.rbで以下の...
rails/generators.rb
#code(Ruby){{
module Rails
module Generators
autoload :Base, 'rails/generators/base'
}}
rails/generators/base.rb
#code(Ruby){{
require 'thor/group'
module Rails
module Generators
class Base < Thor::Group
}}
というわけで、gemが変わってthorに移動します。
thor/group.rb
#code(C){{
require "thor/base"
class Thor::Group # rubocop:disable ClassLength
include Thor::Base
}}
thor/base.rb
#code(Ruby){{
class Thor
module Base
class << self
def included(base) #:nodoc:
base.extend ClassMethods
base.send :include, Invocation
base.send :include, Shell
end
module ClassMethods
def start(given_args = ARGV, config = {})
config[:shell] ||= Thor::Base.shell.new
dispatch(nil, given_args.dup, nil, config)
end
}}
というわけでようやくstartはけ〜んです。
*Thor::Group.dispatch (thor/lib/thor/group.rb) [#v72b37fc]
さてというわけでdispatchというなんかコマンド実行してそう...
#code(Ruby){{
class Thor::Group # rubocop:disable ClassLength
class << self
def dispatch(command, given_args, given_opts, config)...
if Thor::HELP_MAPPINGS.include?(given_args.first)
help(config[:shell])
return
end
args, opts = Thor::Options.split(given_args)
opts = given_opts || opts
instance = new(args, opts, config)
yield instance if block_given?
if command
instance.invoke_command(all_commands[command])
else
instance.invoke_all
end
end
}}
オプションは付けてないので飛ばします。大したことはやって...
で、newされてます。やや見慣れない感じですが、selfはクラス...
railties/lib/rails/generators/rails/app/app_generator.rb
#code(Ruby){{
class AppGenerator < AppBase # :nodoc:
def initialize(*args)
super
unless app_path
raise Error, "Application name should be provided i...
end
if !options[:skip_active_record] && !DATABASES.includ...
raise Error, "Invalid value for --database option. ...
end
end
}}
「app_pathってなに〜!?」って心の叫びを今から解決します...
thor/lib/thor/base.rb
#code(Ruby){{
def initialize(args = [], local_options = {}, config = {}...
# オプションの処理。さくっと省略します
# Add the remaining arguments from the options parser t...
# arguments passed in to initialize. Then remove any po...
# arguments declared using #argument (this is primarily...
# by Thor::Group). Tis will leave us with the remaining
# positional arguments.
to_parse = args
to_parse += opts.remaining unless self.class.strict_arg...
thor_args = Thor::Arguments.new(self.class.arguments)
thor_args.parse(to_parse).each { |k, v| __send__("#{k}=...
@args = thor_args.remaining
end
}}
また謎来ました。見た感じ、渡された引数を定義されている引...
ところで・・・、新しくアプリケーション作るときって、「rai...
railties/lib/rails/generators/rails/app/app_generator.rb
#code(Ruby){{
def prepare!
handle_version_request!(@argv.first)
handle_invalid_command!(@argv.first, @argv) do
handle_rails_rc!(@argv.drop(1))
end
end
def handle_invalid_command!(argument, argv)
if argument == "new"
yield
else
['--help'] + argv.drop(1)
end
end
def handle_rails_rc!(argv)
if argv.find { |arg| arg == '--no-rc' }
argv.reject { |arg| arg == '--no-rc' }
else
railsrc(argv) { |rc_argv, rc| insert_railsrc_into_arg...
end
end
def railsrc(argv)
if (customrc = argv.index{ |x| x.include?("--rc=") })
fname = File.expand_path(argv[customrc].gsub(/--rc=/,...
yield(argv.take(customrc) + argv.drop(customrc + 1), ...
else
yield argv, self.class.default_rc_file
end
end
def insert_railsrc_into_argv!(argv, railsrc)
return argv unless File.exist?(railsrc)
extra_args = read_rc_file railsrc
argv.take(1) + extra_args + argv.drop(1)
end
}}
渡された引数配列をdrop(1)しているのでしれっとnewが除けら...
話を戻してargumentです。AppGeneratorにはapp_pathの定義は...
railties/lib/rails/generators/app_base.rb
#code(Ruby){{
class AppBase < Base # :nodoc:
argument :app_path, type: :string
}}
argumentメソッドを貼るのは省略します。attr_accessorでsett...
*Thor::Invocation::invoke_all (thor/lib/thor/invocation.r...
さて。ここまででやっとインスタンス生成が終わりました。よ...
#code(Ruby){{
class Thor
module Invocation
def invoke_all #:nodoc:
self.class.all_commands.map { |_, command| invoke_c...
end
}}
またまた謎のall_commandsが登場です。こちらはbase.rbに書か...
thor/lib/thor.base.rb
#code(Ruby){{
def all_commands
@all_commands ||= from_superclass(:all_commands, Thor::...
@all_commands.merge(commands)
end
def commands
@commands ||= Thor::CoreExt::OrderedHash.new
end
}}
まあ想像通りかなと思います。OrderedHashってのはまあOrdere...
で・・・、commandsはいつ設定されるの?と先ほどのargument...
railties/lib/rails/generators/rails/app/app_generator.rb
#code(Ruby){{
module Rails
module Generators
class AppGenerator < AppBase # :nodoc:
def create_root_files
build(:readme)
build(:rakefile)
build(:configru)
build(:gitignore) unless options[:skip_git]
build(:gemfile) unless options[:skip_gemfile]
end
}}
明らかにcreate_root_files実行されてる(commandsに入れられ...
thor/lib/thor/base.rb
#code(Ruby){{
def method_added(meth)
meth = meth.to_s
if meth == "initialize"
initialize_added
return
end
# Return if it's not a public instance method
return unless public_method_defined?(meth.to_sym)
@no_commands ||= false
return if @no_commands || !create_command(meth)
is_thor_reserved_word?(meth, :command)
Thor::Base.register_klass_file(self)
end
}}
というわけで、publicメソッドはcommandsとして登録されるよ...
ここまで来たら後は淡々とテンプレートのファイルをコピーす...
*おわりに [#ce896bb4]
今回はRailsアプリケーションのスタート地点、rails newコマ...
rails generateまで読む予定でしたが長くなるので今回はここ...
そういえばbundle install忘れてた。けど、長くなるのでやめ...
ページ名: