-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathinitialization.html
813 lines (714 loc) · 39.3 KB
/
initialization.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="zh-CN" lang="zh-CN">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>Rails 应用的初始化过程 — Ruby on Rails 指南</title>
<link rel="stylesheet" type="text/css" href="stylesheets/style.css" />
<link rel="stylesheet" type="text/css" href="stylesheets/print.css" media="print" />
<link rel="stylesheet" type="text/css" href="stylesheets/syntaxhighlighter/shCore.css" />
<link rel="stylesheet" type="text/css" href="stylesheets/syntaxhighlighter/shThemeRailsGuides.css" />
<link rel="stylesheet" type="text/css" href="stylesheets/fixes.css" />
<link href="images/favicon.ico" rel="shortcut icon" type="image/x-icon" />
</head>
<body class="guide">
<div id="topNav">
<div class="wrapper">
<strong class="more-info-label">更多内容 <a href="http://rubyonrails.org/">rubyonrails.org:</a> </strong>
<span class="red-button more-info-button">
更多内容
</span>
<ul class="more-info-links s-hidden">
<li class="more-info"><a href="http://rubyonrails.org/">综览</a></li>
<li class="more-info"><a href="http://rubyonrails.org/download">下载</a></li>
<li class="more-info"><a href="http://rubyonrails.org/deploy">部署</a></li>
<li class="more-info"><a href="https://github.com/rails/rails">源码</a></li>
<li class="more-info"><a href="http://rubyonrails.org/screencasts">视频</a></li>
<li class="more-info"><a href="http://rubyonrails.org/documentation">文件</a></li>
<li class="more-info"><a href="http://rubyonrails.org/community">社群</a></li>
<li class="more-info"><a href="http://weblog.rubyonrails.org/">Blog</a></li>
</ul>
</div>
</div>
<div id="header">
<div class="wrapper clearfix">
<h1><a href="index.html" title="回首页">Guides.rubyonrails.org</a></h1>
<ul class="nav">
<li><a class="nav-item" href="index.html">首页</a></li>
<li class="guides-index guides-index-large">
<a href="index.html" id="guidesMenu" class="guides-index-item nav-item">指南目录</a>
<div id="guides" class="clearfix" style="display: none;">
<hr />
<dl class="L">
<dt>入门</dt>
<dd><a href="getting_started.html">Rails 入门</a></dd>
<dt>模型</dt>
<dd><a href="active_record_basics.html">Active Record 基础</a></dd>
<dd><a href="active_record_migrations.html">Active Record 数据库迁移</a></dd>
<dd><a href="active_record_validations.html">Active Record 数据验证</a></dd>
<dd><a href="active_record_callbacks.html">Active Record 回调</a></dd>
<dd><a href="association_basics.html">Active Record 关联</a></dd>
<dd><a href="active_record_querying.html">Active Record 查询</a></dd>
<dt>视图</dt>
<dd><a href="layouts_and_rendering.html">Rails 布局和视图渲染</a></dd>
<dd><a href="form_helpers.html">Action View 表单帮助方法</a></dd>
<dt>控制器</dt>
<dd><a href="action_controller_overview.html">Action Controller 简介</a></dd>
<dd><a href="routing.html">Rails 路由全解</a></dd>
</dl>
<dl class="R">
<dt>深入</dt>
<dd><a href="active_support_core_extensions.html">Active Support 核心扩展</a></dd>
<dd><a href="i18n.html">Rails 国际化 API</a></dd>
<dd><a href="action_mailer_basics.html">Action Mailer 基础</a></dd>
<dd><a href="active_job_basics.html">Active Job 基础</a></dd>
<dd><a href="security.html">Rails 安全指南</a></dd>
<dd><a href="debugging_rails_applications.html">调试 Rails 程序</a></dd>
<dd><a href="configuring.html">设置 Rails 程序</a></dd>
<dd><a href="command_line.html">Rails 命令行</a></dd>
<dd><a href="asset_pipeline.html">Asset Pipeline</a></dd>
<dd><a href="working_with_javascript_in_rails.html">在 Rails 中使用 JavaScript</a></dd>
<dd><a href="constant_autoloading_and_reloading.html">Constant Autoloading and Reloading</a></dd>
<dt>扩展 Rails</dt>
<dd><a href="rails_on_rack.html">Rails on Rack</a></dd>
<dd><a href="generators.html">客制与新建 Rails 产生器</a></dd>
<dd><a href="rails_application_templates.html">Rails 应用程式模版</a></dd>
<dt>贡献 Ruby on Rails</dt>
<dd><a href="contributing_to_ruby_on_rails.html">贡献 Ruby on Rails</a></dd>
<dd><a href="api_documentation_guidelines.html">API 文件准则</a></dd>
<dd><a href="ruby_on_rails_guides_guidelines.html">Ruby on Rails 指南准则</a></dd>
<dt>维护方针</dt>
<dd><a href="maintenance_policy.html">维护方针</a></dd>
<dt>发布记</dt>
<dd><a href="upgrading_ruby_on_rails.html">升级 Ruby on Rails</a></dd>
<dd><a href="4_2_release_notes.html">Ruby on Rails 4.2 发布记</a></dd>
<dd><a href="4_1_release_notes.html">Ruby on Rails 4.1 发布记</a></dd>
<dd><a href="4_0_release_notes.html">Ruby on Rails 4.0 发布记</a></dd>
<dd><a href="3_2_release_notes.html">Ruby on Rails 3.2 发布记</a></dd>
<dd><a href="3_1_release_notes.html">Ruby on Rails 3.1 发布记</a></dd>
<dd><a href="3_0_release_notes.html">Ruby on Rails 3.0 发布记</a></dd>
<dd><a href="2_3_release_notes.html">Ruby on Rails 2.3 发布记</a></dd>
<dd><a href="2_2_release_notes.html">Ruby on Rails 2.2 发布记</a></dd>
</dl>
</div>
</li>
<!-- <li><a class="nav-item" href="//github.com/docrails-tw/wiki">参与翻译</a></li> -->
<li><a class="nav-item" href="https://github.com/ruby-china/guides/blob/master/CONTRIBUTING.md">贡献</a></li>
<li><a class="nav-item" href="credits.html">致谢</a></li>
<li class="guides-index guides-index-small">
<select class="guides-index-item nav-item">
<option value="index.html">指南目录</option>
<optgroup label="入门">
<option value="getting_started.html">Rails 入门</option>
</optgroup>
<optgroup label="模型">
<option value="active_record_basics.html">Active Record 基础</option>
<option value="active_record_migrations.html">Active Record 数据库迁移</option>
<option value="active_record_validations.html">Active Record 数据验证</option>
<option value="active_record_callbacks.html">Active Record 回调</option>
<option value="association_basics.html">Active Record 关联</option>
<option value="active_record_querying.html">Active Record 查询</option>
</optgroup>
<optgroup label="视图">
<option value="layouts_and_rendering.html">Rails 布局和视图渲染</option>
<option value="form_helpers.html">Action View 表单帮助方法</option>
</optgroup>
<optgroup label="控制器">
<option value="action_controller_overview.html">Action Controller 简介</option>
<option value="routing.html">Rails 路由全解</option>
</optgroup>
<optgroup label="深入">
<option value="active_support_core_extensions.html">Active Support 核心扩展</option>
<option value="i18n.html">Rails 国际化 API</option>
<option value="action_mailer_basics.html">Action Mailer 基础</option>
<option value="active_job_basics.html">Active Job 基础</option>
<option value="security.html">Rails 安全指南</option>
<option value="debugging_rails_applications.html">调试 Rails 程序</option>
<option value="configuring.html">设置 Rails 程序</option>
<option value="command_line.html">Rails 命令行</option>
<option value="asset_pipeline.html">Asset Pipeline</option>
<option value="working_with_javascript_in_rails.html">在 Rails 中使用 JavaScript</option>
<option value="constant_autoloading_and_reloading.html">Constant Autoloading and Reloading</option>
</optgroup>
<optgroup label="扩展 Rails">
<option value="rails_on_rack.html">Rails on Rack</option>
<option value="generators.html">客制与新建 Rails 产生器</option>
<option value="rails_application_templates.html">Rails 应用程式模版</option>
</optgroup>
<optgroup label="贡献 Ruby on Rails">
<option value="contributing_to_ruby_on_rails.html">贡献 Ruby on Rails</option>
<option value="api_documentation_guidelines.html">API 文件准则</option>
<option value="ruby_on_rails_guides_guidelines.html">Ruby on Rails 指南准则</option>
</optgroup>
<optgroup label="维护方针">
<option value="maintenance_policy.html">维护方针</option>
</optgroup>
<optgroup label="发布记">
<option value="upgrading_ruby_on_rails.html">升级 Ruby on Rails</option>
<option value="4_2_release_notes.html">Ruby on Rails 4.2 发布记</option>
<option value="4_1_release_notes.html">Ruby on Rails 4.1 发布记</option>
<option value="4_0_release_notes.html">Ruby on Rails 4.0 发布记</option>
<option value="3_2_release_notes.html">Ruby on Rails 3.2 发布记</option>
<option value="3_1_release_notes.html">Ruby on Rails 3.1 发布记</option>
<option value="3_0_release_notes.html">Ruby on Rails 3.0 发布记</option>
<option value="2_3_release_notes.html">Ruby on Rails 2.3 发布记</option>
<option value="2_2_release_notes.html">Ruby on Rails 2.2 发布记</option>
</optgroup>
</select>
</li>
</ul>
</div>
</div>
</div>
<hr class="hide" />
<div id="feature">
<div class="wrapper">
<h2>Rails 应用的初始化过程</h2><p>本章节介绍了 Rails 4 应用启动的内部流程,适合有一定经验的Rails应用开发者阅读。</p><p>通过学习本章节,您会学到如下知识:</p>
<ul>
<li>如何使用 <code>rails server</code>;</li>
<li>Rails应用初始化的时间序列;</li>
<li>Rails应用启动过程都用到哪些文件;</li>
<li>Rails::Server接口的定义和使用;</li>
</ul>
<div id="subCol">
<h3 class="chapter"><img src="images/chapters_icon.gif" alt="" />Chapters</h3>
<ol class="chapters">
<li>
<a href="#%E5%90%AF-%E5%8A%A8--bang">启 动 !</a>
<ul>
<li><a href="#railties/bin/rails"><code>railties/bin/rails</code></a></li>
<li><a href="#railties/lib/rails/app_rails_loader.rb"><code>railties/lib/rails/app_rails_loader.rb</code></a></li>
<li><a href="#bin/rails"><code>bin/rails</code></a></li>
<li><a href="#config/boot.rb"><code>config/boot.rb</code></a></li>
<li><a href="#rails/commands.rb"><code>rails/commands.rb</code></a></li>
<li><a href="#rails/commands/command_tasks.rb"><code>rails/commands/command_tasks.rb</code></a></li>
<li><a href="#actionpack/lib/action_dispatch.rb"><code>actionpack/lib/action_dispatch.rb</code></a></li>
<li><a href="#rails/commands/server.rb"><code>rails/commands/server.rb</code></a></li>
<li><a href="#%E5%90%AF-%E5%8A%A8--bang-rack:-lib/rack/server.rb">Rack: <code>lib/rack/server.rb</code></a></li>
<li><a href="#config/application"><code>config/application</code></a></li>
<li><a href="#rails::server#start"><code>Rails::Server#start</code></a></li>
<li><a href="#config/environment.rb"><code>config/environment.rb</code></a></li>
<li><a href="#config/application.rb"><code>config/application.rb</code></a></li>
</ul>
</li>
<li>
<a href="#%E5%8A%A0%E8%BD%BD-rails">加载 Rails</a>
<ul>
<li><a href="#railties/lib/rails/all.rb"><code>railties/lib/rails/all.rb</code></a></li>
<li><a href="#%E5%9B%9E%E5%88%B0-config/environment.rb">回到 <code>config/environment.rb</code></a></li>
<li><a href="#railties/lib/rails/application.rb"><code>railties/lib/rails/application.rb</code></a></li>
<li><a href="#%E5%8A%A0%E8%BD%BD-rails-rack:-lib/rack/server.rb">Rack: lib/rack/server.rb</a></li>
</ul>
</li>
</ol>
</div>
</div>
</div>
<div id="container">
<div class="wrapper">
<div id="mainCol">
<p>本章节通过介绍一个基于Ruby on Rails框架默认配置的 Rails 4 应用程序启动过程中的方法调用,详细介绍了每个调用的细节。通过本章节,我们将了解当你执行<code>rails server</code>命令启动你的Rails应用时,背后究竟都发生了什么。</p><p>提示:本章节中的路径如果没有特别说明都是指Rails应用程序下的路径。</p><p>提示:如果你想浏览Rails的源代码<a href="https://github.com/rails/rails">sourcecode</a>,强烈建议您使用快捷键 <code>t</code>快速查找Github中的文件。</p><h3 id="启-动--bang">1 启 动 !</h3><p>我们现在准备启动和初始化一个Rails 应用。 一个Rails 应用经常是以运行命令 <code>rails console</code> 或者 <code>rails server</code> 开始的。</p><h4 id="railties/bin/rails">1.1 <code>railties/bin/rails</code>
</h4><p>Rails应用中的 <code>rails server</code>命令是Rails应用程序所在文件中的一个Ruby的可执行程序,该程序包含如下操作:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
version = ">= 0"
load Gem.bin_path('railties', 'rails', version)
</pre>
</div>
<p>如果你在Rails 控制台中使用上述命令,你将会看到载入<code>railties/bin/rails</code>这个路径。作为 <code>railties/bin/rails.rb</code>的一部分,包含如下代码:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
require "rails/cli"
</pre>
</div>
<p>模块<code>railties/lib/rails/cli</code> 会调用<code>Rails::AppRailsLoader.exec_app_rails</code>方法.</p><h4 id="railties/lib/rails/app_rails_loader.rb">1.2 <code>railties/lib/rails/app_rails_loader.rb</code>
</h4><p><code>exec_app_rails</code>模块的主要功能是去执行你的Rails应用中<code>bin/rails</code>文件夹下的指令。如果当前文件夹下没有<code>bin/rails</code>文件,它会到父级目录去搜索,直到找到为止(Windows下应该会去搜索环境变量中的路径),在Rails应用程序目录下的任意位置(命令行模式下),都可以执行<code>rails</code>的命令。</p><p>因为<code>rails server</code>命令和下面的操作是等价的:</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$ exec ruby bin/rails server
</pre>
</div>
<h4 id="bin/rails">1.3 <code>bin/rails</code>
</h4><p>文件<code>railties/bin/rails</code>包含如下代码:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
#!/usr/bin/env ruby
APP_PATH = File.expand_path('../../config/application', __FILE__)
require_relative '../config/boot'
require 'rails/commands'
</pre>
</div>
<p><code>APP_PATH</code>稍后会在<code>rails/commands</code>中用到。<code>config/boot</code>在这被引用是因为我们的Rails应用中需要<code>config/boot.rb</code>文件来载入Bundler,并初始化Bundler的配置。</p><h4 id="config/boot.rb">1.4 <code>config/boot.rb</code>
</h4><p><code>config/boot.rb</code> 包含如下代码:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
# Set up gems listed in the Gemfile.
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
</pre>
</div>
<p>在一个标准的Rails应用中的<code>Gemfile</code>文件会配置它的所有依赖项。<code>config/boot.rb</code>文件会根据<code>ENV['BUNDLE_GEMFILE']</code>中的值来查找<code>Gemfile</code>文件的路径。如果<code>Gemfile</code>文件存在,那么<code>bundler/setup</code>操作会被执行,Bundler执行该操作是为了配置Gemfile依赖项的加载路径。</p><p>一个标准的Rails应用会包含若干Gem包,特别是下面这些:</p>
<ul>
<li>actionmailer</li>
<li>actionpack</li>
<li>actionview</li>
<li>activemodel</li>
<li>activerecord</li>
<li>activesupport</li>
<li>arel</li>
<li>builder</li>
<li>bundler</li>
<li>erubis</li>
<li>i18n</li>
<li>mail</li>
<li>mime-types</li>
<li>polyglot</li>
<li>rack</li>
<li>rack-cache</li>
<li>rack-mount</li>
<li>rack-test</li>
<li>rails</li>
<li>railties</li>
<li>rake</li>
<li>sqlite3</li>
<li>thor</li>
<li>treetop</li>
<li>tzinfo</li>
</ul>
<h4 id="rails/commands.rb">1.5 <code>rails/commands.rb</code>
</h4><p>一旦<code>config/boot.rb</code>执行完毕,接下来要引用的是<code>rails/commands</code>文件,这个文件于帮助解析别名。在本应用中,<code>ARGV</code> 数组包含的 <code>server</code>项会被匹配:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
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)
</pre>
</div>
<p>提示: 如你所见,一个空的ARGV数组将会让系统显示相关的帮助项。</p><p>如果我们使用<code>s</code>缩写代替 <code>server</code>,Rails系统会从<code>aliases</code>中查找匹配的命令。</p><h4 id="rails/commands/command_tasks.rb">1.6 <code>rails/commands/command_tasks.rb</code>
</h4><p>当你键入一个错误的rails命令,<code>run_command</code>函数会抛出一个错误信息。如果命令正确,一个与命令同名的方法会被调用。</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
COMMAND_WHITELIST = %(plugin generate destroy console server dbconsole application runner new version help)
def run_command!(command)
command = parse_command(command)
if COMMAND_WHITELIST.include?(command)
send(command)
else
write_error_message(command)
end
end
</pre>
</div>
<p>如果执行<code>server</code>命令,Rails将会继续执行下面的代码:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
def set_application_directory!
Dir.chdir(File.expand_path('../../', APP_PATH)) unless File.exist?(File.expand_path("config.ru"))
end
def server
set_application_directory!
require_command!("server")
Rails::Server.new.tap do |server|
# We need to require application after the server sets environment,
# otherwise the --environment option given to the server won't propagate.
require APP_PATH
Dir.chdir(Rails.application.root)
server.start
end
end
def require_command!(command)
require "rails/commands/#{command}"
end
</pre>
</div>
<p>这个文件将会指向Rails的根目录(与<code>APP_PATH</code>中指向<code>config/application.rb</code>不同),但是如果没找到<code>config.ru</code>文件,接下来将需要<code>rails/commands/server</code>来创建<code>Rails::Server</code>类。</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
require 'fileutils'
require 'optparse'
require 'action_dispatch'
require 'rails'
module Rails
class Server < ::Rack::Server
</pre>
</div>
<p><code>fileutils</code> 和 <code>optparse</code> 是Ruby标准库中帮助操作文件和解析选项的函数。</p><h4 id="actionpack/lib/action_dispatch.rb">1.7 <code>actionpack/lib/action_dispatch.rb</code>
</h4><p>动作分发(Action Dispatch)是Rails框架中的路径组件。它增强了路径,会话和中间件的功能。</p><h4 id="rails/commands/server.rb">1.8 <code>rails/commands/server.rb</code>
</h4><p>这个文件中定义的<code>Rails::Server</code>类是继承自<code>Rack::Server</code>类的。当<code>Rails::Server.new</code>被调用时,会在 <code>rails/commands/server.rb</code>中调用一个<code>initialize</code>方法:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
def initialize(*)
super
set_environment
end
</pre>
</div>
<p>首先,<code>super</code>会调用父类<code>Rack::Server</code>中的<code>initialize</code>方法。</p><h4 id="启-动--bang-rack:-lib/rack/server.rb">1.9 Rack: <code>lib/rack/server.rb</code>
</h4><p><code>Rack::Server</code>会为所有基于Rack的应用提供服务接口,现在它已经是Rails框架的一部分了。</p><p><code>Rack::Server</code>中的<code>initialize</code> 方法会简单的设置一对变量:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
def initialize(options = nil)
@options = options
@app = options[:app] if options && options[:app]
end
</pre>
</div>
<p>在这种情况下,<code>options</code> 的值是 <code>nil</code>,所以在这个方法中相当于什么都没做。</p><p>当<code>Rack::Server</code>中的<code>super</code>方法执行完毕后。我们回到<code>rails/commands/server.rb</code>,此时此刻,<code>Rails::Server</code>对象会调用 <code>set_environment</code> 方法,这个方法貌似看上去什么也没干: </p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
def set_environment
ENV["RAILS_ENV"] ||= options[:environment]
end
</pre>
</div>
<p>事实上,<code>options</code>方法在这做了很多事情。<code>Rack::Server</code> 中的这个方法定义如下:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
def options
@options ||= parse_options(ARGV)
end
</pre>
</div>
<p>接着<code>parse_options</code>方法部分代码如下:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
def parse_options(args)
options = default_options
# Don't evaluate CGI ISINDEX parameters.
# http://www.meb.uni-bonn.de/docs/cgi/cl.html
args.clear if ENV.include?("REQUEST_METHOD")
options.merge! opt_parser.parse!(args)
options[:config] = ::File.expand_path(options[:config])
ENV["RACK_ENV"] = options[:environment]
options
end
</pre>
</div>
<p><code>default_options</code>方法的代码如下:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
def default_options
environment = ENV['RACK_ENV'] || 'development'
default_host = environment == 'development' ? 'localhost' : '0.0.0.0'
{
:environment => environment,
:pid => nil,
:Port => 9292,
:Host => default_host,
:AccessLog => [],
:config => "config.ru"
}
end
</pre>
</div>
<p><code>ENV</code>中没有<code>REQUEST_METHOD</code>项,所以我们可以忽略这一行。接下来是已经在 <code>Rack::Server</code>被定义好的<code>opt_parser</code>方法:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
def opt_parser
Options.new
end
</pre>
</div>
<p><strong>这个</strong>方法已经在<code>Rack::Server</code>被定义过了,但是在<code>Rails::Server</code> 使用不同的参数进行了重载。他的 <code>parse!</code>方法如下:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
def parse!(args)
args, options = args.dup, {}
opt_parser = OptionParser.new do |opts|
opts.banner = "Usage: rails server [mongrel, thin, etc] [options]"
opts.on("-p", "--port=port", Integer,
"Runs Rails on the specified port.", "Default: 3000") { |v| options[:Port] = v }
...
</pre>
</div>
<p>这个方法为<code>options</code>建立一些配置选项,以便给Rails决定如何运行服务提供支持。<code>initialize</code>方法执行完毕后。我们将回到<code>rails/server</code>目录下,就是<code>APP_PATH</code>中的路径。</p><h4 id="config/application">1.10 <code>config/application</code>
</h4><p>当<code>require APP_PATH</code>操作执行完毕后。<code>config/application.rb</code> 被载入了 (重新调用<code>bin/rails</code>中的<code>APP_PATH</code>), 在你的应用中,你可以根据需求对该文件进行配置。</p><h4 id="rails::server#start">1.11 <code>Rails::Server#start</code>
</h4><p><code>config/application</code>载入后,<code>server.start</code>方法被调用了。这个方法定义如下:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
def start
print_boot_information
trap(:INT) { exit }
create_tmp_directories
log_to_stdout if options[:log_stdout]
super
...
end
private
def print_boot_information
...
puts "=> Run `rails server -h` for more startup options"
...
puts "=> Ctrl-C to shutdown server" unless options[:daemonize]
end
def create_tmp_directories
%w(cache pids sessions sockets).each do |dir_to_make|
FileUtils.mkdir_p(File.join(Rails.root, 'tmp', dir_to_make))
end
end
def log_to_stdout
wrapped_app # touch the app so the logger is set up
console = ActiveSupport::Logger.new($stdout)
console.formatter = Rails.logger.formatter
console.level = Rails.logger.level
Rails.logger.extend(ActiveSupport::Logger.broadcast(console))
end
</pre>
</div>
<p>这是Rails初始化过程中的第一次控制台输出。这个方法创建了一个<code>INT</code>中断信号,所以当你在服务端控制台按下<code>CTRL-C</code>键后,这将终止Server的运行。我们可以看到,它创建了<code>tmp/cache</code>,<code>tmp/pids</code>, <code>tmp/sessions</code>和<code>tmp/sockets</code>等目录。在创建和声明<code>ActiveSupport::Logger</code>之前,会调用 <code>wrapped_app</code>方法来创建一个Rake 应用程序。</p><p><code>super</code>会调用<code>Rack::Server.start</code> 方法,该方法定义如下:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
def start &blk
if options[:warn]
$-w = true
end
if includes = options[:include]
$LOAD_PATH.unshift(*includes)
end
if library = options[:require]
require library
end
if options[:debug]
$DEBUG = true
require 'pp'
p options[:server]
pp wrapped_app
pp app
end
check_pid! if options[:pid]
# Touch the wrapped app, so that the config.ru is loaded before
# daemonization (i.e. before chdir, etc).
wrapped_app
daemonize_app if options[:daemonize]
write_pid if options[:pid]
trap(:INT) do
if server.respond_to?(:shutdown)
server.shutdown
else
exit
end
end
server.run wrapped_app, options, &blk
end
</pre>
</div>
<p>上述Rails 应用有趣的部分在最后一行,<code>server.run</code>方法。它再次调用了<code>wrapped_app</code>方法(温故而知新)。</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
@wrapped_app ||= build_app app
</pre>
</div>
<p>这里的<code>app</code>方法定义如下:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
def app
@app ||= options[:builder] ? build_app_from_string : build_app_and_options_from_config
end
...
private
def build_app_and_options_from_config
if !::File.exist? options[:config]
abort "configuration #{options[:config]} not found"
end
app, options = Rack::Builder.parse_file(self.options[:config], opt_parser)
self.options.merge! options
app
end
def build_app_from_string
Rack::Builder.new_from_string(self.options[:builder])
end
</pre>
</div>
<p><code>options[:config]</code>中的值默认会从 <code>config.ru</code> 中获取,包含如下代码:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
# This file is used by Rack-based servers to start the application.
require ::File.expand_path('../config/environment', __FILE__)
run <%= app_const %>
</pre>
</div>
<p><code>Rack::Builder.parse_file</code>方法会从<code>config.ru</code>中获取内容,包含如下代码:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
app = new_from_string cfgfile, config
...
def self.new_from_string(builder_script, file="(rackup)")
eval "Rack::Builder.new {\n" + builder_script + "\n}.to_app",
TOPLEVEL_BINDING, file, 0
end
</pre>
</div>
<p><code>Rack::Builder</code>中的<code>initialize</code>方法会创建一个新的<code>Rack::Builder</code>实例,这是Rails应用初始化过程中主要内容。接下来<code>config.ru</code>中的<code>require</code>项<code>config/environment.rb</code>会继续执行:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
require ::File.expand_path('../config/environment', __FILE__)
</pre>
</div>
<h4 id="config/environment.rb">1.12 <code>config/environment.rb</code>
</h4><p>这是<code>config.ru</code> (<code>rails server</code>)和信使(Passenger)都要用到的文件,是两者交流的媒介。之前的操作都是为了创建Rack和Rails。</p><p>这个文件是以引用 <code>config/application.rb</code>开始的:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
require File.expand_path('../application', __FILE__)
</pre>
</div>
<h4 id="config/application.rb">1.13 <code>config/application.rb</code>
</h4><p>这个文件需要引用<code>config/boot.rb</code>:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
require File.expand_path('../boot', __FILE__)
</pre>
</div>
<p>如果之前在<code>rails server</code>中没有引用上述的依赖项,那么它<strong>将不会</strong>和信使(Passenger)发生联系。</p><p>现在,有趣的部分要开始了!</p><h3 id="加载-rails">2 加载 Rails</h3><p><code>config/application.rb</code>中的下一行是这样的:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
require 'rails/all'
</pre>
</div>
<h4 id="railties/lib/rails/all.rb">2.1 <code>railties/lib/rails/all.rb</code>
</h4><p>本文件中将引用和Rails框架相关的所有内容:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
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
</pre>
</div>
<p>这样Rails框架中的所有组件已经准备就绪了。我们将不会深入介绍这些框架的内部细节,不过强烈建议您去探索和发现她们。</p><p>现在,我们关心的模块比如Rails engines,I18n 和 Rails configuration 都已经准备就绪了。</p><h4 id="回到-config/environment.rb">2.2 回到 <code>config/environment.rb</code>
</h4><p><code>config/application.rb</code>为<code>Rails::Application</code>定义了Rails应用初始化之后所有需要用到的资源。当<code>config/application.rb</code> 加载了Rails和命名空间后,我们回到<code>config/environment.rb</code>,就是初始化完成的地方。比如我们的应用叫‘blog’,我们将在<code>rails/application.rb</code>中调用<code>Rails.application.initialize!</code>方法。</p><h4 id="railties/lib/rails/application.rb">2.3 <code>railties/lib/rails/application.rb</code>
</h4><p><code>initialize!</code>方法部分代码如下: </p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
def initialize!(group=:default) #:nodoc:
raise "Application has been already initialized." if @initialized
run_initializers(group, self)
@initialized = true
self
end
</pre>
</div>
<p>如你所见,一个应用只能初始化一次。初始化器通过在<code>railties/lib/rails/initializable.rb</code>中的<code>run_initializers</code>方法运行:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
def run_initializers(group=:default, *args)
return if instance_variable_defined?(:@ran)
initializers.tsort_each do |initializer|
initializer.run(*args) if initializer.belongs_to?(group)
end
@ran = true
end
</pre>
</div>
<p><code>run_initializers</code>代码本身是有点投机取巧的,Rails在这里要做的是遍历所有的祖先,查找一个<code>initializers</code>方法,之后根据名字进行排序,并依次执行它们。举个例子,<code>Engine</code>类将调用自己和祖先中名为<code>initializers</code>的方法。</p><p><code>Rails::Application</code> 类是在<code>railties/lib/rails/application.rb</code>定义的。定义了<code>bootstrap</code>, <code>railtie</code>和 <code>finisher</code>模块的初始化器。<code>bootstrap</code>的初始化器在应用被加载以前就预加载了。(类似初始化中的日志记录器),<code>finisher</code>的初始化器则是最后加载的。<code>railtie</code>初始化器被定义在<code>Rails::Application</code>中,执行是在<code>bootstrap</code>和 <code>finishers</code>之间。</p><p>这些完成后,我们将回到<code>Rack::Server</code> 。</p><h4 id="加载-rails-rack:-lib/rack/server.rb">2.4 Rack: lib/rack/server.rb</h4><p>上次我们离开的时候,<code>app</code> 方法代码如下:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
def app
@app ||= options[:builder] ? build_app_from_string : build_app_and_options_from_config
end
...
private
def build_app_and_options_from_config
if !::File.exist? options[:config]
abort "configuration #{options[:config]} not found"
end
app, options = Rack::Builder.parse_file(self.options[:config], opt_parser)
self.options.merge! options
app
end
def build_app_from_string
Rack::Builder.new_from_string(self.options[:builder])
end
</pre>
</div>
<p>此时此刻,<code>app</code>是Rails 应用本身(中间件)。接下来就是Rack调用所有的依赖项了(提供支持的中间件):</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
def build_app(app)
middleware[options[:environment]].reverse_each do |middleware|
middleware = middleware.call(self) if middleware.respond_to?(:call)
next unless middleware
klass = middleware.shift
app = klass.new(app, *middleware)
end
app
end
</pre>
</div>
<p>必须牢记,<code>Server#start</code>最后一行中调用了<code>build_app</code>方法(被<code>wrapped_app</code>调用)了。接下来我们看看还剩下什么:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
server.run wrapped_app, options, &blk
</pre>
</div>
<p>此时此刻,调用<code>server.run</code> 方法将依赖于你所用的Server类型 。比如,如果你的Server是Puma, 那么就会是下面这个结果:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
...
DEFAULT_OPTIONS = {
:Host => '0.0.0.0',
:Port => 8080,
:Threads => '0:16',
:Verbose => false
}
def self.run(app, options = {})
options = DEFAULT_OPTIONS.merge(options)
if options[:Verbose]
app = Rack::CommonLogger.new(app, STDOUT)
end
if options[:environment]
ENV['RACK_ENV'] = options[:environment].to_s
end
server = ::Puma::Server.new(app)
min, max = options[:Threads].split(':', 2)
puts "Puma #{::Puma::Const::PUMA_VERSION} starting..."
puts "* Min threads: #{min}, max threads: #{max}"
puts "* Environment: #{ENV['RACK_ENV']}"
puts "* Listening on tcp://#{options[:Host]}:#{options[:Port]}"
server.add_tcp_listener options[:Host], options[:Port]
server.min_threads = min
server.max_threads = max
yield server if block_given?
begin
server.run.join
rescue Interrupt
puts "* Gracefully stopping, waiting for requests to finish"
server.stop(true)
puts "* Goodbye!"
end
end
</pre>
</div>
<p>我们没有深入到服务端配置的细节,因为这是我们探索Rails应用初始化过程之旅的终点了。</p><p>高层次的阅读将有助于您提高编写代码的水平,成为Rail开发高手。如果你想要知道更多,那么去读Rails的源代码将是你的不二选择。</p>
<h3>反馈</h3>
<p>
欢迎帮忙改善指南质量。
</p>
<p>
如发现任何错误,欢迎修正。开始贡献前,可先行阅读<a href="http://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html#contributing-to-the-rails-documentation">贡献指南:文档</a>。
</p>
<p>翻译如有错误,深感抱歉,欢迎 <a href="https://github.com/ruby-china/guides/fork">Fork</a> 修正,或至此处<a href="https://github.com/ruby-china/guides/issues/new">回报</a>。</p>
<p>
文章可能有未完成或过时的内容。请先检查 <a href="http://edgeguides.rubyonrails.org">Edge Guides</a> 来确定问题在 master 是否已经修掉了。再上 master 补上缺少的文件。内容参考 <a href="ruby_on_rails_guides_guidelines.html">Ruby on Rails 指南准则</a>来了解行文风格。
</p>
<p>最后,任何关于 Ruby on Rails 文档的讨论,欢迎到 <a href="http://groups.google.com/group/rubyonrails-docs">rubyonrails-docs 邮件群组</a>。
</p>
</div>
</div>
</div>
<hr class="hide" />
<div id="footer">
<div class="wrapper">
<p>本著作采用<a href="https://creativecommons.org/licenses/by-sa/4.0/">创用 CC 姓名标示-相同方式分享 4.0 国际授权条款</a>授权。</p>
<p>“Rails”、“Ruby on Rails”,以及 Rails logo 为 David Heinemeier Hansson 的商标。版权所有。</p>
</div>
</div>
<script type="text/javascript" src="javascripts/jquery.min.js"></script>
<script type="text/javascript" src="javascripts/responsive-tables.js"></script>
<script type="text/javascript" src="javascripts/guides.js"></script>
<script type="text/javascript" src="javascripts/syntaxhighlighter/shCore.js"></script>
<script type="text/javascript" src="javascripts/syntaxhighlighter/shBrushRuby.js"></script>
<script type="text/javascript" src="javascripts/syntaxhighlighter/shBrushXml.js"></script>
<script type="text/javascript" src="javascripts/syntaxhighlighter/shBrushSql.js"></script>
<script type="text/javascript" src="javascripts/syntaxhighlighter/shBrushPlain.js"></script>
<script type="text/javascript">
SyntaxHighlighter.all();
$(guidesIndex.bind);
</script>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
// ga('create', '', 'ruby-china.github.io');
ga('require', 'displayfeatures');
ga('send', 'pageview');
</script>
</body>
</html>