-
Notifications
You must be signed in to change notification settings - Fork 0
/
getting_started.html
1674 lines (1472 loc) · 107 KB
/
getting_started.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
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-TW">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Rails 起步走 — Ruby on Rails 指南</title>
<meta name="description" content="Ruby on Rails 指南:系統學習 Rails(Rails 4.2 版本)" >
<meta name="keywords" content="Ruby on Rails Guides 指南 中文 學習 免費 網路 Web 開發" >
<meta name="author" content="http://git.io/G_R1sA">
<meta property="fb:admins" content="1340181291">
<meta property="og:title" content="Rails 起步走 — Ruby on Rails 指南" >
<meta property="og:site_name" content="Ruby on Rails 指南">
<meta property="og:image" content="http://rails.ruby.tw/images/rails_guides_cover.jpg">
<meta property="og:url" content="http://rails.ruby.tw/">
<meta property="og:type" content="article">
<meta property="og:description" content="Ruby on Rails 指南:系統學習 Rails(Rails 4.2 版本)">
<link rel="stylesheet" href="stylesheets/application.css">
<link href="http://fonts.googleapis.com/css?family=Noto+Sans:400,700|Noto+Serif:700|Source+Code+Pro" rel="stylesheet">
<link href="images/favicon.ico" rel="shortcut icon" type="image/x-icon">
</head>
<body class="guide">
<div id="fb-root"></div>
<script>(function(d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) return;
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/zh-TW/sdk.js#xfbml=1&appId=837401439623727&version=v2.0";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));</script>
<script type="text/javascript">
window.twttr=(function(d,s,id){var t,js,fjs=d.getElementsByTagName(s)[0];if(d.getElementById(id)){return}js=d.createElement(s);js.id=id;js.src="https://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);return window.twttr||(t={_e:[],ready:function(f){t._e.push(f)}})}(document,"script","twitter-wjs"));
</script>
<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>Models</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>Views</dt>
<dd><a href="layouts_and_rendering.html">Rails 算繪與版型</a></dd>
<dd><a href="form_helpers.html">Action View 表單輔助方法</a></dd>
<dt>Controllers</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">Rake 任務與 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>
<dt>Rails 指南翻譯術語</dt>
<dd><a href="translation_terms.html">翻譯術語</a></dd>
</dl>
</div>
</li>
<li><a class="nav-item" href="//github.com/docrails-tw/guides">貢獻翻譯</a></li>
<li><a class="nav-item" href="contributing_to_ruby_on_rails.html">貢獻</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="Models">
<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="Views">
<option value="layouts_and_rendering.html">Rails 算繪與版型</option>
<option value="form_helpers.html">Action View 表單輔助方法</option>
</optgroup>
<optgroup label="Controllers">
<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">Rake 任務與 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>
<optgroup label="Rails 指南翻譯術語">
<option value="translation_terms.html">翻譯術語</option>
</optgroup>
</select>
</li>
</ul>
</div>
</div>
</div>
<hr class="hide">
<div id="feature">
<div class="wrapper">
<h2>Rails 起步走</h2><p>本篇介紹如何使用 Ruby on Rails。</p><p>讀完本篇,您將了解:</p>
<ul>
<li>如何安裝、新建 Rails 應用程式,如何把應用程式和資料庫連結起來;</li>
<li>Rails 應用程式的結構;</li>
<li>MVC(Model、文件顯示層、控制器)和 RESTful 設計的基本原則;</li>
<li>如何快速產生可執行的 Rails 應用程式;</li>
</ul>
<div id="subCol">
<h3 class="chapter"><img src="images/chapters_icon.gif" alt="" />Chapters</h3>
<ol class="chapters">
<li><a href="#%E5%AD%B8%E5%89%8D%E6%89%80%E9%9C%80%E7%9A%84%E7%9F%A5%E8%AD%98">學前所需的知識</a></li>
<li><a href="#rails-%E6%98%AF%E4%BB%80%E9%BA%BC%EF%BC%9F">Rails 是什麼?</a></li>
<li>
<a href="#%E5%BB%BA%E7%AB%8B%E4%B8%80%E5%80%8B%E6%96%B0%E7%9A%84-rails-%E5%B0%88%E6%A1%88">建立一個新的 Rails 專案</a>
<ul>
<li><a href="#%E5%AE%89%E8%A3%9D-rails">安裝 Rails</a></li>
<li><a href="#%E5%BB%BA%E7%AB%8B%E4%B8%80%E5%80%8B-blog-%E6%87%89%E7%94%A8%E7%A8%8B%E5%BC%8F">建立一個 Blog 應用程式</a></li>
</ul>
</li>
<li>
<a href="#hello-rails-bang">Hello, Rails!</a>
<ul>
<li><a href="#%E5%95%9F%E5%8B%95-web-%E4%BC%BA%E6%9C%8D%E5%99%A8">啟動 Web 伺服器</a></li>
<li><a href="#rails-%E8%AA%AA-hello">Rails 說 "Hello"</a></li>
<li><a href="#%E8%A8%AD%E7%BD%AE%E6%87%89%E7%94%A8%E7%A8%8B%E5%BC%8F%E9%A6%96%E9%A0%81">設置應用程式首頁</a></li>
</ul>
</li>
<li>
<a href="#%E9%96%8B%E5%A7%8B%E5%AF%A6%E4%BD%9C">開始實作</a>
<ul>
<li><a href="#%E5%BB%BA%E7%AB%8B%E5%9F%BA%E6%9C%AC%E5%8A%9F%E8%83%BD">建立基本功能</a></li>
<li><a href="#%E9%96%8B%E5%A7%8B%E7%AC%AC%E4%B8%80%E5%80%8B%E8%A1%A8%E5%96%AE">開始第一個表單</a></li>
<li><a href="#%E6%96%B0%E5%A2%9E%E6%96%87%E7%AB%A0">新增文章</a></li>
<li><a href="#%E5%BB%BA%E7%AB%8B-article-%E6%A8%A1%E5%9E%8B">建立 Article 模型</a></li>
<li><a href="#%E5%9F%B7%E8%A1%8C%E4%B8%80%E5%80%8B-migration">執行一個 Migration</a></li>
<li><a href="#%E5%9C%A8-controller-%E4%B8%AD%E5%84%B2%E5%AD%98%E8%B3%87%E6%96%99">在 controller 中儲存資料</a></li>
<li><a href="#%E9%A1%AF%E7%A4%BA%E6%96%87%E7%AB%A0">顯示文章</a></li>
<li><a href="#%E9%A1%AF%E7%A4%BA%E6%89%80%E6%9C%89%E6%96%87%E7%AB%A0">顯示所有文章</a></li>
<li><a href="#%E5%BB%BA%E7%AB%8B%E9%80%A3%E7%B5%90">建立連結</a></li>
<li><a href="#%E5%8A%A0%E5%85%A5%E4%B8%80%E4%BA%9B%E9%A9%97%E8%AD%89">加入一些驗證</a></li>
<li><a href="#%E6%9B%B4%E6%96%B0%E6%96%87%E7%AB%A0">更新文章</a></li>
<li><a href="#%E4%BD%BF%E7%94%A8-partials-%E5%88%AA%E9%99%A4-views-%E4%B8%AD%E9%87%8D%E8%A4%87%E9%83%A8%E4%BB%BD">使用 partials 刪除 views 中重複部份</a></li>
<li><a href="#%E5%88%AA%E9%99%A4%E6%96%87%E7%AB%A0">刪除文章</a></li>
</ul>
</li>
<li>
<a href="#%E5%8A%A0%E5%85%A5%E7%AC%AC%E4%BA%8C%E5%80%8B%E6%A8%A1%E5%9E%8B">加入第二個模型</a>
<ul>
<li><a href="#%E7%94%A2%E7%94%9F%E4%B8%80%E5%80%8B%E6%A8%A1%E5%9E%8B">產生一個模型</a></li>
<li><a href="#%E9%97%9C%E8%81%AF%E6%A8%A1%E5%9E%8B">關聯模型</a></li>
<li><a href="#%E9%87%9D%E5%B0%8D%E7%95%99%E8%A8%80%E9%83%A8%E4%BB%BD%E6%96%B0%E5%A2%9E-route-%E8%A6%8F%E5%89%87">針對留言部份新增 Route 規則</a></li>
<li><a href="#%E5%BB%BA%E7%AB%8B-controller">建立 Controller</a></li>
</ul>
</li>
<li>
<a href="#%E9%87%8D%E6%A7%8B">重構</a>
<ul>
<li><a href="#render-partial-%E4%B8%AD%E7%9A%84%E9%9B%86%E5%90%88">Render Partial 中的集合</a></li>
<li><a href="#render-%E4%B8%80%E5%80%8B-partial-%E8%A1%A8%E5%96%AE">Render 一個 Partial 表單</a></li>
</ul>
</li>
<li>
<a href="#%E5%88%AA%E9%99%A4%E7%95%99%E8%A8%80">刪除留言</a>
<ul>
<li><a href="#%E5%88%AA%E9%99%A4-associated-%E7%89%A9%E4%BB%B6">刪除 Associated 物件</a></li>
</ul>
</li>
<li>
<a href="#%E5%AE%89%E5%85%A8">安全</a>
<ul>
<li><a href="#%E5%9F%BA%E6%9C%AC%E8%AA%8D%E8%AD%89">基本認證</a></li>
<li><a href="#%E5%85%B6%E4%BB%96%E5%AE%89%E5%85%A8%E6%80%A7%E8%80%83%E9%87%8F">其他安全性考量</a></li>
</ul>
</li>
<li><a href="#%E5%BE%8C%E7%BA%8C">後續</a></li>
<li><a href="#%E8%A8%AD%E5%AE%9A%E4%B8%8A%E7%9B%B8%E9%97%9C%E5%95%8F%E9%A1%8C">設定上相關問題</a></li>
</ol>
</div>
</div>
</div>
<div id="container">
<div class="wrapper">
<div id="mainCol">
<div class="warning"><p><strong>本文仍存在許多錯誤,歡迎協助閱讀並至<a href="https://github.com/docrails-tw/guides/issues/74" target="_blank">此處</a>回報,感謝!</strong></p></div><h3 id="學前所需的知識">1 學前所需的知識</h3><p>本文是為了想從頭學 Rails 的初學者所寫,無需具備任何 Rails 的開發經驗。不過需要先安裝:</p>
<ul>
<li>
<a href="https://www.ruby-lang.org/en/downloads">Ruby</a> 1.9.3 及以上版本。</li>
<li>
<a href="https://rubygems.org">RubyGems</a> 一個伴隨 Ruby 1.9+ 安裝的套件管理程式。如果想學習更多有關於 RubyGems,請參考 <a href="http://guides.rubygems.org">RubyGems 指南</a>。</li>
<li>
<a href="https://www.sqlite.org">SQLite3 資料庫</a>。</li>
</ul>
<p>Rails 是一個使用 Ruby 開發的 Web 框架。如果沒有 Ruby 相關的經驗就開始學 Rails,將會發現學習曲線非常陡峭。這裡提供幾個 Ruby 學習的線上資源:</p>
<ul>
<li>
<a href="https://www.ruby-lang.org/zh_tw/documentation/">Ruby 程式語言的官方網站</a>。</li>
<li>
<a href="http://resrc.io/list/10/list-of-free-programming-books/#ruby">reSRC 免費程式設計書單</a>。</li>
</ul>
<p>有些線上資源雖然很好,但是針對 Ruby 1.8 版或更舊的 1.6 版而寫,沒有涵蓋 Rails 一些新的語法。</p><h3 id="rails-是什麼?">2 Rails 是什麼?</h3><p>Rails 是一個用 Ruby 所寫的 Web 開發框架。這個框架把開發過程的細節的都設想周到,讓開發 Web 應用程式變成一件簡單的事情。與其他程式語言或開發框架比較的話,它可以讓你用更簡短的程式碼來實現相同或更完整的功能。
多數資深的 Rails 開發者認為 Rails 可以使開發 Web 應用程式變的更加有趣。</p><p>Rails 是一個有先見之明的軟體。當事情有最好的處理方法,他的設計會傾向讓你去使用這個方法,而不是花很多時間去找尋跟嘗試。
所以當學完“The Rails Way”之後,那你的開發效率將會進展到另一個境界。但有個前提就是你不能堅持把其他程式語言的開發習慣或思維帶到 Rails 中,否則一開始會對 Rails 有不好的印象。</p><p>在 Rails 開發哲學中有著兩個主要的原則:</p>
<ul>
<li>
<strong>不要重複你自己</strong>:DRY 是一個軟體工程的開發原則,“系統中每個功能的構思都必須要有單一、明確且讓人認同的表達方式”儘量避免一再重複的相同資訊,所寫的程式才容易維護、有擴展性且不容易出現 Bug。</li>
<li>
<strong>(約定優於配置)</strong>:Rails 不希望你浪費太多時間無止境的配置設定上,而是直接把最好的一些 Web 開發方式設為預設值,讓你熟悉之後就可以上手了。</li>
</ul>
<h3 id="建立一個新的-rails-專案">3 建立一個新的 Rails 專案</h3><p>閱讀這篇教學,最佳的方法是照著每個步驟走,本篇教學沒有遺漏任何程式片段或步驟,所以你可以完全跟著教學一步一步來。</p><p>一開始我們會建立一個取名為 <code>blog</code> 的 Rails 專案,以一個簡易的網誌作為學習範例。不過在這之前,你要先確定是否已經裝了 Rails。</p><div class="info"><p>本文的範例中會用 <code>$</code> 來表示類 Unix 系統的命令提示字元,但實際上顯示可能因客制化而不同。如果你是 Windows 的使用者,那命令提示字元會類似於 <code>c:\source_code></code>。</p></div><h4 id="安裝-rails">3.1 安裝 Rails</h4><p>首先打開命令列。在 Mac OS X 底下請打開 Terminal.app ,如果是在 Windows 下請在開始功能表選擇“執行”並且輸入 <code>cmd.exe</code> 後開啟命令視窗。只要是錢號 <code>$</code> 開頭的命令,都是在命令列上執行。現在就用命令檢查,是否已安裝最新的 Ruby 版本:</p><div class="info"><p>其實有很多工具可以幫助你在系統上快速安裝 Ruby 或是 Ruby on Rails。像 Windows 使用者可以參考 <a href="http://railsinstaller.org">Rails Installer</a>,而 Mac OS X 使用者則有 <a href="https://github.com/tokaido/tokaidoapp">Tokaido</a> 可以選擇。</p></div><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$ ruby -v
ruby 2.1.2p95
</pre>
</div>
<p>如果還沒安裝 Ruby ,可以看一下 <a href="https://www.ruby-lang.org/en/installation/">ruby-lang.org</a> ,連結裡會有針對你所用系統的 Ruby 安裝方法。</p><p>熱門的類 Unix 系統都會搭載 SQLite3 的 acceptable 版本。而 Windows 或其他作業系統的安裝教學請參考 <a href="https://www.sqlite.org">SQLite3 的網站</a>。
現在來確定是否有正確安裝且正確新增到 <code>PATH</code> 環境變數中:</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$ sqlite3 --version
</pre>
</div>
<p>命令列會顯示 SQLite 的版本資訊。</p><p>接下來使用 RubyGems 提供的命令 <code>gem install</code> 來安裝 Rails :</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$ gem install rails
</pre>
</div>
<p>如果不確定 Rails 是否有正確安裝的話,請輸入以下命令做確認:</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$ rails --version
</pre>
</div>
<p>如果有看到“Rails 4.2.0”的訊息,那你可以繼續接下來的步驟。</p><h4 id="建立一個-blog-應用程式">3.2 建立一個 Blog 應用程式</h4><p>Rails 中有許多被稱之為產生器 (generators) 的腳本 (scripts) ,主要用來配置開發所需要的檔案及工具,讓開發可以更加順手。
而現在要用的其中一種產生器就是可以幫助我們建構出一個新的 Rails 應用程式,如此一來就不用再花時間重頭寫起。</p><p>要使用產生器之前,請先打開命令提示視窗,切換到有存取權限的目錄接著輸入:</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$ rails new blog
</pre>
</div>
<p>執行完後會在 <code>blog</code> 目錄下建立一個名為 <code>Blog</code> 的 Rails 應用程式,而執行過程中會透過 <code>bundle install</code> 命令安裝 <code>Gemfile</code> 上所列出的相依 Gem。</p><div class="info"><p>執行 <code>rails new -h</code> 可以看到所有 Rails 應用程式產生器可接受的命令列參數。</p></div><p>建立 blog 專案之後,切換到目錄裡:</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$ cd blog
</pre>
</div>
<p>在 <code>blog</code> 這個目錄中有許多自動產生的檔案和資料夾,這些是構成 Rails 應用程式的重要元素。本篇教學中,大部份會著重在 <code>app</code> 這個資料夾,話雖如此,這裡還是附上一張表,將所有預設的檔案及資料夾的功能做個簡單介紹:</p>
<table>
<thead>
<tr>
<th>檔案/資料夾</th>
<th>用途</th>
</tr>
</thead>
<tbody>
<tr>
<td>app/</td>
<td>包含著應用程式的控制器、models、views、輔助方法、mailers 以及 assets 等。接下來的教學中,你將會花多數的心力在這個資料夾上。</td>
</tr>
<tr>
<td>bin/</td>
<td>包含著像是一開始用來建構應用程式的 <code>rails</code> 腳本、環境的設定檔以及用來執行和部署應用程式的腳本</td>
</tr>
<tr>
<td>config/</td>
<td>設定應用程式的路由、資料庫、以及其他等等。詳細請參考<a href="/configuring.html">設定 Rails 應用程式</a>。</td>
</tr>
<tr>
<td>config.ru</td>
<td>用來啟動應用程式的 Rack 設定檔</td>
</tr>
<tr>
<td>db/</td>
<td>包含資料庫的綱要檔案以及資料庫遷移檔案。</td>
</tr>
<tr>
<td>Gemfile、Gemfile.lock</td>
<td>這兩個檔案可以指定 Rails application 所要安裝的 gem 相依套件,並且交由 Bundler gem 做管理。更多關於 Bundler 的資訊請看 <a href="http://bundler.io">Bundler 的網站</a>。</td>
</tr>
<tr>
<td>lib/</td>
<td>應用程式的擴充模組。</td>
</tr>
<tr>
<td>log/</td>
<td>應用程式的記錄檔案。</td>
</tr>
<tr>
<td>public/</td>
<td>唯一對外開放的目錄,裡面包含著靜態檔案和編譯過後的 Assets。</td>
</tr>
<tr>
<td>Rakefile</td>
<td>Rakefile 主要目的是找到並載入可從命令列執行的任務。其中內建任務是定義在各個 Rails 元件當中。若想新增自己寫的任務,不要直接修改 Rakefile,把自訂的任務新增到 lib/tasks 目錄下。</td>
</tr>
<tr>
<td>README.rdoc</td>
<td>這是一份應用程式的操作手冊。你可以編輯這個檔案來告訴別人你的應用程式的功能,以及如何安裝配置等等。</td>
</tr>
<tr>
<td>test/</td>
<td>包含單元測試、假資料、還有其他的測試工具。詳細請參考<a href="/testing.html">測試 Rails 應用程式</a>。</td>
</tr>
<tr>
<td>tmp/</td>
<td>暫存檔(像是快取、PID、Session 等暫存檔案)。</td>
</tr>
<tr>
<td>vendor/</td>
<td>主要放置第三方的程式碼。通常 Rails 應用程式會在這放置第三方的 Gem 套件。</td>
</tr>
</tbody>
</table>
<h3 id="hello-rails-bang">4 Hello, Rails!</h3><p>一開始,如果希望畫面有些簡單的文字輸出。先啟動 Rails 伺服器。</p><h4 id="啟動-web-伺服器">4.1 啟動 Web 伺服器</h4><p>事實上, Rails 應用程式已經可以用了。如果想看執行結果,那必須先啟動 web 伺服器,請在 <code>blog</code> 目錄輸入以下的命令:</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$ bin/rails server
</pre>
</div>
<div class="info"><p>編譯 CoffeeScript 和壓縮 JavaScript 需要一個 JavaScript 直譯器 (runtime)。如果缺少了直譯器就執行,命令列就會跳出 <code>execjs</code> 錯誤。通常 Mac OS X 以及 Windows 都會搭載 JavaScript 直譯器。對於沒有搭載的系統,由於一開始應用程式建立的時候, Rails 將 <code>therubyracer</code> gem 套件註解在 <code>Gemfile</code> 中,所以你只要將這行反註解然後就可以安裝。<code>therubyrhino</code> 是一個 JRuby 使用者推薦的直譯器套件,所以在 JRuby 中是直接把它定義在 <code>Gemfile</code>。
其他一樣有支援的直譯器請參考 <a href="https://github.com/sstephenson/execjs#readme">ExecJS</a>。</p></div><p>這將會啟動 WEBrick ,Ruby 內建的 web 伺服器。想看應用程式執行中的畫面,請打開瀏覽器並在網址列上輸入 <a href="http://localhost:3000">http://localhost:3000</a>。便會看到 Rails 的預設頁面。</p><p><img src="images/getting_started/rails_welcome.png" alt="Welcome aboard screenshot"></p><div class="info"><p>如想停止 web 伺服器,請在已執行中的命令視窗按下 Ctrl+C 跳回命令提示字元就可以終止服務。
大多數類 UNIX 系統,其中也包含 Mac OS X 將會再次看到錢號 <code>$</code>。在開發模式中, Rails 通常是不會要求你重新起動服務;只要有修改過的檔案伺服器就會自動重新載入。</p></div><p>“Welcome aboard”這個頁面對於新建 Rails 應用程式來說是一個“煙霧測試”:測試設定上是否正確,來讓此頁面正確執行。你也可以透過點擊 <em>About your application's environment</em> 連結來看應用程式中環境相關資訊的摘要。</p><h4 id="rails-說-hello">4.2 Rails 說 "Hello"</h4><p>為了讓 Rails 可以顯示 "Hello",你必須先建立一個簡單的控制器和文件顯示層。</p><p>控制器的功能是去接收對於應用程式的 HTTP 請求。而 <em>路由動作 (Routing)</em> 則是決定由那一個控制器去接收請求,通常一個 控制器會有一個以上的路由 (route) 規則對應,藉由不同的 actions 來處理這些不同的路由 (routes) 所決定的請求。Action 的功能就是收集資訊並提供給 view 使用。</p><p>View 的功能是將資訊用普通人可讀的方式呈現出來。View 跟 controller 最大的差別就是 controller 負責資訊的收集,而 view 只是負責資訊的呈現。預設的 view template 是用 eRuby (Embedded Ruby) 所寫的,這部份在結果送到使用者之前就會被 Rails 中 request cycle (從 route 到 view 的一系列請求) 執行到。</p><p>要建立一個 controller ,你必需執行 controller 的產生器,並且附上 controller 名稱以及 action 名稱的參數,就像這樣:</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$ bin/rails generate controller welcome index
</pre>
</div>
<p>Rails 會替你建立一個路由和幾個檔案。</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
create app/controllers/welcome_controller.rb
route get 'welcome/index'
invoke erb
create app/views/welcome
create app/views/welcome/index.html.erb
invoke test_unit
create test/controllers/welcome_controller_test.rb
invoke helper
create app/helpers/welcome_helper.rb
invoke assets
invoke coffee
create app/assets/javascripts/welcome.js.coffee
invoke scss
create app/assets/stylesheets/welcome.css.scss
</pre>
</div>
<p>這些檔案中最重要的當然是位於 <code>app/controllers/welcome_controller.rb</code> 的控制器以及位於 <code>app/views/welcome/index.html.erb</code> 的 View。</p><p>接下來用文字編輯器打開 <code>app/views/welcome/index.html.erb</code> ,並且將檔案所有內容替換成以下的程式碼:</p><div class="code_container">
<pre class="brush: xml; gutter: false; toolbar: false">
<h1>Hello, Rails!</h1>
</pre>
</div>
<h4 id="設置應用程式首頁">4.3 設置應用程式首頁</h4><p>現在我們已經完成了控制器和 view ,再來就是決定什麼時候讓 Rails 秀出 "Hello, Rails!"。這個例子中,我們想在連結應用程式首頁 <a href="http://localhost:3000">http://localhost:3000</a> 來顯示這段訊息。不過目前畫面依舊是 "Welcome aboard"。</p><p>所以接下來,我們要告訴 Rails 正確首頁的所在位置。</p><p>首先用編輯器打開 <code>config/routes.rb</code> 檔案。</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
Rails.application.routes.draw do
get 'welcome/index'
# The priority is based upon order of creation:
# first created -> highest priority.
#
# You can have the root of your site routed with "root"
# root 'welcome#index'
#
# ...
</pre>
</div>
<p>這個是應用程式的路由檔案,內容採用特殊的 DSL 撰寫,透過這些設定,可以告訴 Rails 要如何將連進來的請求對應到控制器和動作來處理。這個路由檔案包含許多已註解的路由規則範例,其中有一條規則是把連到網站根目錄的請求對應到特定的 controller 和 action 做處理。找到以 <code>root</code> 開頭的規則,去掉註解,看起來會像這樣:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
root 'welcome#index'
</pre>
</div>
<p>這一行 <code>root 'welcome#index'</code> 是告訴 Rails 把要連應用程式根目錄的請求對應到 welcome controller 的 index action 作處理。而另一行 <code>get 'welcome/index'</code> 則是告訴 Rails 把要連 <a href="http://localhost:3000/welcome/index">http://localhost:3000/welcome/index</a> 的請求對應到 welcome controller 的 index action 作處理。當你執行過 controller 產生器後 (<code>rails generate controller welcome index</code>) ,這些設定都會被新增到檔案中。</p><p>剛剛如果為了要執行產生器而關掉 web 伺服器的話,那就再次啟動 (<code>rails server</code>)。並且用瀏覽器連到 <a href="http://localhost:3000">http://localhost:3000</a>。你將會看到那些被你放在 <code>app/views/welcome/index.html.erb</code> 的訊息 "Hello, Rails!" ,這說明了這個新的路由規則 (route) 將這個請求交由 <code>WelcomeController</code> 的 <code>index</code> action 處理,並且將 view 正確的 render (算繪) 出來。</p><div class="info"><p>更多關於路由 (routing) 資訊,請參考 <a href="routing.html">Rails Routing from the Outside In</a>。</p></div><h3 id="開始實作">5 開始實作</h3><p>現在你已經知道如何建立 controller、action 還有 view ,接下來我們要建立更實質的一些功能。</p><p>在這 Blog 應用程式中,你將需要創造新的 <em>resource (資源)</em>。Resource (資源) 是一個類似物件的集合,就像 articles (文章集)、people (人群) 或是 animals (動物群)。對於 resource (資源) 的項目你可以 create (建立)、read (讀取)、update (更新) 以及 destroy (刪除) ,而這些操作我們簡稱為 <em>CRUD</em> 操作。</p><p>Rails 提供一個 <code>resources</code> method ,這個 method 可以用來宣告一個標準的 REST resource。以下程式碼將示範如何在 <code>config/routes.rb</code> 宣告一個 <em>article resource</em>。</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
Rails.application.routes.draw do
resources :articles
root 'welcome#index'
end
</pre>
</div>
<p>如果你執行 <code>rake routes</code> ,你可以發現它對於標準 RESTful actions 已經定義了許多 routes。至於 prefix 欄 (以及其他欄位) 的意思我們稍候再看,但這裡有一點值得注意, Rails 對於單數型態的 <code>article</code> 有特別的解釋,而且對於複數型態有意義上的區別。</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$ bin/rake routes
Prefix Verb URI Pattern Controller#Action
articles GET /articles(.:format) articles#index
POST /articles(.:format) articles#create
new_article GET /articles/new(.:format) articles#new
edit_article GET /articles/:id/edit(.:format) articles#edit
article GET /articles/:id(.:format) articles#show
PATCH /articles/:id(.:format) articles#update
PUT /articles/:id(.:format) articles#update
DELETE /articles/:id(.:format) articles#destroy
root GET / welcome#index
</pre>
</div>
<p>在下一個段落,你將可以在應用程式中新增和閱讀文章。這就是 CRUD 中的 "C" 跟 "R" :
creation (建立) 以及 reading (檢視)。而新增文章的表單應該會是如此:</p><p><img src="images/getting_started/new_article.png" alt="The new article form"></p><p>雖然現在看起來有些簡單,但是還可以使用。之後如有需要再來回頭改善樣式設計。</p><h4 id="建立基本功能">5.1 建立基本功能</h4><p>一開始,你在應用程式中會需要一個請求位置來新增文章。適合這個用途的請求位置應該就是 <code>/articles/new</code>。由於剛剛 resource 已經定義了相對應的 route ,所以現在可以向 <code>/articles/new</code> 發送請求。當你連到 <a href="http://localhost:3000/articles/new">http://localhost:3000/articles/new</a> 時,你將會發現一個 routing 錯誤:</p><p><img src="images/getting_started/routing_error_no_controller.png" alt="Another routing error, uninitialized constant ArticlesController"></p><p>這個錯誤會發生的原因是因為這個 route 規則需要有一個 controller 來處理請求,所以要解決這個問題的方法很簡單:建立一個名為 <code>Articlescontroller</code> 的 controller。你可以透過執行以下命令來完成動作:</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$ bin/rails g controller articles
</pre>
</div>
<p>如果你打開剛產生的 <code>app/controllers/articles_controller.rb</code>
你會看到一個空的 controller :</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
class ArticlesController < ApplicationController
end
</pre>
</div>
<p>這個 controller 是繼承 <code>ApplicationController</code> 的簡單類別。在這個類別中你必須定義 method 作為 controller 的 action。而這些 actions 將可以實現在 articles 上的 CRUD 操作。</p><div class="note"><p>在 ruby 中有這幾種 <code>public</code>、<code>private</code>、<code>protected</code> methods ,但只有 <code>public</code> methods 才能作為 controllers 的 actions。
更多詳細資訊請參考 <a href="http://www.ruby-doc.org/docs/ProgrammingRuby/">Programming Ruby</a>。</p></div><p>如果你現在重新整理這個頁面 <a href="http://localhost:3000/articles/new">http://localhost:3000/articles/new</a> , 你會發現另一個新的錯誤:</p><p><img src="images/getting_started/unknown_action_new_for_articles.png" alt="Unknown action new for ArticlesController!"></p><p>這個錯誤提示指出 Rails 在剛剛建立的 <code>ArticlesController</code> 中找不到 <code>new</code> action。這是因為當 controllers 建立在 Rails 中的時候,他們預設內容都是空的,除非在建立 controller 的時候就指定 actions 的名稱。</p><p>想要在 controller 中用手動建立一個 action ,你就必需在 controller 中新增一個 method。首先打開 <code>app/controllers/articles_controller.rb</code> ,再來在 <code>ArticlesController</code> 類別中新增一個 <code>new</code> method ,如此一來 controller 目前會像是如此:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
class ArticlesController < ApplicationController
def new
end
end
</pre>
</div>
<p>由於剛剛已經在 <code>ArticlesController</code> 中定義了 <code>new</code> method ,如果此時重新整理頁面 <a href="http://localhost:3000/articles/new">http://localhost:3000/articles/new</a> ,你將會看到另外一個錯誤:</p><p><img src="images/getting_started/template_is_missing_articles_new.png" alt="Template is missing for articles/new"></p><p>現在會得到這個錯誤是因為 Rails 希望這個空白的 actions 能夠有相對應的 views 來顯示 actions 所提供的資訊. 由於沒有可用的 view ,於是 Rails 就出現這個錯誤提示。</p><p>在上面圖片中最後一行剛好被截掉。我們來看看完整的訊息:</p>
<blockquote>
<p>Missing template articles/new, application/new with {locale:[:en], formats:[:html], handlers:[:erb, :builder, :coffee]}. Searched in: * "/path/to/blog/app/views"</p>
</blockquote>
<p>還滿長的一段文字!我們一起快速瀏覽並且了解每個部份的用意。</p><p>第一個部份我們可以發現缺少了什麼 template。而例子中,我們缺少的就是 <code>articles/new</code> template。Rails 一開始會試著尋找這個相對應的 template ,如果找不到才會試著載入另一個名為 <code>application/new</code> 的 template ,這是因為 <code>ArticlesController</code> 是繼承 <code>ApplicationController</code>的關係。</p><p>第二部份中包含了一個 hash。在這個 hash 中有三個 key , <code>:locale</code> 這個 key 將決定使用什麼語系的 template ,目前預設是使用簡稱為 "en" 的英文 template。下一個 key <code>:formats</code> 是指 template 要使用什麼格式來回覆給使用者,這裡預設的格式是 <code>:html</code> ,所以 Rails 會尋找一個 HTML 的 template。最後一個 <code>:handlers</code> 是告訴我們要使用什麼 <em>template handlers</em> 來將我們的 template render 出來。其中 <code>:erb</code> 是最常用於 HTML templates 的 render , <code>:builder</code> 則是用於 XML templates ,而 <code>:coffee</code> 是使用 CoffeeScript 來建立 JavaScript templates。</p><p>這段訊息的最後部份讓我們得知 Rails 會從什麼地方尋找 templates。Templates 在簡單的 Rails 應用程式中通常存放在單一位置,但是比較複雜的一些應用程式可能會有好幾種不同的路徑來擺放。</p><p>這個例子中,將被執行的是位於 <code>app/views/articles/new.html.erb</code> 的一個簡單 template。其中檔案的副檔名有其意涵:第一個副檔名是 template 的 <em>format</em> 名稱,而第二個則是表示使用了什麼 <em>handler</em> 來處理。Rails 一開始會試著從應用程式的 <code>app/views</code> 位置中找一個叫 <code>articles/new</code> 的 template。這個 template 的 format 只能使用 <code>html</code> ,而 handler 部份必須是 <code>erb</code>, <code>builder</code> 或 <code>coffee</code> 三者其中之一才行。因為接下來想在 template 中新增一個 HTML 表單, 所以你一定要使用 <code>ERB</code> 語言。因此最後這個檔案的名稱應該取作 <code>articles/new.html.erb</code> 並且需擺放於應用程式的 <code>app/views</code> 目錄中。</p><p>前往該目錄然後新增此檔案 <code>app/views/articles/new.html.erb</code> 並且寫上以下內容:</p><div class="code_container">
<pre class="brush: xml; gutter: false; toolbar: false">
<h1>New Article</h1>
</pre>
</div>
<p>當你重新整理此頁面 <a href="http://localhost:3000/articles/new">http://localhost:3000/articles/new</a> 你將會看到標題文字。這也表示 route、controller、action 跟 view 運作的十分順利!現來就來建立 article 的表單。</p><h4 id="開始第一個表單">5.2 開始第一個表單</h4><p>要在 template 中建立一個表單,你將會需要一個 <em>form builder</em>。Rails 的 helper method 有提供一種基本的 form builder ,叫作 <code>form_for</code>。想要使用此 method 的話,先將以下程式碼新增到 <code>app/views/articles/new.html.erb</code> :</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
<%= form_for :article do |f| %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :text %><br>
<%= f.text_area :text %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
</pre>
</div>
<p>如果你現在重新整理頁面,你就會看到與範例相同的表單。在 Rails 中建立表單就是如此簡單!</p><p>當你呼叫 <code>form_for</code> 時,你必須傳遞一個 identifying object (識別物件) 給這個表單。在例子中,這個物件就是用 symbol <code>:article</code> 表示。這告訴了 <code>form_for</code> helper 新建立表單的用途。在這個 method 的區塊中,有個用 <code>f</code> 表示的 <code>FormBuilder</code> 物件,它是被用在建立兩個文字標籤 (text labels) 以及兩個輸入欄位 (text fields) ,其中兩個輸入欄位一個是作為文章的標題另一個則是作為文章內文。最後在 <code>f</code> 這個物件上呼叫 <code>submit</code> ,表單上將會建立一個 submit 的按鈕。</p><p>不過這個表單仍然有一個問題,如果你檢視這個頁面的 HTML 原始碼,你會在表單中看到有個屬性 <code>action</code> 是指向 <code>/articles/new</code> ,這是有問題的,因為 route 導向的頁面正是現在所處的頁面,而且那個頁面只用來顯示新增文章的表單而已。</p><p>這個表單真正需要的是一個不一樣的 URL ,這在設定上其實只需簡單新增 <code>form_for</code> 的選項 <code>:url</code> 就可以達成了。通常在 Rails 中, action 是用來處理表單送出的資料,像這邊的 action 就是 "create" ,所以這裡的表單就會指向所對應的 action 來作處理。</p><p>現在就來編輯 <code>app/views/articles/new.html.erb</code> 中 <code>form_for</code> 那一行,修改結果應該會像這樣:</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
<%= form_for :article, url: articles_path do |f| %>
</pre>
</div>
<p>在範例中,我們將 <code>articles_path</code> helper 代入 <code>:url</code> 選項中。想要了解 Rails 透過這個選項來做什麼,我們再回頭看一下 <code>rake routes</code> 執行結果:</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$ bin/rake routes
Prefix Verb URI Pattern Controller#Action
articles GET /articles(.:format) articles#index
POST /articles(.:format) articles#create
new_article GET /articles/new(.:format) articles#new
edit_article GET /articles/:id/edit(.:format) articles#edit
article GET /articles/:id(.:format) articles#show
PATCH /articles/:id(.:format) articles#update
PUT /articles/:id(.:format) articles#update
DELETE /articles/:id(.:format) articles#destroy
root GET / welcome#index
</pre>
</div>
<p>這裡的 <code>articles_path</code> helper 會告訴 Rails 要把表單指向 Prefix 為 <code>articles</code> 的 URI Pattern ;而且表單預設是使用 <code>POST</code> 來發送請求到這個 route ,最後可推得由目前 <code>ArticlesController</code> controller 的 <code>create</code> action 來處理請求。</p><p>有了表單和已經定義好的 route ,你除了能夠填表單以外還可以按下送出按鈕去開啟一個建立新文章的程序,所以就繼續並且照著剛剛描述的來進行。在你送出表單的時後,你將會看到一個熟悉的錯誤:</p><p><img src="images/getting_started/unknown_action_create_for_articles.png" alt="Unknown action create for ArticlesController"></p><p>現在你必須在 <code>ArticlesController</code> 建立一個 <code>create</code> action 來讓程式正確執行.</p><h4 id="新增文章">5.3 新增文章</h4><p>如果想除去 "Unknown action" 這個錯誤的話,你可以先打開 <code>app/controllers/articles_controller.rb</code> 並且在 <code>ArticlesController</code> 類別中的 <code>new</code> action 下方新定義一個 <code>create</code> action,如下所示:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
class ArticlesController < ApplicationController
def new
end
def create
end
end
</pre>
</div>
<p>如果你又再次提交表單的話,你應該會看到另一個熟悉的錯誤: a template is missing。不過沒關係,我們目前暫且忽略他。這裡 <code>create</code> action 所要做的是將新增的文章存進資料庫中。</p><p>當表單被提交時,表單的欄位值會被當作 <em>parameters (參數)</em> 送到 Rails。所以這些 parameters 可以在 controller 的 actions 中被使用,通常是用來執行特定的工作 (task)。現在就來看看這些 parameters 如何表示,把 <code>create</code> action 替換成如下:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
def create
render plain: params[:article].inspect
end
</pre>
</div>
<p>這裡的 <code>render</code> method 的設定是使用 key 為 <code>plain</code> 和 value 為 <code>params[:article].inspect</code> 的簡單 hash。而 <code>params</code> method 則是一種物件,它是用來表示從表單送出的 parameters (或欄位值)。這個 <code>params</code> method 所回傳是一種類型為 <code>ActiveSupport::HashWithIndifferentAccess</code> 的物件,而這種物件可以讓你使用字串或 symbols 的 key 來從 hash 中得到所相對應值。在這裡我們只關注這些從表單送出的 parameters。</p><div class="info"><p>你要確定是否掌握了 <code>params</code> method 的用法,因為這個 method 以後會滿常使用到。現在來一起思考這個範例網址: <strong><a href="http://www.example.com/?username=dhh&[email protected]">http://www.example.com/?username=dhh&[email protected]</a></strong>。從這網址可得知 <code>params[:username]</code> 的值應該是 "dhh" 而 <code>params[:email]</code> 的值也應該會是 "<a href="mailto:[email protected]">[email protected]</a>"。</p></div><p>如果你再次重新提交表單,你將不會再看到 the missing template 錯誤。而是像下面一樣的訊息:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
{"title"=>"First article!", "text"=>"This is my first article."}
</pre>
</div>
<p>這個 action 顯示了從表單送出的 parameters。然而,這並沒有什麼實質的用處。是的,你除了可看到 parameters 之外沒有其他作用。</p><h4 id="建立-article-模型">5.4 建立 Article 模型</h4><p>對於 Rails 中的模型來說,我們習慣用單數來命名,而且所對應的資料庫資料表,我們則是習慣用複數來命名。Rails 這裡提供一個開發者常用來建立模型的 generator。想要建立模型,請執行以下的命令:</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$ bin/rails generate model Article title:string text:text
</pre>
</div>
<p>我們用這個命令來告訴 Rails 我們要建立一個 <code>Article</code> 模型,而且這個模型需具備 string 型態的 <em>title</em> 屬性和 text 型態的 <em>text</em> 屬性。這些屬性會自動新增到 <code>articles</code> 資料表中並且對應到 <code>Article</code> 模型。</p><p>執行完後 Rails 會建立一長串的檔案。現在我們感興趣的是 <code>app/models/article.rb</code> 以及 <code>db/migrate/20140120191729_create_articles.rb</code> (檔名可能略有不同)。後面那個檔案是負責建立資料庫的結構,這部份是我們接下來所要了解的。</p><div class="info"><p>Active Record 可以很聰明的將欄位名稱對應到模型的屬性,這意思是你不一定要在 Rails 模型中宣告屬性,因為 Active Record 會自動處理好這部份。</p></div><h4 id="執行一個-migration">5.5 執行一個 Migration</h4><p>就如同我們剛剛所見的,執行完 <code>rails generate model</code> 會在 <code>db/migrate</code> 的目錄中建立一個 <em>database migration</em> 的檔案。Migrations 是 Ruby 的一種類別,使得來建立和修改資料庫中的表格能夠更容易。Rails 使用 rake 命令來執行 migrations ,即使資料庫已經套用設定但還是可以回復 migration 動作。Migration 的檔名中包含著時間戳記,如此可以確保依照檔案建立的順序來先後執行。</p><p>如果你打開這個檔案<code>db/migrate/20140120191729_create_articles.rb</code> (記住這個檔案名稱會有些許不同) ,你將會看到:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
class CreateArticles < ActiveRecord::Migration
def change
create_table :articles do |t|
t.string :title
t.text :text
t.timestamps null: false
end
end
end
</pre>
</div>
<p>上面的 migration 建立了一個名為 <code>change</code> 的 method ,當執行這個 migration 的時候會呼叫到這個 method。
而定義在這個 method 中的 action 是可逆的,
也就是說 Rails 知道如何回復這個 migration 所做的更動,
以免有一天你想回復它。當你執行這個 migration 的時候會建立一個 <code>articles</code> 資料表,其中包含著一個 string 型態的欄位以及一個 text 型態的欄位。
同時也會建立兩個時間戳記的欄位來讓 Rails 可以紀錄 article 建立以及更新的時間。</p><p>TIP:更多關於 migrations 的資訊,請參考 <a href="migrations.html">Rails Database Migrations</a>。</p><p>此時,你可以使用 rake 命令來執行 migration 動作:</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
$ bin/rake db:migrate
</pre>
</div>
<p>Rails 將會執行這個 migration 的命令,並且顯示建立 Articles 資料表的訊息。</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
== CreateArticles: migrating ==================================================
-- create_table(:articles)
-> 0.0019s
== CreateArticles: migrated (0.0020s) =========================================
</pre>
</div>
<div class="note"><p>由於你目前的所有操作都在名為 development 的預設環境下,
所以這個命令會套用在<code>config/database.yml</code> 中 <code>development</code> 區塊定義的資料庫,
如果你想在其他環境執行 migrations ,像是 production ,你必須將明確的名稱代入到所下達的命令: <code>rake db:migrate RAILS_ENV=production</code>。</p></div><h4 id="在-controller-中儲存資料">5.6 在 controller 中儲存資料</h4><p>現在回頭看 <code>ArticlesController</code> , 我們必須要在 <code>create</code> action 中使用新增的 <code>Article</code> 模型來將資料存進資料庫。
打開 <code>app/controllers/articles_controller.rb</code> 並且將 <code>create</code> action 內容替換成以下:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
def create
@article = Article.new(params[:article])
@article.save
redirect_to @article
end
</pre>
</div>
<p>我們來看看這段程式碼進行了什麼動作:每個 Rails 模型都可以根據各自的屬性來初始化(實體化), 這些屬性都被自動的對應到各自的資料庫的欄位。
第一行中我們做的就只是所剛剛說的 (記住 <code>params[:article]</code> 中包含著我們有興趣的屬性)。
下一行, <code>@article.save</code> 負責將模型中資料存進資料庫。
最後再將頁面導向晚點會定義的<code>show</code> action。</p><p>TIP:你應該想知道為什麼 <code>Article.new</code> 的 <code>A</code> 是大寫的,而本文中其他地方出現的 article 卻是使用小寫。
在上面的程式碼中,我們所使用的是定義在 <code>\models\article.rb</code> 中名為 <code>Article</code> 的類別。在 Ruby 中類別名稱都是以開頭為大寫的方式命名。</p><p>TIP: <code>@article.save</code> 執行完會回傳一個boolean值來表示是否成功存進資料庫,詳細的我們晚點介紹。</p><p>如果你現在連到 <a href="http://localhost:3000/articles/new">http://localhost:3000/articles/new</a> 你 <em>幾乎</em> 快完成新增文章的動作了。再加把勁! 現在你應該會遇到以下的錯誤:</p><p><img src="images/getting_started/forbidden_attributes_for_new_article.png" alt="Forbidden attributes for new article"></p><p>Rails 有許多安全的機制可以幫助你開發出有安全性的應用程式,
現在你遇到了其中的一個機制。它被稱做 <a href="http://guides.rubyonrails.org/action_controller_overview.html#strong-parameters">strong parameters</a> ,
這個機制需要我們明確的告訴 Rails 哪些 parameters 可以在 controller 的 action 中使用。</p><p>為什麼還要這麼麻煩呢?雖然將 parameters 自動地從 controller 一次代入到模型中,讓開發者的工作簡單了許多,但是這個方便的方法卻允許了一些惡意的使用。如果出現一個向 server 發出的請求,而且這個請求被偽裝成新增文章表單所送出的資料,其中也包含著會破壞應用程式正常運作的額外欄位值,這時候會發生甚麼事?這些惡意資料將會隨著正常資料 'mass assigned(大量賦值)' 進到模型中以及資料庫 - 如此一來應用程式就有被破壞的潛在性或是更糟。</p><p>我們必須將 controller parameters 設置白名單來避免錯誤的 mass assignment ,
在這個例子中,我們需要將 <code>title</code> 和 <code>text</code> 這兩個 parameters 加入允許清單後才能正確執行 create 動作,
要達成上述動作會用到的兩個語法 <code>require</code> 和 <code>permit</code>。現在我們在 <code>create</code> action 稍作一行修正:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
@article = Article.new(params.require(:article).permit(:title, :text))
</pre>
</div>
<p>而 parameters 的部份習慣上會被提出來變成一個 method ,如此一來便可被這個 controller 中的其他 actions 使用,如 <code>create</code> 和 <code>update</code>。除了解決 mass assignment 問題之外,我們通常會將 method 設為 <code>private</code> ,來確保不會在非預期的地方被呼叫。以下是修改後的結果:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
def create
@article = Article.new(article_params)
@article.save
redirect_to @article
end
private
def article_params
params.require(:article).permit(:title, :text)
end
</pre>
</div>
<p>TIP:更多資訊,請參考
<a href="http://weblog.rubyonrails.org/2012/3/21/strong-parameters/">this blog article about Strong Parameters</a>。</p><h4 id="顯示文章">5.7 顯示文章</h4><p>如果你再次送出表單, Rails 會提示找不到 <code>show</code> action。
這樣很不方便,所以我們還是先新增 <code>show</code> action。</p><p>如同之前我們所看 <code>rake routes</code> 的輸出結果,關於 <code>show</code> action 的 route 規則如下:</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
article GET /articles/:id(.:format) articles#show
</pre>
</div>
<p>這個特別的語法 <code>:id</code> 告訴了 rails 這個 route 規則預期會收到一個 <code>:id</code>
parameter ,在我們的例子中這個 parameter 會是文章的 id。</p><p>如同之前我們所做過的,我們要在 <code>app/controllers/articles_controller.rb</code> 中新增 <code>show</code> action 以及新增相對應的 view。</p><p>NOTE:我們習慣在 controller 中的擺放標準 CRUD actions 時按照以下順序: <code>index</code> , <code>show</code> , <code>new</code> , <code>edit</code> , <code>create</code> , <code>update</code> , <code>destroy</code>。當然你可以有自己的擺放順序,但是請記住這些是 public methods ,像之前所提到過的, 這些 methods 在 controller 中一定要放在 private 和 protected method 之前才行。</p><p>考慮上述的習慣作法,我們來新增 <code>show</code> action ,如下:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
class ArticlesController < ApplicationController
def show
@article = Article.find(params[:id])
end
def new
end
# snipped for brevity
</pre>
</div>
<p>這邊有幾件事要注意。我們透過 <code>params[:id]</code> 來取得請求中的 <code>:id</code> parameter,並且將此 parameter 代入到 <code>Article.find</code> 來找到我們想看的文章。
我們還使用了一個 instance variable(實例變數) (以<code>@</code>開頭) 來參考到一個文章物件。我們會這麼做是因為 Rails 會將所有的 instance variable 送到 view 中。</p><p>現在就來建立 view <code>app/views/articles/show.html.erb</code> ,並且新增以下內容:</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
<p>
<strong>Title:</strong>
<%= @article.title %>
</p>
<p>
<strong>Text:</strong>
<%= @article.text %>
</p>
</pre>
</div>
<p>完成以上步驟後,你應該總算可以新增一篇文章了。
現在就連到 <a href="http://localhost:3000/articles/new">http://localhost:3000/articles/new</a> 並且試試看!</p><p><img src="images/getting_started/show_action_for_articles.png" alt="Show action for articles"></p><h4 id="顯示所有文章">5.8 顯示所有文章</h4><p>目前我們仍然需要一個條列出所有文章的功能,所以一起來完成它吧。
根據 <code>rake routes</code> 的輸出結果,有一條 route 規則正是為此制定:</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
articles GET /articles(.:format) articles#index
</pre>
</div>
<p>接下來先找到 <code>app/controllers/articles_controller.rb</code> ,並且將檔案裡頭的 <code>ArticlesController</code> 中加入相對應於這個 route 規則的 <code>index</code> action。
在我們新增 <code>index</code> action 的時候,習慣放在 controller 的第一個 method 的位置上,那我們就開始動手作:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
class ArticlesController < ApplicationController
def index
@articles = Article.all
end
def show
@article = Article.find(params[:id])
end
def new
end
# snipped for brevity
</pre>
</div>
<p>最後,我們在替這個 action 新增一個 view 檔案 <code>app/views/articles/index.html.erb</code> :</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
<h1>Listing articles</h1>
<table>
<tr>
<th>Title</th>
<th>Text</th>
</tr>
<% @articles.each do |article| %>
<tr>
<td><%= article.title %></td>
<td><%= article.text %></td>
</tr>
<% end %>
</table>
</pre>
</div>
<p>現在如果你連到 <a href="http://localhost:3000/articles">http://localhost:3000/articles</a> 你將會看到一整列你所新增的文章.</p><h4 id="建立連結">5.9 建立連結</h4><p>你現在可以新增文章,顯示文章,並且顯示文章清單。現在就來建立一些連結來前往這些頁面。</p><p>打開 <code>app/views/welcome/index.html.erb</code> 並且修改成如下:</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
<h1>Hello, Rails!</h1>
<%= link_to 'My Blog', controller: 'articles' %>
</pre>
</div>
<p>這個 <code>link_to</code> method 是 Rail 內建的其中一個 view helpers。他是用來建立一個文字超連結,並連結指定頁面 - 在這個例子中,是連結到顯示文章列表的頁面。</p><p>我們也為其他 views 建立連結,首先將這個
"New Article" 連結新增到 <code>app/views/articles/index.html.erb</code> ,並且放在 <code><table></code> 上面:</p><div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false">
<%= link_to 'New article', new_article_path %>
</pre>
</div>
<p>這個連結將會連到新增文章的表單頁面。</p><p>在 <code>app/views/articles/new.html.erb</code> 中 form 的底下新增一個連結來返回 <code>index</code> action:</p><div class="code_container">
<pre class="brush: ruby; html-script: true; gutter: false; toolbar: false">
<%= form_for :article, url: articles_path do |f| %>
...
<% end %>
<%= link_to 'Back', articles_path %>
</pre>
</div>
<p>最後,在 <code>app/views/articles/show.html.erb</code> 這個 template 也新增一個可以返回 <code>index</code> action 的連結, 如此一來觀看單篇文章的人也可以返回文章清單:</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
<p>
<strong>Title:</strong>
<%= @article.title %>
</p>
<p>
<strong>Text:</strong>
<%= @article.text %>
</p>
<%= link_to 'Back', articles_path %>
</pre>
</div>
<p>TIP:如果你要連到的 action 都在同一個 controller 下,你無需指定 <code>:controller</code> 選項,因為 Rails 預設的情況下會使用當前的 controller。</p><p>TIP:在 development 模式下 (你現在所有的操作都在此預設模式下) ,每次瀏覽器發出請求的時候 Rails 就會重新載入你的應用程式,所以即使做了一點修改也無需停止並重新啟動 web 服務。</p><h4 id="加入一些驗證">5.10 加入一些驗證</h4><p>目前的模型檔案 <code>app/models/article.rb</code> 內容非常簡單:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
class Article < ActiveRecord::Base
end
</pre>
</div>
<p>雖然這個檔案內容並不多 - 但是請注意 <code>Article</code> 類別是繼承自 <code>ActiveRecord::Base</code>。
Active Record 提供很多的功能來讓你的 Rails models 很大的自由性,包含一些基本的資料庫的操作 CRUD (Create , Read , Update , Destroy) ,資料的驗證,複雜的搜尋,還有將多個模型彼此關聯的功能。</p><p>Rails 提供了一些 methods 來幫你驗證送到模型的資料。
打開 <code>app/models/article.rb</code> 並且編輯:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
class Article < ActiveRecord::Base
validates :title, presence: true,
length: { minimum: 5 }
end
</pre>
</div>
<p>如此一來將確保所有文章的都有一個 title ,而且至少有五個字元長。Rails 可以驗證模型中的各種條件,
包含欄位是否存在或是否唯一,欄位的資料格式,以及是否存在相對應的物件。
驗證的部份在 <a href="active_record_validations.html">Active Record Validations</a> 有更詳盡的介紹。</p><p>有了驗證機制之後,在文章資料不正確的情況下呼叫了 <code>@article.save</code> ,它會回傳 <code>false</code>。如果你再次打開
<code>app/controllers/articles_controller.rb</code> ,你將注意到我們並沒有在 <code>create</code> action 中檢查 <code>@article.save</code> 的回傳結果。
假如 <code>@article.save</code> 執行失敗,我們應該要讓使用者回到新增文章的頁面。要完成這項機制,編輯位於 <code>app/controllers/articles_controller.rb</code> 的 <code>new</code> 以及 <code>create</code> actions :</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
def new
@article = Article.new
end
def create
@article = Article.new(article_params)
if @article.save
redirect_to @article
else
render 'new'
end
end
private
def article_params
params.require(:article).permit(:title, :text)
end
</pre>
</div>
<p>這個 <code>new</code> action 建立了一個名為 <code>@article</code> 的實體變數,至於為什麼要這麼做了晚一點你就會知道了。</p><p>值得注意,當 <code>create</code> action 中的 <code>save</code> 回傳 <code>false</code> 時,我們使用 <code>render</code> 而不是 <code>redirect_to</code>。
當使用<code>render</code> method 時,可讓 <code>@article</code> 物件傳回到 <code>new</code> template,
這樣一來,當 render 完成後,表單仍然可以保留送出前的結果,反之 <code>redirect_to</code> 則是讓瀏覽器去發出一個新的請求。</p><p>如果你重新整理了 <a href="http://localhost:3000/articles/new">http://localhost:3000/articles/new</a> 並且嘗試送出一個沒有標題的文章, Rails 將會將你導回送出前的表單頁面,但這功能還不夠完整。我們還必須讓使用者知道哪邊出錯,因此你需要修改
<code>app/views/articles/new.html.erb</code> 來新增錯誤訊息的提示:</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
<%= form_for :article, url: articles_path do |f| %>
<% if @article.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(@article.errors.count, "error") %> prohibited
this article from being saved:
</h2>
<ul>
<% @article.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :text %><br>
<%= f.text_area :text %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
<%= link_to 'Back', articles_path %>
</pre>
</div>
<p>繼續看到新增的部份。我們用 <code>@article.errors.any?</code> 來檢查是否有錯誤,如果有,我們就用 <code>@article.errors.full_messages</code> 來顯示錯誤清單。</p><p><code>pluralize</code> 是一個 rails helper ,它需要代入兩個分別為數字及字串的參數。而當數字參數大於一時, 字串參數就會自動轉換為複數型態。</p><p>我們需要在 <code>ArticlesController</code> 中加入 <code>@article = Article.new</code> ,否則在 view 中的 <code>@article</code> 將會是 <code>nil</code> , 在呼叫 <code>@article.errors.any?</code> 時就會出現錯誤。</p><p>TIP: Rails 會自動的將有錯誤的欄位用 class 為 <code>field_with_errors</code> 的 div 包起來。你可以定義一個 css 樣式來強調它們。</p><p>現在你將會得到一個有用的錯誤提示當你在嘗試去送出新增文章表單卻忘了輸入標題 <a href="http://localhost:3000/articles/new">http://localhost:3000/articles/new</a> :</p><p><img src="images/getting_started/form_with_errors.png" alt="Form With Errors"></p><h4 id="更新文章">5.11 更新文章</h4><p>我們已經看過了 CRUD 的 "CR" 部份了。現在我們來著重在 "U" 這部份, updating
articles(更新文章)。</p><p>我們第一個採取的步驟是在 <code>ArticlesController</code> 中新增 <code>edit</code> action ,通常這個 action 會擺放在 <code>new</code> 跟 <code>creat</code> actions 之間,如下所示:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
def new
@article = Article.new
end
def edit
@article = Article.find(params[:id])
end
def create
@article = Article.new(article_params)
if @article.save
redirect_to @article
else
render 'new'
end
end
</pre>
</div>
<p>接下要建立的 view 會包含一個類似於新增文章時所用到的表單。先建立並命名這個檔案 <code>app/views/articles/edit.html.erb</code> 並且編輯內容如下:</p><div class="code_container">
<pre class="brush: plain; gutter: false; toolbar: false">
<h1>Editing article</h1>
<%= form_for :article, url: article_path(@article), method: :patch do |f| %>
<% if @article.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(@article.errors.count, "error") %> prohibited
this article from being saved:
</h2>
<ul>
<% @article.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :text %><br>
<%= f.text_area :text %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
<%= link_to 'Back', articles_path %>
</pre>
</div>
<p>這個表單之後會被 <code>update</code> action 所使用,雖然目前還沒定義,不過也快了。</p><p>表單中的 <code>method: :patch</code> 選項告訴 Rails 我們想使用 <code>PATCH</code> HTTP method 來送出表單,因為根據 REST protocol ,我們應該使用 HTTP method 來更新資料。</p><p><code>form_for</code> 的第一個參數也可以是一個物件,例如 <code>@article</code> ,這個物件會讓 helper 使用它的欄位值來填入表單。而傳入一個跟 instance variable (<code>@article</code>) 相同名子的 symbol (<code>:article</code>) 會有一樣的效果。以上是這邊的介紹。
更多詳細細節請參考 <a href="http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html#method-i-form_for">form_for documentation</a>。</p><p>下一步,我們需要在 <code>app/controllers/articles_controller.rb</code> 建立一個 <code>update</code> action。
並且擺放在 <code>create</code> action 跟 <code>private</code> method 之間:</p><div class="code_container">
<pre class="brush: ruby; gutter: false; toolbar: false">
def create
@article = Article.new(article_params)
if @article.save
redirect_to @article
else
render 'new'
end
end
def update
@article = Article.find(params[:id])
if @article.update(article_params)
redirect_to @article
else
render 'edit'
end
end
private
def article_params
params.require(:article).permit(:title, :text)
end
</pre>