Ruby on Rails4/ファイル生成周りを読む/rails generate時に行われる処理
をテンプレートにして作成
[
トップ
] [
新規
|
一覧
|
単語検索
|
最終更新
|
ヘルプ
]
開始行:
[[Ruby on Rails4を読む]]
#contents
*はじめに [#n73ec655]
前回はrails new時の挙動について見てきました。railsコマン...
rails generate scaffold user name:string email:string
のように使用します。今回はこの場合にどういう処理が行われ...
*Rails::AppRailsLoader (railties/lib/rails/app_rails_load...
さて、前回も見た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
}}
前回は下部のrails/commands/application.rbに進みました。今...
#code(Ruby){{
module Rails
module AppRailsLoader
RUBY = Gem.ruby
EXECUTABLES = ['bin/rails', 'script/rails']
def self.exec_app_rails
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 stu...
elsif exe.end_with?('bin/rails') && contents.in...
$stderr.puts(BUNDLER_WARNING)
Object.const_set(:APP_PATH, File.expand_path(...
require File.expand_path('../boot', APP_PATH)
require 'rails/commands'
break
end
end
# If we exhaust the search there is no executable...
# call to generate a new application, so restore ...
Dir.chdir(original_cwd) and return if Pathname.ne...
# Otherwise keep moving upwards in search of an e...
Dir.chdir('..')
end
end
def self.find_executable
EXECUTABLES.find { |exe| File.file?(exe) }
end
}}
何しているかというと、アプリケーションディレクトリのbin/r...
#code(Ruby){{
#!/usr/bin/env ruby
APP_PATH = File.expand_path('../../config/application', ...
require_relative '../config/boot'
require 'rails/commands'
}}
*railties/lib/rails/commands.rb [#vc7d5786]
全部見ると長くなるのでrails/commandsに進みます。
#code(Ruby){{
ARGV << '--help' if ARGV.empty?
aliases = {
"g" => "generate",
"d" => "destroy",
"c" => "console",
"s" => "server",
"db" => "dbconsole",
"r" => "runner"
}
command = ARGV.shift
command = aliases[command] || command
require 'rails/commands/commands_tasks'
Rails::CommandsTasks.new(ARGV).run_command!(command)
}}
commands_tasks.rbへ。
#code(Ruby){{
module Rails
class CommandsTasks # :nodoc:
def run_command!(command)
command = parse_command(command)
if COMMAND_WHITELIST.include?(command)
send(command)
else
write_error_message(command)
end
end
def generate
generate_or_destroy(:generate)
end
def generate_or_destroy(command)
require 'rails/generators'
require_application_and_environment!
Rails.application.load_generators
require "rails/commands/#{command}"
end
}}
メソッド呼び出しとかrequireとかしてて知らぬ間にいろいろ設...
railties/lib/rails/commands/generate.rb
#code(Ruby){{
require 'rails/generators'
if [nil, "-h", "--help"].include?(ARGV.first)
Rails::Generators.help 'generate'
exit
end
name = ARGV.shift
root = defined?(ENGINE_ROOT) ? ENGINE_ROOT : Rails.root
Rails::Generators.invoke name, ARGV, behavior: :invoke, d...
}}
忘れかけているので確認。この時点でのARGVは
scaffold user name:string email:string
です。
invokeメソッドに進む。
railties/lib/rails/generators.rb
#code(Ruby){{
def self.invoke(namespace, args=ARGV, config={})
names = namespace.to_s.split(':')
if klass = find_by_namespace(names.pop, names.any? && n...
args << "--help" if args.empty? && klass.arguments.an...
klass.start(args, config)
else
puts "Could not find generator #{namespace}."
end
end
}}
*find_by_namespace (railties/lib/rails/generators.rb) [#q...
そろそろ見出しを変えます。与えられたname(コマンド。names...
#code(Ruby){{
def self.find_by_namespace(name, base=nil, context=nil) #...
lookups = []
lookups << "#{base}:#{name}" if base
lookups << "#{name}:#{context}" if context
unless base || context
unless name.to_s.include?(?:)
lookups << "#{name}:#{name}"
lookups << "rails:#{name}"
end
lookups << "#{name}"
end
lookup(lookups)
namespaces = Hash[subclasses.map { |klass| [klass.names...
lookups.each do |namespace|
klass = namespaces[namespace]
return klass if klass
end
invoke_fallbacks_for(name, base) || invoke_fallbacks_fo...
end
}}
loookupsは["scaffold/scaffold", "rails/scaffold", "scaffo...
#code(Ruby){{
def self.lookup(namespaces) #:nodoc:
paths = namespaces_to_paths(namespaces)
paths.each do |raw_path|
["rails/generators", "generators"].each do |base|
path = "#{base}/#{raw_path}_generator"
begin
require path
return
rescue LoadError => e
raise unless e.message =~ /#{Regexp.escape(path)}$/
rescue Exception => e
warn "[WARNING] Could not load generator #{path.i...
end
end
end
end
def self.namespaces_to_paths(namespaces) #:nodoc:
paths = []
namespaces.each do |namespace|
pieces = namespace.split(":")
paths << pieces.dup.push(pieces.last).join("/")
paths << pieces.join("/")
end
paths.uniq!
paths
end
}}
最終的に、rails/generators/rails/scaffold/scaffold_genera...
*railties/lib/rails/generators/rails/scaffold/scaffold_ge...
#code(Ruby){{
require 'rails/generators/rails/resource/resource_generat...
module Rails
module Generators
class ScaffoldGenerator < ResourceGenerator # :nodoc:
}}
rails/generators/rails/resource/resource_generator.rb
#code(Ruby){{
module Rails
module Generators
class ResourceGenerator < ModelGenerator # :nodoc:
}}
rails/generators/rails/model/model_generator.rb
#code(Ruby){{
module Rails
module Generators
class ModelGenerator < NamedBase # :nodoc:
argument :attributes, type: :array, default: [], ba...
}}
rails/generators/named_base.rb
#code(Ruby){{
module Rails
module Generators
class NamedBase < Base
argument :name, type: :string
}}
というわけで、前回見たRails::Generator::Baseまで来ました...
name: "user"
arguments: ["name:string", "email:string"]
が設定されます。
さてでは次に実行されるコマンドを・・・と見てみてもそれっ...
railties/lib/rails/generators/rails/scaffold/scaffold_gen...
#code(Ruby){{
module Rails
module Generators
class ScaffoldGenerator < ResourceGenerator # :nodoc:
hook_for :scaffold_controller, required: true
}}
rails/generators/rails/scaffold_controller/template/contr...
*hook_for (railties/lib/rails/generator/base.rb) [#m8d60d...
hook_forはRails::Generator::Baseに定義されています。
#code(Ruby){{
def self.hook_for(*names, &block)
options = names.extract_options!
in_base = options.delete(:in) || base_name
as_hook = options.delete(:as) || generator_name
names.each do |name|
unless class_options.key?(name)
defaults = if options[:type] == :boolean
{ }
elsif [true, false].include?(default_value_for_opti...
{ banner: "" }
else
{ desc: "#{name.to_s.humanize} to be invoked", ba...
end
class_option(name, defaults.merge!(options))
end
hooks[name] = [ in_base, as_hook ]
invoke_from_option(name, options, &block)
end
end
def self.base_name
@base_name ||= begin
if base = name.to_s.split('::').first
base.underscore
end
end
end
def self.generator_name
@generator_name ||= begin
if generator = name.to_s.split('::').last
generator.sub!(/Generator$/, '')
generator.underscore
end
end
end
}}
base_name、generator_nameで使われているnameはクラス名を表...
invoke_from_optionはThor::Groupで定義されています。
#code(Ruby){{
def invoke_from_option(*names, &block) # rubocop:disable ...
options = names.last.is_a?(Hash) ? names.pop : {}
verbose = options.fetch(:verbose, :white)
names.each do |name|
unless class_options.key?(name)
fail ArgumentError, "You have to define the option ...
"before setting invoke_from_op...
end
invocations[name] = true
invocation_blocks[name] = block if block_given?
class_eval <<-METHOD, __FILE__, __LINE__
def _invoke_from_option_#{name.to_s.gsub(/\W/, "_")}
return unless options[#{name.inspect}]
value = options[#{name.inspect}]
value = #{name.inspect} if TrueClass === value
klass, command = self.class.prepare_for_invocatio...
if klass
say_status :invoke, value, #{verbose.inspect}
block = self.class.invocation_blocks[#{name.ins...
_invoke_for_class_method klass, command, &block
else
say_status :error, %(\#{value} [not found]), :red
end
end
METHOD
end
end
}}
一目見ただけで邪悪さ120%なコードです。このメソッドが実行...
prepare_for_invocationはnameで指定されたものに対応するク...
#code(Ruby){{
def self.prepare_for_invocation(name, value) #:nodoc:
return super unless value.is_a?(String) || value.is_a?(...
if value && constants = self.hooks[name]
value = name if TrueClass === value
Rails::Generators.find_by_namespace(value, *constants)
elsif klass = Rails::Generators.find_by_namespace(value)
klass
else
super
end
end
}}
ifは一番上が実行されます。まあともかくscaffold_controller...
*orm [#o94b4b7b]
ところでここでrails generate scaffoldの出力を見てみましょ...
rails generate scaffold user name:string email:string
invoke active_record
create db/migrate/20140809033025_create_users.rb
create app/models/user.rb
invoke test_unit
create test/models/user_test.rb
create test/fixtures/users.yml
invoke resource_route
route resources :users
invoke scaffold_controller
create app/controllers/users_controller.rb
invoke erb
create app/views/users
create app/views/users/index.html.erb
create app/views/users/edit.html.erb
create app/views/users/show.html.erb
create app/views/users/new.html.erb
create app/views/users/_form.html.erb
invoke test_unit
create test/controllers/users_controller_test...
invoke helper
create app/helpers/users_helper.rb
invoke test_unit
create test/helpers/users_helper_test.rb
invoke jbuilder
create app/views/users/index.json.jbuilder
create app/views/users/show.json.jbuilder
invoke assets
invoke coffee
create app/assets/javascripts/users.js.coffee
invoke scss
create app/assets/stylesheets/users.css.scss
invoke scss
create app/assets/stylesheets/scaffolds.css.scss
invokeというのはgenerator呼んでいるということでしょう。ま...
さて、上記の出力結果とScaffoldGenerator、並びにスーパーク...
#code(Ruby){{
class ScaffoldGenerator < ResourceGenerator # :nodoc:
remove_hook_for :resource_controller
hook_for :scaffold_controller, required: true
hook_for :assets do |assets|
invoke assets, [controller_name]
end
hook_for :stylesheet_engine do |stylesheet_engine|
if behavior == :invoke
invoke stylesheet_engine, [controller_name]
end
end
class ResourceGenerator < ModelGenerator # :nodoc:
hook_for :resource_controller, required: true do |contr...
invoke controller, [ controller_name, options[:action...
end
hook_for :resource_route, required: true
class ModelGenerator < NamedBase # :nodoc:
hook_for :orm, required: true
}}
で、hook_for :ormと書いてあるのになんでactive_recordが呼...
とりあえずormでgrepかけるとrails/engine.rbに以下のコメン...
#code(Ruby){{
# == Generators
#
# You can set up generators for engines with <tt>config.g...
#
# class MyEngine < Rails::Engine
# config.generators do |g|
# g.orm :active_record
# g.template_engine :erb
# g.test_framework :test_unit
# end
# end
#
# You can also set generators for an application by using...
#
# class MyEngine < Rails::Engine
# # note that you can also pass block to app_generato...
# # can pass it to generators method
# config.app_generators.orm :datamapper
# end
}}
普通に考えるとormのgeneratorとしてactive_recordが設定され...
gems全体をgrepすると以下のコードが引っ掛かります。
activerecord/lib/active_record/railtie.rb
#code(Ruby){{
module ActiveRecord
# = Active Record Railtie
class Railtie < Rails::Railtie # :nodoc:
config.active_record = ActiveSupport::OrderedOptions....
config.app_generators.orm :active_record, :migration ...
:timestamps...
}}
見つかりました。ちなみにこのファイルは前半読み飛ばした以...
railties/lib/rails/commands/commands_tasks
#code(Ruby){{
module Rails
class CommandsTasks # :nodoc:
def generate_or_destroy(command)
require 'rails/generators'
require_application_and_environment!
Rails.application.load_generators
require "rails/commands/#{command}"
end
def require_application_and_environment!
require APP_PATH
Rails.application.require_environment!
end
}}
<APP_DIR>/bin/rails
#code(Ruby){{
APP_PATH = File.expand_path('../../config/application', ...
}}
<APP_DIR>/config/applications
#code(Ruby){{
require 'rails/all'
}}
railties/lib/rails/all.rb
#code(Ruby){{
require "rails"
%w(
active_record
action_controller
action_view
action_mailer
rails/test_unit
sprockets
).each do |framework|
begin
require "#{framework}/railtie"
rescue LoadError
end
end
}}
今まで気にせずにrsiltieという単語を使ってきましたが、rail...
#code(Ruby){{
# Railtie is the core of the Rails framework and provides...
# Rails and/or modify the initialization process.
#
# Every major component of Rails (Action Mailer, Action C...
# Action View and Active Record) is a Railtie. Each of
# them is responsible for their own initialization. This ...
# absent of any component hooks, allowing other component...
# place of any of the Rails defaults.
}}
かなり長くなってきましたがまだ続きます。
railties/lib/rails/railtie.rb
#code(Ruby){{
module Rails
class Railtie
class << self
delegate :config, to: :instance
def instance
@instance ||= new
end
def config
@config ||= Railtie::Configuration.new
end
}}
railties/lib/rails/railtie/configuration.rb
#code(Ruby){{
module Rails
class Railtie
class Configuration
def app_generators
@@app_generators ||= Rails::Configuration::Genera...
yield(@@app_generators) if block_given?
@@app_generators
end
}}
ralties/lib/rails/configuration.rb
#code(Ruby)){{
module Rails
module Configuration
class Generators #:nodoc:
}}
さてと、Rails::Configuration::Generatorsまで来ましたが、o...
#code(Ruby){{
def method_missing(method, *args)
method = method.to_s.sub(/=$/, '').to_sym
return @options[method] if args.empty?
if method == :rails || args.first.is_a?(Hash)
namespace, configuration = method, args.shift
else
namespace, configuration = args.shift, args.shift
namespace = namespace.to_sym if namespace.respond_to?...
@options[:rails][method] = namespace
end
if configuration
aliases = configuration.delete(:aliases)
@aliases[namespace].merge!(aliases) if aliases
@options[namespace].merge!(configuration)
end
end
}}
というわけで、@options[:rails][:orm]として:active_record...
これで、ormに対してactive_recordが使われるようになりまし...
*おわりに [#c43367ae]
というわけでrails generate時の動作を見てきました。今回も...
ともかくこれでファイルの生成は終わりました。次回は「rake ...
終了行:
[[Ruby on Rails4を読む]]
#contents
*はじめに [#n73ec655]
前回はrails new時の挙動について見てきました。railsコマン...
rails generate scaffold user name:string email:string
のように使用します。今回はこの場合にどういう処理が行われ...
*Rails::AppRailsLoader (railties/lib/rails/app_rails_load...
さて、前回も見た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
}}
前回は下部のrails/commands/application.rbに進みました。今...
#code(Ruby){{
module Rails
module AppRailsLoader
RUBY = Gem.ruby
EXECUTABLES = ['bin/rails', 'script/rails']
def self.exec_app_rails
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 stu...
elsif exe.end_with?('bin/rails') && contents.in...
$stderr.puts(BUNDLER_WARNING)
Object.const_set(:APP_PATH, File.expand_path(...
require File.expand_path('../boot', APP_PATH)
require 'rails/commands'
break
end
end
# If we exhaust the search there is no executable...
# call to generate a new application, so restore ...
Dir.chdir(original_cwd) and return if Pathname.ne...
# Otherwise keep moving upwards in search of an e...
Dir.chdir('..')
end
end
def self.find_executable
EXECUTABLES.find { |exe| File.file?(exe) }
end
}}
何しているかというと、アプリケーションディレクトリのbin/r...
#code(Ruby){{
#!/usr/bin/env ruby
APP_PATH = File.expand_path('../../config/application', ...
require_relative '../config/boot'
require 'rails/commands'
}}
*railties/lib/rails/commands.rb [#vc7d5786]
全部見ると長くなるのでrails/commandsに進みます。
#code(Ruby){{
ARGV << '--help' if ARGV.empty?
aliases = {
"g" => "generate",
"d" => "destroy",
"c" => "console",
"s" => "server",
"db" => "dbconsole",
"r" => "runner"
}
command = ARGV.shift
command = aliases[command] || command
require 'rails/commands/commands_tasks'
Rails::CommandsTasks.new(ARGV).run_command!(command)
}}
commands_tasks.rbへ。
#code(Ruby){{
module Rails
class CommandsTasks # :nodoc:
def run_command!(command)
command = parse_command(command)
if COMMAND_WHITELIST.include?(command)
send(command)
else
write_error_message(command)
end
end
def generate
generate_or_destroy(:generate)
end
def generate_or_destroy(command)
require 'rails/generators'
require_application_and_environment!
Rails.application.load_generators
require "rails/commands/#{command}"
end
}}
メソッド呼び出しとかrequireとかしてて知らぬ間にいろいろ設...
railties/lib/rails/commands/generate.rb
#code(Ruby){{
require 'rails/generators'
if [nil, "-h", "--help"].include?(ARGV.first)
Rails::Generators.help 'generate'
exit
end
name = ARGV.shift
root = defined?(ENGINE_ROOT) ? ENGINE_ROOT : Rails.root
Rails::Generators.invoke name, ARGV, behavior: :invoke, d...
}}
忘れかけているので確認。この時点でのARGVは
scaffold user name:string email:string
です。
invokeメソッドに進む。
railties/lib/rails/generators.rb
#code(Ruby){{
def self.invoke(namespace, args=ARGV, config={})
names = namespace.to_s.split(':')
if klass = find_by_namespace(names.pop, names.any? && n...
args << "--help" if args.empty? && klass.arguments.an...
klass.start(args, config)
else
puts "Could not find generator #{namespace}."
end
end
}}
*find_by_namespace (railties/lib/rails/generators.rb) [#q...
そろそろ見出しを変えます。与えられたname(コマンド。names...
#code(Ruby){{
def self.find_by_namespace(name, base=nil, context=nil) #...
lookups = []
lookups << "#{base}:#{name}" if base
lookups << "#{name}:#{context}" if context
unless base || context
unless name.to_s.include?(?:)
lookups << "#{name}:#{name}"
lookups << "rails:#{name}"
end
lookups << "#{name}"
end
lookup(lookups)
namespaces = Hash[subclasses.map { |klass| [klass.names...
lookups.each do |namespace|
klass = namespaces[namespace]
return klass if klass
end
invoke_fallbacks_for(name, base) || invoke_fallbacks_fo...
end
}}
loookupsは["scaffold/scaffold", "rails/scaffold", "scaffo...
#code(Ruby){{
def self.lookup(namespaces) #:nodoc:
paths = namespaces_to_paths(namespaces)
paths.each do |raw_path|
["rails/generators", "generators"].each do |base|
path = "#{base}/#{raw_path}_generator"
begin
require path
return
rescue LoadError => e
raise unless e.message =~ /#{Regexp.escape(path)}$/
rescue Exception => e
warn "[WARNING] Could not load generator #{path.i...
end
end
end
end
def self.namespaces_to_paths(namespaces) #:nodoc:
paths = []
namespaces.each do |namespace|
pieces = namespace.split(":")
paths << pieces.dup.push(pieces.last).join("/")
paths << pieces.join("/")
end
paths.uniq!
paths
end
}}
最終的に、rails/generators/rails/scaffold/scaffold_genera...
*railties/lib/rails/generators/rails/scaffold/scaffold_ge...
#code(Ruby){{
require 'rails/generators/rails/resource/resource_generat...
module Rails
module Generators
class ScaffoldGenerator < ResourceGenerator # :nodoc:
}}
rails/generators/rails/resource/resource_generator.rb
#code(Ruby){{
module Rails
module Generators
class ResourceGenerator < ModelGenerator # :nodoc:
}}
rails/generators/rails/model/model_generator.rb
#code(Ruby){{
module Rails
module Generators
class ModelGenerator < NamedBase # :nodoc:
argument :attributes, type: :array, default: [], ba...
}}
rails/generators/named_base.rb
#code(Ruby){{
module Rails
module Generators
class NamedBase < Base
argument :name, type: :string
}}
というわけで、前回見たRails::Generator::Baseまで来ました...
name: "user"
arguments: ["name:string", "email:string"]
が設定されます。
さてでは次に実行されるコマンドを・・・と見てみてもそれっ...
railties/lib/rails/generators/rails/scaffold/scaffold_gen...
#code(Ruby){{
module Rails
module Generators
class ScaffoldGenerator < ResourceGenerator # :nodoc:
hook_for :scaffold_controller, required: true
}}
rails/generators/rails/scaffold_controller/template/contr...
*hook_for (railties/lib/rails/generator/base.rb) [#m8d60d...
hook_forはRails::Generator::Baseに定義されています。
#code(Ruby){{
def self.hook_for(*names, &block)
options = names.extract_options!
in_base = options.delete(:in) || base_name
as_hook = options.delete(:as) || generator_name
names.each do |name|
unless class_options.key?(name)
defaults = if options[:type] == :boolean
{ }
elsif [true, false].include?(default_value_for_opti...
{ banner: "" }
else
{ desc: "#{name.to_s.humanize} to be invoked", ba...
end
class_option(name, defaults.merge!(options))
end
hooks[name] = [ in_base, as_hook ]
invoke_from_option(name, options, &block)
end
end
def self.base_name
@base_name ||= begin
if base = name.to_s.split('::').first
base.underscore
end
end
end
def self.generator_name
@generator_name ||= begin
if generator = name.to_s.split('::').last
generator.sub!(/Generator$/, '')
generator.underscore
end
end
end
}}
base_name、generator_nameで使われているnameはクラス名を表...
invoke_from_optionはThor::Groupで定義されています。
#code(Ruby){{
def invoke_from_option(*names, &block) # rubocop:disable ...
options = names.last.is_a?(Hash) ? names.pop : {}
verbose = options.fetch(:verbose, :white)
names.each do |name|
unless class_options.key?(name)
fail ArgumentError, "You have to define the option ...
"before setting invoke_from_op...
end
invocations[name] = true
invocation_blocks[name] = block if block_given?
class_eval <<-METHOD, __FILE__, __LINE__
def _invoke_from_option_#{name.to_s.gsub(/\W/, "_")}
return unless options[#{name.inspect}]
value = options[#{name.inspect}]
value = #{name.inspect} if TrueClass === value
klass, command = self.class.prepare_for_invocatio...
if klass
say_status :invoke, value, #{verbose.inspect}
block = self.class.invocation_blocks[#{name.ins...
_invoke_for_class_method klass, command, &block
else
say_status :error, %(\#{value} [not found]), :red
end
end
METHOD
end
end
}}
一目見ただけで邪悪さ120%なコードです。このメソッドが実行...
prepare_for_invocationはnameで指定されたものに対応するク...
#code(Ruby){{
def self.prepare_for_invocation(name, value) #:nodoc:
return super unless value.is_a?(String) || value.is_a?(...
if value && constants = self.hooks[name]
value = name if TrueClass === value
Rails::Generators.find_by_namespace(value, *constants)
elsif klass = Rails::Generators.find_by_namespace(value)
klass
else
super
end
end
}}
ifは一番上が実行されます。まあともかくscaffold_controller...
*orm [#o94b4b7b]
ところでここでrails generate scaffoldの出力を見てみましょ...
rails generate scaffold user name:string email:string
invoke active_record
create db/migrate/20140809033025_create_users.rb
create app/models/user.rb
invoke test_unit
create test/models/user_test.rb
create test/fixtures/users.yml
invoke resource_route
route resources :users
invoke scaffold_controller
create app/controllers/users_controller.rb
invoke erb
create app/views/users
create app/views/users/index.html.erb
create app/views/users/edit.html.erb
create app/views/users/show.html.erb
create app/views/users/new.html.erb
create app/views/users/_form.html.erb
invoke test_unit
create test/controllers/users_controller_test...
invoke helper
create app/helpers/users_helper.rb
invoke test_unit
create test/helpers/users_helper_test.rb
invoke jbuilder
create app/views/users/index.json.jbuilder
create app/views/users/show.json.jbuilder
invoke assets
invoke coffee
create app/assets/javascripts/users.js.coffee
invoke scss
create app/assets/stylesheets/users.css.scss
invoke scss
create app/assets/stylesheets/scaffolds.css.scss
invokeというのはgenerator呼んでいるということでしょう。ま...
さて、上記の出力結果とScaffoldGenerator、並びにスーパーク...
#code(Ruby){{
class ScaffoldGenerator < ResourceGenerator # :nodoc:
remove_hook_for :resource_controller
hook_for :scaffold_controller, required: true
hook_for :assets do |assets|
invoke assets, [controller_name]
end
hook_for :stylesheet_engine do |stylesheet_engine|
if behavior == :invoke
invoke stylesheet_engine, [controller_name]
end
end
class ResourceGenerator < ModelGenerator # :nodoc:
hook_for :resource_controller, required: true do |contr...
invoke controller, [ controller_name, options[:action...
end
hook_for :resource_route, required: true
class ModelGenerator < NamedBase # :nodoc:
hook_for :orm, required: true
}}
で、hook_for :ormと書いてあるのになんでactive_recordが呼...
とりあえずormでgrepかけるとrails/engine.rbに以下のコメン...
#code(Ruby){{
# == Generators
#
# You can set up generators for engines with <tt>config.g...
#
# class MyEngine < Rails::Engine
# config.generators do |g|
# g.orm :active_record
# g.template_engine :erb
# g.test_framework :test_unit
# end
# end
#
# You can also set generators for an application by using...
#
# class MyEngine < Rails::Engine
# # note that you can also pass block to app_generato...
# # can pass it to generators method
# config.app_generators.orm :datamapper
# end
}}
普通に考えるとormのgeneratorとしてactive_recordが設定され...
gems全体をgrepすると以下のコードが引っ掛かります。
activerecord/lib/active_record/railtie.rb
#code(Ruby){{
module ActiveRecord
# = Active Record Railtie
class Railtie < Rails::Railtie # :nodoc:
config.active_record = ActiveSupport::OrderedOptions....
config.app_generators.orm :active_record, :migration ...
:timestamps...
}}
見つかりました。ちなみにこのファイルは前半読み飛ばした以...
railties/lib/rails/commands/commands_tasks
#code(Ruby){{
module Rails
class CommandsTasks # :nodoc:
def generate_or_destroy(command)
require 'rails/generators'
require_application_and_environment!
Rails.application.load_generators
require "rails/commands/#{command}"
end
def require_application_and_environment!
require APP_PATH
Rails.application.require_environment!
end
}}
<APP_DIR>/bin/rails
#code(Ruby){{
APP_PATH = File.expand_path('../../config/application', ...
}}
<APP_DIR>/config/applications
#code(Ruby){{
require 'rails/all'
}}
railties/lib/rails/all.rb
#code(Ruby){{
require "rails"
%w(
active_record
action_controller
action_view
action_mailer
rails/test_unit
sprockets
).each do |framework|
begin
require "#{framework}/railtie"
rescue LoadError
end
end
}}
今まで気にせずにrsiltieという単語を使ってきましたが、rail...
#code(Ruby){{
# Railtie is the core of the Rails framework and provides...
# Rails and/or modify the initialization process.
#
# Every major component of Rails (Action Mailer, Action C...
# Action View and Active Record) is a Railtie. Each of
# them is responsible for their own initialization. This ...
# absent of any component hooks, allowing other component...
# place of any of the Rails defaults.
}}
かなり長くなってきましたがまだ続きます。
railties/lib/rails/railtie.rb
#code(Ruby){{
module Rails
class Railtie
class << self
delegate :config, to: :instance
def instance
@instance ||= new
end
def config
@config ||= Railtie::Configuration.new
end
}}
railties/lib/rails/railtie/configuration.rb
#code(Ruby){{
module Rails
class Railtie
class Configuration
def app_generators
@@app_generators ||= Rails::Configuration::Genera...
yield(@@app_generators) if block_given?
@@app_generators
end
}}
ralties/lib/rails/configuration.rb
#code(Ruby)){{
module Rails
module Configuration
class Generators #:nodoc:
}}
さてと、Rails::Configuration::Generatorsまで来ましたが、o...
#code(Ruby){{
def method_missing(method, *args)
method = method.to_s.sub(/=$/, '').to_sym
return @options[method] if args.empty?
if method == :rails || args.first.is_a?(Hash)
namespace, configuration = method, args.shift
else
namespace, configuration = args.shift, args.shift
namespace = namespace.to_sym if namespace.respond_to?...
@options[:rails][method] = namespace
end
if configuration
aliases = configuration.delete(:aliases)
@aliases[namespace].merge!(aliases) if aliases
@options[namespace].merge!(configuration)
end
end
}}
というわけで、@options[:rails][:orm]として:active_record...
これで、ormに対してactive_recordが使われるようになりまし...
*おわりに [#c43367ae]
というわけでrails generate時の動作を見てきました。今回も...
ともかくこれでファイルの生成は終わりました。次回は「rake ...
ページ名: