想知道rails stats干什么,于是检查下ARGV会在哪里被调用,修改bin/rails如下

class << ARGV
  instance_methods.each do |m|
    old_m = instance_method m
    define_method m do |*args, &blk|
      puts "#{m} #{caller[0]}"
      old_m.bind(ARGV).call *args, &blk
    end
  end
end


没看出什么,再检查stats这个字符在哪被调用

(因ARGV内的字符串都被freeze了,无法重定义单例实例方法,所以另给stats字符串塞进ARGV)

ARGV[0] = "stats"

class << ARGV[0]
  instance_methods.each do |m|
    old_m = instance_method m
    define_method m do |*args, &blk|
      puts "#{m} #{caller[0]}"
      old_m.bind(ARGV[0]).call *args, &blk
    end
  end
end

require 'rails/commands'


还是没看出什么

但细看下stats的输出,猜测可直接在源码中查找“Code LOC”在哪里出现

+----------------------+--------+--------+---------+---------+-----+-------+
| Name                 |  Lines |    LOC | Classes | Methods | M/C | LOC/M |
+----------------------+--------+--------+---------+---------+-----+-------+
| Controllers          |    467 |    330 |       9 |      54 |   6 |     4 |
| Helpers              |     21 |     21 |       0 |       1 |   0 |    19 |
| Jobs                 |      2 |      2 |       1 |       0 |   0 |     0 |
| Models               |     71 |     63 |       6 |       5 |   0 |    10 |
| Mailers              |      4 |      4 |       1 |       0 |   0 |     0 |
| Channels             |      8 |      8 |       2 |       0 |   0 |     0 |
| Javascripts          |     54 |      4 |       0 |       1 |   0 |     2 |
| Libraries            |      0 |      0 |       0 |       0 |   0 |     0 |
| Tasks                |      0 |      0 |       0 |       0 |   0 |     0 |
| Controller tests     |    331 |    256 |       8 |      43 |   5 |     3 |
| Helper tests         |      0 |      0 |       0 |       0 |   0 |     0 |
| Model tests          |     66 |     41 |       5 |       3 |   0 |    11 |
| Mailer tests         |      0 |      0 |       0 |       0 |   0 |     0 |
| Integration tests    |      0 |      0 |       0 |       0 |   0 |     0 |
+----------------------+--------+--------+---------+---------+-----+-------+
| Total                |   1024 |    729 |      32 |     107 |   3 |     4 |
+----------------------+--------+--------+---------+---------+-----+-------+
  Code LOC: 432     Test LOC: 297     Code to Test Ratio: 1:0.7


于是

$ gems git:(master) grep 'Code LOC' -rn *
railties-5.0.2/lib/rails/code_statistics.rb:110:      puts "  Code LOC: #{code}     Test LOC: #{tests}     Code to Test Ratio: 1:#{sprintf("%.1f", tests.to_f/code)}"


看上去应该就是这里,于是加入caller,检查哪里会生成这个CodeStatistics

class CodeStatistics
  def initialize(*pairs)
    puts caller
    @pairs      = pairs
    @statistics = calculate_statistics
    @total      = calculate_total if pairs.length > 1
  end
end


出来的调用栈如下

