-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathchapter1.html
More file actions
1367 lines (1207 loc) · 127 KB
/
chapter1.html
File metadata and controls
1367 lines (1207 loc) · 127 KB
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
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8"/>
<title>Ruby on Rails 教程 - 第 1 章 从零开始,完成一次部署</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta name="description" content="最好的 Ruby on Rails 入门教程"/>
<meta name="keywords" content="ruby, rails, tutorial"/>
<meta name="author" content="Michael Hartl"/>
<meta name="translator" content="安道"/>
<meta name="generator" content="persie 0.0.5.1"/>
<link rel="stylesheet" type="text/css" href="//railstutorial-china.org/assets/css/main.css"/>
<link rel="stylesheet" type="text/css" href="book.css"/>
<script type="text/javascript" src="//railstutorial-china.org/assets/js/global.js"></script>
</head>
<body class="book-page">
<nav class="navbar">
<div class="container">
<div class="clearfix">
<a class="navbar-brand hidden-sm-up" href="//railstutorial-china.org/" title="Ruby on Rails 教程">Ruby on Rails 教程</a>
<button class="navbar-toggler hidden-sm-up pull-xs-right" type="button" data-toggle="collapse" data-target="#main-nav">☰</button>
</div>
<a class="navbar-brand hidden-xs-down" href="//railstutorial-china.org/" title="Ruby on Rails 教程">Ruby on Rails 教程</a>
<div class="collapse navbar-toggleable-xs pull-sm-right" id="main-nav">
<ul class="nav navbar-nav">
<li class="nav-item"><a class="nav-link" href="//railstutorial-china.org/" title="首页">首页</a></li>
<li class="nav-item"><a class="nav-link" href="//railstutorial-china.org/blog/" title="博客">博客</a></li>
<li class="nav-item active"><a class="nav-link" href="//railstutorial-china.org/book/" title="阅读">阅读</a></li>
<li class="nav-item"><a class="nav-link" href="//railstutorial-china.org/#ebook" title="电子书">电子书</a></li>
</ul>
</div>
</div>
</nav>
<div class="content">
<div class="container">
<div class="row">
<div class="col-lg-offset-2 col-lg-8">
<div class="book-versions">
选择版本:
<a class="btn btn-primary" href="//railstutorial-china.org/book/" title="Ruby on Rails 教程(原书第 4 版,针对 Rails 5)">Rails 5</a>
<a class="btn btn-secondary" href="//railstutorial-china.org/rails42/" title="Ruby on Rails 教程(原书第 3 版,针对 Rails 4.2)">Rails 4.2</a>
<a class="btn btn-secondary" href="//railstutorial-china.org/rails4/" title="Ruby on Rails 教程(原书第 3 版,针对 Rails 4.0)">Rails 4.0</a>
<a class="btn btn-secondary" href="//railstutorial-china.org/rails3/" title="Ruby on Rails 教程(原书第 2 版,针对 Rails 3.2)">Rails 3.2</a>
</div>
<div class="alert alert-warning">
<p>在线版的内容可能落后于电子书,如果想及时获得更新,请<a href="//railstutorial-china.org/#ebook" title="购买电子书">购买电子书</a>。</p>
</div>
<article class="article">
<section data-type="chapter" id="beginning">
<h1><span class="title-label">第 1 章</span> 从零开始,完成一次部署</h1>
<p>欢迎阅读《Ruby on Rails 教程:通过 Rails 学习 Web 开发》。本书的目的是教你开发 Web 应用,而我们选择的工具是流行的 Web 框架 <a href="http://rubyonrails.org" class="external-link">Ruby on Rails</a>。本书主要教你 Web 开发的一般性知识(不限定于 Rails),此外还会教你更多的技能,让你认清“技术是复杂的”(<a class="xref-link" href="#aside-technical-sophistication">旁注 1.1</a>)。这正是 <a href="http://learnenough.com/story" class="external-link">Learn Enough to Be Dangerous</a> 系列教程力图解决的问题。这一系列教程适合作为本书的预备知识阅读,最先发布的是《<a href="http://learnenough.com/command-line-tutorial" class="external-link">Learn Enough Command Line to Be Dangerous</a>》,这个教程完全针对初学者(与市面上的书不同)。</p>
<div data-type="sidebar" id="aside-technical-sophistication" class="sidebar">
<h5><span class="title-label">旁注 1.1</span>:技术是复杂的</h5>
<p>本书是 <a href="http://learnenough.com/story" class="external-link">Learn Enough to Be Dangerous</a> 系列教程的一部分。这一系列教程力图让你认清“技术是复杂的”,不要以为软硬技能兼备就能解决任何技术问题(<a class="xref-link" href="#fig-tech-support-cheat-sheet">图 1.1</a>)。Web 开发,以及一般的计算机编程,最能体现这一点。除了技术本身,还要知道如何点击菜单项,学会使用某个应用;要知道如何使用 Google,弄清错误消息的意思;以及何时要放弃,直接重启设备。</p>
<p>Web 应用涉及的知识特别多,因此从中更容易认清“技术是复杂的”。就使用 Rails 做 Web 开发而言,体现这一点的包括:确保使用的 Ruby gem 版本正确,执行 <code>bundle install</code> 或 <code>bundle update</code> 命令,以及遇到问题时重启本地 Web 服务器。(如果你完全不知道我在说什么,不用担心,本书会涵盖这里提到的所有话题。)</p>
<p>在阅读本书的过程中,你可能偶尔会卡住,得不到预期的结果。书中的文字已经着重标出了易于出错的步骤,但是难免百密一疏。我建议你积极去解决这些拦路虎,借此提高自己的技能。说不定,就像极客们所说的:这不是缺陷,而是特性!</p>
</div>
<p>本书会向你详细介绍 Web 应用开发的方方面面,包括 Ruby、Rails、HTML 和 CSS、数据库、版本控制、测试,以及部署的基本知识。学会这些知识足以为你赢得一份 Web 开发者的工作,或者还可以让你成为一名技术创业者。如果你已经了解 Web 开发,阅读本书能快速学会 Rails 框架的基础,包括 MVC 和 REST、生成器、迁移、路由,以及嵌入式 Ruby。不管怎样,读完本书之后,以你所掌握的知识,已经能够阅读讨论更高级话题的图书和博客,或者观看视频,这些都是繁荣的编程教学生态圈的一部分。</p>
<p>本书采用一种综合式方法讲授 Web 开发,在学习的过程中我们将开发三个演示应用:第一个最简单,叫 <code>hello_app</code>(<a class="xref-link" href="#the-hello-application">1.3 节</a>);第二个功能多一些,叫 <code>toy_app</code>(<a class="xref-link" href="chapter2.html#a-toy-app">第 2 章</a>);第三个是真正的演示应用,叫 <code>sample_app</code>(<a class="xref-link" href="chapter3.html#mostly-static-pages">第 3 章</a>到<a class="xref-link" href="chapter14.html#following-users">第 14 章</a>)。从这三个应用的名字可以看出,书中开发的应用不限定于某种特定类型的网站。最后一个演示应用有点儿类似于 <a href="https://twitter.com" class="external-link">Twitter</a>(很巧,这个网站起初也是使用 Rails 开发的)。不过,本书的重点是介绍通用原则,所以不管你想开发什么样的 Web 应用,读完本书后都能建立扎实的基础。</p>
<div id="fig-tech-support-cheat-sheet" class="figure"><img src="images/chapter1/tech_support_cheat_sheet.png" alt="tech support cheat sheet" /><div class="figcaption"><span class="title-label">图 1.1</span>:“技术支持速查表”(原图出自 <a href="https://m.xkcd.com/627/" class="external-link">xkcd</a>)<sup>[<a id="fn-ref-1" href="#fn-1">1</a>]</sup></div></div>
<p>在第 1 章,我们将安装 Ruby on Rails 以及所需的全部软件,而且还将搭建开发环境(<a class="xref-link" href="#up-and-running">1.2 节</a>)。然后,我们将创建第一个 Rails 应用,名为 <code>hello_app</code>。本书旨在介绍优秀的软件开发实践,所以在创建第一个应用之后,我们会立即将它纳入版本控制系统 Git 中(<a class="xref-link" href="#version-control">1.4 节</a>)。你可能不相信,在这一章,我们还将部署那个应用(<a class="xref-link" href="#deploying">1.5 节</a>),把它放到网上。</p>
<p><a class="xref-link" href="chapter2.html#a-toy-app">第 2 章</a>会创建第二个项目,演示 Rails 应用的一些基本操作。为了提升速度,我们将使用脚手架(<a class="xref-link" href="#aside-scaffolding">旁注 1.2</a>)创建那个应用(名为 <code>toy_app</code>)。因为脚手架生成的代码很丑也很复杂,所以<a class="xref-link" href="chapter2.html#a-toy-app">第 2 章</a>将集中精力在浏览器中,使用 URI(经常称为 URL)<sup>[<a id="fn-ref-2" href="#fn-2">2</a>]</sup>与那个应用交互。</p>
<p>本书剩下的章节将集中精力开发一个真实的大型演示应用(名为 <code>sample_app</code>),所有代码都从零开始编写。在开发这个应用的过程中,我们会用到构思图、测试驱动开发(Test-driven Development,简称 TDD)理念和集成测试(integration test)。<a class="xref-link" href="chapter3.html#mostly-static-pages">第 3 章</a>创建静态页面,然后增加一些动态内容。<a class="xref-link" href="chapter4.html#rails-flavored-ruby">第 4 章</a>简要介绍 Rails 使用的 Ruby 程序语言。<a class="xref-link" href="chapter5.html#filling-in-the-layout">第 5 章</a>到<a class="xref-link" href="chapter12.html#password-reset">第 12 章</a>逐步完善这个应用的低层结构,包括网站的布局、<code>User</code> 数据模型,以及完整的注册和身份验证系统(含有账户激活和密码重设功能)。最后,<a class="xref-link" href="chapter13.html#user-microposts">第 13 章</a>和<a class="xref-link" href="chapter14.html#following-users">第 14 章</a>添加微博和社交功能,最终开发出一个可以正常运行的演示网站。</p>
<div data-type="sidebar" id="aside-scaffolding" class="sidebar">
<h5><span class="title-label">旁注 1.2</span>:脚手架:更快、更简单、更诱人</h5>
<p>Rails 出现伊始就吸引了众多目光,特别是 Rails 创始人 David Heinemeier Hansson 录制的著名的“<a href="http://v.youku.com/v_show/id_XNzg5MjUwOTU2.html" class="external-link">15分钟开发一个博客</a>”视频。这个视频及其衍生版本是窥探 Rails 强大功能一种很好的方式,我推荐你看一下这些视频。不过事先提醒一下,这些视频中的演示能控制在 15 分钟以内,得益于一种叫做脚手架(scaffold)的功能,通过 Rails 提供的 <code>generate scaffold</code> 命令生成大量的代码。</p>
<p>写作本书时,我也想过使用脚手架,因为它<a href="http://en.wikipedia.org/wiki/Dark_side_(Star_Wars)" class="external-link">更快、更简单、更诱人</a>。不过脚手架生成的代码量多且复杂,会让初学者困惑。这样虽然能学会脚手架的用法,但并不明白到底发生了什么。使用脚手架,你只是一个脚本生成器的使用者,无法提升对 Rails 的认识。</p>
<p>本书将采用一种不同的方式,虽然<a class="xref-link" href="chapter2.html#a-toy-app">第 2 章</a>会用脚手架开发一个小型的玩具应用,但本书的核心是从<a class="xref-link" href="chapter3.html#mostly-static-pages">第 3 章</a>起开发的演示应用。在开发那个演示应用的每个阶段,我们只会编写少量的代码,易于理解但又具有一定的挑战性。通过这样一个过程,最终你会对 Rails 有较为深刻的理解,而且能灵活运用,开发几乎任何类型的 Web 应用。</p>
</div>
<section data-type="sect1" id="introduction">
<h1><span class="title-label">1.1</span> 简介</h1>
<p>Ruby on Rails(或者简称 Rails)是一个 Web 开发框架,使用 Ruby 编程语言开发。自 2004 年出现之后,Rails 迅速成为动态 Web 应用开发领域功能最强大、最受欢迎的框架之一。使用 Rails 的公司有很多,例如 <a href="http://airbnb.com/" class="external-link">Airbnb</a>、<a href="http://basecamp.com/" class="external-link">Basecamp</a>、<a href="http://disney.com" class="external-link">Disney</a>、<a href="http://github.com/" class="external-link">GitHub</a>、<a href="http://hulu.com/" class="external-link">Hulu</a>、<a href="http://kickstarter.com/" class="external-link">Kickstarter</a>、<a href="http://shopify.com/" class="external-link">Shopify</a>、<a href="http://twitter.com/" class="external-link">Twitter</a> 和 <a href="http://yellowpages.com/" class="external-link">Yellow Pages</a>。此外,还有很多 Web 开发工作室专门从事 Rails 应用开发,例如 <a href="http://entp.com/" class="external-link">ENTP</a>、<a href="http://thoughtbot.com/" class="external-link">thoughtbot</a>、<a href="http://pivotallabs.com/" class="external-link">Pivotal Labs</a>、<a href="http://hashrocket.com/" class="external-link">Hashrocket</a> 和 <a href="http://www.happyfuncorp.com/" class="external-link">HappyFunCorp</a>。除此之外还有无数独立顾问、培训人员和项目承包商。</p>
<p>Rails 为何如此成功呢?首先,Rails 完全开源,基于宽松的 <a href="http://www.opensource.org/licenses/mit-license.php" class="external-link">MIT 许可证</a>发布,可以免费下载、使用。Rails 的成功很大程度上得益于它优雅而紧凑的设计。Rails 熟谙 Ruby 语言的可扩展性,开发了一套用于编写 Web 应用的<a href="http://en.wikipedia.org/wiki/Domain_Specific_Language" class="external-link">领域特定语言</a>(Domain-specific Language,简称 DSL)。所以 Web 编程中很多常见的任务,例如生成 HTML、创建数据模型和 URL 路由,在 Rails 中都很容易实现,最终得到的应用代码简洁而且可读性高。</p>
<p>Rails 还会快速跟进 Web 开发领域最新的技术和框架设计方式。例如,Rails 是最早使用 REST 架构风格组织 Web 应用的框架之一(这个架构贯穿本书)。当其他框架开发出成功的新技术后,Rails 的创建者 <a href="http://loudthinking.com/" class="external-link">David Heinemeier Hansson</a> 及其<a href="http://rubyonrails.org/core" class="external-link">核心开发团队</a>会毫不犹豫地将其吸纳进来。或许最典型的例子是 Rails 和 Merb 两个项目的合并,自此 Rails 继承了 Merb 的模块化设计、稳定的 <a href="http://en.wikipedia.org/wiki/Application_programming_interface" class="external-link">API</a>,性能也得到了提升。</p>
<p>最后一点,Rails 社区特别热情,乐于助人。社区中有数以千计的开源<a href="http://contributors.rubyonrails.org/" class="external-link">贡献者</a>,以及与会者众多的<a href="http://railsconf.com/" class="external-link">开发者大会</a>,而且还开发了大量的 <a href="https://rubygems.org/" class="external-link">gem</a>(代码库,一个 gem 解决一个特定的问题,例如分页和图像上传),有很多内容丰富的博客,以及一些讨论组和 IRC 频道。有如此多的 Rails 程序员也使得处理应用错误变得简单了:在 Google 中搜索错误消息,几乎总能找到一篇相关的博客文章或讨论组中的话题。</p>
<section data-type="sect2" id="prerequisites">
<h2><span class="title-label">1.1.1</span> 预备知识</h2>
<p>阅读本书不需要具备特定的预备知识。本书不仅介绍 Rails,还涉及底层的 Ruby 语言、Rails 默认使用的测试框架(MiniTest)、Unix 命令行、<a href="http://en.wikipedia.org/wiki/HTML" class="external-link">HTML</a> 和 <a href="http://en.wikipedia.org/wiki/CSS" class="external-link">CSS</a>、少量的 <a href="http://en.wikipedia.org/wiki/JavaScript" class="external-link">JavaScript</a>,以及一点 <a href="http://en.wikipedia.org/wiki/SQL" class="external-link">SQL</a>。我们要掌握的知识很多,所以我一般建议阅读本书之前先具备一些 HTML 和编程知识。如果你刚接触软件开发,建议你先阅读 <a href="http://learnenough.com/" class="external-link">Learn Enough to Be Dangerous</a> 系列教程。</p>
<ol class="arabic">
<li>
<p>开发者基础知识</p>
<ul>
<li>
<p><a href="http://www.learnenough.com/command-line-tutorial" class="external-link">Learn Enough Command Line to Be Dangerous</a></p>
</li>
<li>
<p><a href="http://www.learnenough.com/text-editor-tutorial" class="external-link">Learn Enough Text Editor to Be Dangerous</a></p>
</li>
<li>
<p><a href="http://www.learnenough.com/git-tutorial" class="external-link">Learn Enough Git to Be Dangerous</a></p>
</li>
</ul>
</li>
<li>
<p>Web 基础知识</p>
<ul>
<li>
<p><a href="http://www.learnenough.com/html-tutorial" class="external-link">Learn Enough HTML to Be Dangerous</a></p>
</li>
<li>
<p><a href="http://www.learnenough.com/css-and-layout-tutorial" class="external-link">Learn Enough CSS & Layout to Be Dangerous</a></p>
</li>
</ul>
</li>
<li>
<p>开发入门</p>
<ul>
<li>
<p><a href="http://www.learnenough.com/javascript-tutorial" class="external-link">Learn Enough JavaScript to Be Dangerous</a></p>
</li>
<li>
<p><a href="http://www.learnenough.com/ruby-tutorial" class="external-link">Learn Enough Ruby to Be Dangerous</a></p>
</li>
</ul>
</li>
<li>
<p>应用开发</p>
<ul>
<li>
<p><a href="http://railstutorial-china.org" class="external-link">Ruby on Rails 教程</a></p>
</li>
</ul>
</li>
</ol>
<p>学习 Rails 时经常有人问,要不要先学 Ruby?这个问题的答案取决于你个人的学习方式以及编程经验。如果你希望较为系统地彻底学习,或者以前从未编程过,那么先学 Ruby 或许更合适。学习 Ruby,推荐你按照上述列表中的顺序阅读整个 Learn Enough 系列教程。很多 Rails 初学者很想立即着手开发 Web 应用,而不是在此之前先读完一本介绍 Ruby 的书。如果你是这类人,建议你先把本书过一遍,如果觉得太难,再回过头去阅读 Learn Enough 系列教程。</p>
<p>不管你从哪里开始,读完本书后都应该可以接着学习 Rails 中高级知识了。以下是我特别推荐的学习资源:</p>
<ul>
<li>
<p>Learn Enough Society:这是一项收费订阅服务,包含本书的特别增强版和 15 个多小时的流媒体视频课程。视频中介绍了众多技巧,还有真人演示,这是阅读纸质书不可获得的。这项服务还包括 Learn Enough 系列教程的文字版和视频。提供教育优惠。</p>
</li>
<li>
<p><a href="https://www.bloc.io" class="external-link">Bloc</a>:一个在线训练营,有结构化的课程、个人导师,通过具体的项目学习知识。使用 BLOCLOVESHARTL 优惠码可以节省 $500 报名费。</p>
</li>
<li>
<p><a href="http://launchschool.com/railstutorial" class="external-link">Launch School</a>:不错的在线 Rails 开发训练营(包括高级课程)</p>
</li>
<li>
<p><a href="http://www.thefirehoseproject.com/?tid=HARTL-RAILS-TUT-EB2&pid=HARTL-RAILS-TUT-EB2" class="external-link">Firehose Project</a>:导师制在线编程训练营,专注于具体的编程技能,如测试驱动开发、算法,以及敏捷 Web 应用开发。有两周免费的入门课程。</p>
</li>
<li>
<p><a href="http://www.thinkful.com/a/railstutorial" class="external-link">Thinkful</a>:在线课程,由专业的工程师辅导开发项目;</p>
</li>
<li>
<p><a href="https://pragmaticstudio.com/refs/railstutorial" class="external-link">Pragmatic Studio</a>:Mike 和 Nicole Clark 主讲的在线 Ruby 和 Rails 课程。Mike 与《Programming Ruby》的作者 Dave Thomas 主讲的 Rails 教程是我学习的第一个 Rails 课程,历史可追溯到 2006 年。</p>
</li>
<li>
<p><a href="https://tutorials.railsapps.org/hartl" class="external-link">RailsApps</a>:很多针对特定话题的 Rails 项目和教程,说明详细;</p>
</li>
<li>
<p><a href="http://rails.guide/book/" class="external-link">Rails 指南</a>:按话题编写的 Rails 参考,经常更新</p>
</li>
</ul>
<h5 id="exercises-prerequisites" class="discrete">练习</h5>
<p>书中有大量练习,强烈建议你在阅读的过程中做这些练习。</p>
<p>为了避免练习妨碍主线,解答通常不会影响后续的代码清单。不过,有极少数的练习解答在后面要用到,此时正文中会给出解答。</p>
<p>如果你想记录自己的答案,想查看解答,可以加入 <a href="http://learnenough.com/story" class="external-link">Learn Enough Society</a>。这是 <a href="http://learnenough.com/story" class="external-link">Learn Enough to Be Dangerous</a> 推出的一项订阅服务,包含本书的特别增强版。</p>
<p>很多练习具有一定难度,我们先来做几个简单的,热热身:</p>
<ol class="arabic">
<li>
<p>Ruby on Rails 的 Ruby gem 托管在哪个网站中?提示:如果不知道,<a href="http://lmgtfy.com/?q=ruby+gem" class="external-link">使用 Google 搜索</a>。</p>
</li>
<li>
<p>Rails 目前的版本号是多少?</p>
</li>
<li>
<p>截至目前,Ruby on Rails 总计被下载了多少次?</p>
</li>
</ol>
</section>
<section data-type="sect2" id="conventions-in-this-book">
<h2><span class="title-label">1.1.2</span> 排版约定</h2>
<p>本书使用的排版方式,很多都不用再做解释。本节说一下那些意义不是很清晰的排版。</p>
<p>书中很多代码清单用到了命令行命令。为了行文简便,所有命令都使用 Unix 风格命令行提示符(一个美元符号),如下所示:</p>
<div data-type="listing">
<div class="highlight language-sh"><pre><code><span class="nv">$ </span><span class="nb">echo</span> <span class="s2">"hello, world!"</span>
hello, world!
</code></pre></div>
</div>
<p>Rails 提供了很多可以在命令行中运行的命令。例如,在 <a class="xref-link" href="#rails-server">1.3.2 节</a>,我们会使用 <code>rails server</code> 命令启动本地 Web 开发服务器:</p>
<div data-type="listing">
<div class="highlight language-sh"><pre><code><span class="nv">$ </span>rails server
</code></pre></div>
</div>
<p>与命令行提示符一样,本书也使用 Unix 惯用的目录分隔符(即斜线 <code>/</code>)。例如,演示应用的配置文件 <code>production.rb</code>,它的路径是:</p>
<div data-type="listing">
<div class="highlight language-text"><pre><code>config/environments/production.rb
</code></pre></div>
</div>
<p>上述文件路径相对于应用的根目录而言。在不同的系统中,根目录会有差别。在云端 IDE 中(<a class="xref-link" href="#development-environment">1.2.1 节</a>),根目录像下面这样:</p>
<div data-type="listing">
<div class="highlight language-text"><pre><code>/home/ec2-user/environment/sample_app/
</code></pre></div>
</div>
<p>所以,<code>production.rb</code> 文件的完整路径是:</p>
<div data-type="listing">
<div class="highlight language-text"><pre><code>/home/ec2-user/environment/sample_app/config/environments/production.rb
</code></pre></div>
</div>
<p>通常,我会省略应用的路径,简写成 <code>config/environments/production.rb</code>。</p>
<p>本书经常需要显示一些来自其他程序的输出。因为系统之间存在细微的差异,你看到的输出结果可能和书中显示的不完全一致,但是无需担心。而且,有些命令在某些操作系统中可能会导致错误,本书不会一一说明这些错误的解决方法,你可以在 Google 中搜索错误消息,自己尝试解决——这也是为现实中的软件开发做准备。如果你在阅读本书的过程中遇到了问题,我建议你看一下<a href="http://railstutorial.org/help" class="external-link">本书网站帮助区</a>中列出的资源。</p>
<p>本书涵盖 Rails 应用测试,所以最好知道某段代码能让测试组件失败(使用红色表示)还是通过(使用绿色表示)。为了方便,导致测试失败的代码使用“<strong class="red">RED</strong>”标记,能让测试通过的代码使用“<strong class="green">GREEN</strong>”标记。</p>
<p>最后,为了方便,本书使用两种排版方式,以便让代码清单更易于理解。第一种,有些代码清单中包含一到多个突出的代码行,如下所示:</p>
<div data-type="listing">
<div class="highlight language-ruby"><pre><code><span class="k">class</span> <span class="nc">User</span> <span class="o"><</span> <span class="no">ApplicationRecord</span>
<span class="n">validates</span> <span class="ss">:name</span><span class="p">,</span> <span class="ss">presence: </span><span class="kp">true</span>
<span class="hll"> <span class="n">validates</span> <span class="ss">:email</span><span class="p">,</span> <span class="ss">presence: </span><span class="kp">true</span></span>
<span class="k">end</span>
</code></pre></div>
</div>
<p>突出的代码行一般用于标出这段代码清单中最重要的新代码,偶尔也用来表示当前代码清单和之前的代码清单的差异。第二种,为了行文简洁,书中很多代码清单中都有竖排的点号,如下所示:</p>
<div data-type="listing">
<div class="highlight language-ruby"><pre><code><span class="k">class</span> <span class="nc">User</span> <span class="o"><</span> <span class="no">ApplicationRecord</span>
<span class="p">.</span>
<span class="nf">.</span>
<span class="p">.</span>
<span class="nf">has_secure_password</span>
<span class="k">end</span>
</code></pre></div>
</div>
<p>这些点号表示省略的代码,不要直接复制。</p>
</section>
</section>
<section data-type="sect1" id="up-and-running">
<h1><span class="title-label">1.2</span> 搭建环境</h1>
<p>就算对经验丰富的 Rails 开发者来说,安装 Ruby、Rails,以及相关的所有软件,也要几经波折。这些问题是由环境的多样性导致的。不同的操作系统、版本号、文本编辑器的偏好设置和集成开发环境(Integrated Development Environment,简称 IDE)等,都会导致环境有所不同。为此,本书提供了两种推荐的解决方案。第一种方案是,按照 <a class="xref-link" href="#prerequisites">1.1.1 节</a>提到的 Learn Enough 系列教程做,最终你能搭建出本书所需的环境。</p>
<p>第二种方案针对初学者,建议这些人使用云端集成开发环境,从而避免安装和配置问题。本书使用的云端 IDE 运行在普通的 Web 浏览器中,因此在不同的平台中表现一致,这对 Rails 开发一直很困难的操作系统(例如 Windows)来说尤其有用。此外,这个云端 IDE 还能保存当前的工作状态,因此我们可以休息一会,然后再从离开的地方继续学习。</p>
<section data-type="sect2" id="development-environment">
<h2><span class="title-label">1.2.1</span> 开发环境</h2>
<p>不同的人有不同的喜好,每个 Rails 程序员都有自己的一套开发环境。为了避免问题复杂化,本书使用一个标准的云端开发环境——<a href="https://c9.io" class="external-link">Cloud9</a>(隶属 Amazon Web Services)。<sup>[<a id="fn-ref-3" href="#fn-3">3</a>]</sup>这个开发环境预装了 Rails 开发所需的大多数软件,包括 Ruby、RubyGems 和 Git(其实,唯有 Rails 要单独安装,而且这么做是有目的的,详情参见 <a class="xref-link" href="#installing-rails">1.2.2 节</a>)。</p>
<p>虽然可以在本地开发应用,但是搭建 Rails 开发环境有一定的挑战,所以我建议多数读者使用这个云端 IDE。如果你确实想在本地开发,可以跟着 Learn Enough Dev Environment to Be Dangerous 中的步骤搭建试试——这个过程能让你体会到技术是复杂的(<a class="xref-link" href="#aside-technical-sophistication">旁注 1.1</a>)。</p>
<p>这个云端 IDE 包含 Web 应用开发所需的三个基本组件:文本编辑器、文件系统浏览器和命令行终端(<a class="xref-link" href="#fig-ide-anatomy">图 1.2</a>)。云端 IDE 中的文本编辑器功能很多,其中一项是“在文件中查找”的全局搜索功能<sup>[<a id="fn-ref-4" href="#fn-4">4</a>]</sup>,我觉得这个功能对大型 Rails 项目来说是必备的。即便最终在现实中你不使用云端 IDE(我始终建议不断学习其他工具),通过它也能了解文本编辑器和其他开发工具的基本功能。</p>
<div id="fig-ide-anatomy" class="figure"><img src="images/chapter1/ide_anatomy_aws.png" alt="ide anatomy aws" /><div class="figcaption"><span class="title-label">图 1.2</span>:云端 IDE 的界面布局</div></div>
<p>这个云端开发环境的使用步骤如下:<sup>[<a id="fn-ref-5" href="#fn-5">5</a>]</sup></p>
<ol class="arabic">
<li>
<p>Cloud9 隶属 Amazon Web Services(AWS),如果已有 AWS 账户,可以直接<a href="https://aws.amazon.com/" class="external-link">登录</a>。为了新建 Cloud9 工作环境,访问 <a href="https://console.aws.amazon.com/" class="external-link">AWS 控制台</a>,在搜索框中输入“Cloud9”。</p>
</li>
<li>
<p>如果没有 AWS 账户,可以到 <a href="https://portal.aws.amazon.com/billing/signup" class="external-link">AWS Cloud9 网站</a>注册一个免费账户。未免被滥用,AWS 要求注册时提供有效的信用卡,不过工作空间是 100% 免费的(写作本书时是免费一年),不会从信用卡上扣钱。可能要等 24 小时待账户激活,不过我就等了十分钟。</p>
</li>
<li>
<p>进入 Cloud9 管理页面后(<a class="xref-link" href="#fig-cloud9-page-aws">图 1.3</a>)后,点击“Create environment”(创建环境),直到看到类似 <a class="xref-link" href="#fig-cloud9-new-workspace">图 1.4</a> 的界面。输入图中所示的信息,然后点击确认按钮,直到 Cloud9 开始配置 IDE(<a class="xref-link" href="#fig-cloud9-ide-aws">图 1.5</a>)。你可能会遇到一个提醒消息,说要“root”用户,目前可以放心忽略。[我们将在 <a class="xref-link" href="chapter13.html#image-upload-in-production">13.4.4 节</a>讨论推荐但较为复杂的做法,叫做 Identity and Access Management(IAM)用户。]</p>
</li>
</ol>
<div id="fig-cloud9-page-aws" class="figure"><img src="images/chapter1/cloud9_page_aws.png" alt="cloud9 page aws" /><div class="figcaption"><span class="title-label">图 1.3</span>:Cloud9 的管理页面</div></div>
<div id="fig-cloud9-new-workspace" class="figure"><img src="images/chapter1/cloud9_name_environment.png" alt="cloud9 name environment" /><div class="figcaption"><span class="title-label">图 1.4</span>:在 AWS Cloud9 中新建一个工作环境</div></div>
<div id="fig-cloud9-ide-aws" class="figure"><img src="images/chapter1/cloud9_ide_aws.png" alt="cloud9 ide aws" /><div class="figcaption"><span class="title-label">图 1.5</span>:默认的云端 IDE</div></div>
<div id="fig-cloud9-two-spaces" class="figure"><img src="images/chapter1/cloud9_two_spaces_aws.png" alt="cloud9 two spaces aws" /><div class="figcaption"><span class="title-label">图 1.6</span>:让 Cloud9 使用两个空格缩进</div></div>
<p>因为使用两个空格缩进几乎是 Ruby 圈通用的约定,所以我建议你修改编辑器的配置,把默认的四个空格改为两个。配置方法是,点击右上角的齿轮图标,然后选择“Code Editor (Ace)”(Ace 代码编辑器),编辑“Soft Tabs”(软制表符)设置,如<a class="xref-link" href="#fig-cloud9-two-spaces">图 1.6</a> 所示。(注意,设置修改后立即生效,无需点击“Save”按钮。)</p>
<p>使用云端 IDE 的用户还要仔细阅读针对 Bitbucket(<a class="xref-link" href="#bitbucket">1.4.3 节</a>)和 Heroku(<a class="xref-link" href="#heroku-setup">1.5.1 节</a>)的安装说明,这部分内容自 Cloud9 服务独立之后有一些变化。</p>
<p>最后,想像 <a class="xref-link" href="chapter3.html#advanced-testing-setup">3.6 节</a>那样设置测试的高级用户可能还要在云端 IDE 中做些额外配置,详情参见 <a class="xref-link" href="chapter3.html#guard">3.6.2 节</a>。</p>
</section>
<section data-type="sect2" id="installing-rails">
<h2><span class="title-label">1.2.2</span> 安装 Rails</h2>
<p>前一节创建的开发环境包含所有软件,但没有 Rails。首先要做些准备工作,以免浪费时间在本地安装 Ruby 文档。这项配置每个系统只需做一次(因此也就无需费心去理解下述命令的作用),如下所示:<sup>[<a id="fn-ref-6" href="#fn-6">6</a>]</sup></p>
<div data-type="listing">
<div class="highlight language-sh"><pre><code><span class="nv">$ </span><span class="nb">printf</span> <span class="s2">"install: --no-rdoc --no-ri</span><span class="se">\n</span><span class="s2">update: --no-rdoc --no-ri</span><span class="se">\n</span><span class="s2">"</span> <span class="o">>></span> ~/.gemrc
</code></pre></div>
</div>
<p>为了安装 Rails,我们要使用包管理器 RubyGems 提供的 <code>gem</code> 命令,在命令行终端里输入<a class="xref-link" href="#listing-installing-rails">代码清单 1.1</a> 所示的命令。(如果在本地系统中开发,在终端窗口中输入这个命令;如果使用云端 IDE,在<a class="xref-link" href="#fig-ide-anatomy">图 1.2</a> 中的“命令行终端”区域输入这个命令。)</p>
<div id="listing-installing-rails" data-type="listing">
<h5><span class="title-label">代码清单 1.1</span>:安装指定版本的 Rails</h5>
<div class="highlight language-sh"><pre><code><span class="nv">$ </span>gem <span class="nb">install </span>rails <span class="nt">-v</span> 5.1.6
</code></pre></div>
</div>
<p><code>-v</code> 旗标的作用是指定安装哪个 Rails 版本。你使用的版本必须和我一样,这样学习的过程中,你我得到的结果才相同。</p>
</section>
</section>
<section data-type="sect1" id="the-hello-application">
<h1><span class="title-label">1.3</span> 第一个应用</h1>
<p>按照计算机编程领域<a href="http://www.catb.org/jargon/html/H/hello-world.html" class="external-link">长期沿用的传统</a>,第一个应用的目的是编写一个“hello, world”程序。具体来讲,我们将创建一个简单的应用,在网页中显示字符串“hello, world!”,在开发环境(<a class="xref-link" href="#hello-world">1.3.4 节</a>)和线上网站(<a class="xref-link" href="#deploying">1.5 节</a>)中都是如此。</p>
<p>Rails 应用一般都从 <code>rails new</code> 命令开始,这个命令会在你指定的目录中创建 Rails 应用的骨架。如果你没使用 <a class="xref-link" href="#development-environment">1.2.1 节</a>推荐的 Cloud9 IDE,首先要新建一个目录,命名为 <code>environment</code>,然后进入目录,如<a class="xref-link" href="#listing-mkdir-rails-projects">代码清单 1.2</a> 所示。(<a class="xref-link" href="#listing-mkdir-rails-projects">代码清单 1.2</a> 中使用了 Unix 命令 <code>cd</code> 和 <code>mkdir</code>,如果你不熟悉这些命令,请阅读<a class="xref-link" href="#aside-unix-commands">旁注 1.3</a>。)</p>
<div id="listing-mkdir-rails-projects" data-type="listing">
<h5><span class="title-label">代码清单 1.2</span>:为 Rails 项目新建一个目录,命名为 <code>environment</code>(在云端环境中不用做这一步)</h5>
<div class="highlight language-sh"><pre><code><span class="nv">$ </span><span class="nb">cd</span> <span class="c"># 进入家目录</span>
<span class="nv">$ </span><span class="nb">mkdir </span>environment <span class="c"># 新建 environment 目录</span>
<span class="nv">$ </span><span class="nb">cd </span>environment/ <span class="c"># 进入 environment 目录</span>
</code></pre></div>
</div>
<div data-type="sidebar" id="aside-unix-commands" class="sidebar">
<h5><span class="title-label">旁注 1.3</span>:Unix 命令行速成课</h5>
<p>使用 Windows 和 macOS(数量较少,但增长势头迅猛)的用户可能对 Unix 命令行不熟悉。如果使用推荐的云端环境,很幸运,这个环境提供了 Unix(Linux)命令行——在标准的 <a href="http://en.wikipedia.org/wiki/Shell_(computing)" class="external-link">shell 命令行界面</a>中运行的 <a href="http://en.wikipedia.org/wiki/Bash_(Unix_shell)" class="external-link">Bash</a>。</p>
<p>命令行的基本思想很简单:使用简短的命令执行很多操作,例如创建目录(<code>mkdir</code>),移动和复制文件(<code>mv</code> 和 <code>cp</code>),以及变换目录浏览文件系统(<code>cd</code>)。主要使用图形化界面(Graphical User Interface,简称 GUI)的用户可能觉得命令行落后,其实是被表象蒙蔽了:命令行是开发者最强大的工具之一。其实,你经常会看到经验丰富的开发者开着多个终端窗口,运行着多个命令行 shell。</p>
<p>这是一门很深的学问,但在本书中只会用到一些最常用的 Unix 命令行命令,如<a class="xref-link" href="#table-unix-commands">表 1.1</a> 所示。若想更深入地学习 Unix 命令行,请阅读 <a href="http://learnenough.com/" class="external-link">Learn Enough</a> 系列教程的第一本,《<a href="http://learnenough.com/command-line-tutorial" class="external-link">Learn Enough Command Line to Be Dangerous</a>》。</p>
</div>
<table id="table-unix-commands" class="tableblock frame-all grid-all" style="width: 100%;">
<caption><span class="title-label">表 1.1</span>:一些常用的 Unix 命令</caption>
<colgroup>
<col style="width: 33.3333%;" />
<col style="width: 33.3333%;" />
<col style="width: 33.3334%;" />
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">作用</th>
<th class="tableblock halign-left valign-top">命令</th>
<th class="tableblock halign-left valign-top">示例</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">列出内容</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>ls</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>$ ls -l</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">新建目录</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>mkdir <dirname></code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>$ mkdir environment</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">变换目录</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>cd <dirname></code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>$ cd environment/</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">进入上层目录</p></td>
<td class="tableblock halign-left valign-top"></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>$ cd ..</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">进入家目录</p></td>
<td class="tableblock halign-left valign-top"></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>$ cd ~</code> 或 <code>$ cd</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">进入家目录中的文件夹</p></td>
<td class="tableblock halign-left valign-top"></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>$ cd ~/environment/</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">移动文件(重命名)</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>mv <source> <target></code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>$ mv foo bar</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">复制文件</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>cp <source> <target></code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>$ cp foo bar</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">删除文件</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>rm <file></code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>$ rm foo</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">删除空目录</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>rmdir <directory></code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>$ rmdir environment/</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">删除非空目录</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>rm -rf <directory></code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>$ rm -rf tmp/</code></p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock">拼接并显示文件的内容</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>cat <file></code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>$ cat ~/.ssh/id_rsa.pub</code></p></td>
</tr>
</tbody>
</table>
<p>不管在本地还是在云端 IDE 中,下一步都是使用<a class="xref-link" href="#listing-rails-command">代码清单 1.3</a>中的命令创建第一个应用。注意,在这个代码清单中,我们明确指定了 Rails 版本。这么做的目的是,确保使用<a class="xref-link" href="#listing-installing-rails">代码清单 1.1</a> 中安装的 Rails 版本来创建应用的文件结构。(执行<a class="xref-link" href="#listing-rails-command">代码清单 1.3</a> 中的命令时,如果返回“Could not find 'railties'”这样的错误,说明你没安装正确的 Rails 版本,请再次确认你安装 Rails 时执行的命令与<a class="xref-link" href="#listing-installing-rails">代码清单 1.1</a> 完全一样。)</p>
<div id="listing-rails-command" data-type="listing">
<h5><span class="title-label">代码清单 1.3</span>:执行 <code>rails new</code> 命令(明确指定版本号)</h5>
<div class="highlight language-sh"><pre><code><span class="hll"><span class="nv">$ </span><span class="nb">cd</span> ~/environment</span>
<span class="hll"><span class="nv">$ </span>rails _5.1.6_ new hello_app</span>
create
create README.md
create Rakefile
create config.ru
create .gitignore
create Gemfile
create app
create app/assets/config/manifest.js
create app/assets/javascripts/application.js
create app/assets/javascripts/cable.js
create app/assets/stylesheets/application.css
create app/channels/application_cable/channel.rb
create app/channels/application_cable/connection.rb
create app/controllers/application_controller.rb
<span class="nb">.</span>
<span class="nb">.</span>
<span class="nb">.</span>
create tmp/cache/assets
create vendor/assets/javascripts
create vendor/assets/javascripts/.keep
create vendor/assets/stylesheets
create vendor/assets/stylesheets/.keep
remove config/initializers/cors.rb
run bundle <span class="nb">install</span>
Fetching gem metadata from https://rubygems.org/..........
Fetching additional metadata from https://rubygems.org/..
Resolving dependencies...
Installing rake 11.1.2
Using concurrent-ruby 1.0.2
<span class="nb">.</span>
<span class="nb">.</span>
<span class="nb">.</span>
Your bundle is <span class="nb">complete</span><span class="o">!</span>
Use <span class="sb">`</span>bundle show <span class="o">[</span>gemname]<span class="sb">`</span> to see where a bundled gem is installed.
run bundle <span class="nb">exec </span>spring binstub <span class="nt">--all</span>
<span class="k">*</span> bin/rake: spring inserted
<span class="k">*</span> bin/rails: spring inserted
</code></pre></div>
</div>
<p>如<a class="xref-link" href="#listing-rails-command">代码清单 1.3</a> 所示,执行 <code>rails new</code> 命令生成所有文件之后,会自动执行 <code>bundle install</code> 命令。我们将在 <a class="xref-link" href="#bundler">1.3.1 节</a>说明这个命令的作用。</p>
<p>留意一下 <code>rails new</code> 命令创建的文件和目录。这个标准的文件结构(<a class="xref-link" href="#fig-directory-structure-rails">图 1.7</a>)是 Rails 的众多优势之一:让你从零开始快速创建一个可运行的功能最简的应用。而且,所有 Rails 应用都使用这种文件结构,阅读他人的代码时很快就能理清头绪。</p>
<div id="fig-directory-structure-rails" class="figure"><img src="images/chapter1/directory_structure_rails_4th_edition.png" alt="directory structure rails 4th edition" /><div class="figcaption"><span class="title-label">图 1.7</span>:新建 Rails 应用时生成的目录结构</div></div>
<p>这些文件的作用如<a class="xref-link" href="#table-rails-directory-structure">表 1.2</a> 所示,本书后面的内容将介绍其中大多数文件和目录。从 <a class="xref-link" href="chapter5.html#the-asset-pipeline">5.2.1 节</a>开始,我们将介绍 <code>app/assets</code> 目录,这是 Asset Pipeline 的一部分。Asset Pipeline 简化了层叠样式表和 JavaScript 等静态资源文件的组织和部署方式。</p>
<table id="table-rails-directory-structure" class="tableblock frame-all grid-all" style="width: 100%;">
<caption><span class="title-label">表 1.2</span>:Rails 目录结构简介</caption>
<colgroup>
<col style="width: 30%;" />
<col style="width: 70%;" />
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top">文件/文件夹</th>
<th class="tableblock halign-left valign-top">作用</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>app/</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">应用的核心文件,包含模型、视图、控制器和辅助方法</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>app/assets</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">应用的静态资源文件,例如层叠样式表(CSS)、JavaScript 文件和图像</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>bin/</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">可执行的二进制文件</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>config/</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">应用的配置</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>db/</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">数据库文件</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>doc/</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">应用的文档</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>lib/</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">代码库模块文件</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>lib/assets</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">代码库的静态资源文件,例如层叠样式表(CSS)、JavaScript 文件和图像</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>log/</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">应用的日志文件</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>public/</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">公共(如浏览器)可访问的文件,例如错误页面</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>bin/rails</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">生成代码、打开终端会话或启动本地服务器的程序</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>test/</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">应用的测试</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>tmp/</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">临时文件</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>vendor/</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">第三方代码,例如插件和 gem</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>vendor/assets</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">第三方静态资源文件,例如层叠样式表(CSS)、JavaScript 文件和图像</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>README.md</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">应用简介</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>Rakefile</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">使用 <code>rake</code> 命令执行的实用任务</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>Gemfile</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">应用所需的 gem</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>Gemfile.lock</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">gem 列表,确保这个应用的副本使用相同版本的 gem</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>config.ru</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="http://rack.github.io/" class="external-link">Rack 中间件</a>的配置文件</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>.gitignore</code></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">Git 忽略的文件模式</p></td>
</tr>
</tbody>
</table>
<section data-type="sect2" id="bundler">
<h2><span class="title-label">1.3.1</span> Bundler</h2>
<p>创建完一个新的 Rails 应用后,下一步是使用 Bundler 安装和引入该应用所需的 gem。<a class="xref-link" href="#the-hello-application">1.3 节</a>简单提到过,执行 <code>rails new</code> 命令时会自动运行 Bundler(<code>bundle install</code> 命令)。不过这一节,我们要修改应用默认使用的 gem,然后再次运行 Bundler。首先,在文本编辑器中打开 <code>Gemfile</code> 文件。(在云端 IDE 中要点击文件系统浏览器中的应用目录,然后双击 <code>Gemfile</code> 文件。)虽然具体的版本号和内容或许有所不同,但大概与<a class="xref-link" href="#listing-default-gemfile">代码清单 1.4</a> 和<a class="xref-link" href="#fig-cloud9-gemfile">图 1.8</a> 差不多。(这个文件中的内容是 Ruby 代码,现在先不关心句法,<a class="xref-link" href="chapter4.html#rails-flavored-ruby">第 4 章</a>会详细介绍 Ruby。)如果你没看到如<a class="xref-link" href="#fig-cloud9-gemfile">图 1.8</a> 所示的文件和目录,点击文件浏览器中的齿轮图标,然后选择“Refresh File Tree”(刷新文件树)。(通常,如果某个文件或目录没出现,就可以刷新文件树。)<sup>[<a id="fn-ref-7" href="#fn-7">7</a>]</sup></p>
<div id="listing-default-gemfile" data-type="listing">
<h5><span class="title-label">代码清单 1.4</span>:<code>hello_app</code> 目录中默认生成的 <code>Gemfile</code> 文件</h5>
<div class="highlight language-ruby"><pre><code><span class="n">source</span> <span class="s1">'https://rubygems.org'</span>
<span class="n">git_source</span><span class="p">(</span><span class="ss">:github</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">repo_name</span><span class="o">|</span>
<span class="n">repo_name</span> <span class="o">=</span> <span class="s2">"</span><span class="si">#{</span><span class="n">repo_name</span><span class="si">}</span><span class="s2">/</span><span class="si">#{</span><span class="n">repo_name</span><span class="si">}</span><span class="s2">"</span> <span class="k">unless</span> <span class="n">repo_name</span><span class="p">.</span><span class="nf">include?</span><span class="p">(</span><span class="s2">"/"</span><span class="p">)</span>
<span class="s2">"https://github.com/</span><span class="si">#{</span><span class="n">repo_name</span><span class="si">}</span><span class="s2">.git"</span>
<span class="k">end</span>
<span class="c1"># Bundle edge Rails instead: gem 'rails', github: 'rails/rails'</span>
<span class="n">gem</span> <span class="s1">'rails'</span><span class="p">,</span> <span class="s1">'~> 5.1.6'</span>
<span class="c1"># Use sqlite3 as the database for Active Record</span>
<span class="n">gem</span> <span class="s1">'sqlite3'</span>
<span class="c1"># Use Puma as the app server</span>
<span class="n">gem</span> <span class="s1">'puma'</span><span class="p">,</span> <span class="s1">'~> 3.7'</span>
<span class="c1"># Use SCSS for stylesheets</span>
<span class="n">gem</span> <span class="s1">'sass-rails'</span><span class="p">,</span> <span class="s1">'~> 5.0'</span>
<span class="c1"># Use Uglifier as compressor for JavaScript assets</span>
<span class="n">gem</span> <span class="s1">'uglifier'</span><span class="p">,</span> <span class="s1">'>= 1.3.0'</span>
<span class="c1"># See https://github.com/rails/execjs#readme for more supported runtimes</span>
<span class="c1"># gem 'therubyracer', platforms: :ruby</span>
<span class="c1"># Use CoffeeScript for .coffee assets and views</span>
<span class="n">gem</span> <span class="s1">'coffee-rails'</span><span class="p">,</span> <span class="s1">'~> 4.2'</span>
<span class="c1"># Turbolinks makes navigating your web application faster.</span>
<span class="c1"># Read more: https://github.com/turbolinks/turbolinks</span>
<span class="n">gem</span> <span class="s1">'turbolinks'</span><span class="p">,</span> <span class="s1">'~> 5'</span>
<span class="c1"># Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder</span>
<span class="n">gem</span> <span class="s1">'jbuilder'</span><span class="p">,</span> <span class="s1">'~> 2.5'</span>
<span class="c1"># Use Redis adapter to run Action Cable in production</span>
<span class="c1"># gem 'redis', '~> 3.0'</span>
<span class="c1"># Use ActiveModel has_secure_password</span>
<span class="c1"># gem 'bcrypt', '~> 3.1.7'</span>
<span class="c1"># Use Capistrano for deployment</span>
<span class="c1"># gem 'capistrano-rails', group: :development</span>
<span class="n">group</span> <span class="ss">:development</span><span class="p">,</span> <span class="ss">:test</span> <span class="k">do</span>
<span class="c1"># Call 'byebug' anywhere in the code to stop execution</span>
<span class="c1"># and get a debugger console</span>
<span class="n">gem</span> <span class="s1">'byebug'</span><span class="p">,</span> <span class="ss">platforms: </span><span class="p">[</span><span class="ss">:mri</span><span class="p">,</span> <span class="ss">:mingw</span><span class="p">,</span> <span class="ss">:x64_mingw</span><span class="p">]</span>
<span class="c1"># Adds support for Capybara system testing and selenium driver</span>
<span class="n">gem</span> <span class="s1">'capybara'</span><span class="p">,</span> <span class="s1">'~> 2.13'</span>
<span class="n">gem</span> <span class="s1">'selenium-webdriver'</span>
<span class="k">end</span>
<span class="n">group</span> <span class="ss">:development</span> <span class="k">do</span>
<span class="c1"># Access an IRB console on exception pages or by using</span>
<span class="c1"># <%= console %> anywhere in the code.</span>
<span class="n">gem</span> <span class="s1">'web-console'</span>
<span class="n">gem</span> <span class="s1">'listen'</span><span class="p">,</span> <span class="s1">'>= 3.0.5'</span><span class="p">,</span> <span class="s1">'< 3.2'</span>
<span class="c1"># Spring speeds up development by keeping your application running</span>
<span class="c1"># in the background. Read more: https://github.com/rails/spring</span>
<span class="n">gem</span> <span class="s1">'spring'</span>
<span class="n">gem</span> <span class="s1">'spring-watcher-listen'</span><span class="p">,</span> <span class="s1">'~> 2.0.0'</span>
<span class="k">end</span>
<span class="c1"># Windows does not include zoneinfo files, so bundle the tzinfo-data gem</span>
<span class="n">gem</span> <span class="s1">'tzinfo-data'</span><span class="p">,</span> <span class="ss">platforms: </span><span class="p">[</span><span class="ss">:mingw</span><span class="p">,</span> <span class="ss">:mswin</span><span class="p">,</span> <span class="ss">:x64_mingw</span><span class="p">,</span> <span class="ss">:jruby</span><span class="p">]</span>
</code></pre></div>
</div>
<p>其中很多行代码都用 <code>#</code> 符号注释掉了(<a class="xref-link" href="chapter4.html#comments">4.2.1 节</a>),这些代码放在这是为了告诉你一些常用的 gem,也是为了展示 Bundler 的句法。现在,除了这些默认的 gem 之外,我们还不需要其他的 gem。</p>
<p>如果没在 <code>gem</code> 指令中指定版本号,Bundler 会自动安装最新版。下面就是一例:</p>
<div data-type="listing">
<div class="highlight language-ruby"><pre><code><span class="n">gem</span> <span class="s1">'sqlite3'</span>
</code></pre></div>
</div>
<div id="fig-cloud9-gemfile" class="figure"><img src="images/chapter1/cloud9_gemfile_aws.png" alt="cloud9 gemfile aws" /><div class="figcaption"><span class="title-label">图 1.8</span>:在文本编辑器中打开默认生成的 <code>Gemfile</code> 文件</div></div>
<p>还有两种常用的方法,用于指定 gem 版本的范围,在一定程度上控制 Rails 使用的版本。首先看下面这行代码:</p>
<div data-type="listing">
<div class="highlight language-ruby"><pre><code><span class="n">gem</span> <span class="s1">'uglifier'</span><span class="p">,</span> <span class="s1">'>= 1.3.0'</span>
</code></pre></div>
</div>
<p>这行代码的意思是,安装版本号大于或等于 <code>1.3.0</code> 的 <code>uglifier</code>(作用是压缩 Asset Pipeline 中的文件),就算是 <code>7.2</code> 版也会安装。第二种方法如下所示:</p>
<div data-type="listing">
<div class="highlight language-ruby"><pre><code><span class="n">gem</span> <span class="s1">'coffee-rails'</span><span class="p">,</span> <span class="s1">'~> 4.0.0'</span>
</code></pre></div>
</div>
<p>这行代码的意思是,安装版本号大于或等于 <code>4.0.0</code>,但不大于 <code>4.1</code> 的 <code>coffee-rails</code>。也就是说,<code>>=</code> 的意思是始终安装最新版;<code>~> 4.0.0</code> 的意思是安装 <code>4.0.1</code> 版(如果有这一版的话),而不安装 <code>4.1.0</code> 版。不过,经验告诉我们,即使是最小版本的升级也可能导致错误,所以在本教程中我们基本上会为所有的 gem 都指定精确的版本号。你可以使用任何 gem 的最新版本,还可以在 <code>Gemfile</code> 文件中使用 <code>~></code>(一般推荐有经验的用户使用),但事先提醒你,这可能会导致本教程开发的应用表现异常。</p>
<p>修改<a class="xref-link" href="#listing-default-gemfile">代码清单 1.4</a> 中的 <code>Gemfile</code> 文件,换用精确的版本号,得到的结果如<a class="xref-link" href="#listing-gemfile-sqlite-version">代码清单 1.5</a> 所示。注意,借此机会我们还变动了 <code>sqlite3</code> gem 的位置,只在开发环境和测试环境(<a class="xref-link" href="chapter7.html#rails-environments">7.1.1 节</a>)中安装,从而避免跟 Heroku 所用的数据库冲突(<a class="xref-link" href="#deploying">1.5 节</a>)。</p>
<div id="listing-gemfile-sqlite-version" data-type="listing">
<h5><span class="title-label">代码清单 1.5</span>:在 <code>Gemfile</code> 文件中为所有 gem 指定精确的版本号</h5>
<div class="highlight language-ruby"><pre><code><span class="n">source</span> <span class="s1">'https://rubygems.org'</span>
<span class="n">gem</span> <span class="s1">'rails'</span><span class="p">,</span> <span class="s1">'5.1.6'</span>
<span class="n">gem</span> <span class="s1">'puma'</span><span class="p">,</span> <span class="s1">'3.9.1'</span>
<span class="n">gem</span> <span class="s1">'sass-rails'</span><span class="p">,</span> <span class="s1">'5.0.6'</span>
<span class="n">gem</span> <span class="s1">'uglifier'</span><span class="p">,</span> <span class="s1">'3.2.0'</span>
<span class="n">gem</span> <span class="s1">'coffee-rails'</span><span class="p">,</span> <span class="s1">'4.2.2'</span>
<span class="n">gem</span> <span class="s1">'jquery-rails'</span><span class="p">,</span> <span class="s1">'4.3.1'</span>
<span class="n">gem</span> <span class="s1">'turbolinks'</span><span class="p">,</span> <span class="s1">'5.0.1'</span>
<span class="n">gem</span> <span class="s1">'jbuilder'</span><span class="p">,</span> <span class="s1">'2.7.0'</span>
<span class="n">group</span> <span class="ss">:development</span><span class="p">,</span> <span class="ss">:test</span> <span class="k">do</span>
<span class="n">gem</span> <span class="s1">'sqlite3'</span><span class="p">,</span> <span class="s1">'1.3.13'</span>
<span class="n">gem</span> <span class="s1">'byebug'</span><span class="p">,</span> <span class="s1">'9.0.6'</span><span class="p">,</span> <span class="ss">platform: :mri</span>
<span class="k">end</span>
<span class="n">group</span> <span class="ss">:development</span> <span class="k">do</span>
<span class="n">gem</span> <span class="s1">'web-console'</span><span class="p">,</span> <span class="s1">'3.5.1'</span>
<span class="n">gem</span> <span class="s1">'listen'</span><span class="p">,</span> <span class="s1">'3.1.5'</span>
<span class="n">gem</span> <span class="s1">'spring'</span><span class="p">,</span> <span class="s1">'2.0.2'</span>
<span class="n">gem</span> <span class="s1">'spring-watcher-listen'</span><span class="p">,</span> <span class="s1">'2.0.1'</span>
<span class="k">end</span>
<span class="c1"># Windows does not include zoneinfo files, so bundle the tzinfo-data gem</span>
<span class="n">gem</span> <span class="s1">'tzinfo-data'</span><span class="p">,</span> <span class="ss">platforms: </span><span class="p">[</span><span class="ss">:mingw</span><span class="p">,</span> <span class="ss">:mswin</span><span class="p">,</span> <span class="ss">:x64_mingw</span><span class="p">,</span> <span class="ss">:jruby</span><span class="p">]</span>
</code></pre></div>
</div>
<p>把<a class="xref-link" href="#listing-gemfile-sqlite-version">代码清单 1.5</a> 中的内容写入应用的 <code>Gemfile</code> 文件之后,执行 <code>bundle install</code> 命令<sup>[<a id="fn-ref-8" href="#fn-8">8</a>]</sup>安装这些 gem:</p>
<div data-type="listing">
<div class="highlight language-sh"><pre><code><span class="hll"><span class="nv">$ </span><span class="nb">cd </span>hello_app/</span>
<span class="hll"><span class="nv">$ </span>bundle <span class="nb">install</span></span>
Fetching <span class="nb">source </span>index <span class="k">for </span>https://rubygems.org/
<span class="nb">.</span>
<span class="nb">.</span>
.
</code></pre></div>
</div>
<p><code>bundle install</code> 命令可能要执行一会儿,不过结束后我们的应用就能运行了。</p>
<p>顺便说一下,执行 <code>bundle install</code> 命令时可能会提醒你先执行 <code>bundle update</code> 命令。此时,应该按照提醒,先执行 <code>bundle update</code>。(在事情不能按计划进行时要学着不要惊慌,这是应对“技术是复杂的”这一问题的关键。冷静下来你便会惊奇地发现,“错误”消息中往往包含修正问题的具体说明。)</p>
</section>
<section data-type="sect2" id="rails-server">
<h2><span class="title-label">1.3.2</span> <code>rails server</code></h2>
<p>运行完 <a class="xref-link" href="#the-hello-application">1.3 节</a>中的 <code>rails new</code> 命令和 <a class="xref-link" href="#bundler">1.3.1 节</a>中的 <code>bundle install</code> 命令之后,我们的应用就可以运行了。但是怎么运行呢?Rails 自带了一个命令行程序(或叫脚本),可以运行一个本地服务器,协助我们的开发工作。这个命令是 <code>rails server</code>(<a class="xref-link" href="#listing-local-server">代码清单 1.6</a>)。</p>
<div id="listing-local-server" data-type="listing">
<h5><span class="title-label">代码清单 1.6</span>:运行 Rails 服务器</h5>
<div class="highlight language-sh"><pre><code><span class="hll"><span class="nv">$ </span><span class="nb">cd</span> ~/environment/hello_app/</span>
<span class="hll"><span class="nv">$ </span>rails server</span>
<span class="o">=></span> Booting Puma
<span class="o">=></span> Rails application starting on http://localhost:3000
<span class="o">=></span> Run <span class="sb">`</span>rails server <span class="nt">-h</span><span class="sb">`</span> <span class="k">for </span>more startup options
<span class="o">=></span> Ctrl-C to shutdown server
</code></pre></div>
</div>
<p>如果系统提示缺少 JavaScript 运行时,访问 <a href="https://github.com/sstephenson/execjs" class="external-link">execjs 在 GitHub 中的项目主页</a>,查看解决方法。我非常推荐安装 <a href="http://nodejs.org/" class="external-link">Node.js</a>。</p>
<p>我建议你在另一个终端标签页中执行 <code>rails server</code> 命令,这样你就可以继续在第一个标签页中执行其他命令了,如<a class="xref-link" href="#fig-new-terminal-tab">图 1.9</a> 和<a class="xref-link" href="#fig-rails-server-new-tab">图 1.10</a> 所示。(如果你已经在第一个标签页中启动了服务器,可以按 Ctrl-C 键关闭服务器。<sup>[<a id="fn-ref-9" href="#fn-9">9</a>]</sup>)在本地环境中,把 <a href="http://0.0.0.0:3000" class="external-link bare">http://0.0.0.0:3000</a> 粘贴到浏览器的地址栏中;在云端 IDE 中,打开“Preview”(预览)菜单,点击“Preview Running Application”(预览运行中的应用)(<a class="xref-link" href="#fig-share-workspace">图 1.11</a>),在新窗口或标签页中打开(<a class="xref-link" href="#fig-full-browser-window-aws">图 1.12</a>)。在这两种环境中,显示的页面应该都与<a class="xref-link" href="#fig-riding-rails">图 1.13</a> 类似。</p>
<h5 id="exercises-rails-server" class="discrete">练习</h5>
<ol class="arabic">
<li>
<p>根据 Rails 默认页面中的信息,你的系统使用的 Ruby 是哪个版本?在命令行中执行 <code>ruby -v</code> 命令确认一下。</p>
</li>
<li>
<p>Rails 是哪个版本?确认是否与<a class="xref-link" href="#listing-installing-rails">代码清单 1.1</a> 中指定的版本一样。</p>
</li>
</ol>
<div id="fig-new-terminal-tab" class="figure"><img src="images/chapter1/new_terminal_tab_aws.png" alt="new terminal tab aws" /><div class="figcaption"><span class="title-label">图 1.9</span>:再打开一个终端标签页</div></div>
<div id="fig-rails-server-new-tab" class="figure"><img src="images/chapter1/rails_server_new_tab_aws.png" alt="rails server new tab aws" /><div class="figcaption"><span class="title-label">图 1.10</span>:在另一个标签页中运行 Rails 服务器</div></div>
<div id="fig-share-workspace" class="figure"><img src="images/chapter1/share_workspace_aws.png" alt="share workspace aws" /><div class="figcaption"><span class="title-label">图 1.11</span>:分享运行在云端工作空间中的本地服务器</div></div>
<div id="fig-full-browser-window-aws" class="figure"><img src="images/chapter1/full_browser_window_aws.png" alt="full browser window aws" /><div class="figcaption"><span class="title-label">图 1.12</span>:在新窗口或标签页中打开运行中的应用</div></div>
<div id="fig-riding-rails" class="figure"><img src="images/chapter1/riding_rails_4th_edition.png" alt="riding rails 4th edition" /><div class="figcaption"><span class="title-label">图 1.13</span>:执行 <code>rails server</code> 命令后看到的 Rails 默认页面</div></div>
</section>
<section data-type="sect2" id="mvc">
<h2><span class="title-label">1.3.3</span> 模型-视图-控制器</h2>
<p>在初期阶段,概览一下 Rails 应用的工作方式(<a class="xref-link" href="#fig-mvc">图 1.14</a>)多少会有些帮助。你可能已经注意到了,在 Rails 应用的标准文件结构中有一个名为 <code>app/</code> 的目录(<a class="xref-link" href="#fig-directory-structure-rails">图 1.7</a>),其中有三个子目录:<code>models</code>、<code>views</code> 和 <code>controllers</code>。这表明 Rails 采用了“模型-视图-控制器”(简称 MVC)架构模式。这种模式把应用中的数据(例如用户信息)与显示数据的代码分开,这是图形用户界面(Graphical User Interface,简称 GUI)常用的架构方式。</p>
<div id="fig-mvc" class="figure"><img src="images/chapter1/mvc_schematic.png" alt="mvc schematic" /><div class="figcaption"><span class="title-label">图 1.14</span>:MVC 架构图解</div></div>
<p>与 Rails 应用交互时,浏览器发出一个请求(request),Web 服务器收到请求之后将其传给 Rails 应用的控制器,决定下一步做什么。某些情况下,控制器会立即渲染视图(view),生成 HTML,然后发送给浏览器。在动态网站中,更常见的是控制器与模型(model)交互。模型是一个 Ruby 对象,表示网站中的一个元素(例如一个用户),并且负责与数据库通信。与模型交互后,控制器再渲染视图,把生成的 HTML 返回给浏览器。</p>
<p>如果你觉得这些内容有点抽象,不用担心,后面会进一步讨论这些概念。在 <a class="xref-link" href="#hello-world">1.3.4 节</a>,我们将首次使用 MVC 架构编写应用;在 <a class="xref-link" href="chapter2.html#mvc-in-action">2.2.2 节</a>中,会以一个应用为例较为深入地讨论 MVC;在最后那个演示应用中会使用完整的 MVC 架构。从 <a class="xref-link" href="chapter3.html#static-pages">3.2 节</a>开始,介绍控制器和视图;从 <a class="xref-link" href="chapter6.html#user-model">6.1 节</a>开始,介绍模型;<a class="xref-link" href="chapter7.html#a-users-resource">7.1.2 节</a>则把这三部分结合在一起使用。</p>
</section>
<section data-type="sect2" id="hello-world">
<h2><span class="title-label">1.3.4</span> Hello, world!</h2>
<p>接下来我们要对这个使用 MVC 框架开发的第一个应用做些小改动:添加一个控制器动作(controller action),渲染字符串“hello, world!”,以此替代 Rails 的默认页面(<a class="xref-link" href="#fig-riding-rails">图 1.13</a>)。(从 <a class="xref-link" href="chapter2.html#mvc-in-action">2.2.2 节</a>开始,我们将深入学习控制器动作。)</p>
<p>从“控制器动作”这个名字可以看出,动作在控制器中定义。我们要在 <code>Application</code> 控制器中定义这个动作,将其命名为 <code>hello</code>。其实,现在我们的应用只有这一个控制器。执行下述命令可以验证这一点:</p>
<div data-type="listing">
<div class="highlight language-sh"><pre><code><span class="nv">$ </span><span class="nb">ls </span>app/controllers/<span class="k">*</span>_controller.rb
</code></pre></div>
</div>
<p><code>hello</code> 动作的定义如<a class="xref-link" href="#listing-hello-action">代码清单 1.7</a> 所示,它调用 <code>render</code> 方法返回 HTML 文本“hello, world!”。(现在先不管 Ruby 的句法,<a class="xref-link" href="chapter4.html#rails-flavored-ruby">第 4 章</a>会详细介绍。)</p>
<div id="listing-hello-action" data-type="listing">
<h5><span class="title-label">代码清单 1.7</span>:在 <code>ApplicationController</code> 中添加 <code>hello</code> 动作</h5>
<div class="source-file">app/controllers/application_controller.rb</div>
<div class="highlight language-ruby"><pre><code><span class="k">class</span> <span class="nc">ApplicationController</span> <span class="o"><</span> <span class="no">ActionController</span><span class="o">::</span><span class="no">Base</span>
<span class="n">protect_from_forgery</span> <span class="ss">with: :exception</span>
<span class="k">def</span> <span class="nf">hello</span>
<span class="hll"> <span class="n">render</span> <span class="ss">html: </span><span class="s2">"hello, world!"</span></span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre></div>
</div>
<p>定义好返回所需字符串的动作之后,我们要告诉 Rails 使用这个动作,而不再显示默认的首页(<a class="xref-link" href="#fig-riding-rails">图 1.13</a>)。为此,我们要修改 Rails 路由器(router)。路由器在控制器之前(<a class="xref-link" href="#fig-mvc">图 1.14</a>),决定浏览器发给应用的请求由哪个动作处理。(简单起见,<a class="xref-link" href="#fig-mvc">图 1.14</a> 中省略了路由器,从 <a class="xref-link" href="chapter2.html#mvc-in-action">2.2.2 节</a>开始会详细介绍路由器。)具体而言,我们要修改默认的首页,也就是根路由(root route)。这个路由决定根 URL 显示哪个页面。根 URL 是 http://www.example.com/ 这种形式(最后一个斜线后面没有任何内容),所以经常简化使用 /(斜线)表示。</p>
<p>如<a class="xref-link" href="#listing-default-root-route">代码清单 1.8</a> 所示,Rails 路由文件(<code>config/routes.rb</code>)中有一行注释,让我们阅读 <a href="http://guides.rubyonrails.org/routing.html" class="external-link">Rails 指南中讲解路由的文章</a>。那篇文章说明了如何定义根路由,句法如下:</p>
<div data-type="listing">
<div class="highlight language-ruby"><pre><code><span class="n">root</span> <span class="s1">'controller_name#action_name'</span>
</code></pre></div>
</div>
<p>这里,控制器的名称是 <code>application</code>,动作的名称是 <code>hello</code>,因此根路由要像<a class="xref-link" href="#listing-hello-root-route">代码清单 1.9</a> 那样定义。</p>
<div id="listing-default-root-route" data-type="listing">
<h5><span class="title-label">代码清单 1.8</span>:默认的路由文件(我重新编排了)</h5>
<div class="source-file">config/routes.rb</div>
<div class="highlight language-ruby"><pre><code><span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">routes</span><span class="p">.</span><span class="nf">draw</span> <span class="k">do</span>
<span class="c1"># For details on the DSL available within this file,</span>
<span class="c1"># see http://guides.rubyonrails.org/routing.html</span>
<span class="k">end</span>
</code></pre></div>
</div>
<div id="listing-hello-root-route" data-type="listing">
<h5><span class="title-label">代码清单 1.9</span>:设置根路由</h5>
<div class="source-file">config/routes.rb</div>
<div class="highlight language-ruby"><pre><code><span class="no">Rails</span><span class="p">.</span><span class="nf">application</span><span class="p">.</span><span class="nf">routes</span><span class="p">.</span><span class="nf">draw</span> <span class="k">do</span>
<span class="n">root</span> <span class="s1">'application#hello'</span>
<span class="k">end</span>
</code></pre></div>
</div>
<div id="fig-hello-world-hello-app" class="figure"><img src="images/chapter1/hello_world_hello_app.png" alt="hello world hello app" /><div class="figcaption"><span class="title-label">图 1.15</span>:在浏览器中查看显示“hello, world!”的页面</div></div>
<p>有了<a class="xref-link" href="#listing-hello-action">代码清单 1.7</a> 和<a class="xref-link" href="#listing-hello-root-route">代码清单 1.9</a> 中的代码,根路由就会按照我们的要求显示“hello, world!”了,如<a class="xref-link" href="#fig-hello-world-hello-app">图 1.15</a> 所示。<sup>[<a id="fn-ref-10" href="#fn-10">10</a>]</sup></p>
<h5 id="exercises-hello-world" class="discrete">练习</h5>
<ol class="arabic">
<li>
<p>把 <code>hello</code> 动作(<a class="xref-link" href="#listing-hello-action">代码清单 1.7</a>)中的“hello, world!”改成“hola, mundo!”。</p>
</li>
<li>
<p>使用倒置的感叹号(如“¡Hola, mundo!”中的第一个字符),证明 Rails 支持非 <a href="http://en.wikipedia.org/wiki/ASCII" class="external-link">ASCII</a> 字符。<sup>[<a id="fn-ref-11" href="#fn-11">11</a>]</sup>结果如<a class="xref-link" href="#fig-hola-mundo">图 1.16</a> 所示。在 Mac 中输入 ¡ 字符的方法是按 Option-1 键;此外,也可以直接把这个字符复制粘贴到编辑器中。</p>
</li>
<li>
<p>按照编写 <code>hello</code> 动作的方式(<a class="xref-link" href="#listing-hello-action">代码清单 1.7</a>),再添加一个动作,命名为 <code>goodbye</code>,渲染文本“goodbye, world!”。然后修改路由文件(<a class="xref-link" href="#listing-hello-root-route">代码清单 1.9</a>),把根路由改成 <code>goodbye</code>。结果如<a class="xref-link" href="#fig-goodbye-world">图 1.17</a> 所示。</p>
</li>
</ol>
<div id="fig-hola-mundo" class="figure"><img src="images/chapter1/hola_mundo.png" alt="hola mundo" /><div class="figcaption"><span class="title-label">图 1.16</span>:修改根路由,返回“¡Hola, mundo!”</div></div>
<div id="fig-goodbye-world" class="figure"><img src="images/chapter1/goodbye_world.png" alt="goodbye world" /><div class="figcaption"><span class="title-label">图 1.17</span>:修改根路由,返回“goodbye, world!”</div></div>
</section>
</section>
<section data-type="sect1" id="version-control">
<h1><span class="title-label">1.4</span> 使用 Git 做版本控制</h1>
<p>我们创建了一个“hello, world”应用,接下来要花点时间做一件事。虽然这件事不是必须的,但是经验丰富的软件开发者都认为这是最基本的事情,即把应用的源代码纳入版本控制系统。版本控制系统可以跟踪项目中代码的变化,便于和他人协作;如果出现问题(例如不小心删除了文件),还可以回滚到以前的版本。每个专业的软件开发者都应该学习使用版本控制系统。</p>
<p>版本控制系统种类很多,Rails 社区基本都使用 <a href="http://git-scm.com/" class="external-link">Git</a>。Git 是一个分布式版本控制系统,由 Linus Torvalds 开发,最初的目的是存储 Linux 内核代码。Git 相关的知识很多,本书只会介绍一些皮毛。如果想深入了解,请阅读《<a href="http://learnenough.com/git-tutorial" class="external-link">Learn Enough Git to Be Dangerous</a>》。</p>
<p>之所以强烈推荐使用 Git 做版本控制,不仅因为 Rails 社区都在用,还因为使用 Git 更易于分享代码(<a class="xref-link" href="#bitbucket">1.4.3 节</a>),而且也便于部署应用(<a class="xref-link" href="#deploying">1.5 节</a>)。</p>
<section data-type="sect2" id="installation-and-setup">
<h2><span class="title-label">1.4.1</span> 安装和设置</h2>
<p><a class="xref-link" href="#development-environment">1.2.1 节</a>推荐使用的云端 IDE 默认自带 Git,不用再安装。如果你没使用云端 IDE,可以参照《<a href="http://learnenough.com/git-tutorial" class="external-link">Learn Enough Git to Be Dangerous</a>》中的说明,在自己的系统中安装 Git。</p>
<section data-type="sect3" id="first-time-system-setup">
<h3>第一次运行前要做的系统设置</h3>
<p>使用 Git 之前,要做些一次性设置。这些设置对整个系统都有效,因此一台电脑只需设置一次:</p>
<div data-type="listing">
<div class="highlight language-sh"><pre><code><span class="nv">$ </span>git config <span class="nt">--global</span> user.name <span class="s2">"Your Name"</span>
<span class="nv">$ </span>git config <span class="nt">--global</span> user.email your.email@example.com
</code></pre></div>
</div>
<p>注意,在 Git 配置中设定的名字和电子邮件地址会在所有公开的仓库中显示。</p>
</section>
<section data-type="sect3" id="first-time-repository-setup">
<h3>第一次使用仓库前要做的设置</h3>
<p>下面的步骤每次新建仓库(repository,有时简称 repo)时都要执行。首先进入第一个应用的根目录,初始化一个新仓库:</p>
<div data-type="listing">
<div class="highlight language-sh"><pre><code><span class="nv">$ </span>git init
Initialized empty Git repository <span class="k">in</span> /home/ec2-user/environment/environment/hello_app/.git/
</code></pre></div>
</div>
<p>然后执行 <code>git add -A</code> 命令,把项目中的所有文件都放到仓库中:</p>
<div data-type="listing">
<div class="highlight language-sh"><pre><code><span class="nv">$ </span>git add <span class="nt">-A</span>
</code></pre></div>
</div>
<p>这个命令会把当前目录中的所有文件都放到仓库中,但是匹配特殊文件 <code>.gitignore</code> 中模式的文件除外。<code>rails new</code> 命令会自动生成一个适用于 Rails 项目的 <code>.gitignore</code> 文件,此外你还可以添加其他模式。<sup>[<a id="fn-ref-12" href="#fn-12">12</a>]</sup></p>
<p>加入仓库的文件一开始位于暂存区(staging area),这一区用于存放待提交的内容。执行 <code>status</code> 命令可以查看暂存区中有哪些文件:</p>
<div data-type="listing">
<div class="highlight language-sh"><pre><code><span class="nv">$ </span>git status
On branch master
Initial commit
Changes to be committed:
<span class="o">(</span>use <span class="s2">"git rm --cached <file>..."</span> to unstage<span class="o">)</span>
new file: .gitignore
new file: Gemfile
new file: Gemfile.lock
new file: README.md
new file: Rakefile
<span class="nb">.</span>
<span class="nb">.</span>
.
</code></pre></div>
</div>
<p>如果想让 Git 保存这些改动,使用 <code>commit</code> 命令:</p>
<div data-type="listing">
<div class="highlight language-sh"><pre><code><span class="nv">$ </span>git commit <span class="nt">-m</span> <span class="s2">"Initialize repository"</span>
<span class="o">[</span>master <span class="o">(</span>root-commit<span class="o">)</span> df0a62f] Initialize repository
<span class="nb">.</span>
<span class="nb">.</span>
.
</code></pre></div>
</div>
<p><code>-m</code> 旗标的意思是为这次提交添加一个说明。如果没指定 <code>-m</code> 旗标,Git 会打开系统默认使用的编辑器,让你在其中输入说明。(本书所有的示例都会使用 <code>-m</code> 旗标。)</p>
<p>有一点要特别注意:Git 提交只发生在本地,也就是说只在执行提交操作的设备中存储内容。<a class="xref-link" href="#git-commands">1.4.4 节</a>会介绍如何把改动推送到远程仓库(使用 <code>git push</code> 命令)。</p>
<p>顺便说一下,可以使用 <code>log</code> 命令查看提交历史:</p>
<div data-type="listing">
<div class="highlight language-sh"><pre><code><span class="nv">$ </span>git log
commit af72946fbebc15903b2770f92fae9081243dd1a1
Author: Michael Hartl <michael@michaelhartl.com>
Date: Thu May 12 19:25:07 2016 +0000
Initialize repository
</code></pre></div>
</div>
<p>如果仓库的提交历史很多,可能需要输入 <code>q</code> 退出。(《<a href="http://learnenough.com/git-tutorial" class="external-link">Learn Enough Git to Be Dangerous</a>》说道,<code>git log</code> 用到了《<a href="http://learnenough.com/command-line-tutorial" class="external-link">Learn Enough Command Line to Be Dangerous</a>》中介绍的 <code>less</code> 接口。)</p>
</section>
</section>
<section data-type="sect2" id="what-good-does-git-do-you">
<h2><span class="title-label">1.4.2</span> 使用 Git 有什么好处</h2>
<p>如果以前从未用过版本控制系统,现在可能不完全明白版本控制的好处。那我举个例子说明一下吧。假如你不小心做了某个操作,例如把重要的 <code>app/controllers/</code> 目录删除了:</p>
<div data-type="listing">
<div class="highlight language-sh"><pre><code><span class="nv">$ </span><span class="nb">ls </span>app/controllers/
application_controller.rb concerns/
<span class="nv">$ </span><span class="nb">rm</span> <span class="nt">-rf</span> app/controllers/
<span class="nv">$ </span><span class="nb">ls </span>app/controllers/
<span class="nb">ls</span>: app/controllers/: No such file or directory
</code></pre></div>
</div>
<p>这里,我们用 Unix 中的 <code>ls</code> 命令列出 <code>app/controllers/</code> 目录里的内容,然后用 <code>rm</code> 命令删除这个目录(<a class="xref-link" href="#table-unix-commands">表 1.1</a>)。如《<a href="http://learnenough.com/command-line-tutorial" class="external-link">Learn Enough Command Line to Be Dangerous</a>》所述,<code>-rf</code> 旗标的意思是“强制递归”,无需明确征求同意就递归删除所有文件、目录和子目录等。</p>
<p>检查状态,看看发生了什么:</p>
<div data-type="listing">
<div class="highlight language-sh"><pre><code><span class="nv">$ </span>git status
On branch master
Changed but not updated:
<span class="o">(</span>use <span class="s2">"git add/rm <file>..."</span> to update what will be committed<span class="o">)</span>
<span class="o">(</span>use <span class="s2">"git checkout -- <file>..."</span> to discard changes <span class="k">in </span>working directory<span class="o">)</span>
deleted: app/controllers/application_controller.rb
no changes added to commit <span class="o">(</span>use <span class="s2">"git add"</span> and/or <span class="s2">"git commit -a"</span><span class="o">)</span>
</code></pre></div>
</div>
<p>可以看出,我们删除了一个文件。但是这个改动只发生在工作区(working tree)中,还未提交到仓库。这意味着,我们可以使用 <code>checkout</code> 命令,并指定 <code>-f</code> 旗标,强制撤销这次改动:</p>
<div data-type="listing">
<div class="highlight language-sh"><pre><code><span class="nv">$ </span>git checkout <span class="nt">-f</span>
<span class="nv">$ </span>git status
<span class="c"># On branch master</span>
nothing to commit <span class="o">(</span>working directory clean<span class="o">)</span>
<span class="nv">$ </span><span class="nb">ls </span>app/controllers/
application_controller.rb concerns/
</code></pre></div>
</div>
<p>删除的目录和文件又回来了,这下放心了!</p>
</section>
<section data-type="sect2" id="bitbucket">
<h2><span class="title-label">1.4.3</span> Bitbucket</h2>
<p>我们已经把项目纳入 Git 版本控制系统了,接下来可以把代码推送到 <a href="http://www.bitbucket.com/" class="external-link">Bitbucket</a>。这是一个专门用来托管和分享 Git 仓库的网站。(《<a href="http://learnenough.com/git-tutorial" class="external-link">Learn Enough Git to Be Dangerous</a>》使用的是 <a href="http://www.github.com/" class="external-link">GitHub</a>,换用 Bitbucket 的原因参见<a class="xref-link" href="#aside-github-bitbucket">旁注 1.4</a>。)在 Bitbucket 中放一份 Git 仓库的副本有两个目的:其一,对代码做个完整备份(包括所有提交历史);其二,便于以后协作。</p>
<div data-type="sidebar" id="aside-github-bitbucket" class="sidebar">
<h5><span class="title-label">旁注 1.4</span>:GitHub 和 Bitbucket</h5>
<p>目前,托管 Git 仓库最受欢迎的网站是 GitHub 和 Bitbucket。这两个网站有很多相似之处:都能托管仓库,也可以协作,而且浏览和搜索仓库很方便。但二者之间有个重要的区别(对本书而言):GitHub 为开源项目提供无限量的免费仓库,但私有仓库收费;而 Bitbucket 提供了无限量的私有仓库,仅当协作者超过一定数量时才收费。所以,选择哪个网站,取决于具体的需求。</p>
<p>《<a href="http://learnenough.com/git-tutorial" class="external-link">Learn Enough Git to Be Dangerous</a>》(以及本书前几版)使用的是 GitHub,因为它对开源项目来说有很多好用的功能,但我越来越关注安全,所以推荐所有 Web 应用都放在私有仓库中。因为 Web 应用的仓库中可能包含潜在的敏感信息,例如密钥和密码,可能会威胁到使用这份代码的网站的安全。当然,这类信息也有安全的处理方法(例如,让 Git 将其忽略),但是容易出错,而且需要很多专业知识。</p>
<p>本书开发的演示应用可以安全地公开,但这只是特例,不能推广。因此,为了尽量提高安全,我们不能冒险,还是默认就使用私有仓库保险。既然 GitHub 对私有仓库收费,而 Bitbucket 提供了不限量的免费私有仓库,就我们目前的需求来说,Bitbucket 比 GitHub 更合适。</p>
<p>(顺便说一下,最近出现了第三个主流的 Git 托管公司,叫 GitLab。GitLab 一开始致力于开发开源的 Git 工具,供人们自己托管,而现在 GitLab 也提供托管的版本,而且公开仓库和私有仓库都不限量。因此,以后托管项目时,在 GitHub 和 Bitbucket 之外又多了一个选择。)</p>
</div>
<p>Bitbucket 的使用方法很简单,但毕竟“技术是复杂的”(<a class="xref-link" href="#aside-technical-sophistication">旁注 1.1</a>),因此可能也不是那么容易做对:</p>
<ol class="arabic">
<li>
<p>如果没有账户,先<a href="https://bitbucket.org/account/signup/" class="external-link">注册一个 Bitbucket 账户</a>;</p>
</li>
<li>
<p>把你的<a href="https://en.wikipedia.org/wiki/Public-key_cryptography" class="external-link">公钥</a>复制到剪切板。云端 IDE 用户可以使用 <code>cat</code> 命令查看公钥,如<a class="xref-link" href="#listing-cat-public-key">代码清单 1.10</a> 所示,然后选中公钥,复制。如果没有输出,或者遇到错误,请参阅“<a href="https://confluence.atlassian.com/x/YwV9E" class="external-link">如何在你的 Bitbucket 账户中设定公钥</a>”一文,然后再执行<a class="xref-link" href="#listing-cat-public-key">代码清单 1.10</a> 中的命令;</p>
</li>
<li>
<p>点击右上角的头像,选择“Bitbucket settings”(Bitbucket 设置),然后点击“SSH keys”(SSH 密钥),如<a class="xref-link" href="#fig-add-public-key">图 1.18</a> 所示。</p>
</li>
</ol>
<div id="fig-add-public-key" class="figure"><img src="images/chapter1/add_public_key.png" alt="add public key" /><div class="figcaption"><span class="title-label">图 1.18</span>:添加 SSH 公钥</div></div>
<div id="listing-cat-public-key" data-type="listing">
<h5><span class="title-label">代码清单 1.10</span>:使用 <code>cat</code> 命令打印公钥</h5>
<div class="highlight language-sh"><pre><code><span class="nv">$ </span><span class="nb">cat</span> ~/.ssh/id_rsa.pub
</code></pre></div>
</div>
<p>添加公钥之后,点击“Create”(创建)按钮,<a href="https://bitbucket.org/repo/create" class="external-link">新建一个仓库</a>,如<a class="xref-link" href="#fig-create-first-repository">图 1.19</a> 所示。填写项目的信息时,记得要选中“This is a private repository”(这是私有仓库)。(如果看到创建 <code>README</code> 文件的选项,选择“No”。)填完后点击“Create repository”(创建仓库)按钮,然后按照说明操作(<a class="xref-link" href="#fig-add-repository">图 1.20</a>),把仓库推送到 Bitbucket 中,如<a class="xref-link" href="#listing-bitbucket-add-push">代码清单 1.11</a> 所示。(如果与<a class="xref-link" href="#listing-bitbucket-add-push">代码清单 1.11</a> 不同,可能是公钥没正确添加,我建议你再试一次。)推送仓库时,如果询问“Are you sure you want to continue connecting (yes/no)?”(确定继续连接吗?),输入“yes”。</p>
<div id="listing-bitbucket-add-push" data-type="listing">
<h5><span class="title-label">代码清单 1.11</span>:添加 Bitbucket,然后推送仓库</h5>
<div class="highlight language-sh"><pre><code><span class="nv">$ </span>git remote add origin git@bitbucket.org:<username>/hello_app.git
<span class="nv">$ </span>git push <span class="nt">-u</span> origin master
</code></pre></div>
</div>
<div id="fig-create-first-repository" class="figure"><img src="images/chapter1/create_first_repository_bitbucket_4th_ed.png" alt="create first repository bitbucket 4th ed" /><div class="figcaption"><span class="title-label">图 1.19</span>:在 Bitbucket 中创建存放这个应用的仓库</div></div>
<div id="fig-add-repository" class="figure"><img src="images/chapter1/add_repository.png" alt="add repository" /><div class="figcaption"><span class="title-label">图 1.20</span>:添加现有仓库的方法</div></div>
<p><a class="xref-link" href="#listing-bitbucket-add-push">代码清单 1.11</a> 的意思是,先告诉 Git,你想添加 Bitbucket 作为这个仓库的源(origin),然后再把仓库推送到这个远端的源。(别管 <code>-u</code> 旗标的意思,如果好奇,可以搜索“git set upstream”。)当然了,你要把 <code><username></code> 换成你自己的用户名。例如,我执行的命令是:</p>
<div data-type="listing">
<div class="highlight language-sh"><pre><code><span class="nv">$ </span>git remote add origin git@bitbucket.org:railstutorial/hello_app.git
</code></pre></div>
</div>
<p>推送完毕后,在 Bitbucket 中会显示 <code>hello_app</code> 仓库的页面。在这个页面中可以浏览文件、查看完整的提交历史,除此之外还有很多其他功能(<a class="xref-link" href="#fig-bitbucket-repository-page">图 1.21</a>)。<sup>[<a id="fn-ref-13" href="#fn-13">13</a>]</sup></p>
</section>
<section data-type="sect2" id="git-commands">
<h2><span class="title-label">1.4.4</span> 分支、编辑、提交、合并</h2>
<p>如果你跟着 <a class="xref-link" href="#bitbucket">1.4.3 节</a>中的步骤做,可能注意到了,Bitbucket 自动渲染了仓库中的 README 文件,如<a class="xref-link" href="#fig-bitbucket-repository-page">图 1.21</a> 所示。这个 <code>README.md</code> 文件由<a class="xref-link" href="#listing-rails-command">代码清单 1.3</a> 中的命令自动生成。从文件的扩展名可以看出,这个文件使用 Markdown 编写。Markdown 是一门人类可读的标记语言,易于转换成 HTML——Bitbucket 就这么做了。</p>
<p>自动生成 README 文件很贴心,不过我们最好修改里面的内容,描述手上的项目。这一节,我们将修改 README 文件,添加一些针对本书的内容。在修改的过程中,我们将首次演示我推荐在 Git 中使用的工作流程,即“分支、编辑、提交、合并”。<sup>[<a id="fn-ref-14" href="#fn-14">14</a>]</sup></p>
<div id="fig-bitbucket-repository-page" class="figure"><img src="images/chapter1/bitbucket_repository_page_4th_ed.png" alt="bitbucket repository page 4th ed" /><div class="figcaption"><span class="title-label">图 1.21</span>:一个 Bitbucket 仓库的页面</div></div>
<div id="fig-bitbucket-default-readme" class="figure has-border"><img src="images/chapter1/bitbucket_default_readme.png" alt="bitbucket default readme" /><div class="figcaption"><span class="title-label">图 1.22</span>:Bitbucket 渲染的 Rails 默认生成的 README 文件</div></div>
<section data-type="sect3" id="branch">
<h3>分支</h3>
<p>Git 分支(branch)的功能很强大。分支是对仓库的高效复制,在分支中所做的改动(或许是实验性质的)不会影响父级文件。大多数情况下,父级仓库是 <code>master</code> 分支。我们可以使用 <code>checkout</code> 命令,并指定 <code>-b</code> 旗标,创建一个新主题分支(topic branch):</p>
<div data-type="listing">
<div class="highlight language-sh"><pre><code><span class="hll"><span class="nv">$ </span>git checkout <span class="nt">-b</span> modify-README</span>
Switched to a new branch <span class="s1">'modify-README'</span>
<span class="hll"><span class="nv">$ </span>git branch</span>
master
<span class="k">*</span> modify-README
</code></pre></div>
</div>
<p>其中,第二个命令 <code>git branch</code> 的作用是列出所有本地分支。星号(<code>*</code>)表示当前所在的分支。注意,<code>git checkout -b modify-README</code> 命令先创建一个新分支,然后再切换到这个新分支——<code>modify-README</code> 分支前面的星号证明了这一点。</p>
<p>只有多个开发者协作开发一个项目时,才能体现分支的全部价值。<sup>[<a id="fn-ref-15" href="#fn-15">15</a>]</sup>如果只有一个开发者,分支也有作用。一般情况下,要把主题分支的改动和主分支隔离开,这样即便搞砸了,随时都可以切换到主分支,然后删除主题分支,丢掉改动。本节末尾会介绍具体做法。</p>
<p>顺便说一下,像这种小改动,我一般不会新建分支(而是直接在主分支中修改)。现在我这么做是为了让你养成好习惯。</p>
</section>
<section data-type="sect3" id="edit">
<h3>编辑</h3>
<p>创建好主题分支之后,我们要在 README 文件中添加一些内容,如<a class="xref-link" href="#listing-new-readme">代码清单 1.12</a> 所示。</p>
<div id="listing-new-readme" data-type="listing">
<h5><span class="title-label">代码清单 1.12</span>:新的 <code>README</code> 文件</h5>