Rails チュートリアル 1章 勉強メモ7
Rails チュートリアルの勉強メモ
1章続き rails new 何やってんの?
gems/thor-0.20.3/lib/thor/command.rb
20: def run(instance, args = []) 21: arity = nil 22: 23: if private_method?(instance) 24: instance.class.handle_no_command_error(name) 25: elsif public_method?(instance) 26: arity = instance.method(name).arity => 27: instance.__send__(name, *args) 28: elsif local_method?(instance, :method_missing) 29: instance.__send__(:method_missing, name.to_sym, *args) 30: else 31: instance.class.handle_no_command_error(name) 32: end 33: rescue ArgumentError => e 34: handle_argument_error?(instance, e, caller) ? instance.class.handle_argument_error(self, e, args, arity) : (raise e) 35: rescue NoMethodError => e 36: handle_no_method_error?(instance, e, caller) ? instance.class.handle_no_command_error(name) : (raise e) 37: end
- ここから読みます
send は 第一引数のメソッドを実行する name は
application
source_location してみる
[7] pry(#<Thor::Command>)> instance.method(:application).source_location => ["$HOME/.rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/railties-5.1.6/lib/rails/commands/application/application_command.rb", 23] [8] pry(#<Thor::Command>)> instance.method(:perform).source_location => ["$HOME/.rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/railties-5.1.6/lib/rails/commands/application/application_command.rb", 23]
なんで・・・?
- alias_method というものがあるらしいので original_name してみる
[14] pry(#<Thor::Command>)> instance.method(:application).original_name => :perform
なるほど・・・どこかで alias してるのか・・・
> export RAILS=$HOME/.rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/railties-5.1.6/lib/rails > grep "alias" $RAILS/**/*.rb | grep "perform "
それっぽいものはない
> grep "perform" $RAILS/**/*.rb"
してみた
$RAILS/command/base.rb: if meth == "perform"
怪しい
- command/base.rb 見てみる
# Allow the command method to be called perform. def create_command(meth) if meth == "perform" alias_method command_name, meth else # Prevent exception about command without usage. # Some commands define their documentation differently. @usage ||= "" @desc ||= "" super end end
絶対これじゃん!
- binding.pry で止めてみる
122: def create_command(meth) 123: if meth == "perform" 124: binding.pry => 125: alias_method command_name, meth 126: else 127: # Prevent exception about command without usage. 128: # Some commands define their documentation differently. 129: @usage ||= "" 130: @desc ||= "" 131: 132: super 133: end 134: end [1] pry(Rails::Command::ApplicationCommand)> command_name => "application"
解決!!
perform をみる
- rails/commands/application/application_command.rb
23: def perform(*args) => 24: Rails::Generators::AppGenerator.start \ 25: Rails::Generators::ARGVScrubber.new(args).prepare! 26: end
509: def prepare! => 510: handle_version_request!(@argv.first) 511: handle_invalid_command!(@argv.first, @argv) do 512: handle_rails_rc!(@argv.drop(1)) 513: end 514: end
handle_version_request!
は 引数が -v, --version だったら rails のバージョンを返す今回は
new
なのでスキップhandle_invalid_command! をみる
530: def handle_invalid_command!(argument, argv) => 531: if argument == "new" 532: yield 533: else 534: ["--help"] + argv.drop(1) 535: end 536: end
- ついに
new
が渡された! new
以外だと 引数の配列から落として["--help"]
を突っ込んでる- 間違った引数を入れると
--help
を出すって動作はここでやってたのか・・・
- 間違った引数を入れると
yield
なのでprepare!
の ブロックを読むhandle_rails_rc! をみる
538: def handle_rails_rc!(argv) => 539: if argv.find { |arg| arg == "--no-rc" } 540: argv.reject { |arg| arg == "--no-rc" } 541: else 542: railsrc(argv) { |rc_argv, rc| insert_railsrc_into_argv!(rc_argv, rc) } 543: end 544: end
argv.drop(1)
してるのでargv
は["hello_app"]
--no-rc
をつけると railsrc を読みに行かない- railsrc とかあるんだ・・・
次は
Rails::Generators::AppGenerator.start
を読む
Rails::Generators::AppGenerator.start
- thor/base.rb
464: def start(given_args = ARGV, config = {}) => 465: config[:shell] ||= Thor::Base.shell.new 466: dispatch(nil, given_args.dup, nil, config) 467: rescue Thor::Error => e 468: config[:debug] || ENV["THOR_DEBUG"] == "1" ? (raise e) : config[:shell].error(e.message) 469: exit(false) if exit_on_failure? 470: rescue Errno::EPIPE 471: # This happens if a thor command is piped to something like `head`, 472: # which closes the pipe when it's done reading. This will also 473: # mean that if the pipe is closed, further unnecessary 474: # computation will not occur. 475: exit(true) 476: end
[10] pry(Rails::Generators::AppGenerator)> config => {:shell=>#<Thor::Shell::Color:0x00007fc3671f5f90 @always_force=false, @base=nil, @mute=false, @padding=0>}
dispatch(nil, given_args.dup, nil, config)
へthor/group.rb @ line 218 Thor::Group.dispatch:
217: def dispatch(command, given_args, given_opts, config) #:nodoc: => 218: if Thor::HELP_MAPPINGS.include?(given_args.first) 219: help(config[:shell]) 220: return 221: end 222: 223: args, opts = Thor::Options.split(given_args) 224: opts = given_opts || opts 225: 226: instance = new(args, opts, config) 227: yield instance if block_given? 228: 229: if command 230: instance.invoke_command(all_commands[command]) 231: else 232: instance.invoke_all 233: end 234: end
--help
とか入っていたら help 実行handle_invalid_command!
とかで--help
入っていたらここで実行
args, opts = Thor::Options.split(given_args)
- オプションを切り分けてる?
- thor/parser/arguments.rb @ line 11 Thor::Arguments.split: 8: def self.split(args) 9: arguments = [] 10: => 11: args.each do |item| 12: break if item =~ /^-/ 13: arguments << item 14: end 15: 16: [arguments, args[Range.new(arguments.size, -1)]] 17: end
-
がついた引数、つまりオプション以降をopts
に渡しているinstance = new(args, opts, config)
でRails::Generators::AppGenerator
を生成
232: def initialize(*args) => 233: super 234: 235: if !options[:skip_active_record] && !DATABASES.include?(options[:database]) 236: raise Error, "Invalid value for --database option. Supported for preconfiguration are: #{DATABASES.join(", ")}." 237: end 238: 239: # Force sprockets and yarn to be skipped when generating API only apps. 240: # Can't modify options hash as it's frozen by default. 241: if options[:api] 242: self.options = options.merge(skip_sprockets: true, skip_javascript: true, skip_yarn: true).freeze 243: end 244: end
super
追っていくとThor
を読むことになりそうなので一旦置いとく- さらっと読んでみても良いかもしれない
[28] pry(Rails::Generators::AppGenerator)> instance => #<Rails::Generators::AppGenerator:0x00007ff0863b6db0 @_initializer=[["hello_app"], [], {:shell=>#<Thor::Shell::Color:0x00007ff0864b6d00 @always_force=false, @base=#<Rails::Generators::AppGenerator:0x00007ff0863b6db0 ...>, @mute=false, @padding=0>}], @_invocations={}, @after_bundle_callbacks=[], @app_path="hello_app", @args=[], @behavior=:invoke, @destination_stack=["/Users/mitsuki/workspace/Rails/environment"], @extra_entries=[], @gem_filter=#<Proc:0x00007ff0878feb78@/$HOME/.rbenv/versions/2.6.2/lib/ruby/gems/2.6.0/gems/railties-5.1.6/lib/rails/generators/app_base.rb:99 (lambda)>, @in_group=nil, @options= {"ruby"=>$HOME/.rbenv/versions/2.6.2/bin/ruby", "database"=>"sqlite3", "skip_yarn"=>false, "skip_gemfile"=>false, "skip_git"=>false, "skip_keeps"=>false, "skip_action_mailer"=>false, "skip_active_record"=>false, "skip_puma"=>false, "skip_action_cable"=>false, "skip_sprockets"=>false, "skip_spring"=>false, "skip_listen"=>false, "skip_coffee"=>false, "skip_javascript"=>false, "skip_turbolinks"=>false, "skip_test"=>false, "skip_system_test"=>false, "dev"=>false, "edge"=>false, "no_rc"=>false, "api"=>false, "skip_bundle"=>false}, @shell=#<Thor::Shell::Color:0x00007ff0864b6d00 @always_force=false, @base=#<Rails::Generators::AppGenerator:0x00007ff0863b6db0 ...>, @mute=false, @padding=0>>
block_given? = false
,command = nil
なので, 次はinstance.invoke_all
- thor/invocation.rb @ line 133 Thor::Invocation#invoke_all: 132: def invoke_all #:nodoc: => 133: self.class.all_commands.map { |_, command| invoke_command(command) } 134: end
all_commands
は, Thorクラスとサブクラスのすべてのコマンドを返す
# Returns the commands for this Thor class and all subclasses. # # ==== Returns # OrderedHash:: An ordered hash with commands names as keys and Thor::Command # objects as values. # def all_commands @all_commands ||= from_superclass(:all_commands, Thor::CoreExt::OrderedHash.new) @all_commands.merge!(commands) end
all_commands
の command をひとつづ取り出してinvoke_command(command)
で実行!疲れたので今回は一旦ここで終わり
- 次は
all_commands
の最初のset_default_accessors!
から読んでいきます
- 次は
ちなみに
all_commands
の結果
[2] pry(#<Rails::Generators::AppGenerator>)> self.class.all_commands => {"set_default_accessors!"=>#<struct Thor::Command name="set_default_accessors!", description=nil, long_description=nil, usage=nil, options={}, ancestor_name=nil>, "create_root"=>#<struct Thor::Command name="create_root", description=nil, long_description=nil, usage=nil, options={}, ancestor_name=nil>, "create_root_files"=>#<struct Thor::Command name="create_root_files", description=nil, long_description=nil, usage=nil, options={}, ancestor_name=nil>, "create_app_files"=>#<struct Thor::Command name="create_app_files", description=nil, long_description=nil, usage=nil, options={}, ancestor_name=nil>, "create_bin_files"=>#<struct Thor::Command name="create_bin_files", description=nil, long_description=nil, usage=nil, options={}, ancestor_name=nil>, "create_config_files"=>#<struct Thor::Command name="create_config_files", description=nil, long_description=nil, usage=nil, options={}, ancestor_name=nil>, "create_boot_file"=>#<struct Thor::Command name="create_boot_file", description=nil, long_description=nil, usage=nil, options={}, ancestor_name=nil>, "create_active_record_files"=>#<struct Thor::Command name="create_active_record_files", description=nil, long_description=nil, usage=nil, options={}, ancestor_name=nil>, "create_db_files"=>#<struct Thor::Command name="create_db_files", description=nil, long_description=nil, usage=nil, options={}, ancestor_name=nil>, "create_lib_files"=>#<struct Thor::Command name="create_lib_files", description=nil, long_description=nil, usage=nil, options={}, ancestor_name=nil>, "create_log_files"=>#<struct Thor::Command name="create_log_files", description=nil, long_description=nil, usage=nil, options={}, ancestor_name=nil>, "create_public_files"=>#<struct Thor::Command name="create_public_files", description=nil, long_description=nil, usage=nil, options={}, ancestor_name=nil>, "create_test_files"=>#<struct Thor::Command name="create_test_files", description=nil, long_description=nil, usage=nil, options={}, ancestor_name=nil>, "create_system_test_files"=>#<struct Thor::Command name="create_system_test_files", description=nil, long_description=nil, usage=nil, options={}, ancestor_name=nil>, "create_tmp_files"=>#<struct Thor::Command name="create_tmp_files", description=nil, long_description=nil, usage=nil, options={}, ancestor_name=nil>, "create_vendor_files"=>#<struct Thor::Command name="create_vendor_files", description=nil, long_description=nil, usage=nil, options={}, ancestor_name=nil>, "delete_app_assets_if_api_option"=>#<struct Thor::Command name="delete_app_assets_if_api_option", description=nil, long_description=nil, usage=nil, options={}, ancestor_name=nil>, "delete_app_helpers_if_api_option"=>#<struct Thor::Command name="delete_app_helpers_if_api_option", description=nil, long_description=nil, usage=nil, options={}, ancestor_name=nil>, "delete_application_layout_file_if_api_option"=>#<struct Thor::Command name="delete_application_layout_file_if_api_option", description=nil, long_description=nil, usage=nil, options={}, ancestor_name=nil>, "delete_public_files_if_api_option"=>#<struct Thor::Command name="delete_public_files_if_api_option", description=nil, long_description=nil, usage=nil, options={}, ancestor_name=nil>, "delete_js_folder_skipping_javascript"=>#<struct Thor::Command name="delete_js_folder_skipping_javascript", description=nil, long_description=nil, usage=nil, options={}, ancestor_name=nil>, "delete_assets_initializer_skipping_sprockets"=>#<struct Thor::Command name="delete_assets_initializer_skipping_sprockets", description=nil, long_description=nil, usage=nil, options={}, ancestor_name=nil>, "delete_application_record_skipping_active_record"=>#<struct Thor::Command name="delete_application_record_skipping_active_record", description=nil, long_description=nil, usage=nil, options={}, ancestor_name=nil>, "delete_action_mailer_files_skipping_action_mailer"=>#<struct Thor::Command name="delete_action_mailer_files_skipping_action_mailer", description=nil, long_description=nil, usage=nil, options={}, ancestor_name=nil>, "delete_action_cable_files_skipping_action_cable"=>#<struct Thor::Command name="delete_action_cable_files_skipping_action_cable", description=nil, long_description=nil, usage=nil, options={}, ancestor_name=nil>, "delete_non_api_initializers_if_api_option"=>#<struct Thor::Command name="delete_non_api_initializers_if_api_option", description=nil, long_description=nil, usage=nil, options={}, ancestor_name=nil>, "delete_api_initializers"=>#<struct Thor::Command name="delete_api_initializers", description=nil, long_description=nil, usage=nil, options={}, ancestor_name=nil>, "delete_new_framework_defaults"=>#<struct Thor::Command name="delete_new_framework_defaults", description=nil, long_description=nil, usage=nil, options={}, ancestor_name=nil>, "delete_bin_yarn_if_skip_yarn_option"=>#<struct Thor::Command name="delete_bin_yarn_if_skip_yarn_option", description=nil, long_description=nil, usage=nil, options={}, ancestor_name=nil>, "finish_template"=>#<struct Thor::Command name="finish_template", description=nil, long_description=nil, usage=nil, options={}, ancestor_name=nil>, "apply_rails_template"=>#<struct Thor::Command name="apply_rails_template", description=nil, long_description=nil, usage=nil, options={}, ancestor_name=nil>, "run_bundle"=>#<struct Thor::Command name="run_bundle", description=nil, long_description=nil, usage=nil, options={}, ancestor_name=nil>, "run_webpack"=>#<struct Thor::Command name="run_webpack", description=nil, long_description=nil, usage=nil, options={}, ancestor_name=nil>, "generate_spring_binstubs"=>#<struct Thor::Command name="generate_spring_binstubs", description=nil, long_description=nil, usage=nil, options={}, ancestor_name=nil>, "run_after_bundle_callbacks"=>#<struct Thor::Command name="run_after_bundle_callbacks", description=nil, long_description=nil, usage=nil, options={}, ancestor_name=nil>}