/home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/railties-5.0.2/lib/rails/tasks/statistics.rake:28:in `new'
/home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/railties-5.0.2/lib/rails/tasks/statistics.rake:28:in `block in <top (required)="">'
/home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/rake-12.0.0/lib/rake/task.rb:250:in `block in execute'
/home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/rake-12.0.0/lib/rake/task.rb:250:in `each'
/home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/rake-12.0.0/lib/rake/task.rb:250:in `execute'
/home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/rake-12.0.0/lib/rake/task.rb:194:in `block in invoke_with_call_chain'
/home/z/.rbenv/versions/2.4.0/lib/ruby/2.4.0/monitor.rb:214:in `mon_synchronize'
/home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/rake-12.0.0/lib/rake/task.rb:187:in `invoke_with_call_chain'
/home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/rake-12.0.0/lib/rake/task.rb:180:in `invoke'
/home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/rake-12.0.0/lib/rake/application.rb:152:in `invoke_task'
/home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/rake-12.0.0/lib/rake/application.rb:108:in `block (2 levels) in top_level'
/home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/rake-12.0.0/lib/rake/application.rb:108:in `each'
/home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/rake-12.0.0/lib/rake/application.rb:108:in `block in top_level'
/home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/rake-12.0.0/lib/rake/application.rb:117:in `run_with_threads'
/home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/rake-12.0.0/lib/rake/application.rb:102:in `top_level'
/home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/railties-5.0.2/lib/rails/commands/rake_proxy.rb:14:in `block in run_rake_task'
/home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/rake-12.0.0/lib/rake/application.rb:178:in `standard_exception_handling'
/home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/railties-5.0.2/lib/rails/commands/rake_proxy.rb:11:in `run_rake_task'
/home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/railties-5.0.2/lib/rails/commands/commands_tasks.rb:51:in `run_command!'
/home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/railties-5.0.2/lib/rails/commands.rb:18:in `<top (required)="">'
/home/z/test_rails/depot/bin/rails:25:in `require'
/home/z/test_rails/depot/bin/rails:25:in `<top (required)="">'
/home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/spring-2.0.1/lib/spring/client/rails.rb:28:in `load'
/home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/spring-2.0.1/lib/spring/client/rails.rb:28:in `call'
/home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/spring-2.0.1/lib/spring/client/command.rb:7:in `call'
/home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/spring-2.0.1/lib/spring/client.rb:30:in `run'
/home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/spring-2.0.1/bin/spring:49:in `<top (required)="">'
/home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/spring-2.0.1/lib/spring/binstub.rb:31:in `load'
/home/z/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/spring-2.0.1/lib/spring/binstub.rb:31:in `<top (required)="">'
/home/z/.rbenv/versions/2.4.0/lib/ruby/2.4.0/rubygems/core_ext/kernel_require.rb:68:in `require'
/home/z/.rbenv/versions/2.4.0/lib/ruby/2.4.0/rubygems/core_ext/kernel_require.rb:68:in `require'
/home/z/test_rails/depot/bin/spring:15:in `<top (required)="">'
bin/rails:3:in `load'
bin/rails:3:in `</top></top></top></top></top></top>
'


而railties-5.0.2/lib/rails/tasks/statistics.rake是这样的

STATS_DIRECTORIES = [
  %w(Controllers        app/controllers),
  %w(Helpers            app/helpers),
  %w(Jobs               app/jobs),
  %w(Models             app/models),
  %w(Mailers            app/mailers),
  %w(Channels           app/channels),
  %w(Javascripts        app/assets/javascripts),
  %w(Libraries          lib/),
  %w(Tasks              lib/tasks),
  %w(APIs               app/apis),
  %w(Controller\ tests  test/controllers),
  %w(Helper\ tests      test/helpers),
  %w(Model\ tests       test/models),
  %w(Mailer\ tests      test/mailers),
  %w(Job\ tests         test/jobs),
  %w(Integration\ tests test/integration),
].collect do |name, dir|
  [ name, "#{File.dirname(Rake.application.rakefile_location)}/#{dir}" ]
end.select { |name, dir| File.directory?(dir) }

desc "Report code statistics (KLOCs, etc) from the application or engine"
task :stats do
  require 'rails/code_statistics'
  CodeStatistics.new(*STATS_DIRECTORIES).to_s
end


同时,可发现rails还定义了以下task

$ gems git:(master) tree railties-5.0.2/lib/rails/tasks/
railties-5.0.2/lib/rails/tasks/
├── annotations.rake
├── dev.rake
├── engine.rake
├── framework.rake
├── initializers.rake
├── log.rake
├── middleware.rake
├── misc.rake
├── restart.rake
├── routes.rake
├── statistics.rake
└── tmp.rake