aboutsummaryrefslogtreecommitdiffstats
path: root/railties/guides
diff options
context:
space:
mode:
authorPratik Naik <pratiknaik@gmail.com>2009-02-24 12:29:25 +0000
committerPratik Naik <pratiknaik@gmail.com>2009-02-24 12:29:25 +0000
commit53cd102b39eb62567298430cbd94e40dd78d46a0 (patch)
tree3d8a087421f0d74da7a7c3878e3ad1dddbf23697 /railties/guides
parente56b3e4c0b60b2b86f5ca9c5e5a0b22fa34d37ab (diff)
downloadrails-53cd102b39eb62567298430cbd94e40dd78d46a0.tar.gz
rails-53cd102b39eb62567298430cbd94e40dd78d46a0.tar.bz2
rails-53cd102b39eb62567298430cbd94e40dd78d46a0.zip
Merge with docrails
Diffstat (limited to 'railties/guides')
-rwxr-xr-xrailties/guides/files/javascripts/code_highlighter.js2
-rw-r--r--railties/guides/files/stylesheets/main.css675
-rwxr-xr-xrailties/guides/files/stylesheets/print.css2
-rwxr-xr-xrailties/guides/files/stylesheets/reset.css2
-rw-r--r--railties/guides/files/stylesheets/syntax.css2
-rw-r--r--railties/guides/rails_guides.rb3
-rw-r--r--railties/guides/rails_guides/generator.rb60
-rw-r--r--railties/guides/rails_guides/indexer.rb8
-rw-r--r--railties/guides/rails_guides/textile_extensions.rb3
-rw-r--r--railties/guides/source/2_2_release_notes.textile8
-rw-r--r--railties/guides/source/2_3_release_notes.textile51
-rw-r--r--railties/guides/source/action_controller_overview.textile424
-rw-r--r--railties/guides/source/action_mailer_basics.textile189
-rw-r--r--railties/guides/source/active_record_basics.textile18
-rw-r--r--railties/guides/source/active_record_querying.textile631
-rw-r--r--railties/guides/source/activerecord_validations_callbacks.textile258
-rw-r--r--railties/guides/source/association_basics.textile60
-rw-r--r--railties/guides/source/caching_with_rails.textile166
-rw-r--r--railties/guides/source/command_line.textile95
-rw-r--r--railties/guides/source/configuring.textile10
-rw-r--r--railties/guides/source/contribute.textile77
-rw-r--r--railties/guides/source/credits.erb.textile29
-rw-r--r--railties/guides/source/debugging_rails_applications.textile2
-rw-r--r--railties/guides/source/form_helpers.textile54
-rw-r--r--railties/guides/source/getting_started.textile9
-rw-r--r--railties/guides/source/i18n.textile2
-rw-r--r--railties/guides/source/index.erb.textile10
-rw-r--r--railties/guides/source/layout.html.erb7
-rw-r--r--railties/guides/source/layouts_and_rendering.textile158
-rw-r--r--railties/guides/source/migrations.textile87
-rw-r--r--railties/guides/source/performance_testing.textile66
-rw-r--r--railties/guides/source/plugins.textile12
-rw-r--r--railties/guides/source/rails_on_rack.textile136
-rw-r--r--railties/guides/source/routing.textile43
-rw-r--r--railties/guides/source/security.textile92
-rw-r--r--railties/guides/source/testing.textile18
36 files changed, 2065 insertions, 1404 deletions
diff --git a/railties/guides/files/javascripts/code_highlighter.js b/railties/guides/files/javascripts/code_highlighter.js
index a234ba888c..ce983dad52 100755
--- a/railties/guides/files/javascripts/code_highlighter.js
+++ b/railties/guides/files/javascripts/code_highlighter.js
@@ -185,4 +185,4 @@ CodeHighlighter.init = function() {
for (var i=0; i < this.styleSets.length; i++) {
highlightCode(this.styleSets[i]);
}
-} \ No newline at end of file
+}
diff --git a/railties/guides/files/stylesheets/main.css b/railties/guides/files/stylesheets/main.css
index 64243be945..f8302505c5 100644
--- a/railties/guides/files/stylesheets/main.css
+++ b/railties/guides/files/stylesheets/main.css
@@ -7,416 +7,549 @@
/* General
--------------------------------------- */
-.left {float: left; margin-right: 1em;}
-.right {float: right; margin-left: 1em;}
-.small {font-size: smaller;}
-.large {font-size: larger;}
-.hide {display: none;}
+.left {
+ float: left;
+ margin-right: 1em;
+}
+.right {
+ float: right;
+ margin-left: 1em;
+}
+.small {
+ font-size: smaller;
+}
+.large {
+ font-size: larger;
+}
+.hide {
+ display: none;
+}
-li ul, li ol { margin:0 1.5em; }
-ul, ol { margin: 0 1.5em 1.5em 1.5em; }
+li ul, li ol {
+ margin: 0 1.5em;
+}
+ul, ol {
+ margin: 0 1.5em 1.5em 1.5em;
+}
-ul { list-style-type: disc; }
-ol { list-style-type: decimal; }
+ul {
+ list-style-type: disc;
+}
+ol {
+ list-style-type: decimal;
+}
-dl { margin: 0 0 1.5em 0; }
-dl dt { font-weight: bold; }
-dd { margin-left: 1.5em;}
+dl {
+ margin: 0 0 1.5em 0;
+}
+dl dt {
+ font-weight: bold;
+}
+dd {
+ margin-left: 1.5em;
+}
-pre,code { margin: 1.5em 0; white-space: pre; }
-pre,code,tt { font: 1em 'andale mono', 'lucida console', monospace; line-height: 1.5; }
+pre,code {
+ margin: 1.5em 0;
+ white-space: pre;
+}
+pre,code {
+ font: 1em 'andale mono', 'lucida console', monospace;
+ line-height: 1.5;
+}
-abbr, acronym { border-bottom: 1px dotted #666; }
-address { margin: 0 0 1.5em; font-style: italic; }
-del { color:#666; }
+abbr, acronym {
+ border-bottom: 1px dotted #666;
+}
+address {
+ margin: 0 0 1.5em;
+ font-style: italic;
+}
+del {
+ color: #666;
+}
-blockquote { margin: 1.5em; color: #666; font-style: italic; }
-strong { font-weight: bold; }
-em, dfn { font-style: italic; }
-dfn { font-weight: bold; }
-sup, sub { line-height: 0; }
-p {margin: 0 0 1.5em;}
+blockquote {
+ margin: 1.5em;
+ color: #666;
+ font-style: italic;
+}
+strong {
+ font-weight: bold;
+}
+em, dfn {
+ font-style: italic;
+}
+dfn {
+ font-weight: bold;
+}
+sup, sub {
+ line-height: 0;
+}
+p {
+ margin: 0 0 1.5em;
+}
-label { font-weight: bold; }
-fieldset { padding:1.4em; margin: 0 0 1.5em 0; border: 1px solid #ccc; }
-legend { font-weight: bold; font-size:1.2em; }
+label {
+ font-weight: bold;
+}
+fieldset {
+ padding: 1.4em;
+ margin: 0 0 1.5em 0;
+ border: 1px solid #ccc;
+}
+legend {
+ font-weight: bold;
+ font-size: 1.2em;
+}
-input.text, input.title,
-textarea, select {
- margin:0.5em 0;
- border:1px solid #bbb;
+input.text, input.title, textarea, select {
+ margin: 0.5em 0em;
+ border: 1px solid #bbb;
}
table {
- margin: 1em 0;
- border: 1px solid #ddd;
- background: #f4f4f4;
- border-spacing: 0;
+ margin: 1em 0;
+ border: 1px solid #ddd;
+ background: #f4f4f4;
+ border-spacing: 0;
}
- table th, table td {
+table th, table td {
padding: 0.25em;
border-right: 1px dotted #e0e0e0;
border-bottom: 1px dotted #e0e0e0;
- }
+}
- table th:last-child, table td:last-child {
- border-right: none;
- }
+table th:last-child, table td:last-child {
+ border-right: none;
+}
- table th {
+table th {
border-bottom: 1px solid #ddd;
background: #f0f0f0;
font-weight: bold;
- }
-
- table td {
- }
+}
- table tt {
+table tt {
padding: 0.1em;
- }
+}
/* Structure and Layout
--------------------------------------- */
body {
- text-align: center;
- font-family: Helvetica, Arial, sans-serif;
- font-size: 87.5%;
- line-height: 1.5em;
- background: #222;
- color: #999;
- }
+ text-align: center;
+ font-family: Helvetica, Arial, sans-serif;
+ font-size: 87.5%;
+ line-height: 1.5em;
+ background: #222;
+ color: #999;
+}
.wrapper {
- text-align: left;
- margin: 0 auto;
- width: 69em;
- }
+ text-align: left;
+ margin: 0 auto;
+ width: 69em;
+}
#topNav {
- padding: 1em 0;
- color: #565656;
+ padding: 1em 0;
+ color: #565656;
}
#header {
- background: #c52f24 url(../../images/header_tile.gif) repeat-x;
- color: #FFF;
- padding: 1.5em 0;
- position: relative;
- z-index: 99;
- }
+ background: #c52f24 url(../../images/header_tile.gif) repeat-x;
+ color: #FFF;
+ padding: 1.5em 0;
+ position: relative;
+ z-index: 99;
+}
#feature {
- background: #d5e9f6 url(../../images/feature_tile.gif) repeat-x;
- color: #333;
- padding: 0.5em 0 1.5em;
+ background: #d5e9f6 url(../../images/feature_tile.gif) repeat-x;
+ color: #333;
+ padding: 0.5em 0 1.5em;
}
#container {
- background: #FFF;
- color: #333;
- padding: 0.5em 0 1.5em 0;
- }
+ background: #FFF;
+ color: #333;
+ padding: 0.5em 0 1.5em 0;
+}
#mainCol {
- width: 45em;
- margin-left: 2em;
- }
+ width: 45em;
+ margin-left: 2em;
+}
#subCol {
- position: absolute;
- z-index: 0;
- top: 0;
- right: 0;
- background: #FFF;
- padding: 1em 1.5em 1em 1.25em;
- width: 17em;
- font-size: 0.9285em;
- line-height: 1.3846em;
- }
-
-#extraCol {display: none;}
+ position: absolute;
+ z-index: 0;
+ top: 0;
+ right: 0;
+ background: #FFF;
+ padding: 1em 1.5em 1em 1.25em;
+ width: 17em;
+ font-size: 0.9285em;
+ line-height: 1.3846em;
+}
+
+#extraCol {
+ display: none;
+}
#footer {
- padding: 2em 0;
- background: url(../../images/footer_tile.gif) repeat-x;
- }
+ padding: 2em 0;
+ background: url(../../images/footer_tile.gif) repeat-x;
+}
#footer .wrapper {
- padding-left: 2em;
- width: 67em;
+ padding-left: 2em;
+ width: 67em;
}
-#header .wrapper, #topNav .wrapper, #feature .wrapper {padding-left: 1em; width: 68em;}
-#feature .wrapper {width: 45em; padding-right: 23em; position: relative; z-index: 0;}
+#header .wrapper, #topNav .wrapper, #feature .wrapper {
+ padding-left: 1em;
+ width: 68em;
+}
+#feature .wrapper {
+ width: 45em;
+ padding-right: 23em;
+ position: relative;
+ z-index: 0;
+}
/* Links
--------------------------------------- */
a, a:link, a:visited {
- color: #ee3f3f;
- text-decoration: underline;
- }
+ color: #ee3f3f;
+ text-decoration: underline;
+}
-#mainCol a, #subCol a {color: #980905;}
+#mainCol a, #subCol a {
+ color: #980905;
+}
/* Navigation
--------------------------------------- */
-.nav {margin: 0; padding: 0;}
-.nav li {display: inline; list-style: none;}
+.nav {
+ margin: 0;
+ padding: 0;
+}
+.nav li {
+ display: inline;
+ list-style: none;
+}
#header .nav {
- float: right;
- margin-top: 1.5em;
- font-size: 1.2857em;
+ float: right;
+ margin-top: 1.5em;
+ font-size: 1.2857em;
}
-#header .nav li {margin: 0 0 0 0.5em;}
-#header .nav a {color: #FFF; text-decoration: none;}
-#header .nav a:hover {text-decoration: underline;}
+#header .nav li {
+ margin: 0 0 0 0.5em;
+}
+#header .nav a {
+ color: #FFF;
+ text-decoration: none;
+}
+#header .nav a:hover {
+ text-decoration: underline;
+}
#header .nav .index {
- padding: 0.5em 1.5em;
- border-radius: 1em;
- -webkit-border-radius: 1em;
- -moz-border-radius: 1em;
- background: #980905;
- position: relative;
+ padding: 0.5em 1.5em;
+ border-radius: 1em;
+ -webkit-border-radius: 1em;
+ -moz-border-radius: 1em;
+ background: #980905;
+ position: relative;
}
#header .nav .index a {
- background: #980905 url(../../images/nav_arrow.gif) no-repeat right top;
- padding-right: 1em;
- position: relative;
- z-index: 15;
- padding-bottom: 0.125em;
+ background: #980905 url(../../images/nav_arrow.gif) no-repeat right top;
+ padding-right: 1em;
+ position: relative;
+ z-index: 15;
+ padding-bottom: 0.125em;
+}
+#header .nav .index:hover a, #header .nav .index a:hover {
+ background-position: right -81px;
}
-#header .nav .index:hover a, #header .nav .index a:hover {background-position: right -81px;}
#guides {
- width: 27em;
- display: block;
- background: #980905;
- border-radius: 1em;
- -webkit-border-radius: 1em;
- -moz-border-radius: 1em;
- -webkit-box-shadow: 0.25em 0.25em 1em rgba(0,0,0,0.25);
- -moz-box-shadow: rgba(0,0,0,0.25) 0.25em 0.25em 1em;
- color: #f1938c;
- padding: 1.5em 2em;
- position: absolute;
- z-index: 10;
- top: -0.25em;
- right: 0;
- padding-top: 2em;
+ width: 27em;
+ display: block;
+ background: #980905;
+ border-radius: 1em;
+ -webkit-border-radius: 1em;
+ -moz-border-radius: 1em;
+ -webkit-box-shadow: 0.25em 0.25em 1em rgba(0,0,0,0.25);
+ -moz-box-shadow: rgba(0,0,0,0.25) 0.25em 0.25em 1em;
+ color: #f1938c;
+ padding: 1.5em 2em;
+ position: absolute;
+ z-index: 10;
+ top: -0.25em;
+ right: 0;
+ padding-top: 2em;
}
#guides dt, #guides dd {
- font-weight: normal;
- font-size: 0.722em;
- margin: 0;
- padding: 0;
-}
-#guides dt {padding:0; margin: 0.5em 0 0;}
-#guides a {color: #FFF; background: none !important;}
-#guides .L, #guides .R {float: left; width: 50%; margin: 0; padding: 0;}
-#guides .R {float: right;}
+ font-weight: normal;
+ font-size: 0.722em;
+ margin: 0;
+ padding: 0;
+}
+#guides dt {
+ padding: 0;
+ margin: 0.5em 0 0;
+}
+#guides a {
+ color: #FFF;
+ background: none !important;
+}
+#guides .L, #guides .R {
+ float: left;
+ width: 50%;
+ margin: 0;
+ padding: 0;
+}
+#guides .R {
+ float: right;
+}
#guides hr {
- display: block;
- border: none;
- height: 1px;
- color: #f1938c;
- background: #f1938c;
+ display: block;
+ border: none;
+ height: 1px;
+ color: #f1938c;
+ background: #f1938c;
}
/* Headings
--------------------------------------- */
h1 {
- font-size: 2.5em;
- line-height: 1em;
- margin: 0.6em 0 .2em;
- font-weight: bold;
- }
+ font-size: 2.5em;
+ line-height: 1em;
+ margin: 0.6em 0 .2em;
+ font-weight: bold;
+}
h2 {
- font-size: 2.1428em;
- line-height: 1em;
- margin: 0.7em 0 .2333em;
- font-weight: bold;
- }
+ font-size: 2.1428em;
+ line-height: 1em;
+ margin: 0.7em 0 .2333em;
+ font-weight: bold;
+}
h3 {
- font-size: 1.7142em;
- line-height: 1.286em;
- margin: 0.875em 0 0.2916em;
- font-weight: bold;
- }
+ font-size: 1.7142em;
+ line-height: 1.286em;
+ margin: 0.875em 0 0.2916em;
+ font-weight: bold;
+}
h4 {
- font-size: 1.2857em;
- line-height: 1.2em;
- margin: 1.6667em 0 .3887em;
- font-weight: bold;
- }
+ font-size: 1.2857em;
+ line-height: 1.2em;
+ margin: 1.6667em 0 .3887em;
+ font-weight: bold;
+}
h5 {
- font-size: 1em;
- line-height: 1.5em;
- margin: 1em 0 .5em;
- font-weight: bold;
+ font-size: 1em;
+ line-height: 1.5em;
+ margin: 1em 0 .5em;
+ font-weight: bold;
}
h6 {
- font-size: 1em;
- line-height: 1.5em;
- margin: 1em 0 .5em;
- font-weight: normal;
- }
+ font-size: 1em;
+ line-height: 1.5em;
+ margin: 1em 0 .5em;
+ font-weight: normal;
+}
/* Content
--------------------------------------- */
.pic {
- margin: 0 2em 2em 0;
+ margin: 0 2em 2em 0;
}
-#topNav strong {color: #999; margin-right: 0.5em;}
-#topNav strong a {color: #FFF;}
+#topNav strong {
+ color: #999;
+ margin-right: 0.5em;
+}
+#topNav strong a {
+ color: #FFF;
+}
#header h1 {
- float: left;
- background: url(../../images/ruby_guides_logo.gif) no-repeat;
- width: 492px;
- text-indent: -9999em;
- margin: 0;
- padding: 0;
+ float: left;
+ background: url(../../images/ruby_guides_logo.gif) no-repeat;
+ width: 492px;
+ text-indent: -9999em;
+ margin: 0;
+ padding: 0;
}
#header h1 a {
- text-decoration: none;
- display: block;
- height: 77px;
+ text-decoration: none;
+ display: block;
+ height: 77px;
}
#feature p {
- font-size: 1.2857em;
- margin-bottom: 0.75em;
+ font-size: 1.2857em;
+ margin-bottom: 0.75em;
}
-#feature ul {margin-left: 0;}
+#feature ul {
+ margin-left: 0;
+}
#feature ul li {
- list-style: none;
- background: url(../../images/check_bullet.gif) no-repeat left 0.5em;
- padding: 0.5em 1.75em 0.5em 1.75em;
- font-size: 1.1428em;
- font-weight: bold;
+ list-style: none;
+ background: url(../../images/check_bullet.gif) no-repeat left 0.5em;
+ padding: 0.5em 1.75em 0.5em 1.75em;
+ font-size: 1.1428em;
+ font-weight: bold;
}
#mainCol dd, #subCol dd {
- padding: 0.25em 0 1em;
- border-bottom: 1px solid #CCC;
- margin-bottom: 1em;
- margin-left: 0;
- padding-left: 28px;
+ padding: 0.25em 0 1em;
+ border-bottom: 1px solid #CCC;
+ margin-bottom: 1em;
+ margin-left: 0;
+ padding-left: 28px;
}
#mainCol dt, #subCol dt {
- font-size: 1.2857em;
- padding: 0.125em 0 0.25em 28px;
- margin-bottom: 0;
- background: url(../../images/book_icon.gif) no-repeat left top;
+ font-size: 1.2857em;
+ padding: 0.125em 0 0.25em 28px;
+ margin-bottom: 0;
+ background: url(../../images/book_icon.gif) no-repeat left top;
}
#mainCol dd.ticket, #subCol dd.ticket {
- background: #fff9d8 url(../../images/tab_yellow.gif) no-repeat left top;
- border: none;
- padding: 1.25em 1em 1.25em 48px;
- margin-left: 0;
- margin-top: 0.25em;
+ background: #fff9d8 url(../../images/tab_yellow.gif) no-repeat left top;
+ border: none;
+ padding: 1.25em 1em 1.25em 48px;
+ margin-left: 0;
+ margin-top: 0.25em;
}
#mainCol dd.warning, #subCol dd.warning {
- background: #f9d9d8 url(../../images/tab_red.gif) no-repeat left top;
- border: none;
- padding: 1.25em 1.25em 1.25em 48px;
- margin-left: 0;
- margin-top: 0.25em;
-}
-
-#subCol .chapters {color: #980905;}
-#subCol .chapters a {font-weight: bold;}
-#subCol .chapters ul a {font-weight: normal;}
-#subCol .chapters li {margin-bottom: 0.75em;}
-#subCol h3.chapter {margin-top: 0.25em;}
-#subCol h3.chapter img {vertical-align: text-bottom;}
-#subCol .chapters ul {margin-left: 0; margin-top: 0.5em;}
+ background: #f9d9d8 url(../../images/tab_red.gif) no-repeat left top;
+ border: none;
+ padding: 1.25em 1.25em 1.25em 48px;
+ margin-left: 0;
+ margin-top: 0.25em;
+}
+
+#subCol .chapters {
+ color: #980905;
+}
+#subCol .chapters a {
+ font-weight: bold;
+}
+#subCol .chapters ul a {
+ font-weight: normal;
+}
+#subCol .chapters li {
+ margin-bottom: 0.75em;
+}
+#subCol h3.chapter {
+ margin-top: 0.25em;
+}
+#subCol h3.chapter img {
+ vertical-align: text-bottom;
+}
+#subCol .chapters ul {
+ margin-left: 0;
+ margin-top: 0.5em;
+}
#subCol .chapters ul li {
- list-style: none;
- padding: 0 0 0 1em;
- background: url(../../images/bullet.gif) no-repeat left 0.45em;
- margin-left: 0;
- font-size: 1em;
- font-weight: normal;
+ list-style: none;
+ padding: 0 0 0 1em;
+ background: url(../../images/bullet.gif) no-repeat left 0.45em;
+ margin-left: 0;
+ font-size: 1em;
+ font-weight: normal;
+}
+#subCol .chapters p {
+ font-size: 1em;
}
tt {
- background: #EEE;
- border: 1px solid #CCC;
- padding: 0.25em 0.5em;
- font-family: monaco, "Bitstream Vera Sans Mono", "Courier New", courier, monospace;
+ font-family: monaco, "Bitstream Vera Sans Mono", "Courier New", courier, monospace;
}
code, pre {
- font-family: monaco, "Bitstream Vera Sans Mono", "Courier New", courier, monospace;
- background: #EEE url(../../images/tab_grey.gif) no-repeat left top;
- border: none;
- padding: 0.25em 1em 0.5em 48px;
- margin-left: 0;
- margin-top: 0.25em;
- display: block;
+ font-family: monaco, "Bitstream Vera Sans Mono", "Courier New", courier, monospace;
+ background: #EEE url(../../images/tab_grey.gif) no-repeat left top;
+ border: none;
+ padding: 0.25em 1em 0.5em 48px;
+ margin-left: 0;
+ margin-top: 0.25em;
+ display: block;
+ min-height: 45px;
+ overflow: auto;
+}
+
+.info code, .info pre {
+ background-image: none;
+ background-color: #C5D9E6;
+ padding: 0.25em 1em;
+ min-height: 1px;
}
.note {
- background: #fff9d8 url(../../images/tab_note.gif) no-repeat left top;
- border: none;
- padding: 1em 1em 0.25em 48px;
- margin-left: 0;
- margin-top: 0.25em;
+ background: #fff9d8 url(../../images/tab_note.gif) no-repeat left top;
+ border: none;
+ padding: 1em 1em 0.25em 48px;
+ margin-left: 0;
+ margin-top: 0.25em;
}
.info {
- background: #d5e9f6 url(../../images/tab_info.gif) no-repeat left top;
- border: none;
- padding: 1em 1em 0.25em 48px;
- margin-left: 0;
- margin-top: 0.25em;
+ background: #d5e9f6 url(../../images/tab_info.gif) no-repeat left top;
+ border: none;
+ padding: 1em 1em 0.25em 48px;
+ margin-left: 0;
+ margin-top: 0.25em;
}
.warning {
- background: #f9d9d8 url(../../images/tab_red.gif) no-repeat left top;
- border: none;
- padding: 1em 1em 0.25em 48px;
- margin-left: 0;
- margin-top: 0.25em;
+ background: #f9d9d8 url(../../images/tab_red.gif) no-repeat left top;
+ border: none;
+ padding: 1em 1em 0.25em 48px;
+ margin-left: 0;
+ margin-top: 0.25em;
}
-.warning tt, .note tt, .info tt {border:none; background: none; padding: 0;}
+.warning tt, .note tt, .info tt {
+ border: none;
+ background: none;
+ padding: 0;
+}
em.highlight {
- background: #fffcdb;
- padding: 0 0.25em;
+ background: #fffcdb;
+ padding: 0 0.25em;
}
#mainCol ul li {
- list-style:none;
- background: url(../../images/grey_bullet.gif) no-repeat left 0.5em;
- padding-left: 1em;
- margin-left: 0;
+ list-style: none;
+ background: url(../../images/grey_bullet.gif) no-repeat left 0.5em;
+ padding-left: 1em;
+ margin-left: 0;
}
/* Clearing
@@ -430,7 +563,15 @@ em.highlight {
visibility: hidden;
}
-.clearfix {display: inline-block;}
-* html .clearfix {height: 1%;}
-.clearfix {display: block;}
-.clear { clear:both; }
+.clearfix {
+ display: inline-block;
+}
+* html .clearfix {
+ height: 1%;
+}
+.clearfix {
+ display: block;
+}
+.clear {
+ clear: both;
+}
diff --git a/railties/guides/files/stylesheets/print.css b/railties/guides/files/stylesheets/print.css
index 98780ab029..628da105d4 100755
--- a/railties/guides/files/stylesheets/print.css
+++ b/railties/guides/files/stylesheets/print.css
@@ -49,4 +49,4 @@ blockquote {
}
.small { font-size: .9em; }
-.large { font-size: 1.1em; } \ No newline at end of file
+.large { font-size: 1.1em; }
diff --git a/railties/guides/files/stylesheets/reset.css b/railties/guides/files/stylesheets/reset.css
index cd5f8e846e..cb14fbcc55 100755
--- a/railties/guides/files/stylesheets/reset.css
+++ b/railties/guides/files/stylesheets/reset.css
@@ -40,4 +40,4 @@ blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
-} \ No newline at end of file
+}
diff --git a/railties/guides/files/stylesheets/syntax.css b/railties/guides/files/stylesheets/syntax.css
index cf5df03494..55fc5b209f 100644
--- a/railties/guides/files/stylesheets/syntax.css
+++ b/railties/guides/files/stylesheets/syntax.css
@@ -28,4 +28,4 @@
.ruby .symbol {
color: green;
-} \ No newline at end of file
+}
diff --git a/railties/guides/rails_guides.rb b/railties/guides/rails_guides.rb
index 7617dff5a3..6da7de890e 100644
--- a/railties/guides/rails_guides.rb
+++ b/railties/guides/rails_guides.rb
@@ -3,12 +3,13 @@ $: << pwd
$: << File.join(pwd, "../../activesupport/lib")
$: << File.join(pwd, "../../actionpack/lib")
+require "action_controller"
require "action_view"
# Require rubygems after loading Action View
require 'rubygems'
begin
- gem 'RedCloth', '= 4.1.1'# Need exactly 4.1.1
+ gem 'RedCloth', '>= 4.1.1'# Need exactly 4.1.1
rescue Gem::LoadError
$stderr.puts %(Missing the RedCloth 4.1.1 gem.\nPlease `gem install -v=4.1.1 RedCloth` to generate the guides.)
exit 1
diff --git a/railties/guides/rails_guides/generator.rb b/railties/guides/rails_guides/generator.rb
index 3cbc2eb1f4..41d22e37fd 100644
--- a/railties/guides/rails_guides/generator.rb
+++ b/railties/guides/rails_guides/generator.rb
@@ -25,29 +25,7 @@ module RailsGuides
end
guides.each do |guide|
- guide =~ /(.*?)(\.erb)?\.textile/
- name = $1
-
- puts "Generating #{name}"
-
- file = File.join(output, "#{name}.html")
- File.open(file, 'w') do |f|
- @view = ActionView::Base.new(view_path)
- @view.extend(Helpers)
-
- if guide =~ /\.erb\.textile/
- # Generate the erb pages with textile formatting - e.g. index/authors
- result = view.render(:layout => 'layout', :file => guide)
- f.write textile(result)
- else
- body = File.read(File.join(view_path, guide))
- body = set_header_section(body, @view)
- body = set_index(body, @view)
-
- result = view.render(:layout => 'layout', :text => textile(body))
- f.write result
- end
- end
+ generate_guide(guide)
end
# Copy images and css files to html directory
@@ -55,6 +33,32 @@ module RailsGuides
FileUtils.cp_r File.join(guides_dir, 'files'), File.join(output, 'files')
end
+ def generate_guide(guide)
+ guide =~ /(.*?)(\.erb)?\.textile/
+ name = $1
+
+ puts "Generating #{name}"
+
+ file = File.join(output, "#{name}.html")
+ File.open(file, 'w') do |f|
+ @view = ActionView::Base.new(view_path)
+ @view.extend(Helpers)
+
+ if guide =~ /\.erb\.textile/
+ # Generate the erb pages with textile formatting - e.g. index/authors
+ result = view.render(:layout => 'layout', :file => name)
+ f.write textile(result)
+ else
+ body = File.read(File.join(view_path, guide))
+ body = set_header_section(body, @view)
+ body = set_index(body, @view)
+
+ result = view.render(:layout => 'layout', :text => textile(body))
+ f.write result
+ end
+ end
+ end
+
def set_header_section(body, view)
new_body = body.gsub(/(.*?)endprologue\./m, '').strip
header = $1
@@ -72,21 +76,19 @@ module RailsGuides
def set_index(body, view)
index = <<-INDEX
<div id="subCol">
- <h3 class="chapter"><img src="images/chapters_icon.gif" alt="" /> Chapters</h3>
+ <h3 class="chapter"><img src="images/chapters_icon.gif" alt="" />Chapters</h3>
<ol class="chapters">
- INDEX
+ INDEX
i = Indexer.new(body)
i.index
# Set index for 2 levels
i.level_hash.each do |key, value|
- bookmark = '#' + key.gsub(/[^a-z0-9\-_\+]+/i, '').underscore.dasherize
- link = view.content_tag(:a, :href => bookmark) { key }
+ link = view.content_tag(:a, :href => key[:id]) { textile(key[:title]) }
children = value.keys.map do |k|
- bm = '#' + k.gsub(/[^a-z0-9\-_\+]+/i, '').underscore.dasherize
- l = view.content_tag(:a, :href => bm) { k }
+ l = view.content_tag(:a, :href => k[:id]) { textile(k[:title]) }
view.content_tag(:li, l)
end
diff --git a/railties/guides/rails_guides/indexer.rb b/railties/guides/rails_guides/indexer.rb
index 52809829e2..7cb254d0b0 100644
--- a/railties/guides/rails_guides/indexer.rb
+++ b/railties/guides/rails_guides/indexer.rb
@@ -29,13 +29,17 @@ module RailsGuides
return level_hash
elsif level == current_level
index = counters.join(".")
- bookmark = '#' + title.gsub(/[^a-z0-9\-_\+]+/i, '').underscore.dasherize
+ bookmark = '#' + title.gsub(/[^a-z0-9\-_]+/i, '').underscore.dasherize
raise "Parsing Fail" unless @result.sub!(matched, "h#{level}(#{bookmark}). #{index}#{title}")
+ key = {
+ :title => title,
+ :id => bookmark
+ }
# Recurse
counters << 1
- level_hash[title.strip] = process(s.post_match, current_level + 1, counters)
+ level_hash[key] = process(s.post_match, current_level + 1, counters)
counters.pop
# Increment the current level
diff --git a/railties/guides/rails_guides/textile_extensions.rb b/railties/guides/rails_guides/textile_extensions.rb
index a7fc5ba49c..7ebbf0090e 100644
--- a/railties/guides/rails_guides/textile_extensions.rb
+++ b/railties/guides/rails_guides/textile_extensions.rb
@@ -25,6 +25,9 @@ module RailsGuides
body.gsub!(/\+(.*?)\+/) do |m|
"<notextile><tt>#{$1}</tt></notextile>"
end
+
+ # The real plus sign
+ body.gsub!('<plus>', '+')
end
def code(body)
diff --git a/railties/guides/source/2_2_release_notes.textile b/railties/guides/source/2_2_release_notes.textile
index 6f882f6d7d..f60af01050 100644
--- a/railties/guides/source/2_2_release_notes.textile
+++ b/railties/guides/source/2_2_release_notes.textile
@@ -164,9 +164,9 @@ h4. New Dynamic Finders
Two new sets of methods have been added to Active Record's dynamic finders family.
-h5. +find_last_by_<attribute>+
+h5. +find_last_by_<em>attribute</em>+
-The +find_last_by_<attribute>+ method is equivalent to +Model.last(:conditions => {:attribute => value})+
+The +find_last_by_<em>attribute</em>+ method is equivalent to +Model.last(:conditions => {:attribute => value})+
<ruby>
# Get the last user who signed up from London
@@ -175,9 +175,9 @@ User.find_last_by_city('London')
* Lead Contributor: "Emilio Tagua":http://www.workingwithrails.com/person/9147-emilio-tagua
-h5. +find_by_<attribute>!+
+h5. +find_by_<em>attribute</em>!+
-The new bang! version of +find_by_<attribute>!+ is equivalent to +Model.first(:conditions => {:attribute => value}) || raise ActiveRecord::RecordNotFound+ Instead of returning +nil+ if it can't find a matching record, this method will raise an exception if it cannot find a match.
+The new bang! version of +find_by_<em>attribute</em>!+ is equivalent to +Model.first(:conditions => {:attribute => value}) || raise ActiveRecord::RecordNotFound+ Instead of returning +nil+ if it can't find a matching record, this method will raise an exception if it cannot find a match.
<ruby>
# Raise ActiveRecord::RecordNotFound exception if 'Moby' hasn't signed up yet!
diff --git a/railties/guides/source/2_3_release_notes.textile b/railties/guides/source/2_3_release_notes.textile
index 334416f3f6..4734e32606 100644
--- a/railties/guides/source/2_3_release_notes.textile
+++ b/railties/guides/source/2_3_release_notes.textile
@@ -42,7 +42,7 @@ Here's a summary of the rack-related changes:
h4. Renewed Support for Rails Engines
-After some versions without an upgrade, Rails 2.3 offers some new features for Rails Engines (Rails applications that can be embedded within other applications). First, routing files in engines are automatically loaded and reloaded now, just like your +routes.rb+ file (this also applies to routing files in other plugins). Second, if your plugin has an app folder, then app/[models|controllers|helpers] will automatically be added to the Rails load path. Engines also support adding view paths now.
+After some versions without an upgrade, Rails 2.3 offers some new features for Rails Engines (Rails applications that can be embedded within other applications). First, routing files in engines are automatically loaded and reloaded now, just like your +routes.rb+ file (this also applies to routing files in other plugins). Second, if your plugin has an app folder, then app/[models|controllers|helpers] will automatically be added to the Rails load path. Engines also support adding view paths now, and Action Mailer as well as Action View will use views from engines and other plugins.
h3. Documentation
@@ -50,9 +50,13 @@ The "Ruby on Rails guides":http://guides.rubyonrails.org/ project has published
* More Information: "Rails Documentation Projects":http://weblog.rubyonrails.org/2009/1/15/rails-documentation-projects
+h3. Ruby 1.9.1 Support
+
+Rails 2.3 should pass all of its own tests whether you are running on Ruby 1.8 or the now-released Ruby 1.9.1. You should be aware, though, that moving to 1.9.1 entails checking all of the data adapters, plugins, and other code that you depend on for Ruby 1.9.1 compatibility, as well as Rails core.
+
h3. Active Record
-Active Record gets quite a number of new features and bug fixes in Rails 2.3. The highlights include nested attributes, nested transactions, dynamic scopes, and default scopes.
+Active Record gets quite a number of new features and bug fixes in Rails 2.3. The highlights include nested attributes, nested transactions, dynamic and default scopes, and batch processing.
h4. Nested Attributes
@@ -69,6 +73,13 @@ end
Turning on nested attributes enables a number of things: automatic (and atomic) saving of a record together with its associated children, child-aware validations, and support for nested forms (discussed later).
+You can also specify requirements for any new records that are added via nested attributes using the +:reject_if+ option:
+
+<ruby>
+accepts_nested_attributes_for :author,
+ :reject_if => proc { |attributes| attributes['name'].blank? }
+</ruby>
+
* Lead Contributor: "Eloy Duran":http://www.superalloy.nl/blog/
* More Information: "Nested Model Forms":http://weblog.rubyonrails.org/2009/1/26/nested-model-forms
@@ -115,6 +126,28 @@ Rails 2.3 will introduce the notion of _default scopes_ similar to named scopes,
* Lead Contributor: Paweł Kondzior
* More Information: "What's New in Edge Rails: Default Scoping":http://ryandaigle.com/articles/2008/11/18/what-s-new-in-edge-rails-default-scoping
+h4. Batch Processing
+
+You can now process large numbers of records from an ActiveRecord model with less pressure on memory by using +find_in_batches+:
+
+<ruby>
+Customer.find_in_batches(:conditions => {:active => true}) do |customer_group|
+ customer_group.each { |customer| customer.update_account_balance! }
+end
+</ruby>
+
+You can pass most of the +find+ options into +find_in_batches+. However, you cannot specify the order that records will be returned in (they will always be returned in ascending order of primary key, which must be an integer), or use the +:limit+ option. Instead, use the +:batch_size: option, which defaults to 1000, to set the number of records that will be returned in each batch.
+
+The new +each+ method provides a wrapper around +find_in_batches+ that returns individual records, with the find itself being done in batches (of 1000 by default):
+
+<ruby>
+Customer.each do |customer|
+ customer.update_account_balance!
+end
+</ruby>
+
+Note that you should only use this record for batch processing: for small numbers of records (less than 1000), you should just use the regular find methods with your own loop.
+
h4. Multiple Conditions for Callbacks
When using Active Record callbacks, you can now combine +:if+ and +:unless+ options on the same callback, and supply multiple conditions as an array:
@@ -268,6 +301,10 @@ h4. Localized Views
Rails can now provide localized views, depending on the locale that you have set. For example, suppose you have a +Posts+ controller with a +show+ action. By default, this will render +app/views/posts/show.html.erb+. But if you set +I18n.locale = :da+, it will render +app/views/posts/show.da.html.erb+. If the localized template isn't present, the undecorated version will be used. Rails also includes +I18n#available_locales+ and +I18n::SimpleBackend#available_locales+, which return an array of the translations that are available in the current Rails project.
+h4. Partial Scoping for Translations
+
+A change to the translation API makes things easier and less repetitive to write key translations within partials. If you call +translate(".foo")+ from the +people/index.html.erb+ template, you'll actually be calling +I18n.translate("people.index.foo")+ If you don't prepend the key with a period, then the API doesn't scope, just as before.
+
h4. Other Action Controller Changes
* ETag handling has been cleaned up a bit: Rails will now skip sending an ETag header when there's no body to the response or when sending files with +send_file+.
@@ -399,6 +436,7 @@ h4. Other Action View Changes
* Token generation for CSRF protection has been simplified; now Rails uses a simple random string generated by +ActiveSupport::SecureRandom+ rather than mucking around with session IDs.
* +auto_link+ now properly applies options (such as +:target+ and +:class+) to generated e-mail links.
* The +autolink+ helper has been refactored to make it a bit less messy and more intuitive.
+* +current_page?+ now works properly even when there are multiple query parameters in the URL.
h3. Active Support
@@ -406,7 +444,7 @@ Active Support has a few interesting changes, including the introduction of +Obj
h4. Object#try
-A lot of folks have adopted the notion of using try() to attempt operations on objects. It's especially helpful in views where you can avoid nil-checking by writing code like +<%= @person.try(:name) %>+. Well, now it's baked right into Rails. As implemented in Rails, it raises +NoMethodError+ on private methods and always returns +nil+ if the object is nil.
+A lot of folks have adopted the notion of using try() to attempt operations on objects. It's especially helpful in views where you can avoid nil-checking by writing code like +&lt;%= @person.try(:name) %&gt;+. Well, now it's baked right into Rails. As implemented in Rails, it raises +NoMethodError+ on private methods and always returns +nil+ if the object is nil.
* More Information: "try()":http://ozmm.org/posts/try.html.
@@ -482,6 +520,9 @@ h4. Other Railties Changes
* The dbconsole script now lets you use an all-numeric password without crashing.
* +Rails.root+ now returns a +Pathname+ object, which means you can use it directly with the +join+ method to "clean up existing code":http://afreshcup.com/2008/12/05/a-little-rails_root-tidiness/ that uses +File.join+.
* Various files in /public that deal with CGI and FCGI dispatching are no longer generated in every Rails application by default (you can still get them if you need them by adding +--with-dispatches+ when you run the +rails+ command, or add them later with +rake rails:generate_dispatchers+).
+* Rails Guides have been converted from AsciiDoc to Textile markup.
+* Scaffolded views and controllers have been cleaned up a bit.
+* +script/server+ now accepts a <tt>--path</tt> argument to mount a Rails application from a specific path.
h3. Deprecated
@@ -495,7 +536,9 @@ A few pieces of older code are deprecated in this release:
* The +:digest+ and +:secret+ options to +protect_from_forgery+ are deprecated and have no effect.
* Some integration test helpers have been removed. +response.headers["Status"]+ and +headers["Status"]+ will no longer return anything. Rack does not allow "Status" in its return headers. However you can still use the +status+ and +status_message+ helpers. +response.headers["cookie"]+ and +headers["cookie"]+ will no longer return any CGI cookies. You can inspect +headers["Set-Cookie"]+ to see the raw cookie header or use the +cookies+ helper to get a hash of the cookies sent to the client.
* +formatted_polymorphic_url+ is deprecated. Use +polymorphic_url+ with +:format+ instead.
+* The +:http_only+ option in +ActionController::Response#set_cookie+ has been renamed to +:httponly+.
+* The +:connector+ and +:skip_last_comma+ options of +to_sentence+ have been replaced by +:words_connnector+, +:two_words_connector+, and +:last_word_connector+ options.
h3. Credits
-Release notes compiled by "Mike Gunderloy":http://afreshcup.com
+Release notes compiled by "Mike Gunderloy":http://afreshcup.com. This version of the Rails 2.3 release notes was compiled based on RC2 of Rails 2.3.
diff --git a/railties/guides/source/action_controller_overview.textile b/railties/guides/source/action_controller_overview.textile
index 31a9c6819c..949a962b27 100644
--- a/railties/guides/source/action_controller_overview.textile
+++ b/railties/guides/source/action_controller_overview.textile
@@ -12,9 +12,9 @@ In this guide you will learn how controllers work and how they fit into the requ
endprologue.
-h3. What Does a Controller do?
+h3. What Does a Controller Do?
-Action Controller is the C in MVC. After routing has determined which controller to use for a request, your controller is responsible for making sense of the request and producing the appropriate output. Luckily, Action Controller does most of the groundwork for you and uses smart conventions to make this as straight-forward as possible.
+Action Controller is the C in MVC. After routing has determined which controller to use for a request, your controller is responsible for making sense of the request and producing the appropriate output. Luckily, Action Controller does most of the groundwork for you and uses smart conventions to make this as straightforward as possible.
For most conventional RESTful applications, the controller will receive the request (this is invisible to you as the developer), fetch or save data from a model and use a view to create HTML output. If your controller needs to do things a little differently, that's not a problem, this is just the most common way for a controller to work.
@@ -24,31 +24,16 @@ NOTE: For more details on the routing process, see "Rails Routing from the Outsi
h3. Methods and Actions
-A controller is a Ruby class which inherits from ApplicationController and has methods just like any other class. When your application receives a request, the routing will determine which controller and action to run, then Rails creates an instance of that controller and runs the public method with the same name as the action.
+A controller is a Ruby class which inherits from +ApplicationController+ and has methods just like any other class. When your application receives a request, the routing will determine which controller and action to run, then Rails creates an instance of that controller and runs the method with the same name as the action.
<ruby>
class ClientsController < ApplicationController
-
- # Actions are public methods
def new
end
-
- # Action methods are responsible for producing output
- def edit
- end
-
-# Helper methods are private and can not be used as actions
-private
-
- def foo
- end
-
end
</ruby>
-There's no rule saying a method on a controller has to be an action; they may well be used for other purposes such as filters, which will be covered later in this guide.
-
-As an example, if a user goes to +/clients/new+ in your application to add a new client, Rails will create an instance of ClientsController and run the +new+ method. Note that the empty method from the example above could work just fine because Rails will by default render the +new.html.erb+ view unless the action says otherwise. The +new+ method could make available to the view a +@client+ instance variable by creating a new Client:
+As an example, if a user goes to +/clients/new+ in your application to add a new client, Rails will create an instance of +ClientsController+ and run the +new+ method. Note that the empty method from the example above could work just fine because Rails will by default render the +new.html.erb+ view unless the action says otherwise. The +new+ method could make available to the view a +@client+ instance variable by creating a new +Client+:
<ruby>
def new
@@ -58,54 +43,55 @@ end
The "Layouts & rendering guide":layouts_and_rendering.html explains this in more detail.
-ApplicationController inherits from ActionController::Base, which defines a number of helpful methods. This guide will cover some of these, but if you're curious to see what's in there, you can see all of them in the API documentation or in the source itself.
++ApplicationController+ inherits from +ActionController::Base+, which defines a number of helpful methods. This guide will cover some of these, but if you're curious to see what's in there, you can see all of them in the API documentation or in the source itself.
+Only public methods are callable as actions. It is a best practice to lower the visibility of methods which are not intended to be actions, like auxiliary methods or filters.
h3. Parameters
-You will probably want to access data sent in by the user or other parameters in your controller actions. There are two kinds of parameters possible in a web application. The first are parameters that are sent as part of the URL, called query string parameters. The query string is everything after "?" in the URL. The second type of parameter is usually referred to as POST data. This information usually comes from a HTML form which has been filled in by the user. It's called POST data because it can only be sent as part of an HTTP POST request. Rails does not make any distinction between query string parameters and POST parameters, and both are available in the +params+ hash in your controller:
+You will probably want to access data sent in by the user or other parameters in your controller actions. There are two kinds of parameters possible in a web application. The first are parameters that are sent as part of the URL, called query string parameters. The query string is everything after "?" in the URL. The second type of parameter is usually referred to as POST data. This information usually comes from an HTML form which has been filled in by the user. It's called POST data because it can only be sent as part of an HTTP POST request. Rails does not make any distinction between query string parameters and POST parameters, and both are available in the +params+ hash in your controller:
<ruby>
class ClientsController < ActionController::Base
-
- # This action uses query string parameters because it gets run by a HTTP
- # GET request, but this does not make any difference to the way in which
- # the parameters are accessed. The URL for this action would look like this
- # in order to list activated clients: /clients?status=activated
+ # This action uses query string parameters because it gets run
+ # by an HTTP GET request, but this does not make any difference
+ # to the way in which the parameters are accessed. The URL for
+ # this action would look like this in order to list activated
+ # clients: /clients?status=activated
def index
- if params[:status] = "activated"
+ if params[:status] == "activated"
@clients = Client.activated
else
- @clients = Client.unativated
+ @clients = Client.unactivated
end
end
- # This action uses POST parameters. They are most likely coming from an HTML
- # form which the user has submitted. The URL for this RESTful request will
- # be "/clients", and the data will be sent as part of the request body.
+ # This action uses POST parameters. They are most likely coming
+ # from an HTML form which the user has submitted. The URL for
+ # this RESTful request will be "/clients", and the data will be
+ # sent as part of the request body.
def create
@client = Client.new(params[:client])
if @client.save
redirect_to @client
else
- # This line overrides the default rendering behavior, which would have been
- # to render the "create" view.
+ # This line overrides the default rendering behavior, which
+ # would have been to render the "create" view.
render :action => "new"
end
end
-
end
</ruby>
-h4. Hash and Array Parameters
+h4. Hash and array parameters
-The params hash is not limited to one-dimensional keys and values. It can contain arrays and (nested) hashes. To send an array of values, append "[]" to the key name:
+The +params+ hash is not limited to one-dimensional keys and values. It can contain arrays and (nested) hashes. To send an array of values, append an empty pair of square brackets "[]" to the key name:
-<pre><code>
+<pre>
GET /clients?ids[]=1&ids[]=2&ids[]=3
-</code></pre>
+</pre>
-NOTE: The actual URL in this example will be encoded as "/clients?ids%5b%5d=1&ids%5b%5d=2&ids%5b%5b=3" as [ and ] are not allowed in URLs. Most of the time you don't have to worry about this because the browser will take care of it for you, and Rails will decode it back when it receives it, but if you ever find yourself having to send those requests to the server manually you have to keep this in mind.
+NOTE: The actual URL in this example will be encoded as "/clients?ids%5b%5d=1&ids%5b%5d=2&ids%5b%5b=3" as "[" and "]" are not allowed in URLs. Most of the time you don't have to worry about this because the browser will take care of it for you, and Rails will decode it back when it receives it, but if you ever find yourself having to send those requests to the server manually you have to keep this in mind.
The value of +params[:ids]+ will now be +["1", "2", "3"]+. Note that parameter values are always strings; Rails makes no attempt to guess or cast the type.
@@ -120,21 +106,22 @@ To send a hash you include the key name inside the brackets:
</form>
</html>
-The value of +params[:client]+ when this form is submitted will be +{"name" => "Acme", "phone" => "12345", "address" => {"postcode" => "12345", "city" => "Carrot City"}}+. Note the nested hash in +params[:client][:address]+.
+When this form is submitted, the value of +params[:client]+ will be <tt>{"name" => "Acme", "phone" => "12345", "address" => {"postcode" => "12345", "city" => "Carrot City"}}</tt>. Note the nested hash in +params[:client][:address]+.
-Note that the params hash is actually an instance of HashWithIndifferentAccess from Active Support which is a subclass of Hash which lets you use symbols and strings interchangeably as keys.
+Note that the +params+ hash is actually an instance of +HashWithIndifferentAccess+ from Active Support, which acts like a hash that lets you use symbols and strings interchangeably as keys.
h4. Routing Parameters
The +params+ hash will always contain the +:controller+ and +:action+ keys, but you should use the methods +controller_name+ and +action_name+ instead to access these values. Any other parameters defined by the routing, such as +:id+ will also be available. As an example, consider a listing of clients where the list can show either active or inactive clients. We can add a route which captures the +:status+ parameter in a "pretty" URL:
<ruby>
-# ...
-map.connect "/clients/:status", :controller => "clients", :action => "index", :foo => "bar"
-# ...
+map.connect "/clients/:status",
+ :controller => "clients",
+ :action => "index",
+ :foo => "bar"
</ruby>
-In this case, when a user opens the URL +/clients/active+, +params[:status]+ will be set to "active". When this route is used, +params[:foo]+ will also be set to "bar" just like it was passed in the query string in the same way +params[:action]+ will contain "index".
+In this case, when a user opens the URL +/clients/active+, +params[:status]+ will be set to "active". When this route is used, +params[:foo]+ will also be set to "bar" just like it was passed in the query string. In the same way +params[:action]+ will contain "index".
h4. default_url_options
@@ -142,16 +129,14 @@ You can set global default parameters that will be used when generating URLs wit
<ruby>
class ApplicationController < ActionController::Base
-
- #The options parameter is the hash passed in to +url_for+
+ # The options parameter is the hash passed in to 'url_for'
def default_url_options(options)
{:locale => I18n.locale}
end
-
end
</ruby>
-These options will be used as a starting-point when generating, so it's possible they'll be overridden by +url_for+. Because this method is defined in the controller, you can define it on ApplicationController so it would be used for all URL generation, or you could define it on only one controller for all URLs generated there.
+These options will be used as a starting-point when generating URLs, so it's possible they'll be overridden by +url_for+. Because this method is defined in the controller, you can define it on +ApplicationController+ so it would be used for all URL generation, or you could define it on only one controller for all URLs generated there.
h3. Session
@@ -163,20 +148,39 @@ Your application has a session for each user in which you can store small amount
* MemCacheStore - Stores the data in a memcache.
* ActiveRecordStore - Stores the data in a database using Active Record.
-All session stores use a cookie - this is required and Rails does not allow any part of the session to be passed in any other way (e.g. you can't use the query string to pass a session ID) because of security concerns (it's easier to hijack a session when the ID is part of the URL).
+All session stores use a cookie to store a unique ID for each session (you must use a cookie, Rails will not allow you to pass the session ID in the URL as this is less secure).
+
+For most stores this ID is used to look up the session data on the server, e.g. in a database table. There is one exception, and that is the default and recommended session store - the CookieStore - which stores all session data in the cookie itself (the ID is still available to you if you need it). This has the advantage of being very lightweight and it requires zero setup in a new application in order to use the session. The cookie data is cryptographically signed to make it tamper-proof, but it is not encrypted, so anyone with access to it can read its contents but not edit it (Rails will not accept it if it has been edited).
-Most stores use a cookie to store the session ID which is then used to look up the session data on the server. The default and recommended store, the CookieStore, does not store session data on the server, but in the cookie itself. The data is cryptographically signed to make it tamper-proof, but it is not encrypted, so anyone with access to it can read its contents but not edit it (Rails will not accept it if it has been edited). It can only store about 4kB of data - much less than the others - but this is usually enough. Storing large amounts of data is discouraged no matter which session store your application uses. You should especially avoid storing complex objects (anything other than basic Ruby objects, the most common example being model instances) in the session, as the server might not be able to reassemble them between requests, which will result in an error. The CookieStore has the added advantage that it does not require any setting up beforehand - Rails will generate a "secret key" which will be used to sign the cookie when you create the application.
+The CookieStore can store around 4kB of data -- much less than the others -- but this is usually enough. Storing large amounts of data in the session is discouraged no matter which session store your application uses. You should especially avoid storing complex objects (anything other than basic Ruby objects, the most common example being model instances) in the session, as the server might not be able to reassemble them between requests, which will result in an error.
Read more about session storage in the "Security Guide":security.html.
-If you need a different session storage mechanism, you can change it in the +config/environment.rb+ file:
+If you need a different session storage mechanism, you can change it in the +config/initializers/session_store.rb+ file:
<ruby>
-# Set to one of [:active_record_store, :drb_store, :mem_cache_store, :cookie_store]
-config.action_controller.session_store = :active_record_store
+# Use the database for sessions instead of the cookie-based default,
+# which shouldn't be used to store highly confidential information
+# (create the session table with "rake db:sessions:create")
+# ActionController::Base.session_store = :active_record_store
</ruby>
-h4. Accessing the Session
+Rails sets up a session key (the name of the cookie) and (for the CookieStore) a secret key used when signing the session data. These can also be changed in +config/initializers/session_store.rb+:
+
+<ruby>
+# Your secret key for verifying cookie session data integrity.
+# If you change this key, all old sessions will become invalid!
+# Make sure the secret is at least 30 characters and all random,
+# no regular words or you'll be exposed to dictionary attacks.
+ActionController::Base.session = {
+ :key => '_yourappname_session',
+ :secret => '4f50711b8f0f49572...'
+}
+</ruby>
+
+NOTE: Changing the secret when using the CookieStore will invalidate all existing sessions.
+
+h4. Accessing the session
In your controller you can access the session through the +session+ instance method.
@@ -189,13 +193,14 @@ class ApplicationController < ActionController::Base
private
- # Finds the User with the ID stored in the session with the key :current_user_id
- # This is a common way to handle user login in a Rails application; logging in sets the
- # session value and logging out removes it.
+ # Finds the User with the ID stored in the session with the key
+ # :current_user_id This is a common way to handle user login in
+ # a Rails application; logging in sets the session value and
+ # logging out removes it.
def current_user
- @_current_user ||= session[:current_user_id] && User.find(session[:current_user_id])
+ @_current_user ||= session[:current_user_id] &&
+ User.find(session[:current_user_id])
end
-
end
</ruby>
@@ -203,16 +208,15 @@ To store something in the session, just assign it to the key like a hash:
<ruby>
class LoginsController < ApplicationController
-
# "Create" a login, aka "log the user in"
def create
- if user = User.authenticate(params[:username, params[:password])
- # Save the user ID in the session so it can be used in subsequent requests
+ if user = User.authenticate(params[:username], params[:password])
+ # Save the user ID in the session so it can be used in
+ # subsequent requests
session[:current_user_id] = user.id
redirect_to root_url
end
end
-
end
</ruby>
@@ -220,14 +224,12 @@ To remove something from the session, assign that key to be +nil+:
<ruby>
class LoginsController < ApplicationController
-
# "Delete" a login, aka "log the user out"
def destroy
# Remove the user id from the session
session[:current_user_id] = nil
redirect_to root_url
end
-
end
</ruby>
@@ -239,13 +241,11 @@ The flash is a special part of the session which is cleared with each request. T
<ruby>
class LoginsController < ApplicationController
-
def destroy
session[:current_user_id] = nil
flash[:notice] = "You have successfully logged out"
redirect_to root_url
end
-
end
</ruby>
@@ -272,15 +272,19 @@ If you want a flash value to be carried over to another request, use the +keep+
<ruby>
class MainController < ApplicationController
-
- # Let's say this action corresponds to root_url, but you want all requests here to be redirected to
- # UsersController#index. If an action sets the flash and redirects here, the values would normally be
- # lost when another redirect happens, but you can use keep to make it persist for another request.
+ # Let's say this action corresponds to root_url, but you want
+ # all requests here to be redirected to UsersController#index.
+ # If an action sets the flash and redirects here, the values
+ # would normally be lost when another redirect happens, but you
+ # can use 'keep' to make it persist for another request.
def index
- flash.keep # Will persist all flash values. You can also use a key to keep only that value: flash.keep(:notice)
+ # Will persist all flash values.
+ flash.keep
+
+ # You can also use a key to keep only some kind of value.
+ # flash.keep(:notice)
redirect_to users_url
end
-
end
</ruby>
@@ -290,7 +294,6 @@ By default, adding values to the flash will make them available to the next requ
<ruby>
class ClientsController < ApplicationController
-
def create
@client = Client.new(params[:client])
if @client.save
@@ -300,19 +303,17 @@ class ClientsController < ApplicationController
render :action => "new"
end
end
-
end
</ruby>
h3. Cookies
-Your application can store small amounts of data on the client - called cookies - that will be persisted across requests and even sessions. Rails provides easy access to cookies via the +cookies+ method, which - much like the +session+ - works like a hash:
+Your application can store small amounts of data on the client -- called cookies -- that will be persisted across requests and even sessions. Rails provides easy access to cookies via the +cookies+ method, which -- much like the +session+ -- works like a hash:
<ruby>
class CommentsController < ApplicationController
-
def new
- #Auto-fill the commenter's name if it has been stored in a cookie
+ # Auto-fill the commenter's name if it has been stored in a cookie
@comment = Comment.new(:name => cookies[:commenter_name])
end
@@ -321,10 +322,10 @@ class CommentsController < ApplicationController
if @comment.save
flash[:notice] = "Thanks for your comment!"
if params[:remember_name]
- # Remember the commenter's name
+ # Remember the commenter's name.
cookies[:commenter_name] = @comment.name
else
- # Don't remember, and delete the name if it has been remembered before
+ # Delete cookie for the commenter's name cookie, if any.
cookies.delete(:commenter_name)
end
redirect_to @comment.article
@@ -332,7 +333,6 @@ class CommentsController < ApplicationController
render :action => "new"
end
end
-
end
</ruby>
@@ -340,78 +340,70 @@ Note that while for session values you set the key to +nil+, to delete a cookie
h3. Filters
-Filters are methods that are run before, after or "around" a controller action. For example, one filter might check to see if the logged in user has the right credentials to access that particular controller or action. Filters are inherited, so if you set a filter on ApplicationController, it will be run on every controller in your application. A common, simple filter is one which requires that a user is logged in for an action to be run. You can define the filter method this way:
+Filters are methods that are run before, after or "around" a controller action.
+
+Filters are inherited, so if you set a filter on +ApplicationController+, it will be run on every controller in your application.
+
+Before filters may halt the request cycle. A common before filter is one which requires that a user is logged in for an action to be run. You can define the filter method this way:
<ruby>
class ApplicationController < ActionController::Base
+ before_filter :require_login
private
-
def require_login
unless logged_in?
flash[:error] = "You must be logged in to access this section"
- redirect_to new_login_url # Prevents the current action from running
+ redirect_to new_login_url # halts request cycle
end
end
- # The logged_in? method simply returns true if the user is logged in and
- # false otherwise. It does this by "booleanizing" the current_user method
- # we created previously using a double ! operator. Note that this is not
- # common in Ruby and is discouraged unless you really mean to convert something
- # into true or false.
+ # The logged_in? method simply returns true if the user is logged
+ # in and false otherwise. It does this by "booleanizing" the
+ # current_user method we created previously using a double ! operator.
+ # Note that this is not common in Ruby and is discouraged unless you
+ # really mean to convert something into true or false.
def logged_in?
!!current_user
end
-
end
</ruby>
-The method simply stores an error message in the flash and redirects to the login form if the user is not logged in. If a before filter (a filter which is run before the action) renders or redirects, the action will not run. If there are additional filters scheduled to run after the rendering or redirecting filter, they are also cancelled. To use this filter in a controller, use the +before_filter+ method:
+The method simply stores an error message in the flash and redirects to the login form if the user is not logged in. If a before filter renders or redirects, the action will not run. If there are additional filters scheduled to run after that filter they are also cancelled.
-<ruby>
-class ApplicationController < ActionController::Base
-
- before_filter :require_login
-
-end
-</ruby>
-
-In this example, the filter is added to ApplicationController and thus all controllers in the application. This will make everything in the application require the user to be logged in in order to use it. For obvious reasons (the user wouldn't be able to log in in the first place!), not all controllers or actions should require this. You can prevent this filter from running before particular actions with +skip_before_filter+:
+In this example the filter is added to +ApplicationController+ and thus all controllers in the application inherit it. This will make everything in the application require the user to be logged in in order to use it. For obvious reasons (the user wouldn't be able to log in in the first place!), not all controllers or actions should require this. You can prevent this filter from running before particular actions with +skip_before_filter+:
<ruby>
class LoginsController < Application
-
skip_before_filter :require_login, :only => [:new, :create]
-
end
</ruby>
-Now, the LoginsController's +new+ and +create+ actions will work as before without requiring the user to be logged in. The +:only+ option is used to only skip this filter for these actions, and there is also an +:except+ option which works the other way. These options can be used when adding filters too, so you can add a filter which only runs for selected actions in the first place.
+Now, the +LoginsController+'s +new+ and +create+ actions will work as before without requiring the user to be logged in. The +:only+ option is used to only skip this filter for these actions, and there is also an +:except+ option which works the other way. These options can be used when adding filters too, so you can add a filter which only runs for selected actions in the first place.
-h4. After Filters and Around Filters
+h4. After filters and around filters
-In addition to the before filters, you can run filters after an action has run or both before and after. The after filter is similar to the before filter, but because the action has already been run it has access to the response data that's about to be sent to the client. Obviously, after filters can not stop the action from running. Around filters are responsible for running the action, but they can choose not to, which is the around filter's way of stopping it.
+In addition to before filters, you can run filters after an action has run or both before and after. The after filter is similar to the before filter, but because the action has already been run it has access to the response data that's about to be sent to the client. Obviously, after filters can not stop the action from running.
+
+Around filters are responsible for running the action, but they can choose not to, which is the around filter's way of stopping it.
<ruby>
# Example taken from the Rails API filter documentation:
# http://api.rubyonrails.org/classes/ActionController/Filters/ClassMethods.html
class ApplicationController < Application
-
around_filter :catch_exceptions
private
-
def catch_exceptions
yield
rescue => exception
logger.debug "Caught exception! #{exception}"
raise
end
-
end
</ruby>
-h4. Other Ways to Use Filters
+h4. Other ways to use filters
While the most common way to use filters is by creating private methods and using *_filter to add them, there are two other ways to do the same thing.
@@ -419,32 +411,28 @@ The first is to use a block directly with the *_filter methods. The block receiv
<ruby>
class ApplicationController < ActionController::Base
-
- before_filter { |controller| redirect_to new_login_url unless controller.send(:logged_in?) }
-
+ before_filter do |controller|
+ redirect_to new_login_url unless controller.send(:logged_in?)
+ end
end
</ruby>
Note that the filter in this case uses +send+ because the +logged_in?+ method is private and the filter is not run in the scope of the controller. This is not the recommended way to implement this particular filter, but in more simple cases it might be useful.
-The second way is to use a class (actually, any object that responds to the right methods will do) to handle the filtering. This is useful in cases that are more complex than can not be implemented in a readable and reusable way using the two other methods. As an example, you could rewrite the login filter again to use a class:
+The second way is to use a class (actually, any object that responds to the right methods will do) to handle the filtering. This is useful in cases that are more complex and can not be implemented in a readable and reusable way using the two other methods. As an example, you could rewrite the login filter again to use a class:
<ruby>
class ApplicationController < ActionController::Base
-
before_filter LoginFilter
-
end
class LoginFilter
-
def self.filter(controller)
- unless logged_in?
- controller.flash[:error] = "You must be logged in to access this section"
+ unless controller.send(:logged_in?)
+ controller.flash[:error] = "You must be logged in"
controller.redirect_to controller.new_login_url
end
end
-
end
</ruby>
@@ -454,16 +442,17 @@ The Rails API documentation has "more information on using filters":http://api.r
h3. Verification
-Verifications make sure certain criteria are met in order for a controller or action to run. They can specify that a certain key (or several keys in the form of an array) is present in the +params+, +session+ or +flash+ hashes or that a certain HTTP method was used or that the request was made using XMLHTTPRequest (Ajax). The default action taken when these criteria are not met is to render a 400 Bad Request response, but you can customize this by specifying a redirect URL or rendering something else and you can also add flash messages and HTTP headers to the response. It is described in the "API documentation":http://api.rubyonrails.org/classes/ActionController/Verification/ClassMethods.html as "essentially a special kind of before_filter".
+Verifications make sure certain criteria are met in order for a controller or action to run. They can specify that a certain key (or several keys in the form of an array) is present in the +params+, +session+ or +flash+ hashes or that a certain HTTP method was used or that the request was made using +XMLHTTPRequest+ (Ajax). The default action taken when these criteria are not met is to render a 400 Bad Request response, but you can customize this by specifying a redirect URL or rendering something else and you can also add flash messages and HTTP headers to the response. It is described in the "API documentation":http://api.rubyonrails.org/classes/ActionController/Verification/ClassMethods.html as "essentially a special kind of before_filter".
Here's an example of using verification to make sure the user supplies a username and a password in order to log in:
<ruby>
class LoginsController < ApplicationController
-
verify :params => [:username, :password],
:render => {:action => "new"},
- :add_flash => {:error => "Username and password required to log in"}
+ :add_flash => {
+ :error => "Username and password required to log in"
+ }
def create
@user = User.authenticate(params[:username], params[:password])
@@ -474,7 +463,6 @@ class LoginsController < ApplicationController
render :action => "new"
end
end
-
end
</ruby>
@@ -482,18 +470,22 @@ Now the +create+ action won't run unless the "username" and "password" parameter
<ruby>
class LoginsController < ApplicationController
-
verify :params => [:username, :password],
:render => {:action => "new"},
- :add_flash => {:error => "Username and password required to log in"},
- :only => :create # Only run this verification for the "create" action
-
+ :add_flash => {
+ :error => "Username and password required to log in"
+ },
+ :only => :create # Run only for the "create" action
end
</ruby>
h3. Request Forgery Protection
-Cross-site request forgery is a type of attack in which a site tricks a user into making requests on another site, possibly adding, modifying or deleting data on that site without the user's knowledge or permission. The first step to avoid this is to make sure all "destructive" actions (create, update and destroy) can only be accessed with non-GET requests. If you're following RESTful conventions you're already doing this. However, a malicious site can still send a non-GET request to your site quite easily, and that's where the request forgery protection comes in. As the name says, it protects from forged requests. The way this is done is to add a non-guessable token which is only known to your server to each request. This way, if a request comes in without the proper token, it will be denied access.
+Cross-site request forgery is a type of attack in which a site tricks a user into making requests on another site, possibly adding, modifying or deleting data on that site without the user's knowledge or permission.
+
+The first step to avoid this is to make sure all "destructive" actions (create, update and destroy) can only be accessed with non-GET requests. If you're following RESTful conventions you're already doing this. However, a malicious site can still send a non-GET request to your site quite easily, and that's where the request forgery protection comes in. As the name says, it protects from forged requests.
+
+The way this is done is to add a non-guessable token which is only known to your server to each request. This way, if a request comes in without the proper token, it will be denied access.
If you generate a form like this:
@@ -506,63 +498,61 @@ If you generate a form like this:
You will see how the token gets added as a hidden field:
-<ruby>
+<html>
<form action="/users/1" method="post">
-<div><!-- ... --><input type="hidden" value="67250ab105eb5ad10851c00a5621854a23af5489" name="authenticity_token"/></div>
-<!-- Fields -->
+<input type="hidden"
+ value="67250ab105eb5ad10851c00a5621854a23af5489"
+ name="authenticity_token"/>
+<!-- fields -->
</form>
-</ruby>
+</html>
Rails adds this token to every form that's generated using the "form helpers":form_helpers.html, so most of the time you don't have to worry about it. If you're writing a form manually or need to add the token for another reason, it's available through the method +form_authenticity_token+:
-TODO: Add line below as description
-
-Add a JavaScript variable containing the token for use with Ajax
-
-<erb>
-<%= javascript_tag "MyApp.authenticity_token = '#{form_authenticity_token}'" %>
-</erb>
+The +form_authenticity_token+ generates a valid authentication token. That's useful in places where Rails does not add it automatically, like in custom Ajax calls.
The "Security Guide":security.html has more about this and a lot of other security-related issues that you should be aware of when developing a web application.
-h3. The request and response Objects
+h3. The Request and Response Objects
-In every controller there are two accessor methods pointing to the request and the response objects associated with the request cycle that is currently in execution. The +request+ method contains an instance of AbstractRequest and the +response+ method returns a +response+ object representing what is going to be sent back to the client.
+In every controller there are two accessor methods pointing to the request and the response objects associated with the request cycle that is currently in execution. The +request+ method contains an instance of +AbstractRequest+ and the +response+ method returns a response object representing what is going to be sent back to the client.
-h4. The request Object
+h4. The +request+ object
The request object contains a lot of useful information about the request coming in from the client. To get a full list of the available methods, refer to the "API documentation":http://api.rubyonrails.org/classes/ActionController/AbstractRequest.html. Among the properties that you can access on this object are:
-* host - The hostname used for this request.
-* domain(n=2) - The hostname's first +n+ segments, starting from the right (the TLD)
-* format - The content type requested by the client.
-* method - The HTTP method used for the request.
-* get?, post?, put?, delete?, head? - Returns true if the HTTP method is GET/POST/PUT/DELETE/HEAD.
-* headers - Returns a hash containing the headers associated with the request.
-* port - The port number (integer) used for the request.
-* protocol - Returns a string containing the prototol used plus "://", for example "http://"
-* query_string - The query string part of the URL - everything after "?".
-* remote_ip - The IP address of the client.
-* url - The entire URL used for the request.
-
-h5. path_parameters, query_parameters and request_parameters
+|_.Property of +request+|_.Purpose|
+|host|The hostname used for this request.|
+|domain(n=2)|The hostname's first +n+ segments, starting from the right (the TLD).|
+|format|The content type requested by the client.|
+|method|The HTTP method used for the request.|
+|get?, post?, put?, delete?, head?|Returns true if the HTTP method is GET/POST/PUT/DELETE/HEAD.|
+|headers|Returns a hash containing the headers associated with the request.|
+|port|The port number (integer) used for the request.|
+|protocol|Returns a string containing the protocol used plus "://", for example "http://".|
+|query_string|The query string part of the URL, i.e., everything after "?".|
+|remote_ip|The IP address of the client.|
+|url|The entire URL used for the request.|
+
+h5. +path_parameters+, +query_parameters+, and +request_parameters+
Rails collects all of the parameters sent along with the request in the +params+ hash, whether they are sent as part of the query string or the post body. The request object has three accessors that give you access to these parameters depending on where they came from. The +query_parameters+ hash contains parameters that were sent as part of the query string while the +request_parameters+ hash contains parameters sent as part of the post body. The +path_parameters+ hash contains parameters that were recognized by the routing as being part of the path leading to this particular controller and action.
-h4. The response Object
+h4. The response object
The response object is not usually used directly, but is built up during the execution of the action and rendering of the data that is being sent back to the user, but sometimes - like in an after filter - it can be useful to access the response directly. Some of these accessor methods also have setters, allowing you to change their values.
-* body - This is the string of data being sent back to the client. This is most often HTML.
-* status - The HTTP status code for the response, like 200 for a successful request or 404 for file not found.
-* location - The URL the client is being redirected to, if any.
-* content_type - The content type of the response.
-* charset - The character set being used for the response. Default is "utf8".
-* headers - Headers used for the response.
+|_.Property of +response+|_.Purpose|
+|body|This is the string of data being sent back to the client. This is most often HTML.|
+|status|The HTTP status code for the response, like 200 for a successful request or 404 for file not found.|
+|location|The URL the client is being redirected to, if any.|
+|content_type|The content type of the response.|
+|charset|The character set being used for the response. Default is "utf-8".|
+|headers|Headers used for the response.|
-h5. Setting Custom Headers
+h5. Setting custom headers
-If you want to set custom headers for a response then +response.headers+ is the place to do it. The headers attribute is a hash which maps header names to their values, and Rails will set some of them - like "Content-Type" - automatically. If you want to add or change a header, just assign it to +headers+ with the name and value:
+If you want to set custom headers for a response then +response.headers+ is the place to do it. The headers attribute is a hash which maps header names to their values, and Rails will set some of them automatically. If you want to add or change a header, just assign it to +response.headers+ this way:
<ruby>
response.headers["Content-Type"] = "application/pdf"
@@ -570,74 +560,70 @@ response.headers["Content-Type"] = "application/pdf"
h3. HTTP Authentications
-Rails comes with two built-in HTTP authentication mechanisms :
+Rails comes with two built-in HTTP authentication mechanisms:
* Basic Authentication
* Digest Authentication
-h4. HTTP Basic Authentication
+h4. HTTP basic authentication
-HTTP Basic authentication is an authentication scheme that is supported by the majority of browsers and other HTTP clients. As an example, consider an administration section which will only be available by entering a username and a password into the browser's HTTP Basic dialog window. Using the built-in authentication is quite easy and only requires you to use one method, +authenticate_or_request_with_http_basic+.
+HTTP basic authentication is an authentication scheme that is supported by the majority of browsers and other HTTP clients. As an example, consider an administration section which will only be available by entering a username and a password into the browser's HTTP basic dialog window. Using the built-in authentication is quite easy and only requires you to use one method, +authenticate_or_request_with_http_basic+.
<ruby>
class AdminController < ApplicationController
-
- USERNAME, PASSWORD = "humbaba", "5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8"
+ USERNAME, PASSWORD = "humbaba", "5baa61e4"
before_filter :authenticate
- private
-
+private
def authenticate
authenticate_or_request_with_http_basic do |username, password|
- username == USERNAME && Digest::SHA1.hexdigest(password) == PASSWORD
+ username == USERNAME &&
+ Digest::SHA1.hexdigest(password) == PASSWORD
end
end
-
end
</ruby>
-With this in place, you can create namespaced controllers that inherit from AdminController. The before filter will thus be run for all actions in those controllers, protecting them with HTTP Basic authentication.
+With this in place, you can create namespaced controllers that inherit from +AdminController+. The before filter will thus be run for all actions in those controllers, protecting them with HTTP basic authentication.
-h4. HTTP Digest Authentication
+h4. HTTP digest authentication
-HTTP Digest authentication is superior to the Basic authentication as it does not require the client to send unencrypted password over the network. Using Digest authentication with Rails is quite easy and only requires using one method, +authenticate_or_request_with_http_digest+.
+HTTP digest authentication is superior to the basic authentication as it does not require the client to send an unencrypted password over the network (though HTTP basic authentication is safe over HTTPS). Using digest authentication with Rails is quite easy and only requires using one method, +authenticate_or_request_with_http_digest+.
<ruby>
class AdminController < ApplicationController
-
USERS = { "lifo" => "world" }
before_filter :authenticate
- private
-
+private
def authenticate
authenticate_or_request_with_http_digest do |username|
USERS[username]
end
end
-
end
</ruby>
-As seen in the example above, +authenticate_or_request_with_http_digest+ block takes only one argument - the username. And the block returns the password. Returning +false+ or +nil+ from the +authenticate_or_request_with_http_digest+ will cause authentication failure.
+As seen in the example above, the +authenticate_or_request_with_http_digest+ block takes only one argument - the username. And the block returns the password. Returning +false+ or +nil+ from the +authenticate_or_request_with_http_digest+ will cause authentication failure.
h3. Streaming and File Downloads
-Sometimes you may want to send a file to the user instead of rendering an HTML page. All controllers in Rails have the +send_data+ and the +send_file+ methods, that will both stream data to the client. +send_file+ is a convenience method which lets you provide the name of a file on the disk and it will stream the contents of that file for you.
+Sometimes you may want to send a file to the user instead of rendering an HTML page. All controllers in Rails have the +send_data+ and the +send_file+ methods, which will both stream data to the client. +send_file+ is a convenience method that lets you provide the name of a file on the disk and it will stream the contents of that file for you.
To stream data to the client, use +send_data+:
<ruby>
require "prawn"
class ClientsController < ApplicationController
-
- # Generate a PDF document with information on the client and return it.
- # The user will get the PDF as a file download.
+ # Generates a PDF document with information on the client and
+ # returns it. The user will get the PDF as a file download.
def download_pdf
client = Client.find(params[:id])
- send_data(generate_pdf, :filename => "#{client.name}.pdf", :type => "application/pdf")
+ send_data(generate_pdf,
+ :filename => "#{client.name}.pdf",
+ :type => "application/pdf")
end
private
@@ -649,51 +635,48 @@ private
text "Email: #{client.email}"
end.render
end
-
end
</ruby>
-The +download_pdf+ action in the example above will call a private method which actually generates the file (a PDF document) and returns it as a string. This string will then be streamed to the client as a file download and a filename will be suggested to the user. Sometimes when streaming files to the user, you may not want them to download the file. Take images, for example, which can be embedded into HTML pages. To tell the browser a file is not meant to be downloaded, you can set the +:disposition+ option to "inline". The opposite and default value for this option is "attachment".
+The +download_pdf+ action in the example above will call a private method which actually generates the PDF document and returns it as a string. This string will then be streamed to the client as a file download and a filename will be suggested to the user. Sometimes when streaming files to the user, you may not want them to download the file. Take images, for example, which can be embedded into HTML pages. To tell the browser a file is not meant to be downloaded, you can set the +:disposition+ option to "inline". The opposite and default value for this option is "attachment".
-h4. Sending Files
+h4. Sending files
-If you want to send a file that already exists on disk, use the +send_file+ method. This is usually not recommended, but can be useful if you want to perform some authentication before letting the user download the file.
+If you want to send a file that already exists on disk, use the +send_file+ method.
<ruby>
class ClientsController < ApplicationController
-
- # Stream a file that has already been generated and stored on disk
+ # Stream a file that has already been generated and stored on disk.
def download_pdf
client = Client.find(params[:id])
- send_data("#{RAILS_ROOT}/files/clients/#{client.id}.pdf", :filename => "#{client.name}.pdf", :type => "application/pdf")
+ send_data("#{RAILS_ROOT}/files/clients/#{client.id}.pdf",
+ :filename => "#{client.name}.pdf",
+ :type => "application/pdf")
end
-
end
</ruby>
-This will read and stream the file 4Kb at the time, avoiding loading the entire file into memory at once. You can turn off streaming with the +:stream+ option or adjust the block size with the +:buffer_size+ option.
+This will read and stream the file 4kB at the time, avoiding loading the entire file into memory at once. You can turn off streaming with the +:stream+ option or adjust the block size with the +:buffer_size+ option.
-WARNING: Be careful when using (or just don't use) "outside" data (params, cookies, etc) to locate the file on disk, as this is a security risk that might allow someone to gain access to files they are not meant to see.
+WARNING: Be careful when using data coming from the client (params, cookies, etc.) to locate the file on disk, as this is a security risk that might allow someone to gain access to files they are not meant to see.
-TIP: It is not recommended that you stream static files through Rails if you can instead keep them in a public folder on your web server. It is much more efficient to let the user download the file directly using Apache or another web server, keeping the request from unnecessarily going through the whole Rails stack. Although if you do need the request to go through Rails for some reason, you can set the +:x_sendfile+ option to true, and Rails will let the web server handle sending the file to the user, freeing up the Rails process to do other things. Note that your web server needs to support the +X-Sendfile+ header for this to work, and you still have to be careful not to use user input in a way that lets someone retrieve arbitrary files.
+TIP: It is not recommended that you stream static files through Rails if you can instead keep them in a public folder on your web server. It is much more efficient to let the user download the file directly using Apache or another web server, keeping the request from unnecessarily going through the whole Rails stack. Although if you do need the request to go through Rails for some reason, you can set the +:x_sendfile+ option to true, and Rails will let the web server handle sending the file to the user, freeing up the Rails process to do other things. Note that your web server needs to support the +X-Sendfile+ header for this to work.
-h4. RESTful Downloads
+h4. RESTful downloads
While +send_data+ works just fine, if you are creating a RESTful application having separate actions for file downloads is usually not necessary. In REST terminology, the PDF file from the example above can be considered just another representation of the client resource. Rails provides an easy and quite sleek way of doing "RESTful downloads". Here's how you can rewrite the example so that the PDF download is a part of the +show+ action, without any streaming:
<ruby>
class ClientsController < ApplicationController
-
# The user can request to receive this resource as HTML or PDF.
def show
@client = Client.find(params[:id])
respond_to do |format|
format.html
- format.pdf{ render :pdf => generate_pdf(@client) }
+ format.pdf { render :pdf => generate_pdf(@client) }
end
end
-
end
</ruby>
@@ -707,49 +690,48 @@ NOTE: Configuration files are not reloaded on each request, so you have to resta
Now the user can request to get a PDF version of a client just by adding ".pdf" to the URL:
-<ruby>
+<shell>
GET /clients/1.pdf
-</ruby>
+</shell>
h3. Parameter Filtering
-Rails keeps a log file for each environment (development, test and production) in the +log+ folder. These are extremely useful when debugging what's actually going on in your application, but in a live application you may not want every bit of information to be stored in the log file. The +filter_parameter_logging+ method can be used to filter out sensitive information from the log. It works by replacing certain values in the +params+ hash with "[FILTERED]" as they are written to the log. As an example, let's see how to filter all parameters with keys that include "password":
+Rails keeps a log file for each environment in the +log+ folder. These are extremely useful when debugging what's actually going on in your application, but in a live application you may not want every bit of information to be stored in the log file. The +filter_parameter_logging+ method can be used to filter out sensitive information from the log. It works by replacing certain values in the +params+ hash with "[FILTERED]" as they are written to the log. As an example, let's see how to filter all parameters with keys that include "password":
<ruby>
class ApplicationController < ActionController::Base
-
filter_parameter_logging :password
-
end
</ruby>
-The method works recursively through all levels of the params hash and takes an optional second parameter which is used as the replacement string if present. It can also take a block which receives each key in turn and replaces those for which the block returns true.
+The method works recursively through all levels of the +params+ hash and takes an optional second parameter which is used as the replacement string if present. It can also take a block which receives each key in turn and replaces those for which the block returns true.
h3. Rescue
-Most likely your application is going to contain bugs or otherwise throw an exception that needs to be handled. For example, if the user follows a link to a resource that no longer exists in the database, Active Record will throw the ActiveRecord::RecordNotFound exception. Rails' default exception handling displays a 500 Server Error message for all exceptions. If the request was made locally, a nice traceback and some added information gets displayed so you can figure out what went wrong and deal with it. If the request was remote Rails will just display a simple "500 Server Error" message to the user, or a "404 Not Found" if there was a routing error or a record could not be found. Sometimes you might want to customize how these errors are caught and how they're displayed to the user. There are several levels of exception handling available in a Rails application:
+Most likely your application is going to contain bugs or otherwise throw an exception that needs to be handled. For example, if the user follows a link to a resource that no longer exists in the database, Active Record will throw the +ActiveRecord::RecordNotFound+ exception.
+
+Rails' default exception handling displays a "500 Server Error" message for all exceptions. If the request was made locally, a nice traceback and some added information gets displayed so you can figure out what went wrong and deal with it. If the request was remote Rails will just display a simple "500 Server Error" message to the user, or a "404 Not Found" if there was a routing error or a record could not be found. Sometimes you might want to customize how these errors are caught and how they're displayed to the user. There are several levels of exception handling available in a Rails application:
-h4. The Default 500 and 404 Templates
+h4. The default 500 and 404 templates
By default a production application will render either a 404 or a 500 error message. These messages are contained in static HTML files in the +public+ folder, in +404.html+ and +500.html+ respectively. You can customize these files to add some extra information and layout, but remember that they are static; i.e. you can't use RHTML or layouts in them, just plain HTML.
-h4. rescue_from
+h4. +rescue_from+
-If you want to do something a bit more elaborate when catching errors, you can use +rescue_from+, which handles exceptions of a certain type (or multiple types) in an entire controller and its subclasses. When an exception occurs which is caught by a +rescue_from+ directive, the exception object is passed to the handler. The handler can be a method or a Proc object passed to the +:with+ option. You can also use a block directly instead of an explicit Proc object.
+If you want to do something a bit more elaborate when catching errors, you can use +rescue_from+, which handles exceptions of a certain type (or multiple types) in an entire controller and its subclasses.
-Here's how you can use +rescue_from+ to intercept all ActiveRecord::RecordNotFound errors and do something with them.
+When an exception occurs which is caught by a +rescue_from+ directive, the exception object is passed to the handler. The handler can be a method or a +Proc+ object passed to the +:with+ option. You can also use a block directly instead of an explicit +Proc+ object.
+
+Here's how you can use +rescue_from+ to intercept all +ActiveRecord::RecordNotFound+ errors and do something with them.
<ruby>
class ApplicationController < ActionController::Base
-
rescue_from ActiveRecord::RecordNotFound, :with => :record_not_found
private
-
def record_not_found
render :text => "404 Not Found", :status => 404
end
-
end
</ruby>
@@ -757,20 +739,16 @@ Of course, this example is anything but elaborate and doesn't improve on the def
<ruby>
class ApplicationController < ActionController::Base
-
rescue_from User::NotAuthorized, :with => :user_not_authorized
private
-
def user_not_authorized
flash[:error] = "You don't have access to this section."
redirect_to :back
end
-
end
class ClientsController < ApplicationController
-
# Check that the user has the right authorization to access clients.
before_filter :check_authorization
@@ -780,19 +758,19 @@ class ClientsController < ApplicationController
end
private
-
# If the user is not authorized, just throw the exception.
def check_authorization
raise User::NotAuthorized unless current_user.admin?
end
-
end
</ruby>
-NOTE: Certain exceptions are only rescuable from the ApplicationController class, as they are raised before the controller gets initialized and the action gets executed. See Pratik Naik's "article":http://m.onkey.org/2008/7/20/rescue-from-dispatching on the subject for more information.
+NOTE: Certain exceptions are only rescuable from the +ApplicationController+ class, as they are raised before the controller gets initialized and the action gets executed. See Pratik Naik's "article":http://m.onkey.org/2008/7/20/rescue-from-dispatching on the subject for more information.
h3. Changelog
"Lighthouse Ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/17
+* February 17, 2009: Yet another proofread by Xavier Noria.
+
* November 4, 2008: First release version by Tore Darell
diff --git a/railties/guides/source/action_mailer_basics.textile b/railties/guides/source/action_mailer_basics.textile
index 9c56691dc1..71398382be 100644
--- a/railties/guides/source/action_mailer_basics.textile
+++ b/railties/guides/source/action_mailer_basics.textile
@@ -1,16 +1,16 @@
h2. Action Mailer Basics
-This guide should provide you with all you need to get started in sending and receiving emails from/to your application, and many internals of Action Mailer. It will also cover how to test your mailers.
+This guide should provide you with all you need to get started in sending and receiving emails from/to your application, and many internals of Action Mailer. It also covers how to test your mailers.
endprologue.
h3. Introduction
-Action Mailer allows you to send email from your application using a mailer model and views. Yes, that is correct, in Rails, emails are used by creating models that inherit from ActionMailer::Base. They live alongside other models in `app/models` but they have views just like controllers that appear alongside other views in `app/views`.
+Action Mailer allows you to send emails from your application using a mailer model and views. So, in Rails, emails are used by creating models that inherit from +ActionMailer::Base+ that live alongside other models in +app/models+. Those models have associated views that appear alongside controller views in +app/views+.
h3. Sending Emails
-Let's say you want to send a welcome email to a user after they signup. Here is how you would go about this:
+This section will provide a step-by-step guide to creating a mailer and its views.
h4. Walkthrough to generating a mailer
@@ -26,49 +26,43 @@ create app/models/user_mailer.rb
create test/unit/user_mailer_test.rb
</shell>
-So we got the model, the fixtures, and the tests all created for us
+So we got the model, the fixtures, and the tests.
h5. Edit the model:
-If you look at +app/models/user_mailer.rb+, you will see:
++app/models/user_mailer.rb+ contains an empty mailer:
<ruby>
class UserMailer < ActionMailer::Base
end
</ruby>
-Lets add a method called +welcome_email+, that will send an email to the user's registered email address:
+Let's add a method called +welcome_email+, that will send an email to the user's registered email address:
<ruby>
class UserMailer < ActionMailer::Base
-
def welcome_email(user)
recipients user.email
from "My Awesome Site Notifications <notifications@example.com>"
subject "Welcome to My Awesome Site"
sent_on Time.now
body {:user => user, :url => "http://example.com/login"}
- content_type "text/html"
end
-
end
</ruby>
-So what do we have here?
+Here is a quick explanation of the options presented in the preceding method. For a full list of all available options, please have a look further down at the Complete List of ActionMailer user-settable attributes section.
|recipients| The recipients of the email. It can be a string or, if there are multiple recipients, an array of strings|
-|from| Who the email will appear to come from in the recipients' mailbox|
+|from| The from address of the email|
|subject| The subject of the email|
-|sent_on| Timestamp for the email|
-|content_type| The content type, by default is text/plain|
+|sent_on| The timestamp for the email|
-The keys of the hash passed to `body` become instance variables in the view. Thus, in our example the mailer view will have a @user and a @url instance variables available.
+The keys of the hash passed to +body+ become instance variables in the view. Thus, in our example the mailer view will have a +@user+ and a +@url+ instance variables available.
-h5. Create the mailer view
+h5. Create a mailer view
-Create a file called +welcome_email.html.erb+ in +#{RAILS_ROOT}/app/views/user_mailer/+. This will be the template used for the email. This file will be used for html formatted emails. Had we wanted to send text-only emails, the file would have been called +welcome_email.txt.erb+, and we would have set the content type to text/plain in the mailer model.
-
-The file can look like:
+Create a file called +welcome_email.text.html.erb+ in +app/views/user_mailer/+. This will be the template used for the email, formatted in HTML:
<erb>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
@@ -87,6 +81,8 @@ The file can look like:
</html>
</erb>
+Had we wanted to send text-only emails, the file would have been called +welcome_email.text.plain.erb+. Rails sets the content type of the email to be the one in the filename.
+
h5. Wire it up so that the system sends the email when a user signs up
There are three ways to achieve this. One is to send the email from the controller that sends the email, another is to put it in a +before_create+ callback in the user model, and the last one is to use an observer on the user model. Whether you use the second or third methods is up to you, but staying away from the first is recommended. Not because it's wrong, but because it keeps your controller clean, and keeps all logic related to the user model within the user model. This way, whichever way a user is created (from a web form, or from an API call, for example), we are guaranteed that the email will be sent.
@@ -96,25 +92,15 @@ Let's see how we would go about wiring it up using an observer:
In +config/environment.rb+:
<ruby>
-# Code that already exists
Rails::Initializer.run do |config|
- # Code that already exists
+ # ...
config.active_record.observers = :user_observer
end
</ruby>
-There was a bit of a debate on where to put observers. Some people put them in app/models, but a cleaner method may be to create an app/observers folder to store all observers, and add that to your load path. Open +config/environment.rb+ and make it look like:
+You can place the observer in +app/models+ where it will be loaded automatically by Rails.
-<ruby>
-# Code that already exists
-Rails::Initializer.run do |config|
- # Code that already exists
- config.load_paths += %W(#{RAILS_ROOT}/app/observers)
- config.active_record.observers = :user_observer
-end
-</ruby>
-
-Now create a file called user_observer in +app/models+ or +app/observers+ depending on where you stored it, and make it look like:
+Now create a file called +user_observer.rb+ in +app/models+ depending on where you stored it, and make it look like:
<ruby>
class UserObserver < ActiveRecord::Observer
@@ -124,19 +110,17 @@ class UserObserver < ActiveRecord::Observer
end
</ruby>
-Notice how we call +deliver_welcome_email+? Where is that method? Well if you remember, we created a method called +welcome_email+ in UserMailer, right? Well, as part of the "magic" of Rails, we deliver the email identified by +welcome_email+ by calling +deliver_welcome_email+. The next section will go through this in more detail.
+Notice how we call +deliver_welcome_email+? In Action Mailer we send emails by calling +deliver_&lt;method_name&gt;+. In UserMailer, we defined a method called +welcome_email+, and so we deliver the email by calling +deliver_welcome_email+. The next section will go through how Action Mailer achieves this.
-That's it! Now whenever your users signup, they will be greeted with a nice welcome email.
+h4. Action Mailer and dynamic deliver_&lt;method_name&gt; methods
-h4. Action Mailer and dynamic deliver_<method> methods
+So how does Action Mailer understand this +deliver_welcome_email+ call? If you read the documentation (http://api.rubyonrails.org/files/vendor/rails/actionmailer/README.html), you will find this in the "Sending Emails" section:
-So how does Action Mailer understand this deliver_welcome_email call? If you read the documentation (http://api.rubyonrails.org/files/vendor/rails/actionmailer/README.html), you will find this in the "Sending Emails" section:
-
-You never instantiate your mailer class. Rather, your delivery instance methods are automatically wrapped in class methods that start with the word +deliver_+ followed by the name of the mailer method that you would like to deliver. The +welcome_email+ method defined above is delivered by invoking +Notifier.deliver_welcome_email+.
+You never instantiate your mailer class. Rather, your delivery instance methods are automatically wrapped in class methods that start with the word +deliver_+ followed by the name of the mailer method that you would like to deliver.
So, how exactly does this work?
-In +ActionMailer::Base+, you will find this:
+Looking at the +ActionMailer::Base+ source, you will find this:
<ruby>
def method_missing(method_symbol, *parameters)#:nodoc:
@@ -151,32 +135,31 @@ end
Hence, if the method name starts with +deliver_+ followed by any combination of lowercase letters or underscore, +method_missing+ calls +new+ on your mailer class (+UserMailer+ in our example above), sending the combination of lower case letters or underscore, along with the parameters. The resulting object is then sent the +deliver!+ method, which well... delivers it.
-h4. Complete List of ActionMailer user-settable attributes
-
-|bcc| Specify the BCC addresses for the message|
-|body| Define the body of the message. This is either a Hash (in which case it specifies the variables to pass to the template when it is rendered), or a string, in which case it specifies the actual text of the message.|
-|cc| Specify the CC addresses for the message.|
-|charset| Specify the charset to use for the message. This defaults to the default_charset specified for ActionMailer::Base.|
-|content_type| Specify the content type for the message. This defaults to <text/plain in most cases, but can be automatically set in some situations.|
-|from| Specify the from address for the message.|
-|reply_to| Specify the address (if different than the "from" address) to direct replies to this message.|
-|headers| Specify additional headers to be added to the message.|
-|implicit_parts_order| Specify the order in which parts should be sorted, based on content-type. This defaults to the value for the default_implicit_parts_order.|
-|mime_version| Defaults to "1.0", but may be explicitly given if needed.|
-|recipient| The recipient addresses for the message, either as a string (for a single address) or an array (for multiple addresses).|
-|sent_on| The date on which the message was sent. If not set (the default), the header will be set by the delivery agent.|
-|subject| Specify the subject of the message.|
-|template| Specify the template name to use for current message. This is the "base" template name, without the extension or directory, and may be used to have multiple mailer methods share the same template.|
+h4. Complete list of Action Mailer user-settable attributes
+
+|bcc| The BCC addresses of the email|
+|body| The body of the email. This is either a hash (in which case it specifies the variables to pass to the template when it is rendered), or a string, in which case it specifies the actual body of the message|
+|cc| The CC addresses for the email|
+|charset| The charset to use for the email. This defaults to the +default_charset+ specified for ActionMailer::Base.|
+|content_type| The content type for the email. This defaults to "text/plain" but the filename may specify it|
+|from| The from address of the email|
+|reply_to| The address (if different than the "from" address) to direct replies to this email|
+|headers| Additional headers to be added to the email|
+|implicit_parts_order| The order in which parts should be sorted, based on the content type. This defaults to the value of +default_implicit_parts_order+|
+|mime_version| Defaults to "1.0", but may be explicitly given if needed|
+|recipient| The recipient addresses of the email, either as a string (for a single address) or an array of strings (for multiple addresses)|
+|sent_on| The timestamp on which the message was sent. If unset, the header will be set by the delivery agent|
+|subject| The subject of the email|
+|template| The template to use. This is the "base" template name, without the extension or directory, and may be used to have multiple mailer methods share the same template|
h4. Mailer Views
-Mailer views are located in /app/views/name_of_mailer_class. The specific mailer view is known to the class because it's name is the same as the mailer method. So for example, in our example from above, our mailer view for the welcome_email method will be in /app/views/user_mailer/welcome_email.html.erb for the html version and welcome_email.txt.erb for the plain text version.
+Mailer views are located in the +app/views/name_of_mailer_class+ directory. The specific mailer view is known to the class because it's name is the same as the mailer method. So for example, in our example from above, our mailer view for the +welcome_email+ method will be in +app/views/user_mailer/welcome_email.text.html.erb+ for the HTML version and +welcome_email.text.plain.erb+ for the plain text version.
To change the default mailer view for your action you do something like:
<ruby>
class UserMailer < ActionMailer::Base
-
def welcome_email(user)
recipients user.email
from "My Awesome Site Notifications<notifications@example.com>"
@@ -184,71 +167,63 @@ class UserMailer < ActionMailer::Base
sent_on Time.now
body {:user => user, :url => "http://example.com/login"}
content_type "text/html"
-
- # change the default from welcome_email.[html, txt].erb
- template "some_other_template" # this will be in app/views/user_mailer/some_other_template.[html, txt].erb
- end
-
+ # use some_other_template.text.(html|plain).erb instead
+ template "some_other_template"
end
</ruby>
h4. Action Mailer Layouts
-Just like controller views, you can also have mailer layouts. The layout name needs to end in _mailer to be automatically recognized by your mailer as a layout. So in our UserMailer example, we need to call our layout user_mailer.[html,txt].erb. In order to use a different file just use:
+Just like controller views, you can also have mailer layouts. The layout name needs to end in "_mailer" to be automatically recognized by your mailer as a layout. So in our UserMailer example, we need to call our layout +user_mailer.text.(html|plain).erb+. In order to use a different file just use:
<ruby>
class UserMailer < ActionMailer::Base
-
- layout 'awesome' # will use awesome.html.erb as the layout
-
+ layout 'awesome' # use awesome.text.(html|plain).erb as the layout
end
</ruby>
-Just like with controller views, use yield to render the view inside the layout.
+Just like with controller views, use +yield+ to render the view inside the layout.
-h4. Generating URL's in Action Mailer views
+h4. Generating URLs in Action Mailer views
-URLs can be generated in mailer views using url_for or named routes.
-Unlike controllers from Action Pack, the mailer instance doesn't have any context about the incoming request, so you'll need to provide all of the details needed to generate a URL.
-
-When using url_for you'll need to provide the :host, :controller, and :action:
+URLs can be generated in mailer views using +url_for+ or named routes.
+Unlike controllers, the mailer instance doesn't have any context about the incoming request so you'll need to provide the +:host+, +:controller+, and +:action+:
<erb>
<%= url_for(:host => "example.com", :controller => "welcome", :action => "greeting") %>
</erb>
-When using named routes you only need to supply the :host:
+When using named routes you only need to supply the +:host+:
<erb>
<%= users_url(:host => "example.com") %>
</erb>
-You will want to avoid using the name_of_route_path form of named routes because it doesn't make sense to generate relative URLs in email messages. The reason that it doesn't make sense is because the email is opened on a mail client outside of your environment. Since the email is not being served by your server, a URL like "/users/show/1", will have no context. In order for the email client to properly link to a URL on your server it needs something like "http://yourserver.com/users/show/1".
+Email clients have no web context and so paths have no base URL to form complete web addresses. Thus, when using named routes only the "_url" variant makes sense.
-It is also possible to set a default host that will be used in all mailers by setting the :host option in
-the ActionMailer::Base.default_url_options hash as follows:
+It is also possible to set a default host that will be used in all mailers by setting the +:host+ option in
+the +ActionMailer::Base.default_url_options+ hash as follows:
<erb>
ActionMailer::Base.default_url_options[:host] = "example.com"
</erb>
-This can also be set as a configuration option in config/environment.rb:
+This can also be set as a configuration option in +config/environment.rb+:
<erb>
config.action_mailer.default_url_options = { :host => "example.com" }
</erb>
-If you do decide to set a default :host for your mailers you will want to use the :only_path => false option when using url_for. This will ensure that absolute URLs are generated because the url_for view helper will, by default, generate relative URLs when a :host option isn't explicitly provided.
+If you set a default +:host+ for your mailers you need to pass +:only_path => false+ to +url_for+. Otherwise it doesn't get included.
h4. Sending multipart emails
-Action Mailer will automatically send multipart emails if you have different templates for the same action. So, for our UserMailer example, if you have welcome_email.txt.erb and welcome_email.html.erb in app/views/user_mailer, Action Mailer will automatically send a multipart email with the html and text versions setup as different parts.
+Action Mailer will automatically send multipart emails if you have different templates for the same action. So, for our UserMailer example, if you have +welcome_email.text.plain.erb+ and +welcome_email.text.html.erb+ in +app/views/user_mailer+, Action Mailer will automatically send a multipart email with the HTML and text versions setup as different parts.
To explicitly specify multipart messages, you can do something like:
<ruby>
class UserMailer < ActionMailer::Base
-
def welcome_email(user)
recipients user.email_address
subject "New account information"
@@ -262,17 +237,15 @@ class UserMailer < ActionMailer::Base
p.body = "text content, can also be the name of an action that you call"
end
end
-
end
</ruby>
h4. Sending emails with attachments
-Attachments can be added by using the attachment method:
+Attachments can be added by using the +attachment+ method:
<ruby>
class UserMailer < ActionMailer::Base
-
def welcome_email(user)
recipients user.email_address
subject "New account information"
@@ -286,24 +259,21 @@ class UserMailer < ActionMailer::Base
a.body = generate_your_pdf_here()
end
end
-
end
</ruby>
h3. Receiving Emails
-Receiving and parsing emails with Action Mailer can be a rather complex endeavour. Before your email reaches your Rails app, you would have had to configure your system to somehow forward emails to your app, which needs to be listening for that.
-So, to receive emails in your Rails app you'll need:
+Receiving and parsing emails with Action Mailer can be a rather complex endeavour. Before your email reaches your Rails app, you would have had to configure your system to somehow forward emails to your app, which needs to be listening for that. So, to receive emails in your Rails app you'll need:
-1. Configure your email server to forward emails from the address(es) you would like your app to receive to /path/to/app/script/runner \'UserMailer.receive(STDIN.read)'
+1. Implement a +receive+ method in your mailer.
-2. Implement a receive method in your mailer
+2. Configure your email server to forward emails from the address(es) you would like your app to receive to +/path/to/app/script/runner 'UserMailer.receive(STDIN.read)'+.
-Once a method called receive is defined in any mailer, Action Mailer will parse the raw incoming email into an email object, decode it, instantiate a new mailer, and pass the email object to the mailer object‘s receive method. Here's an example:
+Once a method called +receive+ is defined in any mailer, Action Mailer will parse the raw incoming email into an email object, decode it, instantiate a new mailer, and pass the email object to the mailer +receive+ instance method. Here's an example:
<ruby>
class UserMailer < ActionMailer::Base
-
def receive(email)
page = Page.find_by_address(email.to.first)
page.emails.create(
@@ -320,12 +290,9 @@ class UserMailer < ActionMailer::Base
end
end
end
-
-
end
</ruby>
-
h3. Using Action Mailer Helpers
Action Mailer classes have 4 helper methods available to them:
@@ -353,7 +320,7 @@ The following configuration options are best made in one of the environment file
|default_implicit_parts_order|When a message is built implicitly (i.e. multiple parts are assembled from templates which specify the content type in their filenames) this variable controls how the parts are ordered. Defaults to ["text/html", "text/enriched", "text/plain"]. Items that appear first in the array have higher priority in the mail client and appear last in the mime encoded message. You can also pick a different order from inside a method with implicit_parts_order.|
-h4. Example Action Mailer Configuration
+h4. Example Action Mailer configuration
An example would be:
@@ -372,7 +339,7 @@ h4. Action Mailer Configuration for GMail
Instructions copied from http://http://www.fromjavatoruby.com/2008/11/actionmailer-with-gmail-must-issue.html
-First you must install the action_mailer_tls plugin from http://code.openrain.com/rails/action_mailer_tls/, then all you have to do is configure action mailer.
+First you must install the +action_mailer_tls+ plugin from http://code.openrain.com/rails/action_mailer_tls/, then all you have to do is configure action mailer.
<ruby>
ActionMailer::Base.smtp_settings = {
@@ -387,7 +354,7 @@ ActionMailer::Base.smtp_settings = {
h4. Configure Action Mailer to recognize HAML templates
-In environment.rb, add the following line:
+In +config/environment.rb+, add the following line:
<ruby>
ActionMailer::Base.register_template_extension('haml')
@@ -395,29 +362,27 @@ ActionMailer::Base.register_template_extension('haml')
h3. Mailer Testing
-Testing mailers involves 2 things. One is that the mail was queued and the other that the body contains what we expect it to contain. With that in mind, we could test our example mailer from above like so:
+By default Action Mailer does not send emails in the test environment. They are just added to the +ActionMailer::Base.deliveries+ array.
+
+Testing mailers normally involves two things: One is that the mail was queued, and the other one that the email is correct. With that in mind, we could test our example mailer from above like so:
<ruby>
class UserMailerTest < ActionMailer::TestCase
- tests UserMailer
+ tests UserMailer
- def test_welcome_email
- user = users(:some_user_in_your_fixtures)
+ def test_welcome_email
+ user = users(:some_user_in_your_fixtures)
- # Send the email, then test that it got queued
- email = UserMailer.deliver_welcome_email(user)
- assert !ActionMailer::Base.deliveries.empty?
+ # Send the email, then test that it got queued
+ email = UserMailer.deliver_welcome_email(user)
+ assert !ActionMailer::Base.deliveries.empty?
- # Test the body of the sent email contains what we expect it to
- assert_equal [@user.email], email.to
- assert_equal "Welcome to My Awesome Site", email.subject
- assert email.body =~ /Welcome to example.com, #{user.first_name}/
- end
+ # Test the body of the sent email contains what we expect it to
+ assert_equal [@user.email], email.to
+ assert_equal "Welcome to My Awesome Site", email.subject
+ assert_match /Welcome to example.com, #{user.first_name}/, email.body
end
+end
</ruby>
-What have we done? Well, we sent the email and stored the returned object in the email variable. We then ensured that it was sent (the first assert), then, in the second batch of assertion, we ensure that the email does indeed contain the values that we expect.
-
-h3. Epilogue
-
-This guide presented how to create a mailer and how to test it. In reality, you may find that writing your tests before you actually write your code to be a rewarding experience. It may take some time to get used to TDD (Test Driven Development), but coding this way achieves two major benefits. Firstly, you know that the code does indeed work, because the tests fail (because there's no code), then they pass, because the code that satisfies the tests was written. Secondly, when you start with the tests, you don't have to make time AFTER you write the code, to write the tests, then never get around to it. The tests are already there and testing has now become part of your coding regimen.
+In the test we send the email and store the returned object in the +email+ variable. We then ensure that it was sent (the first assert), then, in the second batch of assertions, we ensure that the email does indeed contain the what we expect.
diff --git a/railties/guides/source/active_record_basics.textile b/railties/guides/source/active_record_basics.textile
index ab3e4c6e96..ed3f6cdd61 100644
--- a/railties/guides/source/active_record_basics.textile
+++ b/railties/guides/source/active_record_basics.textile
@@ -27,15 +27,15 @@ The definition of the Active Record pattern in Martin Fowler's words:
h3. Object Relational Mapping
-The relation between databases and object-oriented software is called ORM, which is short for "Object Relational Mapping". The purpose of an ORM framework is to minimize the mismatch existent between relational databases and object-oriented software. In applications with a domain model, we have objects that represent both the state of the system and the behaviour of the real world elements that were modeled through these objects. Since we need to store the system's state somehow, we can use relational databases, which are proven to be an excelent approach to data management. Usually this may become a very hard thing to do, since we need to create an object-oriented model of everything that lives in the database, from simple columns to complicated relations between different tables. Doing this kind of thing by hand is a tedious and error prone job. This is where an ORM framework comes in.
+The relation between databases and object-oriented software is called ORM, which is short for "Object Relational Mapping". The purpose of an ORM framework is to minimize the mismatch existent between relational databases and object-oriented software. In applications with a domain model, we have objects that represent both the state of the system and the behavior of the real world elements that were modeled through these objects. Since we need to store the system's state somehow, we can use relational databases, which are proven to be an excellent approach to data management. Usually this may become a very hard thing to do, since we need to create an object-oriented model of everything that lives in the database, from simple columns to complicated relations between different tables. Doing this kind of thing by hand is a tedious and error prone job. This is where an ORM framework comes in.
h3. ActiveRecord as an ORM framework
-ActiveRecord gives us several mechanisms, being the most important ones the hability to:
+ActiveRecord gives us several mechanisms, being the most important ones the ability to:
* Represent models.
* Represent associations between these models.
-* Represent inheritance hierarquies through related models.
+* Represent inheritance hierarchies through related models.
* Validate models before they get recorded to the database.
* Perform database operations in an object-oriented fashion.
@@ -43,11 +43,11 @@ It's easy to see that the Rails Active Record implementation goes way beyond the
h3. Active Record inside the MVC model
-Active Record plays the role of model inside the MVC structure followed by Rails applications. Since model objects should encapsulate both state and logic of your applications, it's ActiveRecord responsability to deliver you the easiest possible way to recover this data from the database.
+Active Record plays the role of model inside the MVC structure followed by Rails applications. Since model objects should encapsulate both state and logic of your applications, it's ActiveRecord responsibility to deliver you the easiest possible way to recover this data from the database.
h3. Convention over Configuration in ActiveRecord
-When writing applications using other programming languages or frameworks, it may be necessary to write a lot of configuration code. This is particulary true for ORM frameworks in general. However, if you follow the conventions adopted by Rails, you'll need to write very little configuration (in some case no configuration at all) when creating ActiveRecord models. The idea is that if you configure your applications in the very same way most of the times then this should be the default way. In this cases, explicity configuration would be needed only in those cases where you can't follow the conventions for any reason.
+When writing applications using other programming languages or frameworks, it may be necessary to write a lot of configuration code. This is particularly true for ORM frameworks in general. However, if you follow the conventions adopted by Rails, you'll need to write very little configuration (in some case no configuration at all) when creating ActiveRecord models. The idea is that if you configure your applications in the very same way most of the times then this should be the default way. In this cases, explicit configuration would be needed only in those cases where you can't follow the conventions for any reason.
h4. Naming Conventions
@@ -69,7 +69,7 @@ h4. Schema Conventions
ActiveRecord uses naming conventions for the columns in database tables, depending on the purpose of these columns.
* *Foreign keys* - These fields should be named following the pattern table_id i.e. (item_id, order_id). These are the fields that ActiveRecord will look for when you create associations between your models.
-* *Primary keys* - By default, ActiveRecord will use a integer column named "id" as the table's primary key. When using "Rails Migrations":http://guides.rails.info/migrations.html to create your tables, this column will be automaticaly created.
+* *Primary keys* - By default, ActiveRecord will use a integer column named "id" as the table's primary key. When using "Rails Migrations":http://guides.rails.info/migrations.html to create your tables, this column will be automatically created.
There are also some optional column names that will create additional features to ActiveRecord instances:
@@ -89,7 +89,7 @@ It's very easy to create ActiveRecord models. All you have to do is to subclass
class Product < ActiveRecord::Base; end
</ruby>
-This will create a +Product+ model, mapped to a *products* table at the database. By doing this you'll also have the hability to map the columns of each row in that table with the attributes of the instances of your model. So, suppose that the *products* table was created using a SQL sentence like:
+This will create a +Product+ model, mapped to a *products* table at the database. By doing this you'll also have the ability to map the columns of each row in that table with the attributes of the instances of your model. So, suppose that the *products* table was created using a SQL sentence like:
<sql>
CREATE TABLE products (
@@ -127,9 +127,9 @@ end
h3. Validations
-ActiveRecord gives the hability to validate the state of your models before they get recorded into the database. There are several methods that you can use to hook into the lifecycle of your models and validate that an attribute value is not empty or follow a specific format and so on. You can learn more about validations in the "Active Record Validations and Callbacks guide":http://guides.rails.info/activerecord_validations_callbacks.html#_overview_of_activerecord_validation.
+ActiveRecord gives the ability to validate the state of your models before they get recorded into the database. There are several methods that you can use to hook into the life-cycle of your models and validate that an attribute value is not empty or follow a specific format and so on. You can learn more about validations in the "Active Record Validations and Callbacks guide":http://guides.rails.info/activerecord_validations_callbacks.html#_overview_of_activerecord_validation.
h3. Callbacks
-ActiveRecord callbacks allow you to attach code to certain events in the lifecycle of your models. This way you can add behaviour to your models by transparently executing code when those events occur, like when you create a new record, update it, destroy it and so on. You can learn more about callbacks in the "Active Record Validations and Callbacks guide":http://guides.rails.info/activerecord_validations_callbacks.html#_callbacks.
+ActiveRecord callbacks allow you to attach code to certain events in the life-cycle of your models. This way you can add behavior to your models by transparently executing code when those events occur, like when you create a new record, update it, destroy it and so on. You can learn more about callbacks in the "Active Record Validations and Callbacks guide":http://guides.rails.info/activerecord_validations_callbacks.html#_callbacks.
diff --git a/railties/guides/source/active_record_querying.textile b/railties/guides/source/active_record_querying.textile
index ffffecace3..5da15bbb5c 100644
--- a/railties/guides/source/active_record_querying.textile
+++ b/railties/guides/source/active_record_querying.textile
@@ -16,6 +16,10 @@ If you're used to using raw SQL to find database records then, generally, you wi
Code examples throughout this guide will refer to one or more of the following models:
+TIP: All of the following models uses +id+ as the primary key, unless specified otherwise.
+
+<br />
+
<ruby>
class Client < ActiveRecord::Base
has_one :address
@@ -48,99 +52,147 @@ class Role < ActiveRecord::Base
end
</ruby>
-bq. Active Record will perform queries on the database for you and is compatible with most database systems (MySQL, PostgreSQL and SQLite to name a few). Regardless of which database system you're using, the Active Record method format will always be the same.
+Active Record will perform queries on the database for you and is compatible with most database systems (MySQL, PostgreSQL and SQLite to name a few). Regardless of which database system you're using, the Active Record method format will always be the same.
-h3. Retrieving objects
+h3. Retrieving objects from the database
-To retrieve objects from the database, Active Record provides a primary method called +find+. This method allows you to pass arguments into it to perform certain queries on your database without the need of SQL. If you wanted to find the record with the id of 1, you could type +Client.find(1)+ which would execute this query on your database:
+To retrieve objects from the database, Active Record provides a class method called +Model.find+. This method allows you to pass arguments into it to perform certain queries on your database without the need of writing raw SQL.
-<sql>
-SELECT * FROM clients WHERE (clients.id = 1)
-</sql>
+Primary operation of <tt>Model.find(options)</tt> can be summarized as:
-NOTE: Because this is a standard table created from a migration in Rails, the primary key is defaulted to 'id'. If you have specified a different primary key in your migrations, this is what Rails will find on when you call the find method, not the id column.
+* Convert the supplied options to an equivalent SQL query.
+* Fire the SQL query and retrieve the corresponding results from the database.
+* Instantiate the equivalent Ruby object of the appropriate model for every resulting row.
+* Run +after_find+ callbacks if any.
-If you wanted to find clients with id 1 or 2, you call +Client.find([1,2])+ or +Client.find(1,2)+ and then this will be executed as:
+h4. Retrieving a single object
+
+Active Record lets you retrieve a single object using three different ways.
+
+h5. Using a primary key
+
+Using <tt>Model.find(primary_key, options = nil)</tt>, you can retrieve the object corresponding to the supplied _primary key_ and matching the supplied options (if any). For example:
+
+<ruby>
+# Find the client with primary key (id) 10.
+client = Client.find(10)
+=> #<Client id: 10, name: => "Ryan">
+</ruby>
+
+SQL equivalent of the above is:
<sql>
-SELECT * FROM clients WHERE (clients.id IN (1,2))
+SELECT * FROM clients WHERE (clients.id = 10)
</sql>
-<shell>
->> Client.find(1,2)
-=> [#<Client id: 1, name: => "Ryan", locked: false, orders_count: 2,
- created_at: "2008-09-28 15:38:50", updated_at: "2008-09-28 15:38:50">,
- #<Client id: 2, name: => "Michael", locked: false, orders_count: 3,
- created_at: "2008-09-28 13:12:40", updated_at: "2008-09-28 13:12:40">]
-</shell>
-
-Note that if you pass in a list of numbers that the result will be returned as an array, not as a single Client object.
+<tt>Model.find(primary_key)</tt> will raise an +ActiveRecord::RecordNotFound+ exception if no matching record is found.
-NOTE: If +find(id)+ or +find([id1, id2])+ fails to find any records, it will raise a RecordNotFound exception.
+h5. Find first
-If you wanted to find the first Client object you would simply type +Client.first+ and that would find the first client in your clients table:
+<tt>Model.first(options = nil)</tt> finds the first record matched by the supplied options. If no +options+ are supplied, the first matching record is returned. For example:
-<shell>
->> Client.first
-=> #<Client id: 1, name: => "Ryan", locked: false, orders_count: 2,
- created_at: "2008-09-28 15:38:50", updated_at: "2008-09-28 15:38:50">
-</shell>
+<ruby>
+client = Client.first
+=> #<Client id: 1, name: => "Lifo">
+</ruby>
-If you were reading your log file (the default is log/development.log) you may see something like this:
+SQL equivalent of the above is:
<sql>
SELECT * FROM clients LIMIT 1
</sql>
-Indicating the query that Rails has performed on your database.
+<tt>Model.first</tt> returns +nil+ if no matching record is found. No exception will be raised.
-To find the last Client object you would simply type +Client.last+ and that would find the last client created in your clients table:
+NOTE: +Model.find(:first, options)+ is equivalent to +Model.first(options)+
-<shell>
->> Client.last
-=> #<Client id: 2, name: => "Michael", locked: false, orders_count: 3,
- created_at: "2008-09-28 13:12:40", updated_at: "2008-09-28 13:12:40">
-</shell>
+h5. Find last
+
+<tt>Model.last(options = nil)</tt> finds the last record matched by the supplied options. If no +options+ are supplied, the last matching record is returned. For example:
-If you were reading your log file (the default is log/development.log) you may see something like this:
+<ruby>
+# Find the client with primary key (id) 10.
+client = Client.last
+=> #<Client id: 221, name: => "Russel">
+</ruby>
+
+SQL equivalent of the above is:
<sql>
-SELECT * FROM clients ORDER BY id DESC LIMIT 1
+SELECT * FROM clients ORDER BY clients.id DESC LIMIT 1
</sql>
-NOTE: Please be aware that the syntax that Rails uses to find the first record in the table means that it may not be the actual first record. If you want the actual first record based on a field in your table (e.g. +created_at+) specify an order option in your find call. The last method call works differently: it finds the last record on your table based on the primary key column.
+<tt>Model.last</tt> returns +nil+ if no matching record is found. No exception will be raised.
+
+NOTE: +Model.find(:last, options)+ is equivalent to +Model.last(options)+
+
+h4. Retrieving multiple objects
+
+h5. Using multiple primary keys
+
+<tt>Model.find(array_of_primary_key, options = nil)</tt> also accepts an array of _primary keys_. An array of all the matching records for the supplied _primary keys_ is returned. For example:
+
+<ruby>
+# Find the clients with primary keys 1 and 10.
+client = Client.find(1, 10) # Or even Client.find([1, 10])
+=> [#<Client id: 1, name: => "Lifo">, #<Client id: 10, name: => "Ryan">]
+</ruby>
+
+SQL equivalent of the above is:
<sql>
-SELECT * FROM clients ORDER BY clients.id DESC LIMIT 1
+SELECT * FROM clients WHERE (clients.id IN (1,10))
</sql>
-To find all the Client objects you would simply type +Client.all+ and that would find all the clients in your clients table:
+<tt>Model.find(array_of_primary_key)</tt> will raise an +ActiveRecord::RecordNotFound+ exception unless a matching record is found for <strong>all</strong> of the supplied primary keys.
-<shell>
->> Client.all
-=> [#<Client id: 1, name: => "Ryan", locked: false, orders_count: 2,
- created_at: "2008-09-28 15:38:50", updated_at: "2008-09-28 15:38:50">,
- #<Client id: 2, name: => "Michael", locked: false, orders_count: 3,
- created_at: "2008-09-28 13:12:40", updated_at: "2008-09-28 13:12:40">]
-</shell>
+h5. Find all
-You may see in Rails code that there are calls to methods such as +Client.find(:all)+, +Client.find(:first)+ and +Client.find(:last)+. These methods are just alternatives to +Client.all+, +Client.first+ and +Client.last+ respectively.
+<tt>Model.all(options = nil)</tt> finds all the records matching the supplied +options+. If no +options+ are supplied, all rows from the database are returned.
-Be aware that +Client.first+/+Client.find(:first)+ and +Client.last+/+Client.find(:last)+ will both return a single object, where as +Client.all+/+Client.find(:all)+ will return an array of Client objects, just as passing in an array of ids to +find+ will do also.
+<ruby>
+# Find all the clients.
+clients = Client.all
+=> [#<Client id: 1, name: => "Lifo">, #<Client id: 10, name: => "Ryan">, #<Client id: 221, name: => "Russel">]
+</ruby>
+
+And the equivalent SQL is:
+
+<sql>
+SELECT * FROM clients
+</sql>
+
+<tt>Model.all</tt> returns an empty array +[]+ if no matching record is found. No exception will be raised.
+
+NOTE: +Model.find(:all, options)+ is equivalent to +Model.all(options)+
h3. Conditions
-The +find+ method allows you to specify conditions to limit the records returned. You can specify conditions as a string, array, or hash.
+The +find+ method allows you to specify conditions to limit the records returned, representing the WHERE-part of the SQL statement. Conditions can either be specified as a string, array, or hash.
-h4. Pure String Conditions
+h4. Pure string conditions
If you'd like to add conditions to your find, you could just specify them in there, just like +Client.first(:conditions => "orders_count = '2'")+. This will find all clients where the +orders_count+ field's value is 2.
WARNING: Building your own conditions as pure strings can leave you vulnerable to SQL injection exploits. For example, +Client.first(:conditions => "name LIKE '%#{params[:name]}%'")+ is not safe. See the next section for the preferred way to handle conditions using an array.
-h4. Array Conditions
+h4. Array conditions
+
+Now what if that number could vary, say as a argument from somewhere, or perhaps from the user's level status somewhere? The find then becomes something like:
-Now what if that number could vary, say as a argument from somewhere, or perhaps from the user's level status somewhere? The find then becomes something like +Client.first(:conditions => ["orders_count = ?", params[:orders]])+. Active Record will go through the first element in the conditions value and any additional elements will replace the question marks (?) in the first element. If you want to specify two conditions, you can do it like +Client.first(:conditions => ["orders_count = ? AND locked = ?", params[:orders], false])+. In this example, the first question mark will be replaced with the value in +params[:orders]+ and the second will be replaced with the SQL representation of +false+, which depends on the adapter.
+<ruby>
+Client.first(:conditions => ["orders_count = ?", params[:orders]])
+</ruby>
+
+Active Record will go through the first element in the conditions value and any additional elements will replace the question marks +(?)+ in the first element.
+
+Or if you want to specify two conditions, you can do it like:
+
+<ruby>
+Client.first(:conditions => ["orders_count = ? AND locked = ?", params[:orders], false])
+</ruby>
+
+In this example, the first question mark will be replaced with the value in +params[:orders]+ and the second will be replaced with the SQL representation of +false+, which depends on the adapter.
The reason for doing code like:
@@ -158,7 +210,20 @@ is because of argument safety. Putting the variable directly into the conditions
TIP: For more information on the dangers of SQL injection, see the "Ruby on Rails Security Guide":../security.html#_sql_injection.
-If you're looking for a range inside of a table (for example, users created in a certain timeframe) you can use the conditions option coupled with the IN sql statement for this. If you had two dates coming in from a controller you could do something like this to look for a range:
+h5. Placeholder conditions
+
+Similar to the +(?)+ replacement style of params, you can also specify keys/values hash in your Array conditions:
+
+<ruby>
+Client.all(:conditions =>
+ ["created_at >= :start_date AND created_at <= :end_date", { :start_date => params[:start_date], :end_date => params[:end_date] }])
+</ruby>
+
+This makes for clearer readability if you have a large number of variable conditions.
+
+h5. Range conditions
+
+If you're looking for a range inside of a table (for example, users created in a certain timeframe) you can use the conditions option coupled with the +IN+ SQL statement for this. If you had two dates coming in from a controller you could do something like this to look for a range:
<ruby>
Client.all(:conditions => ["created_at IN (?)",
@@ -178,6 +243,8 @@ SELECT * FROM users WHERE (created_at IN
'2008-12-27','2008-12-28','2008-12-29','2008-12-30','2008-12-31'))
</sql>
+h5. Time and Date conditions
+
Things can get *really* messy if you pass in Time objects as it will attempt to compare your field to *every second* in that range:
<ruby>
@@ -215,20 +282,13 @@ Client.all(:conditions =>
Just like in Ruby. If you want a shorter syntax be sure to check out the "Hash Conditions":hash-conditions section later on in the guide.
-h4. Placeholder Conditions
+h4. Hash conditions
-Similar to the array style of params you can also specify keys in your conditions:
+Active Record also allows you to pass in a hash conditions which can increase the readability of your conditions syntax. With hash conditions, you pass in a hash with keys of the fields you want conditionalised and the values of how you want to conditionalise them:
-<ruby>
-Client.all(:conditions =>
- ["created_at >= :start_date AND created_at <= :end_date", { :start_date => params[:start_date], :end_date => params[:end_date] }])
-</ruby>
-
-This makes for clearer readability if you have a large number of variable conditions.
-
-h4. Hash Conditions
+NOTE: Only equality, range and subset checking are possible with Hash conditions.
-Rails also allows you to pass in a hash conditions which can increase the readability of your conditions syntax. With hash conditions, you pass in a hash with keys of the fields you want conditionalised and the values of how you want to conditionalise them:
+h5. Equality conditions
<ruby>
Client.all(:conditions => { :locked => true })
@@ -240,13 +300,15 @@ The field name does not have to be a symbol it can also be a string:
Client.all(:conditions => { 'locked' => true })
</ruby>
+h5. Range conditions
+
The good thing about this is that we can pass in a range for our fields without it generating a large query as shown in the preamble of this section.
<ruby>
Client.all(:conditions => { :created_at => (Time.now.midnight - 1.day)..Time.now.midnight})
</ruby>
-This will find all clients created yesterday by using a BETWEEN sql statement:
+This will find all clients created yesterday by using a +BETWEEN+ SQL statement:
<sql>
SELECT * FROM clients WHERE (clients.created_at BETWEEN '2008-12-21 00:00:00' AND '2008-12-22 00:00:00')
@@ -254,39 +316,81 @@ SELECT * FROM clients WHERE (clients.created_at BETWEEN '2008-12-21 00:00:00' AN
This demonstrates a shorter syntax for the examples in "Array Conditions":#array-conditions
-You can also join in tables and specify their columns in the hash:
+h5. Subset conditions
+
+If you want to find records using the +IN+ expression you can pass an array to the conditions hash:
<ruby>
-Client.all(:include => "orders", :conditions => { 'orders.created_at' => (Time.now.midnight - 1.day)..Time.now.midnight })
+Client.all(:conditions => { :orders_count => [1,3,5] })
</ruby>
-An alternative and cleaner syntax to this is:
+This code will generate SQL like this:
+
+<sql>
+SELECT * FROM clients WHERE (clients.orders_count IN (1,3,5))
+</sql>
+
+h3. Find options
+
+Apart from +:conditions+, +Model.find+ takes a variety of other options via the options hash for customizing the resulting record set.
<ruby>
-Client.all(:include => "orders", :conditions => { :orders => { :created_at => (Time.now.midnight - 1.day)..Time.now.midnight } })
+Model.find(id_or_array_of_ids, options_hash)
+Model.find(:last, options_hash)
+Model.find(:first, options_hash)
+
+Model.first(options_hash)
+Model.last(options_hash)
+Model.all(options_hash)
</ruby>
-This will find all clients who have orders that were created yesterday, again using a BETWEEN expression.
+The following sections give a top level overview of all the possible keys for the +options_hash+.
+
+h4. Ordering
+
+To retrieve records from the database in a specific order, you can specify the +:order+ option to the +find+ call.
-If you want to find records using the IN expression you can pass an array to the conditions hash:
+For example, if you're getting a set of records and want to order them in ascending order by the +created_at+ field in your table:
<ruby>
-Client.all(:include => "orders", :conditions => { :orders_count => [1,3,5] }
+Client.all(:order => "created_at")
</ruby>
-This code will generate SQL like this:
+You could specify +ASC+ or +DESC+ as well:
-<sql>
-SELECT * FROM clients WHERE (clients.orders_count IN (1,2,3))
-</sql>
+<ruby>
+Client.all(:order => "created_at DESC")
+# OR
+Client.all(:order => "created_at ASC")
+</ruby>
+
+Or ordering by multiple fields:
+
+<ruby>
+Client.all(:order => "orders_count ASC, created_at DESC")
+</ruby>
+
+h4. Selecting specific fields
+
+By default, <tt>Model.find</tt> selects all the fields from the result set using +select *+.
-h3. Ordering
+To select only a subset of fields from the result set, you can specify the subset via +:select+ option on the +find+.
-If you're getting a set of records and want to order them in ascending order by the +created_at+ field in your table, you can use +Client.all(:order => "created_at")+. If you'd like to order it in descending order, just tell it to do that using +Client.all(:order => "created_at desc")+. The value for this option is passed in as sanitized SQL and allows you to sort via multiple fields: +Client.all(:order => "created_at desc, orders_count asc")+.
+NOTE: If the +:select+ option is used, all the returning objects will be "read only":#read-only objects.
-h3. Selecting Certain Fields
+<br />
+
+For example, to select only +viewable_by+ and +locked+ columns:
+
+<ruby>
+Client.all(:select => "viewable_by, locked")
+</ruby>
-To select certain fields, you can use the select option like this: +Client.first(:select => "viewable_by, locked")+. This select option does not use an array of fields, but rather requires you to type SQL-like code. The above code will execute +SELECT viewable_by, locked FROM clients LIMIT 1+ on your database.
+The SQL query used by this find call will be somewhat like:
+
+<sql>
+SELECT viewable_by, locked FROM clients
+</sql>
Be careful because this also means you're initializing a model object with only the fields that you've selected. If you attempt to access a field that is not in the initialized record you'll receive:
@@ -294,13 +398,19 @@ Be careful because this also means you're initializing a model object with only
ActiveRecord::MissingAttributeError: missing attribute: <attribute>
</shell>
-Where <attribute> is the atrribute you asked for. The +id+ method will not raise the +ActiveRecord::MissingAttributeError+, so just be careful when working with associations because they need the +id+ method to function properly.
+Where +<attribute>+ is the attribute you asked for. The +id+ method will not raise the +ActiveRecord::MissingAttributeError+, so just be careful when working with associations because they need the +id+ method to function properly.
+
+You can also call SQL functions within the select option. For example, if you would like to only grab a single record per unique value in a certain field by using the +DISTINCT+ function you can do it like this:
+
+<ruby>
+Client.all(:select => "DISTINCT(name)")
+</ruby>
-You can also call SQL functions within the select option. For example, if you would like to only grab a single record per unique value in a certain field by using the +DISTINCT+ function you can do it like this: +Client.all(:select => "DISTINCT(name)")+.
+h4. Limit and Offset
-h3. Limit & Offset
+To apply +LIMIT+ to the SQL fired by the +Model.find+, you can specify the +LIMIT+ using +:limit+ and +:offset+ options on the find.
-If you want to limit the amount of records to a certain subset of all the records retrieved you usually use limit for this, sometimes coupled with offset. Limit is the maximum number of records that will be retrieved from a query, and offset is the number of records it will start reading from from the first record of the set. Take this code for example:
+If you want to limit the amount of records to a certain subset of all the records retrieved you usually use +:limit+ for this, sometimes coupled with +:offset+. Limit is the maximum number of records that will be retrieved from a query, and offset is the number of records it will start reading from from the first record of the set. For example:
<ruby>
Client.all(:limit => 5)
@@ -312,6 +422,8 @@ This code will return a maximum of 5 clients and because it specifies no offset
SELECT * FROM clients LIMIT 5
</sql>
+Or specifying both +:limit+ and +:offset+:
+
<ruby>
Client.all(:limit => 5, :offset => 5)
</ruby>
@@ -322,9 +434,11 @@ This code will return a maximum of 5 clients and because it specifies an offset
SELECT * FROM clients LIMIT 5, 5
</sql>
-h3. Group
+h4. Group
+
+To apply +GROUP BY+ clause to the SQL fired by the +Model.find+, you can specify the +:group+ option on the find.
-The group option for find is useful, for example, if you want to find a collection of the dates orders were created on. You could use the option in this context:
+For example, if you want to find a collection of the dates orders were created on:
<ruby>
Order.all(:group => "date(created_at)", :order => "created_at")
@@ -338,27 +452,35 @@ The SQL that would be executed would be something like this:
SELECT * FROM orders GROUP BY date(created_at)
</sql>
-h3. Having
+h4. Having
-The +:having+ option allows you to specify SQL and acts as a kind of a filter on the group option. +:having+ can only be specified when +:group+ is specified.
+SQL uses +HAVING+ clause to specify conditions on the +GROUP BY+ fields. You can specify the +HAVING+ clause to the SQL fired by the +Model.find+ using +:having+ option on the find.
-An example of using it would be:
+For example:
<ruby>
Order.all(:group => "date(created_at)", :having => ["created_at > ?", 1.month.ago])
</ruby>
+The SQL that would be executed would be something like this:
+
+<sql>
+SELECT * FROM orders GROUP BY date(created_at) HAVING created_at > '2009-01-15'
+</sql>
+
This will return single order objects for each day, but only for the last month.
-h3. Read Only
+h4. Readonly objects
+
+To explicitly disallow modification/destroyal of the matching records returned by +Model.find+, you could specify the +:readonly+ option as +true+ to the find call.
-+readonly+ is a +find+ option that you can set in order to make that instance of the record read-only. Any attempt to alter or destroy the record will not succeed, raising an ActiveRecord::ReadOnlyRecord exception. To set this option, specify it like this:
+Any attempt to alter or destroy the readonly records will not succeed, raising an +ActiveRecord::ReadOnlyRecord+ exception. To set this option, specify it like this:
<ruby>
Client.first(:readonly => true)
</ruby>
-If you assign this record to a variable client, calling the following code will raise an ActiveRecord::ReadOnlyRecord exception:
+If you assign this record to a variable client, calling the following code will raise an +ActiveRecord::ReadOnlyRecord+ exception:
<ruby>
client = Client.first(:readonly => true)
@@ -366,238 +488,304 @@ client.locked = false
client.save
</ruby>
-h3. Lock
+h4. Locking records for update
-If you're wanting to stop race conditions for a specific record (for example, you're incrementing a single field for a record, potentially from multiple simultaneous connections) you can use the lock option to ensure that the record is updated correctly. For safety, you should use this inside a transaction.
+Locking is helpful for preventing the race conditions when updating records in the database and ensuring atomic updated. Active Record provides two locking mechanism:
+
+* Optimistic Locking
+* Pessimistic Locking
+
+h5. Optimistic Locking
+
+Optimistic locking allows multiple users to access the same record for edits, and assumes a minimum of conflicts with the data. It does this by checking whether another process has made changes to a record since it was opened. An +ActiveRecord::StaleObjectError+ exception is thrown if that has occurred and the update is ignored.
+
+<strong>Optimistic locking column</strong>
+
+In order to use optimistic locking, the table needs to have a column called +lock_version+. Each time the record is updated, Active Record increments the +lock_version+ column and the locking facilities ensure that records instantiated twice will let the last one saved raise an +ActiveRecord::StaleObjectError+ exception if the first was also updated. Example:
<ruby>
-Topic.transaction do
- t = Topic.find(params[:id], :lock => true)
- t.increment!(:views)
-end
+c1 = Client.find(1)
+c2 = Client.find(1)
+
+c1.name = "Michael"
+c1.save
+
+c2.name = "should fail"
+c2.save # Raises a ActiveRecord::StaleObjectError
</ruby>
-You can also pass SQL to this option to allow different types of locks. For example, MySQL has an expression called LOCK IN SHARE MODE where you can lock a record but still allow other queries to read it. To specify this expression just pass it in as the lock option:
+You're then responsible for dealing with the conflict by rescuing the exception and either rolling back, merging, or otherwise apply the business logic needed to resolve the conflict.
+
+NOTE: You must ensure that your database schema defaults the +lock_version+ column to +0+.
+
+<br />
+
+This behavior can be turned off by setting <tt>ActiveRecord::Base.lock_optimistically = false</tt>.
+
+To override the name of the +lock_version+ column, +ActiveRecord::Base+ provides a class method called +set_locking_column+:
<ruby>
-Topic.transaction do
- t = Topic.find(params[:id], :lock => "LOCK IN SHARE MODE")
- t.increment!(:views)
+class Client < ActiveRecord::Base
+ set_locking_column :lock_client_column
end
</ruby>
-h3. Making It All Work Together
+h5. Pessimistic Locking
-You can chain these options together in no particular order as Active Record will write the correct SQL for you. If you specify two instances of the same options inside the +find+ method Active Record will use the last one you specified. This is because the options passed to find are a hash and defining the same key twice in a hash will result in the last definition being used.
+Pessimistic locking uses locking mechanism provided by the underlying database. Passing +:lock => true+ to +Model.find+ obtains an exclusive lock on the selected rows. +Model.find+ using +:lock+ are usually wrapped inside a transaction for preventing deadlock conditions.
-h3. Eager Loading
+For example:
-Eager loading is loading associated records along with any number of records in as few queries as possible. For example, if you wanted to load all the addresses associated with all the clients in a single query you could use +Client.all(:include => :address)+. If you wanted to include both the address and mailing address for the client you would use +Client.find(:all, :include => [:address, :mailing_address])+. Include will first find the client records and then load the associated address records. Running script/server in one window, and executing the code through script/console in another window, the output should look similar to this:
+<ruby>
+Item.transaction do
+ i = Item.first(:lock => true)
+ i.name = 'Jones'
+ i.save
+end
+</ruby>
+
+The above session produces the following SQL for a MySQL backend:
<sql>
-Client Load (0.000383) SELECT * FROM clients
-Address Load (0.119770) SELECT addresses.* FROM addresses
- WHERE (addresses.client_id IN (13,14))
-MailingAddress Load (0.001985) SELECT mailing_addresses.* FROM
- mailing_addresses WHERE (mailing_addresses.client_id IN (13,14))
+SQL (0.2ms) BEGIN
+Item Load (0.3ms) SELECT * FROM `items` LIMIT 1 FOR UPDATE
+Item Update (0.4ms) UPDATE `items` SET `updated_at` = '2009-02-07 18:05:56', `name` = 'Jones' WHERE `id` = 1
+SQL (0.8ms) COMMIT
</sql>
-The numbers +13+ and +14+ in the above SQL are the ids of the clients gathered from the +Client.all+ query. Rails will then run a query to gather all the addresses and mailing addresses that have a client_id of 13 or 14. Although this is done in 3 queries, this is more efficient than not eager loading because without eager loading it would run a query for every time you called +address+ or +mailing_address+ on one of the objects in the clients array, which may lead to performance issues if you're loading a large number of records at once and is often called the "N+1 query problem". The problem is that the more queries your server has to execute, the slower it will run.
+You can also pass raw SQL to the +:lock+ option to allow different types of locks. For example, MySQL has an expression called +LOCK IN SHARE MODE+ where you can lock a record but still allow other queries to read it. To specify this expression just pass it in as the lock option:
-If you wanted to get all the addresses for a client in the same query you would do +Client.all(:joins => :address)+.
-If you wanted to find the address and mailing address for that client you would do +Client.all(:joins => [:address, :mailing_address])+. This is more efficient because it does all the SQL in one query, as shown by this example:
+<ruby>
+Item.transaction do
+ i = Item.find(1, :lock => "LOCK IN SHARE MODE")
+ i.increment!(:views)
+end
+</ruby>
-<sql>
-+Client Load (0.000455) SELECT clients.* FROM clients INNER JOIN addresses
- ON addresses.client_id = client.id INNER JOIN mailing_addresses ON
- mailing_addresses.client_id = client.id
-</sql>
+h3. Joining tables
-This query is more efficent, but there's a gotcha: if you have a client who does not have an address or a mailing address they will not be returned in this query at all. If you have any association as an optional association, you may want to use include rather than joins. Alternatively, you can use a SQL join clause to specify exactly the join you need (Rails always assumes an inner join):
+<tt>Model.find</tt> provides a +:joins+ option for specifying +JOIN+ clauses on the resulting SQL. There multiple different ways to specify the +:joins+ option:
+
+h4. Using a string SQL fragment
+
+You can just supply the raw SQL specifying the +JOIN+ clause to the +:joins+ option. For example:
<ruby>
-Client.all(:joins => “LEFT OUTER JOIN addresses ON
- client.id = addresses.client_id LEFT OUTER JOIN mailing_addresses ON
- client.id = mailing_addresses.client_id”)
+Client.all(:joins => 'LEFT OUTER JOIN addresses ON addresses.client_id = client.id')
</ruby>
-When using eager loading you can specify conditions for the columns of the tables inside the eager loading to get back a smaller subset. If, for example, you want to find a client and all their orders within the last two weeks you could use eager loading with conditions for this:
+This will result in the following SQL:
+
+<sql>
+SELECT clients.* FROM clients INNER JOIN addresses ON addresses.client_id = clients.id
+</sql>
+
+h4. Using Array/Hash of named associations
+
+WARNING: This method only works with +INNER JOIN+,
+
+<br />
+
+Active Record lets you use the names of the "associations":association_basics.html defined on the Model, as a shortcut for specifying the +:joins+ option.
+
+For example, consider the following +Category+, +Post+, +Comments+ and +Guest+ models:
<ruby>
-Client.first(:include => "orders", :conditions =>
- ["orders.created_at >= ? AND orders.created_at <= ?", 2.weeks.ago, Time.now])
-</ruby>
+class Category < ActiveRecord::Base
+ has_many :posts
+end
-h3. Dynamic finders
+class Post < ActiveRecord::Base
+ belongs_to :category
+ has_many :comments
+ has_many :tags
+end
-For every field (also known as an attribute) you define in your table, Active Record provides a finder method. If you have a field called +name+ on your Client model for example, you get +find_by_name+ and +find_all_by_name+ for free from Active Record. If you have also have a +locked+ field on the Client model, you also get +find_by_locked+ and +find_all_by_locked+.
+class Comments < ActiveRecord::Base
+ belongs_to :post
+ has_one :guest
+end
-You can do +find_last_by_*+ methods too which will find the last record matching your argument.
+class Guest < ActiveRecord::Base
+ belongs_to :comment
+end
+</ruby>
-You can specify an exclamation point (!) on the end of the dynamic finders to get them to raise an ActiveRecord::RecordNotFound error if they do not return any records, like +Client.find_by_name!("Ryan")+
+Now all of the following will produce the expected join queries using +INNER JOIN+:
-If you want to find both by name and locked, you can chain these finders together by simply typing +and+ between the fields for example +Client.find_by_name_and_locked("Ryan", true)+.
+h5. Joining a single association
+<ruby>
+Category.all :joins => :posts
+</ruby>
-There's another set of dynamic finders that let you find or create/initialize objects if they aren't found. These work in a similar fashion to the other finders and can be used like +find_or_create_by_name(params[:name])+. Using this will firstly perform a find and then create if the find returns nil. The SQL looks like this for +Client.find_or_create_by_name("Ryan")+:
+This produces:
<sql>
-SELECT * FROM clients WHERE (clients.name = 'Ryan') LIMIT 1
-BEGIN
-INSERT INTO clients (name, updated_at, created_at, orders_count, locked)
- VALUES('Ryan', '2008-09-28 15:39:12', '2008-09-28 15:39:12', 0, '0')
-COMMIT
+SELECT categories.* FROM categories
+ INNER JOIN posts ON posts.category_id = categories.id
</sql>
-+find_or_create+'s sibling, +find_or_initialize+, will find an object and if it does not exist will act similar to calling +new+ with the arguments you passed in. For example:
+h5. Joining multiple associations
<ruby>
-client = Client.find_or_initialize_by_name('Ryan')
+Post.all :joins => [:category, :comments]
</ruby>
-will either assign an existing client object with the name 'Ryan' to the client local variable, or initialize a new object similar to calling +Client.new(:name => 'Ryan')+. From here, you can modify other fields in client by calling the attribute setters on it: +client.locked = true+ and when you want to write it to the database just call +save+ on it.
-
+This produces:
-h3. Finding By SQL
+<sql>
+SELECT posts.* FROM posts
+ INNER JOIN categories ON posts.category_id = categories.id
+ INNER JOIN comments ON comments.post_id = posts.id
+</sql>
-If you'd like to use your own SQL to find records in a table you can use +find_by_sql+. The +find_by_sql+ method will return an array of objects even the underlying query returns just a single record. For example you could run this query:
+h5. Joining nested associations (single level)
<ruby>
-Client.find_by_sql("SELECT * FROM clients INNER JOIN orders ON clients.id = orders.client_id ORDER clients.created_at desc")
+Post.all :joins => {:comments => :guest}
</ruby>
-+find_by_sql+ provides you with a simple way of making custom calls to the database and retrieving instantiated objects.
+h5. Joining nested associations (multiple level)
-h3. select_all
+<ruby>
+Category.all :joins => {:posts => [{:comments => :guest}, :tags]}
+</ruby>
+
+h4. Specifying conditions on the joined tables
-+find_by_sql+ has a close relative called +connection#select_all+. +select_all+ will retrieve objects from the database using custom SQL just like +find_by_sql+ but will not instantiate them. Instead, you will get an array of hashes where each hash indicates a record.
+You can specify conditions on the joined tables using the regular "Array":#arrayconditions and "String":#purestringconditions conditions. "Hash conditions":#hashconditions provides a special syntax for specifying conditions for the joined tables:
<ruby>
-Client.connection.select_all("SELECT * FROM clients WHERE id = '1'")
+time_range = (Time.now.midnight - 1.day)..Time.now.midnight
+Client.all :joins => :orders, :conditions => {'orders.created_at' => time_range}
</ruby>
-h3. Working with Associations
+An alternative and cleaner syntax to this is to nest the hash conditions:
+
+<ruby>
+time_range = (Time.now.midnight - 1.day)..Time.now.midnight
+Client.all :joins => :orders, :conditions => {:orders => {:created_at => time_range}}
+</ruby>
-When you define a has_many association on a model you get the +find+ method and dynamic finders also on that association. This is helpful for finding associated records within the scope of an existing record, for example finding all the orders for a client that have been sent and not received by doing something like +Client.find(params[:id]).orders.find_by_sent_and_received(true, false)+. Having this find method available on associations is extremely helpful when using nested resources.
+This will find all clients who have orders that were created yesterday, again using a +BETWEEN+ SQL expression.
-h3. Named Scopes
+h3. Eager loading associations
-Named scopes are another way to add custom finding behavior to the models in the application. Named scopes provide an object-oriented way to narrow the results of a query.
+Eager loading is the mechanism for loading the associated records of the objects returned by +Model.find+ using as few queries as possible.
-h4. Simple Named Scopes
+<strong>N <plus> 1 queries problem</strong>
-Suppose we want to find all clients who are male. You could use this code:
+Consider the following code, which finds 10 clients and prints their postcodes:
<ruby>
-class Client < ActiveRecord::Base
- named_scope :males, :conditions => { :gender => "male" }
+clients = Client.all(:limit => 10)
+
+clients.each do |client|
+ puts client.address.postcode
end
</ruby>
-Then you could call +Client.males.all+ to get all the clients who are male. Please note that if you do not specify the +all+ on the end you will get a +Scope+ object back, not a set of records which you do get back if you put the +all+ on the end.
+This code looks fine at the first sight. But the problem lies within the total number of queries executed. The above code executes 1 ( to find 10 clients ) <plus> 10 ( one per each client to load the address ) = <strong>11</strong> queries in total.
+
+<strong>Solution to N <plus> 1 queries problem</strong>
-If you wanted to find all the clients who are active, you could use this:
+Active Record lets you specify all the associations in advanced that are going to be loaded. This is possible by specifying the +:include+ option of the +Model.find+ call. By +:include+, Active Record ensures that all the specified associations are loaded using minimum possible number of queries.
+
+Revisiting the above case, we could rewrite +Client.all+ to use eager load addresses:
<ruby>
-class Client < ActiveRecord::Base
- named_scope :active, :conditions => { :active => true }
+clients = Client.all(:include => :address, :limit => 10)
+
+clients.each do |client|
+ puts client.address.postcode
end
</ruby>
-You can call this new named_scope with +Client.active.all+ and this will do the same query as if we just used +Client.all(:conditions => ["active = ?", true])+. If you want to find the first client within this named scope you could do +Client.active.first+.
+The above code will execute just <strong>2</strong> queries, as opposed to <strong>11</strong> queries in the previous case:
-h4. Combining Named Scopes
+<sql>
+SELECT * FROM clients
+SELECT addresses.* FROM addresses
+ WHERE (addresses.client_id IN (1,2,3,4,5,6,7,8,9,10))
+</sql>
-If you wanted to find all the clients who are active and male you can stack the named scopes like this:
+h4. Eager loading multiple associations
-<ruby>
-Client.males.active.all
-</ruby>
+Active Record lets you eager load any possible number of associations with a single +Model.find+ call by using Array, Hash or a nested Hash of Array/Hash with +:include+ find option.
-If you would then like to do a +all+ on that scope, you can. Just like an association, named scopes allow you to call +all+ on them:
+h5. Array of multiple associations
<ruby>
-Client.males.active.all(:conditions => ["age > ?", params[:age]])
+Post.all :include => [:category, :comments]
</ruby>
-h4. Runtime Evaluation of Named Scope Conditions
+This loads all the posts and the associated category and comments for each post.
-Consider the following code:
+h5. Nested assocaitions hash
<ruby>
-class Client < ActiveRecord::Base
- named_scope :recent, :conditions => { :created_at > 2.weeks.ago }
-end
+Category.find 1, :include => {:posts => [{:comments => :guest}, :tags]}
</ruby>
-This looks like a standard named scope that defines a method called +recent+ which gathers all records created any time between now and 2 weeks ago. That's correct for the first time the model is loaded but for any time after that, +2.weeks.ago+ is set to that same value, so you will consistently get records from a certain date until your model is reloaded by something like your application restarting. The way to fix this is to put the code in a lambda block:
+The above code finds the category with id 1 and eager loads all the posts associated with the found category. Additionally, it will also eager load every posts' tags and comments. Every comment's guest association will get eager loaded as well.
-<ruby>
-class Client < ActiveRecord::Base
- named_scope :recent, lambda { { :conditions => ["created_at > ?", 2.weeks.ago] } }
-end
-</ruby>
+h4. Specifying conditions on eager loaded associations
-And now every time the +recent+ named scope is called, the code in the lambda block will be executed, so you'll get actually 2 weeks ago from the code execution, not 2 weeks ago from the time the model was loaded.
+Even though Active Record lets you specify conditions on the eager loaded associations just like +:joins+, the recommended way is to use ":joins":#joiningtables instead.
-h4. Named Scopes with Multiple Models
+h3. Dynamic finders
-In a named scope you can use +:include+ and +:joins+ options just like in +find+.
+For every field (also known as an attribute) you define in your table, Active Record provides a finder method. If you have a field called +name+ on your Client model for example, you get +find_by_name+ and +find_all_by_name+ for free from Active Record. If you have also have a +locked+ field on the Client model, you also get +find_by_locked+ and +find_all_by_locked+.
-<ruby>
-class Client < ActiveRecord::Base
- named_scope :active_within_2_weeks, :joins => :order,
- lambda { { :conditions => ["orders.created_at > ?", 2.weeks.ago] } }
-end
-</ruby>
+You can do +find_last_by_*+ methods too which will find the last record matching your argument.
-This method, called as +Client.active_within_2_weeks.all+, will return all clients who have placed orders in the past 2 weeks.
+You can specify an exclamation point (!) on the end of the dynamic finders to get them to raise an ActiveRecord::RecordNotFound error if they do not return any records, like +Client.find_by_name!("Ryan")+
-h4. Arguments to Named Scopes
+If you want to find both by name and locked, you can chain these finders together by simply typing +and+ between the fields for example +Client.find_by_name_and_locked("Ryan", true)+.
-If you want to pass to a named scope a required arugment, just specify it as a block argument like this:
-<ruby>
-class Client < ActiveRecord::Base
- named_scope :recent, lambda { |time| { :conditions => ["created_at > ?", time] } }
-end
-</ruby>
+There's another set of dynamic finders that let you find or create/initialize objects if they aren't found. These work in a similar fashion to the other finders and can be used like +find_or_create_by_name(params[:name])+. Using this will firstly perform a find and then create if the find returns nil. The SQL looks like this for +Client.find_or_create_by_name("Ryan")+:
-This will work if you call +Client.recent(2.weeks.ago).all+ but not if you call +Client.recent+. If you want to add an optional argument for this, you have to use prefix the arugment with an *.
+<sql>
+SELECT * FROM clients WHERE (clients.name = 'Ryan') LIMIT 1
+BEGIN
+INSERT INTO clients (name, updated_at, created_at, orders_count, locked)
+ VALUES('Ryan', '2008-09-28 15:39:12', '2008-09-28 15:39:12', 0, '0')
+COMMIT
+</sql>
+
++find_or_create+'s sibling, +find_or_initialize+, will find an object and if it does not exist will act similar to calling +new+ with the arguments you passed in. For example:
<ruby>
-class Client < ActiveRecord::Base
- named_scope :recent, lambda { |*args| { :conditions => ["created_at > ?", args.first || 2.weeks.ago] } }
-end
+client = Client.find_or_initialize_by_name('Ryan')
</ruby>
-This will work with +Client.recent(2.weeks.ago).all+ and +Client.recent.all+, with the latter always returning records with a created_at date between right now and 2 weeks ago.
-
-Remember that named scopes are stackable, so you will be able to do +Client.recent(2.weeks.ago).unlocked.all+ to find all clients created between right now and 2 weeks ago and have their locked field set to false.
+will either assign an existing client object with the name 'Ryan' to the client local variable, or initialize a new object similar to calling +Client.new(:name => 'Ryan')+. From here, you can modify other fields in client by calling the attribute setters on it: +client.locked = true+ and when you want to write it to the database just call +save+ on it.
-h4. Anonymous Scopes
+h3. Finding By SQL
-All Active Record models come with a named scope named +scoped+, which allows you to create anonymous scopes. For example:
+If you'd like to use your own SQL to find records in a table you can use +find_by_sql+. The +find_by_sql+ method will return an array of objects even the underlying query returns just a single record. For example you could run this query:
<ruby>
-class Client < ActiveRecord::Base
- def self.recent
- scoped :conditions => ["created_at > ?", 2.weeks.ago]
- end
-end
+Client.find_by_sql("SELECT * FROM clients
+ INNER JOIN orders ON clients.id = orders.client_id
+ ORDER clients.created_at desc")
</ruby>
-Anonymous scopes are most useful to create scopes "on the fly":
++find_by_sql+ provides you with a simple way of making custom calls to the database and retrieving instantiated objects.
+
+h3. select_all
+
+<tt>find_by_sql</tt> has a close relative called +connection#select_all+. +select_all+ will retrieve objects from the database using custom SQL just like +find_by_sql+ but will not instantiate them. Instead, you will get an array of hashes where each hash indicates a record.
<ruby>
-Client.scoped(:conditions => { :gender => "male" })
+Client.connection.select_all("SELECT * FROM clients WHERE id = '1'")
</ruby>
-Just like named scopes, anonymous scopes can be stacked, either with other anonymous scopes or with regular named scopes.
-
h3. Existence of Objects
-If you simply want to check for the existence of the object there's a method called +exists?+. This method will query the database using the same query as +find+, but instead of returning an object or collection of objects it will return either +true+ or false+.
+If you simply want to check for the existence of the object there's a method called +exists?+. This method will query the database using the same query as +find+, but instead of returning an object or collection of objects it will return either +true+ or +false+.
<ruby>
Client.exists?(1)
@@ -617,11 +805,19 @@ Further more, +exists+ takes a +conditions+ option much like find:
Client.exists?(:conditions => "first_name = 'Ryan'")
</ruby>
+It's even possible to use +exists?+ without any arguments:
+
+<ruby>
+Client.exists?
+</ruby>
+
+The above returns +false+ if the +clients+ table is empty and +true+ otherwise.
+
h3. Calculations
This section uses count as an example method in this preamble, but the options described apply to all sub-sections.
-+count+ takes conditions much in the same way +exists?+ does:
+<tt>count</tt> takes conditions much in the same way +exists?+ does:
<ruby>
Client.count(:conditions => "first_name = 'Ryan'")
@@ -701,4 +897,5 @@ h3. Changelog
"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/16
-* December 29 2008: Initial version by Ryan Bigg
+* February 7, 2009: Second version by "Pratik":credits.html#lifo
+* December 29 2008: Initial version by "Ryan Bigg":credits.html#radar
diff --git a/railties/guides/source/activerecord_validations_callbacks.textile b/railties/guides/source/activerecord_validations_callbacks.textile
index 28948eca77..01e52bf01e 100644
--- a/railties/guides/source/activerecord_validations_callbacks.textile
+++ b/railties/guides/source/activerecord_validations_callbacks.textile
@@ -1,38 +1,39 @@
h2. Active Record Validations and Callbacks
-This guide teaches you how to hook into the lifecycle of your Active Record objects. More precisely, you will learn how to validate the state of your objects before they go into the database as well as how to perform custom operations at certain points in the object lifecycle.
+This guide teaches you how to hook into the lifecycle of your Active Record objects. You will learn how to validate the state of objects before they go into the database, and how to perform custom operations at certain points in the object lifecycle.
After reading this guide and trying out the presented concepts, we hope that you'll be able to:
+* Understand the lifecycle of Active Record objects
* Use the built-in Active Record validation helpers
* Create your own custom validation methods
* Work with the error messages generated by the validation process
-* Create callback methods to respond to events in the object lifecycle.
+* Create callback methods that respond to events in the object lifecycle
* Create special classes that encapsulate common behavior for your callbacks
-* Create Rails Observers
+* Create Observers that respond to lifecycle events outside of the original class
endprologue.
-# TODO consider starting with an overview of what validations and callbacks are, and the object lifecycle
-# http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html
-# http://api.rubyonrails.org/classes/ActiveRecord/Base.html
+h3. The Object Lifecycle
+
+During the normal operation of a Rails application, objects may be created, updated, and destroyed. Active Record provides hooks into this <em>object lifecycle</em> so that you can control your application and its data.
-h3. Overview of ActiveRecord Validation
+Validations allow you to ensure that only valid data is stored in your database. Callbacks and observers allow you to trigger logic before or after an alteration of an object's state.
-Before you dive into the detail of validations in Rails, you should understand a bit about how validations fit into the big picture. Why should you use validations? When do these validations take place?
+h3. Validations Overview
-h4. Why Use ActiveRecord Validations?
+Before you dive into the detail of validations in Rails, you should understand a bit about how validations fit into the big picture.
-The main reason for validating your objects before they get into the database is to ensure that only valid data is recorded. It's important to be sure that an email address column only contains valid email addresses, or that the customer's name column will never be empty. Constraints like that keep your database organized and helps your application to work properly.
+h4. Why Use Validations?
-There are several ways that you could validate the data that goes to the database, including native database constraints, client-side validations, and model-level validations. Each of these has pros and cons:
+Validations are used to ensure that only valid data is saved into your database. For example, it may be important to your application to ensure that every user provides a valid email address and mailing address
-* Using database constraints and/or stored procedures makes the validation mechanisms database-dependent and may turn your application into a hard to test and maintain beast. However, if your database is used by other applications, it may be a good idea to use some constraints also at the database level. Additionally, database-level validations can safely handle some things (such as uniqueness in heavily-used tables) that are problematic to implement from the application level.
-* Implementing validations only at the client side can be difficult in web-based applications. Usually this kind of validation is done using javascript, which may be turned off in the user's browser, leading to invalid data getting inside your database. However, if combined with server side validation, client side validation may be useful, since the user can have a faster feedback from the application when trying to save invalid data.
-* Using validation directly in your Active Record classes ensures that only valid data gets recorded, while still keeping the validation code in the right place, avoiding breaking the MVC pattern. Since the validation happens on the server side, the user cannot disable it, so it's also safer. It may be a hard and tedious work to implement some of the logic involved in your models' validations, but fear not: Active Record gives you the ability to easily create validations, providing built-in helpers for common validations while still allowing you to create your own validation methods.
+There are several ways to validate data before it is saved into your database, including native database constraints, client-side validations, controller-level validations, and model-level validations.
-# TODO consider adding a bullet point on validations in controllers, and why model validations should be preferred over complicated controllers.
-# http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model
+* Database constraints and/or stored procedures make the validation mechanisms database-dependent and can make testing and maintenance more difficult. However, if your database is used by other applications, it may be a good idea to use some constraints at the database level. Additionally, database-level validations can safely handle some things (such as uniqueness in heavily-used tables) that can be difficult to implement otherwise.
+* Client-side validations can be useful, but are generally unreliable if used alone. If they are implemented using Javascript, they may be bypassed if Javascript is turned off in the user's browser. However, if combined with other techniques, client-side validation can be a convenient way to provide users with immediate feedback as they use your site.
+* Controller-level validations can be tempting to use, but often become unwieldy and difficult to test and maintain. Whenever possible, it's a good idea to "keep your controllers skinny":http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model, as it will make your application a pleasure to work with in the long run.
+* Model-level validations are the best way to ensure that only valid data is saved into your database. They are database agnostic, cannot be bypassed by end users, and are convenient to test and maintain. Rails makes them easy to use, provides built-in helpers for common needs, and allows you to create your own validation methods as well.
h4. When Does Validation Happen?
@@ -46,8 +47,8 @@ end
We can see how it works by looking at some script/console output:
<shell>
->> p = Person.new(:name => "John Doe", :birthdate => Date.parse("09/03/1979"))
-=> #<Person id: nil, name: "John Doe", birthdate: "1979-09-03", created_at: nil, updated_at: nil>
+>> p = Person.new(:name => "John Doe")
+=> #<Person id: nil, name: "John Doe", created_at: nil, :updated_at: nil>
>> p.new_record?
=> true
>> p.save
@@ -89,7 +90,7 @@ Note that +save+ also has the ability to skip validations (and callbacks!) if pa
h4. Object#valid? and Object#invalid?
-To verify whether or not an object is valid, you can use the +valid?+ method. This runs validations and returns true if no errors were added to the object, and false otherwise.
+To verify whether or not an object is valid, Rails uses the +valid?+ method. You can also use this method on your own. +valid?+ triggers your validations and returns true if no errors were added to the object, and false otherwise.
<ruby>
class Person < ActiveRecord::Base
@@ -97,12 +98,12 @@ class Person < ActiveRecord::Base
end
Person.create(:name => "John Doe").valid? # => true
-Person.create.valid? # => false
+Person.create(:name => nil).valid? # => false
</ruby>
When Active Record is performing validations, any errors found are collected into an +errors+ instance variable and can be accessed through an +errors+ instance method. An object is considered invalid if it has errors, and calling +save+ or +save!+ will not save it to the database.
-However, note that an object instantiated with +new+ will not report errors even if it's technically invalid, because validations are not run when using +new+.
+Note that an object instantiated with +new+ will not report errors even if it's technically invalid, because validations are not run when using +new+.
<ruby>
class Person < ActiveRecord::Base
@@ -112,20 +113,25 @@ end
>> p = Person.new
=> #<Person id: nil, name: nil>
>> p.errors
-=> #<ActiveRecord::Errors:0x3b8b46c @base=#<Person id: nil, name: nil>, @errors={}>
+=> #<ActiveRecord::Errors..., @errors={}>
+
>> p.valid?
=> false
>> p.errors
-=> #<ActiveRecord::Errors:0x3b8b46c @base=#<Person id: nil, name: nil>, @errors={"name"=>["can't be blank"]}>
+=> #<ActiveRecord::Errors..., @errors={"name"=>["can't be blank"]}>
+
>> p = Person.create
=> #<Person id: nil, name: nil>
>> p.errors
-=> #<ActiveRecord::Errors:0x3b8b46c @base=#<Person id: nil, name: nil>, @errors={"name"=>["can't be blank"]}>
+=> #<ActiveRecord::Errors..., @errors={"name"=>["can't be blank"]}>
+
>> p.save
=> false
+
>> p.save!
=> ActiveRecord::RecordInvalid: Validation failed: Name can't be blank
->> p = Person.create!
+
+>> Person.create!
=> ActiveRecord::RecordInvalid: Validation failed: Name can't be blank
</ruby>
@@ -140,7 +146,9 @@ end
>> Person.create.errors.invalid?(:name) # => true
</ruby>
-h3. Declarative Validation Helpers
+We'll cover validation errors in greater depth in the *Working with Validation Errors* section. For now, let's turn to the built-in validation helpers that Rails provides by default.
+
+h3. Validation Helpers
Active Record offers many pre-defined validation helpers that you can use directly inside your class definitions. These helpers create validation rules that are commonly used. Every time a validation fails, an error message is added to the object's +errors+ collection, and this message is associated with the field being validated.
@@ -309,7 +317,6 @@ Besides +:only_integer+, the +validates_numericality_of+ helper also accepts the
* +:odd+ - Specifies the value must be an odd number if set to true. The default error message for this option is "_must be odd_"
* +:even+ - Specifies the value must be an even number if set to true. The default error message for this option is "_must be even_"
-
The default error message for +validates_numericality_of+ is "_is not a number_".
h4. validates_presence_of
@@ -322,7 +329,7 @@ class Person < ActiveRecord::Base
end
</ruby>
-NOTE: If you want to be sure that an association is present, you'll need to test whether the foreign key used to map the association is present, and not the associated object itself.
+If you want to be sure that an association is present, you'll need to test whether the foreign key used to map the association is present, and not the associated object itself.
<ruby>
class LineItem < ActiveRecord::Base
@@ -331,7 +338,7 @@ class LineItem < ActiveRecord::Base
end
</ruby>
-NOTE: If you want to validate the presence of a boolean field (where the real values are true and false), you should use validates_inclusion_of :field_name, :in => [true, false] This is due to the way Object#blank? handles boolean values. false.blank? # => true
+If you want to validate the presence of a boolean field (where the real values are true and false), you should use +validates_inclusion_of :field_name, :in => [true, false]+. This is due to the way that +Object#blank?+ handles boolean values (+false.blank? # => true+).
The default error message for +validates_presence_of+ is "_can't be empty_".
@@ -429,11 +436,11 @@ class Person < ActiveRecord::Base
end
</ruby>
-h3. Conditional validation
+h3. Conditional Validation
Sometimes it will make sense to validate an object just when a given predicate is satisfied. You can do that by using the +:if+ and +:unless+ options, which can take a symbol, a string or a Ruby Proc. You may use the +:if+ option when you want to specify when the validation *should* happen. If you want to specify when the validation *should not* happen, then you may use the +:unless+ option.
-h4. Using a symbol with :if and :unless
+h4. Using a Symbol with :if and :unless
You can associate the +:if+ and +:unless+ options with a symbol corresponding to the name of a method that will get called right before validation happens. This is the most commonly used option.
@@ -447,7 +454,7 @@ class Order < ActiveRecord::Base
end
</ruby>
-h4. Using a string with the :if and :unless
+h4. Using a String with :if and :unless
You can also use a string that will be evaluated using +:eval+ and needs to contain valid Ruby code. You should use this option only when the string represents a really short condition.
@@ -457,7 +464,7 @@ class Person < ActiveRecord::Base
end
</ruby>
-h4. Using a Proc object with :if and :unless
+h4. Using a Proc with :if and :unless
Finally, it's possible to associate +:if+ and +:unless+ with a Ruby Proc object which will be called. Using a Proc object can give you the ability to write a condition that will be executed only when the validation happens and not when your code is loaded by the Ruby interpreter. This option is best suited when writing short validation methods, usually one-liners.
@@ -468,9 +475,13 @@ class Account < ActiveRecord::Base
end
</ruby>
-h3. Writing your own validation methods
+h3. Creating Custom Validation Methods
-When the built-in validation helpers are not enough for your needs, you can write your own validation methods. You can do that by implementing methods that verify the state of your models and add messages to their +errors+ collection when they are invalid. You must then register those methods by using one or more of the +validate+, +validate_on_create+ or +validate_on_update+ class methods, passing in the symbols for the validation methods' names. You can pass more than one symbol for each class method and the respective validations will be ran in the same order as they were registered.
+When the built-in validation helpers are not enough for your needs, you can write your own validation methods.
+
+Simply create methods that verify the state of your models and add messages to the +errors+ collection when they are invalid. You must then register these methods by using one or more of the +validate+, +validate_on_create+ or +validate_on_update+ class methods, passing in the symbols for the validation methods' names.
+
+You can pass more than one symbol for each class method and the respective validations will be ran in the same order as they were registered.
<ruby>
class Invoice < ActiveRecord::Base
@@ -506,7 +517,7 @@ module ActiveRecord
end
</ruby>
-The recipe is simple: just create a new validation method inside the +ActiveRecord::Validations::ClassMethods+ module. You can put this code in a file inside your application's *lib* folder, and then requiring it from your *environment.rb* or any other file inside *config/initializers*. You can use this helper like this:
+Simply create a new validation method inside the +ActiveRecord::Validations::ClassMethods+ module. You can put this code in a file inside your application's *lib* folder, and then requiring it from your *environment.rb* or any other file inside *config/initializers*. You can use this helper like this:
<ruby>
class Person < ActiveRecord::Base
@@ -514,15 +525,15 @@ class Person < ActiveRecord::Base
end
</ruby>
-h3. Manipulating the errors collection
+h3. Working with Validation Errors
-# TODO consider renaming section to something more simple, like "Validation errors"
-# Consider combining with new section at the top with Object#valid? and Object#invalid?
-# Consider moving this stuff above the current 2-6 sections (e.g. save the validation details until after this?)
+In addition to the +valid?+ and +invalid?+ methods covered earlier, Rails provides a number of methods for working with the +errors+ collection and inquiring about the validity of objects.
-You can do more than just call +valid?+ upon your objects based on the existence of the +errors+ collection. Here is a list of the other available methods that you can use to manipulate errors or ask for an object's state.
+The following is a list of the most commonly used methods. Please refer to the ActiveRecord::Errors documentation for an exhaustive list that covers all of the available methods.
-* +add_to_base+ lets you add errors messages that are related to the object's state as a whole, instead of being related to a specific attribute. You can use this method when you want to say that the object is invalid, no matter the values of it's attributes. +add_to_base+ receives a string with the message.
+h4. errors.add_to_base
+
++add_to_base+ lets you add errors messages that are related to the object's state as a whole, instead of being related to a specific attribute. You can use this method when you want to say that the object is invalid, no matter the values of it's attributes. +add_to_base+ simply receives a string and uses this as the error message.
<ruby>
class Person < ActiveRecord::Base
@@ -532,28 +543,29 @@ class Person < ActiveRecord::Base
end
</ruby>
-* +add+ lets you manually add messages that are related to particular attributes. When writing those messages, keep in mind that Rails will prepend them with the name of the attribute that holds the error, so write it in a way that makes sense. +add+ receives a symbol with the name of the attribute that you want to add the message to and the message itself.
+h4. errors.add
+
++add+ lets you manually add messages that are related to particular attributes. Note that Rails will prepend the name of the attribute to the error message you pass it. You can use the +full_messages+ method to view the messages in the form they might be displayed to a user. +add+ receives a symbol with the name of the attribute that you want to add the message to, and the message itself.
<ruby>
class Person < ActiveRecord::Base
def a_method_used_for_validation_purposes
- errors.add(:name, "can't have the characters !@#$%*()_-+=")
+ errors.add(:name, "cannot contain the characters !@#$%*()_-+=")
end
end
-</ruby>
-* +invalid?+ is used when you want to check if a particular attribute is invalid. It receives a symbol with the name of the attribute that you want to check.
+person = Person.create(:name => "!@#$")
-<ruby>
-class Person < ActiveRecord::Base
- validates_presence_of :name, :email
-end
+person.errors.on(:name)
+# => "is too short (minimum is 3 characters)"
-person = Person.new(:name => "John Doe")
-person.errors.invalid?(:email) # => true
+person.errors.full_messages
+# => ["Name is too short (minimum is 3 characters)"]
</ruby>
-* +on+ is used when you want to check the error messages for a specific attribute. It will return different kinds of objects depending on the state of the +errors+ collection for the given attribute. If there are no errors related to the attribute, +on+ will return +nil+. If there is just one errors message for this attribute, +on+ will return a string with the message. When +errors+ holds two or more error messages for the attribute, +on+ will return an array of strings, each one with one error message.
+h4. errors.on
+
++on+ is used when you want to check the error messages for a specific attribute. It will return different kinds of objects depending on the state of the +errors+ collection for the given attribute. If there are no errors related to the attribute, +on+ will return +nil+. If there is just one errors message for this attribute, +on+ will return a string with the message. When +errors+ holds two or more error messages for the attribute, +on+ will return an array of strings, each one with one error message.
<ruby>
class Person < ActiveRecord::Base
@@ -576,7 +588,9 @@ person.errors.on(:name)
# => ["can't be blank", "is too short (minimum is 3 characters)"]
</ruby>
-* +clear+ is used when you intentionally want to clear all the messages in the +errors+ collection. However, calling +errors.clear+ upon an invalid object won't make it valid: the +errors+ collection will now be empty, but the next time you call +valid?+ or any method that tries to save this object to the database, the validations will run. If any of them fails, the +errors+ collection will get filled again.
+h4. errors.clear
+
++clear+ is used when you intentionally want to clear all the messages in the +errors+ collection. Of course, calling +errors.clear+ upon an invalid object won't actually make it valid: the +errors+ collection will now be empty, but the next time you call +valid?+ or any method that tries to save this object to the database, the validations will run again. If any of the validations fail, the +errors+ collection will be filled again.
<ruby>
class Person < ActiveRecord::Base
@@ -591,17 +605,35 @@ person.errors.on(:name)
person.errors.clear
person.errors.empty? # => true
+
p.save # => false
+
p.errors.on(:name)
# => ["can't be blank", "is too short (minimum is 3 characters)"]
</ruby>
-# TODO consider discussing other methods (e.g. errors.size)
-# http://api.rubyonrails.org/classes/ActiveRecord/Errors.html
+h4. errors.size
+
++size+ returns the total number of errors added. Two errors added to the same object will be counted as such.
+
+<ruby>
+class Person < ActiveRecord::Base
+ validates_presence_of :name
+ validates_length_of :name, :minimum => 3
+end
+
+person = Person.new
+person.valid? # => false
+person.errors.size # => 2
+</ruby>
+
+h3. Displaying Validation Errors in the View
+
+Rails provides built-in helpers to display the error messages of your models in your view templates.
-h3. Using error collection in views
+h4. error_messages and error_messages_for
-Rails provides built-in helpers to display the error messages of your models in your view templates. When creating a form with the form_for helper, you can use the error_messages method on the form builder to render all failed validation messages for the current model instance.
+When creating a form with the form_for helper, you can use the error_messages method on the form builder to render all failed validation messages for the current model instance.
<ruby>
class Product < ActiveRecord::Base
@@ -651,6 +683,8 @@ Which results in the following content
If you pass +nil+ to any of these options, it will get rid of the respective section of the +div+.
+h4. Customizing the Error Messages CSS
+
It's also possible to change the CSS classes used by the +error_messages+ helper. These classes are automatically defined at the *scaffold.css* file, generated by the scaffold script. If you're not using scaffolding, you can still define those CSS classes at your CSS files. Here is a list of the default CSS classes.
* +.fieldWithErrors+ - Style for the form fields with errors.
@@ -659,9 +693,11 @@ It's also possible to change the CSS classes used by the +error_messages+ helper
* +#errorExplanation p+ - Style for the paragraph that holds the message that appears right below the header of the +div+ element.
* +#errorExplanation ul li+ - Style for the list of error messages.
-h4. Changing the way form fields with errors are displayed
+h4. Customizing the Error Messages HTML
+
+By default, form fields with errors are displayed enclosed by a +div+ element with the +fieldWithErrors+ CSS class. However, it's possible to override the way Rails treats those fields by default.
-By default, form fields with errors are displayed enclosed by a +div+ element with the +fieldWithErrors+ CSS class. However, we can write some Ruby code to override the way Rails treats those fields by default. Here is a simple example where we change the Rails behaviour to always display the error messages in front of each of the form fields with errors. The error messages will be enclosed by a +span+ element with a +validation-error+ CSS class. There will be no +div+ element enclosing the +input+ element, so we get rid of that red border around the text field. You can use the +validation-error+ CSS class to style it anyway you want.
+Here is a simple example where we change the Rails behaviour to always display the error messages in front of each of the form fields with errors. The error messages will be enclosed by a +span+ element with a +validation-error+ CSS class. There will be no +div+ element enclosing the +input+ element, so we get rid of that red border around the text field. You can use the +validation-error+ CSS class to style it anyway you want.
<ruby>
ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
@@ -684,7 +720,7 @@ The way form fields with errors are treated is defined by the +ActionView::Base.
* A string with the HTML tag
* An object of the +ActionView::Helpers::InstanceTag+ class.
-h3. Callbacks
+h3. Callbacks Overview
Callbacks are methods that get called at certain moments of an object's lifecycle. With callbacks it's possible to write code that will run whenever an Active Record object is created, saved, updated, deleted or loaded from the database.
@@ -693,7 +729,7 @@ Callbacks are methods that get called at certain moments of an object's lifecycl
# http://api.rubyonrails.org/classes/ActiveRecord/Base.html#M002220
# http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html
-h4. Callback registration
+h4. Callback Registration
In order to use the available callbacks, you need to register them. You can do that by implementing them as an ordinary methods, and then using a macro-style class method to register then as callbacks.
@@ -712,7 +748,7 @@ class User < ActiveRecord::Base
end
</ruby>
-The macro-style class methods can also receive a block. Rails best practices say that you should only use this style of registration if the code inside your block is so short that it fits in just one line.
+The macro-style class methods can also receive a block. Consider using this style if the code inside your block is so short that it fits in just one line.
<ruby>
class User < ActiveRecord::Base
@@ -722,15 +758,13 @@ class User < ActiveRecord::Base
end
</ruby>
-CAUTION: Remember to always declare the callback methods as being protected or private. These methods should never be public, otherwise it will be possible to call them from code outside the model, violating object encapsulation and exposing implementation details.
+It's considered good practice to declare callback methods as being protected or private. If left public, they can be called from outside of the model and violate the principle of object encapsulation.
-# TODO consider not making this a warning, just a note (this is an implementation detail, not a requirement of the library)
-
-h3. Conditional callbacks
+h3. Conditional Callbacks
Like in validations, we can also make our callbacks conditional, calling then only when a given predicate is satisfied. You can do that by using the +:if+ and +:unless+ options, which can take a symbol, a string or a Ruby Proc. You may use the +:if+ option when you want to specify when the callback *should* get called. If you want to specify when the callback *should not* be called, then you may use the +:unless+ option.
-h4. Using a symbol with the :if and :unless
+h4. Using :if and :unless with a Symbol
You can associate the +:if+ and +:unless+ options with a symbol corresponding to the name of a method that will get called right before the callback. If this method returns +false+ the callback won't be executed. This is the most common option. Using this form of registration it's also possible to register several different methods that should be called to check the if the callback should be executed.
@@ -740,7 +774,7 @@ class Order < ActiveRecord::Base
end
</ruby>
-h4. Using a string with the :if and :unless
+h4. Using :if and :unless with a String
You can also use a string that will be evaluated using +:eval+ and needs to contain valid Ruby code. You should use this option only when the string represents a really short condition.
@@ -750,7 +784,7 @@ class Order < ActiveRecord::Base
end
</ruby>
-h4. Using a Proc object with the :if and :unless
+h4. Using :if and :unless with a Proc
Finally, it's possible to associate +:if+ and +:unless+ with a Ruby Proc object. This option is best suited when writing short validation methods, usually one-liners.
@@ -772,48 +806,43 @@ class Comment < ActiveRecord::Base
end
</ruby>
-h3. Available callbacks
-
-# TODO consider moving above the code examples and details, possibly combining with intro lifecycle stuff?
+h3. Available Callbacks
Here is a list with all the available Active Record callbacks, listed in the same order in which they will get called during the respective operations.
-h4. Callbacks called both when creating or updating a record.
-
-# TODO consider just listing these in the possible lifecycle of an object, roughly as they do here:
-# http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html
+h4. Creating and/or Updating an Object
* +before_validation+
* +after_validation+
* +before_save+
-* *INSERT OR UPDATE OPERATION*
+* INSERT OR UPDATE OPERATION
* +after_save+
-h4. Callbacks called only when creating a new record.
+h4. Creating an Object
* +before_validation_on_create+
* +after_validation_on_create+
* +before_create+
-* *INSERT OPERATION*
+* INSERT OPERATION
* +after_create+
-h4. Callbacks called only when updating an existing record.
+h4. Updating an Object
* +before_validation_on_update+
* +after_validation_on_update+
* +before_update+
-* *UPDATE OPERATION*
+* UPDATE OPERATION
* +after_update+
-h4. Callbacks called when removing a record from the database.
+h4. Destroying an Object
* +before_destroy+
-* *DELETE OPERATION*
+* DELETE OPERATION
* +after_destroy+
-The +before_destroy+ and +after_destroy+ callbacks will only be called if you delete the model using either the +destroy+ instance method or one of the +destroy+ or +destroy_all+ class methods of your Active Record class. If you use +delete+ or +delete_all+ no callback operations will run, since Active Record will not instantiate any objects, accessing the records to be deleted directly in the database.
+CAUTION: The +before_destroy+ and +after_destroy+ callbacks will only be called if you delete the model using either the +destroy+ instance method or one of the +destroy+ or +destroy_all+ class methods of your Active Record class. If you use +delete+ or +delete_all+ no callback operations will run, since Active Record will not instantiate any objects, accessing the records to be deleted directly in the database.
-h4. after_initialize and after_find callbacks
+h4. after_initialize and after_find
The +after_initialize+ callback will be called whenever an Active Record object is instantiated, either by directly using +new+ or when a record is loaded from the database. It can be useful to avoid the need to directly override your Active Record +initialize+ method.
@@ -825,7 +854,7 @@ h3. Halting Execution
As you start registering new callbacks for your models, they will be queued for execution. This queue will include all your model's validations, the registered callbacks and the database operation to be executed. However, if at any moment one of the +before_create+, +before_save+, +before_update+ or +before_destroy+ callback methods returns a boolean +false+ (not +nil+) value or raise and exception, this execution chain will be halted and the desired operation will not complete: your model will not get persisted in the database, or your records will not get deleted and so on. It's because the whole callback chain is wrapped in a transaction, so raising an exception or returning +false+ fires a database ROLLBACK.
-h3. Callback classes
+h3. Callback Classes
Sometimes the callback methods that you'll write will be useful enough to be reused at other models. Active Record makes it possible to create classes that encapsulate the callback methods, so it becomes very easy to reuse them.
@@ -869,53 +898,58 @@ You can declare as many callbacks as you want inside your callback classes.
h3. Observers
-Active Record callbacks are a powerful feature, but they can pollute your model implementation with code that's not directly related to the model's purpose. In object-oriented software, it's always a good idea to design your classes with a single responsibility in the whole system. For example, it wouldn't make much sense to have a +User+ model with a method that writes data about a login attempt to a log file. Whenever you're using callbacks to write code that's not directly related to your model class purposes, it may be a good moment to create an Observer.
-
-An Active Record Observer is an object that links itself to a model and registers its methods for callbacks. Your model's implementation remains clean, while you can reuse the code in the Observer to add behaviour to more than one model class. OK, you may say that we can also do that using callback classes, but it would still force us to add code to our model's implementation.
+Observers are similar to callbacks, but with important differences. Whereas callbacks can pollute a model with code that isn't directly related to its purpose, observers allow you to add functionality outside of a model. For example, it could be argued that a +User+ model should not include code to send registration confirmation emails. Whenever you use callbacks with code that isn't directly related to your model, you may want to consider creating an observer instead.
-Observer classes are subclasses of the ActiveRecord::Observer class. When this class is subclassed, Active Record will look at the name of the new class and then strip the 'Observer' part to find the name of the Active Record class to observe.
+h4. Creating observers
-Consider a Registration model, where we want to send an email every time a new registration is created. Since sending emails is not directly related to our model's purpose, we could create an Observer to do just that:
+For example, imagine a +User+ model where we want to send an email every time a new user is created. Because sending emails is not directly related to our model's purpose, we could create an observer to contain this functionality.
<ruby>
-class RegistrationObserver < ActiveRecord::Observer
+class UserObserver < ActiveRecord::Observer
def after_create(model)
- # code to send registration confirmation emails...
+ # code to send confirmation email...
end
end
</ruby>
-Like in callback classes, the observer's methods receive the observed model as a parameter.
+As with callback classes, the observer's methods receive the observed model as a parameter.
-Sometimes using the ModelName + Observer naming convention won't be the best choice, mainly when you want to use the same observer for more than one model class. It's possible to explicity specify the models that our observer should observe.
+h4. Registering Observers
-<ruby>
-class Auditor < ActiveRecord::Observer
- observe User, Registration, Invoice
-end
-</ruby>
-
-h4. Registering observers
-
-If you paid attention, you may be wondering where Active Record Observers are referenced in our applications, so they get instantiated and begin to interact with our models. For observers to work we need to register them somewhere. The usual place to do that is in our application's *config/environment.rb* file. In this file there is a commented-out line where we can define the observers that our application should load at start-up.
+Observers should be placed inside of your *app/models* directory and registered in your application's *config/environment.rb* file. For example, the +UserObserver+ above would be saved as *app/models/user_observer.rb* and registered in *config/environment.rb*.
<ruby>
# Activate observers that should always be running
-config.active_record.observers = :registration_observer, :auditor
+config.active_record.observers = :user_observer
</ruby>
-You can uncomment the line with +config.active_record.observers+ and change the symbols for the name of the observers that should be registered.
+As usual, settings in *config/environments/* take precedence over those in *config/environment.rb*. So, if you prefer that an observer not run in all environments, you can simply register it in a specific environment instead.
+
+h4. Sharing Observers
-It's also possible to register callbacks in any of the files living at *config/environments/*, if you want an observer to work only in a specific environment. There is not a +config.active_record.observers+ line at any of those files, but you can simply add it.
+By default, Rails will simply strip 'observer' from an observer's name to find the model it should observe. However, observers can also be used to add behaviour to more than one model, and so it's possible to manually specify the models that our observer should observe.
-h4. Where to put the observers' source files
+<ruby>
+class MailerObserver < ActiveRecord::Observer
+ observe :registration, :user
+
+ def after_create(model)
+ # code to send confirmation email...
+ end
+end
+</ruby>
-By convention, you should always save your observers' source files inside *app/models*.
+In this example, the +after_create+ method would be called whenever a +Registration+ or +User+ was created. Note that this new +MailerObserver+ would also need to be registered in *config/environment.rb* in order to take effect.
-# TODO this probably doesn't need it's own section and entry in the table of contents.
+<ruby>
+# Activate observers that should always be running
+config.active_record.observers = :mailer_observer
+</ruby>
h3. Changelog
"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213/tickets/26-active-record-validations-and-callbacks
-January 9, 2009: Initial version by "Cássio Marques":credits.html#cmarques
+* February 10, 2009: Observers revision by Trevor Turk
+* February 5, 2009: Initial revision by Trevor Turk
+* January 9, 2009: Initial version by "Cássio Marques":credits.html#cmarques
diff --git a/railties/guides/source/association_basics.textile b/railties/guides/source/association_basics.textile
index 43e81123e9..e82f32d35e 100644
--- a/railties/guides/source/association_basics.textile
+++ b/railties/guides/source/association_basics.textile
@@ -41,7 +41,7 @@ With Active Record associations, we can streamline these - and other - operation
<ruby>
class Customer < ActiveRecord::Base
- has_many :orders
+ has_many :orders, :dependent => :destroy
end
class Order < ActiveRecord::Base
@@ -65,7 +65,7 @@ To learn more about the different types of associations, read the next section o
h3. The Types of Associations
-In Rails, an _association_ is a connection between two Active Record models. Associations are implemented using macro-style calls, so that you can declaratively add features to your models. For example, by declaring that one model +belongs_to+ another, you instruct Rails to maintain Primary Key-Foreign Key information between instances of the two models, and you also get a number of utility methods added to your model. Rails supports six types of association:
+In Rails, an _association_ is a connection between two Active Record models. Associations are implemented using macro-style calls, so that you can declaratively add features to your models. For example, by declaring that one model +belongs_to+ another, you instruct Rails to maintain Primary Key–Foreign Key information between instances of the two models, and you also get a number of utility methods added to your model. Rails supports six types of association:
* +belongs_to+
* +has_one+
@@ -195,7 +195,7 @@ end
h4. Choosing Between belongs_to and has_one
-If you want to set up a 1-1 relationship between two models, you'll need to add +belongs_to+ to one, and +has_one+ to the other. How do you know which is which?
+If you want to set up a 1–1 relationship between two models, you'll need to add +belongs_to+ to one, and +has_one+ to the other. How do you know which is which?
The distinction is in where you place the foreign key (it goes on the table for the class declaring the +belongs_to+ association), but you should give some thought to the actual meaning of the data as well. The +has_one+ relationship says that one of something is yours - that is, that something points back to you. For example, it makes more sense to say that a supplier owns an account than that an account owns a supplier. This suggests that the correct relationships are like this:
@@ -233,7 +233,7 @@ class CreateSuppliers < ActiveRecord::Migration
end
</ruby>
-NOTE: Using +t.integer :supplier_id+ makes the foreign key naming obvious and implicit. In current versions of Rails, you can abstract away this implementation detail by using +t.references :supplier+ instead.
+NOTE: Using +t.integer :supplier_id+ makes the foreign key naming obvious and explicit. In current versions of Rails, you can abstract away this implementation detail by using +t.references :supplier+ instead.
h4. Choosing Between has_many :through and has_and_belongs_to_many
@@ -268,7 +268,7 @@ class Part < ActiveRecord::Base
end
</ruby>
-The simplest rule of thumb is that you should set up a +has_many :through+ relationship if you need to work with the relationship model as an independent entity. If you don't need to do anything with the relationship model, it may be simpler to set up a +has_and_belongs_to_many+ relationship (though you'll need to remember to create the joining table).
+The simplest rule of thumb is that you should set up a +has_many :through+ relationship if you need to work with the relationship model as an independent entity. If you don't need to do anything with the relationship model, it may be simpler to set up a +has_and_belongs_to_many+ relationship (though you'll need to remember to create the joining table in the database).
You should use +has_many :through+ if you need validations, callbacks, or extra attributes on the join model.
@@ -335,12 +335,12 @@ end
h4. Self Joins
-In designing a data model, you will sometimes find a model that should have a relation to itself. For example, you may want to store all employees in a single database model, but be able to trace relationships such as manager and subordinates. This situation can be modeled with self-joining associations:
+In designing a data model, you will sometimes find a model that should have a relation to itself. For example, you may want to store all employees in a single database model, but be able to trace relationships such as between manager and subordinates. This situation can be modeled with self-joining associations:
<ruby>
class Employee < ActiveRecord::Base
has_many :subordinates, :class_name => "Employee",
- :foreign_key => "manager_id"
+ :foreign_key => "manager_id"
belongs_to :manager, :class_name => "Employee"
end
</ruby>
@@ -358,7 +358,7 @@ Here are a few things you should know to make efficient use of Active Record ass
h4. Controlling Caching
-All of the association methods are built around caching that keeps the result of the most recent query available for further operations. The cache is even shared across methods. For example:
+All of the association methods are built around caching, which keeps the result of the most recent query available for further operations. The cache is even shared across methods. For example:
<ruby>
customer.orders # retrieves orders from the database
@@ -415,9 +415,9 @@ If you create an association some time after you build the underlying model, you
h5. Creating Join Tables for has_and_belongs_to_many Associations
-If you create a +has_and_belongs_to_many+ association, you need to explicitly create the joining table. Unless the name of the join table is explicitly specified by using the +:join_table+ option, Active Record create the name by using the lexical order of the class names. So a join between customer and order models will give the default join table name of "customers_orders" because "c" outranks "o" in lexical ordering.
+If you create a +has_and_belongs_to_many+ association, you need to explicitly create the joining table. Unless the name of the join table is explicitly specified by using the +:join_table+ option, Active Record creates the name by using the lexical order of the class names. So a join between customer and order models will give the default join table name of "customers_orders" because "c" outranks "o" in lexical ordering.
-WARNING: The precedence between model names is calculated using the +<+ operator for +String+. This means that if the strings are of different lengths, and the strings are equal when compared up to the shortest length, then the longer string is considered of higher lexical precedence than the shorter one. For example, one would expect the tables "paper_boxes" and "papers" to generate a join table name of "papers_paper_boxes" because of the length of the name "paper_boxes", but it in fact generates a join table name of "paper_boxes_papers".
+WARNING: The precedence between model names is calculated using the +<+ operator for +String+. This means that if the strings are of different lengths, and the strings are equal when compared up to the shortest length, then the longer string is considered of higher lexical precedence than the shorter one. For example, one would expect the tables "paper_boxes" and "papers" to generate a join table name of "papers_paper_boxes" because of the length of the name "paper_boxes", but it in fact generates a join table name of "paper_boxes_papers" (because the underscore '_' is lexicographically _less_ than 's' in common encodings).
Whatever the name, you must manually generate the join table with an appropriate migration. For example, consider these associations:
@@ -466,7 +466,7 @@ module MyApplication
end
</ruby>
-This will work fine, because both the +Supplier+ and the +Account+ class are defined within the same scope. But this will not work, because +Supplier+ and +Account+ are defined in different scopes:
+This will work fine, because both the +Supplier+ and the +Account+ class are defined within the same scope. But the following will _not_ work, because +Supplier+ and +Account+ are defined in different scopes:
<ruby>
module MyApplication
@@ -514,7 +514,7 @@ The +belongs_to+ association creates a one-to-one match with another model. In d
h5. Methods Added by belongs_to
-When you declare a +belongs_to+ assocation, the declaring class automatically gains five methods related to the association:
+When you declare a +belongs_to+ association, the declaring class automatically gains five methods related to the association:
* <tt><em>association</em>(force_reload = false)</tt>
* <tt><em>association</em>=(associate)</tt>
@@ -552,7 +552,7 @@ If the associated object has already been retrieved from the database for this o
h6. _association_=(associate)
-The <tt><tm>association</em>=</tt> method assigns an associated object to this object. Behind the scenes, this means extracting the primary key from the associate object and setting this object's foreign key to the same value.
+The <tt><em>association</em>=</tt> method assigns an associated object to this object. Behind the scenes, this means extracting the primary key from the associate object and setting this object's foreign key to the same value.
<ruby>
@order.customer = @customer
@@ -695,7 +695,7 @@ TIP: In any case, Rails will not create foreign key columns for you. You need to
h6. :include
-You can use the :include option to specify second-order associations that should be eager-loaded when this association is used. For example, consider these models:
+You can use the +:include+ option to specify second-order associations that should be eager-loaded when this association is used. For example, consider these models:
<ruby>
class LineItem < ActiveRecord::Base
@@ -733,7 +733,7 @@ NOTE: There's no need to use +:include+ for immediate associations - that is, if
h6. :polymorphic
-Passing +true+ to the +:polymorphic+ option indicates that this is a polymorphic association. Polymorphic associations were discussed in detail earlier in this guide.
+Passing +true+ to the +:polymorphic+ option indicates that this is a polymorphic association. Polymorphic associations were discussed in detail <a href="#polymorphic-associations">earlier in this guide</a>.
h6. :readonly
@@ -859,7 +859,7 @@ The +has_one+ association supports these options:
h6. :as
-Setting the +:as+ option indicates that this is a polymorphic association. Polymorphic associations are discussed in detail later in this guide.
+Setting the +:as+ option indicates that this is a polymorphic association. Polymorphic associations were discussed in detail <a href="#polymorphic-associations">earlier in this guide</a>.
h6. :autosave
@@ -903,7 +903,7 @@ TIP: In any case, Rails will not create foreign key columns for you. You need to
h6. :include
-You can use the :include option to specify second-order associations that should be eager-loaded when this association is used. For example, consider these models:
+You can use the +:include+ option to specify second-order associations that should be eager-loaded when this association is used. For example, consider these models:
<ruby>
class Supplier < ActiveRecord::Base
@@ -963,7 +963,7 @@ The +:source_type+ option specifies the source association type for a +has_one :
h6. :through
-The +:through+ option specifies a join model through which to perform the query. +has_one :through+ associations are discussed in detail later in this guide.
+The +:through+ option specifies a join model through which to perform the query. +has_one :through+ associations were discussed in detail <a href="#thehas-onethrough-association">earlier in this guide</a>.
h6. :validate
@@ -1056,7 +1056,7 @@ WARNING: Objects will be in addition destroyed if they're associated with +:depe
h6. <em>collection</em>=objects
-The <tt><em>collection</em></tt>= method makes the collection contain only the supplied objects, by adding and deleting as appropriate.
+The <tt><em>collection</em>=</tt> method makes the collection contain only the supplied objects, by adding and deleting as appropriate.
h6. <em>collection_singular</em>_ids
@@ -1159,7 +1159,7 @@ The +has_many+ association supports these options:
h6. :as
-Setting the +:as+ option indicates that this is a polymorphic association, as discussed earlier in this guide.
+Setting the +:as+ option indicates that this is a polymorphic association, as discussed <a href="#polymorphic-associations">earlier in this guide</a>.
h6. :autosave
@@ -1211,7 +1211,7 @@ NOTE: This option is ignored when you use the +:through+ option on the associati
h6. :extend
-The +:extend+ option specifies a named module to extend the association proxy. Association extensions are discussed in detail later in this guide.
+The +:extend+ option specifies a named module to extend the association proxy. Association extensions are discussed in detail <a href="#association-extensions">later in this guide</a>.
h6. :finder_sql
@@ -1241,7 +1241,7 @@ end
h6. :include
-You can use the :include option to specify second-order associations that should be eager-loaded when this association is used. For example, consider these models:
+You can use the +:include+ option to specify second-order associations that should be eager-loaded when this association is used. For example, consider these models:
<ruby>
class Customer < ActiveRecord::Base
@@ -1324,7 +1324,7 @@ The +:source_type+ option specifies the source association type for a +has_many
h6. :through
-The +:through+ option specifies a join model through which to perform the query. +has_many :through+ associations provide a way to implement many-to-many relationships, as discussed earlier in this guide.
+The +:through+ option specifies a join model through which to perform the query. +has_many :through+ associations provide a way to implement many-to-many relationships, as discussed <a href="#thehas-manythrough-association">earlier in this guide</a>.
h6. :uniq
@@ -1366,7 +1366,7 @@ When you declare a +has_and_belongs_to_many+ association, the declaring class au
* <tt><em>collection</em>.build(attributes = {})</tt>
* <tt><em>collection</em>.create(attributes = {})</tt>
-In all of these methods, <tt><em>collection</em></tt> is replaced with the symbol passed as the first argument to +has_many+, and <tt><em>collection_singular</em></tt> is replaced with the singularized version of that symbol.. For example, given the declaration:
+In all of these methods, <tt><em>collection</em></tt> is replaced with the symbol passed as the first argument to +has_and_belongs_to_many+, and <tt><em>collection_singular</em></tt> is replaced with the singularized version of that symbol.. For example, given the declaration:
<ruby>
class Part < ActiveRecord::Base
@@ -1443,7 +1443,7 @@ The <tt><em>collection_singular</em>_ids=</tt> method makes the collection conta
h6. <em>collection</em>.clear
-The <tt><em>collection</em>.clear</tt> method removes every object from the collection by deleting the rows from the joining tableassociation. This does not destroy the associated objects.
+The <tt><em>collection</em>.clear</tt> method removes every object from the collection by deleting the rows from the joining table. This does not destroy the associated objects.
h6. <em>collection</em>.empty?
@@ -1487,7 +1487,7 @@ The <tt><em>collection</em>.build</tt> method returns a new object of the associ
h6. <em>collection</em>.create(attributes = {})
-The <tt><em>collection</em>.create</tt> method returns a new object of the associated type. This objects will be instantiated from the passed attributes, the link through the join table will be created, and the associated object _will_ be saved (assuming that it passes any validations).
+The <tt><em>collection</em>.create</tt> method returns a new object of the associated type. This object will be instantiated from the passed attributes, the link through the join table will be created, and the associated object _will_ be saved (assuming that it passes any validations).
<ruby>
@assembly = @part.assemblies.create(
@@ -1496,7 +1496,7 @@ The <tt><em>collection</em>.create</tt> method returns a new object of the assoc
h5. Options for has_and_belongs_to_many
-In many situations, you can use the default behavior for +has_and_belongs_to_many+ without any customization. But you can alter that behavior in a number of ways. This section cover the options that you can pass when you create a +has_and_belongs_to_many+ association. For example, an association with several options might look like this:
+In many situations, you can use the default behavior for +has_and_belongs_to_many+ without any customization. But you can alter that behavior in a number of ways. This section covers the options that you can pass when you create a +has_and_belongs_to_many+ association. For example, an association with several options might look like this:
<ruby>
class Parts < ActiveRecord::Base
@@ -1590,7 +1590,7 @@ Normally Rails automatically generates the proper SQL to remove links between th
h6. :extend
-The +:extend+ option specifies a named module to extend the association proxy. Association extensions are discussed in detail later in this guide.
+The +:extend+ option specifies a named module to extend the association proxy. Association extensions are discussed in detail <a href="#association-extensions">later in this guide</a>.
h6. :finder_sql
@@ -1620,7 +1620,7 @@ end
h6. :include
-You can use the :include option to specify second-order associations that should be eager-loaded when this association is used.
+You can use the +:include+ option to specify second-order associations that should be eager-loaded when this association is used.
h6. :insert_sql
@@ -1637,7 +1637,7 @@ The +:limit+ option lets you restrict the total number of objects that will be f
<ruby>
class Parts < ActiveRecord::Base
has_and_belongs_to_many :assemblies, :order => "created_at DESC",
- :limit => 50
+ :limit => 50
end
</ruby>
diff --git a/railties/guides/source/caching_with_rails.textile b/railties/guides/source/caching_with_rails.textile
index 940466ecb3..9736be8443 100644
--- a/railties/guides/source/caching_with_rails.textile
+++ b/railties/guides/source/caching_with_rails.textile
@@ -55,13 +55,13 @@ want to put other static html in /public, but changing this will require web
server reconfiguration to let the web server know where to serve the cached
files from.
-The Page Caching mechanism will automatically add a +.html+ exxtension to
+The Page Caching mechanism will automatically add a +.html+ extension to
requests for pages that do not have an extension to make it easy for the
webserver to find those pages and this can be configured by changing the
configuration setting +config.action_controller.page_cache_extension+.
In order to expire this page when a new product is added we could extend our
-example controler like this:
+example controller like this:
<ruby>
class ProductsController < ActionController
@@ -80,7 +80,7 @@ end
If you want a more complicated expiration scheme, you can use cache sweepers
to expire cached objects when things change. This is covered in the section on Sweepers.
-[More: caching paginated results? more examples? Walk-through of page caching?]
+Note: Page caching ignores all parameters, so /products/list?page=1 will be written out to the filesystem as /products/list.html and if someone requests /products/list?page=2, they will be returned the same result as page=1, so be careful when page caching GET parameters in the URL!
h4. Action Caching
@@ -122,11 +122,14 @@ layout so that dynamic information in the layout such as logged in user info
or the number of items in the cart can be left uncached. This feature is
available as of Rails 2.2.
+You can modify the default action cache path by passing a +:cache_path+ option.
+This will be passed directly to ActionCachePath.path_for. This is handy for
+actions with multiple possible routes that should be cached differently. If
+a block is given, it is called with the current controller instance.
-[More: more examples? Walk-through of Action Caching from request to response?
- Description of Rake tasks to clear cached files? Show example of
- subdomain caching? Talk about :cache_path, :if and assing blocks/Procs
- to expire_action?]
+Finally, if you are using memcached, you can also pass +:expires_in+. In fact,
+all parameters not used by caches_action are sent to the underlying cache
+store.
h4. Fragment Caching
@@ -190,8 +193,6 @@ the key and can be expired the same way:
expire_fragment(:key => ['all_available_products', @latest_product.created_at].join(':'))
</ruby>
-[More: more examples? description of fragment keys and expiration, etc? pagination?]
-
h4. Sweepers
Cache sweeping is a mechanism which allows you to get around having a ton of
@@ -259,8 +260,6 @@ class ProductsController < ActionController
end
</ruby>
-[More: more examples? better sweepers?]
-
h4. SQL Caching
Query caching is a Rails feature that caches the result set returned by each
@@ -307,51 +306,111 @@ that action and thus persist only for the duration of the action.
h4. Cache stores
-Rails provides different stores for the cached data for action and fragment
-caches. Page caches are always stored on disk.
+Rails (as of 2.1) provides different stores for the cached data for action and
+fragment caches. Page caches are always stored on disk.
+
+Rails 2.1 and above provide ActiveSupport::Cache::Store which can be used to
+cache strings. Some cache store implementations, like MemoryStore, are able to
+cache arbitrary Ruby objects, but don't count on every cache store to be able
+to do that.
+
+The default cache stores provided include:
+
+1) ActiveSupport::Cache::MemoryStore: A cache store implementation which stores
+everything into memory in the same process. If you're running multiple Ruby on
+Rails server processes (which is the case if you're using mongrel_cluster or
+Phusion Passenger), then this means that your Rails server process instances
+won't be able to share cache data with each other. If your application never
+performs manual cache item expiry (e.g. when you‘re using generational cache
+keys), then using +MemoryStore+ is ok. Otherwise, consider carefully whether you
+should be using this cache store.
-The cache stores provided include:
++MemoryStore+ is not only able to store strings, but also arbitrary Ruby objects.
-1) Memory store: Cached data is stored in the memory allocated to the Rails
- process, which is fine for WEBrick and for FCGI (if you
- don't care that each FCGI process holds its own fragment
- store). It's not suitable for CGI as the process is thrown
- away at the end of each request. It can potentially also
- take up a lot of memory since each process keeps all the
- caches in memory.
++MemoryStore+ is not thread-safe. Use +SynchronizedMemoryStore+ instead if you
+need thread-safety.
+
<ruby>
ActionController::Base.cache_store = :memory_store
</ruby>
-2) File store: Cached data is stored on the disk, this is the default store
- and the default path for this store is: /tmp/cache. Works
- well for all types of environments and allows all processes
- running from the same application directory to access the
- cached content.
+2) ActiveSupport::Cache::FileStore: Cached data is stored on the disk, this is
+the default store and the default path for this store is: /tmp/cache. Works
+well for all types of environments and allows all processes running from the
+same application directory to access the cached content. If /tmp/cache does not
+exist, the default store becomes MemoryStore.
<ruby>
ActionController::Base.cache_store = :file_store, "/path/to/cache/directory"
</ruby>
-3) DRb store: Cached data is stored in a separate shared DRb process that all
- servers communicate with. This works for all environments and
- only keeps one cache around for all processes, but requires
- that you run and manage a separate DRb process.
+3) ActiveSupport::Cache::DRbStore: Cached data is stored in a separate shared
+DRb process that all servers communicate with. This works for all environments
+and only keeps one cache around for all processes, but requires that you run
+and manage a separate DRb process.
+
<ruby>
ActionController::Base.cache_store = :drb_store, "druby://localhost:9192"
</ruby>
4) MemCached store: Works like DRbStore, but uses Danga's MemCache instead.
- Rails uses the bundled memcached-client gem by default.
+Rails uses the bundled memcached-client gem by default. This is currently the
+most popular cache store for production websites.
+
+Special features:
+ * Clustering and load balancing. One can specify multiple memcached servers,
+ and MemCacheStore will load balance between all available servers. If a
+ server goes down, then MemCacheStore will ignore it until it goes back
+ online.
+ * Time-based expiry support. See write and the +:expires_in+ option.
+ * Per-request in memory cache for all communication with the MemCache server(s).
+
+It also accepts a hash of additional options:
+
+ * +:namespace+- specifies a string that will automatically be prepended to keys when accessing the memcached store.
+ * +:readonly+- a boolean value that when set to true will make the store read-only, with an error raised on any attempt to write.
+ * +:multithread+ - a boolean value that adds thread safety to read/write operations - it is unlikely you'll need to use this option as the Rails threadsafe! method offers the same functionality.
+
+The read and write methods of the MemCacheStore accept an options hash too.
+When reading you can specify +:raw => true+ to prevent the object being
+marshaled
+(by default this is false which means the raw value in the cache is passed to
+Marshal.load before being returned to you.)
+
+When writing to the cache it is also possible to specify +:raw => true+ means
+the value is not passed to Marshal.dump before being stored in the cache (by
+default this is false).
+
+The write method also accepts an +:unless_exist+ flag which determines whether
+the memcached add (when true) or set (when false) method is used to store the
+item in the cache and an +:expires_in+ option that specifies the time-to-live
+for the cached item in seconds.
+
<ruby>
ActionController::Base.cache_store = :mem_cache_store, "localhost"
</ruby>
-5) Custom store: You can define your own cache store (new in Rails 2.1)
+5) ActiveSupport::Cache::SynchronizedMemoryStore: Like ActiveSupport::Cache::MemoryStore but thread-safe.
+
+
+<ruby>
+ActionController::Base.cache_store = :synchronized_memory_store
+</ruby>
+
+6) ActiveSupport::Cache::CompressedMemCacheStore: Works just like the regular
+MemCacheStore but uses GZip to decompress/compress on read/write.
+
+
+<ruby>
+ActionController::Base.cache_store = :compressed_mem_cache_store, "localhost"
+</ruby>
+
+7) Custom store: You can define your own cache store (new in Rails 2.1)
+
<ruby>
ActionController::Base.cache_store = MyOwnStore.new("parameter")
@@ -361,10 +420,22 @@ ActionController::Base.cache_store = MyOwnStore.new("parameter")
ActionController::Base.cache_store in your Rails::Initializer.run block in
environment.rb+
+In addition to all of this, Rails also adds the ActiveRecord::Base#cache_key
+method that generates a key using the class name, id and updated_at timestamp
+(if available).
+
+An example:
+
+<ruby>
+Rails.cache.read("city") # => nil
+Rails.cache.write("city", "Duckburgh")
+Rails.cache.read("city") # => "Duckburgh"
+</ruby>
+
h3. Conditional GET support
Conditional GETs are a facility of the HTTP spec that provide a way for web
-servers to tell browsers that the response to a GET request hasn’t changed
+servers to tell browsers that the response to a GET request hasn't changed
since the last request and can be safely pulled from the browser cache.
They work by using the HTTP_IF_NONE_MATCH and HTTP_IF_MODIFIED_SINCE headers to
@@ -374,7 +445,7 @@ identifier (etag) or last modified since timestamp matches the server’s versio
then the server only needs to send back an empty response with a not modified
status.
-It is the server’s (i.e. our) responsibility to look for a last modified
+It is the server's (i.e. our) responsibility to look for a last modified
timestamp and the if-none-match header and determine whether or not to send
back the full response. With conditional-get support in rails this is a pretty
easy task:
@@ -400,8 +471,8 @@ class ProductsController < ApplicationController
end
</ruby>
-If you don’t have any special response processing and are using the default
-rendering mechanism (i.e. you’re not using respond_to or calling render
+If you don't have any special response processing and are using the default
+rendering mechanism (i.e. you're not using respond_to or calling render
yourself) then you’ve got an easy helper in fresh_when:
<ruby>
@@ -421,9 +492,24 @@ h3. Advanced Caching
Along with the built-in mechanisms outlined above, a number of excellent
plugins exist to help with finer grained control over caching. These include
-Chris Wanstrath's excellent cache_fu plugin (more info here:
-http://errtheblog.com/posts/57-kickin-ass-w-cachefu) and Evan Weaver's
-interlock plugin (more info here:
-http://blog.evanweaver.com/articles/2007/12/13/better-rails-caching/). Both
+Chris Wanstrath's excellent cache_fu plugin (more info "here": http://errtheblog.com/posts/57-kickin-ass-w-cachefu) and Evan Weaver's
+interlock plugin (more info "here": http://blog.evanweaver.com/articles/2007/12/13/better-rails-caching/). Both
of these plugins play nice with memcached and are a must-see for anyone
seriously considering optimizing their caching needs.
+
+Also the new "Cache money":http://github.com/nkallen/cache-money/tree/master plugin is supposed to be mad cool.
+
+h3. References
+ * "RailsEnvy, Rails Caching Tutorial, Part 1":http://www.railsenvy.com/2007/2/28/rails-caching-tutorial
+ * "RailsEnvy, Rails Caching Tutorial, Part 1":http://www.railsenvy.com/2007/3/20/ruby-on-rails-caching-tutorial-part-2
+ * "ActiveSupport::Cache documentation":http://api.rubyonrails.org/classes/ActiveSupport/Cache.html
+ * "Rails 2.1 integrated caching tutorial":http://thewebfellas.com/blog/2008/6/9/rails-2-1-now-with-better-integrated-caching
+
+
+h3. Changelog
+"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/10-guide-to-caching
+
+February 22, 2009: Beefed up the section on cache_stores
+December 27, 2008: Typo fixes
+November 23, 2008: Incremental updates with various suggested changes and formatting cleanup
+September 15, 2008: Initial version by Aditya Chadha
diff --git a/railties/guides/source/command_line.textile b/railties/guides/source/command_line.textile
index 0523f33b14..078eefd9df 100644
--- a/railties/guides/source/command_line.textile
+++ b/railties/guides/source/command_line.textile
@@ -83,7 +83,7 @@ Usage: ./script/generate generator [options] [args]
...
Installed Generators
- Builtin: controller, integration_test, mailer, migration, model, observer, performance_test, plugin, resource, scaffold, session_migration
+ Built-in: controller, integration_test, mailer, migration, model, observer, performance_test, plugin, resource, scaffold, session_migration
...
...
@@ -126,10 +126,10 @@ Modules Example:
Ah, the controller generator is expecting parameters in the form of +generate controller ControllerName action1 action2+. Let's make a +Greetings+ controller with an action of *hello*, which will say something nice to us.
<shell>
-$ ./script/generate controller Greeting hello
+$ ./script/generate controller Greetings hello
exists app/controllers/
exists app/helpers/
- create app/views/greeting
+ create app/views/greetings
exists test/functional/
create app/controllers/greetings_controller.rb
create test/functional/greetings_controller_test.rb
@@ -139,10 +139,10 @@ $ ./script/generate controller Greeting hello
Look there! Now what all did this generate? It looks like it made sure a bunch of directories were in our application, and created a controller file, a functional test file, a helper for the view, and a view file.
-Let's check out the controller and modify it a little (in +app/controllers/greeting_controller.rb+):
+Let's check out the controller and modify it a little (in +app/controllers/greetings_controller.rb+):
<ruby>
-class GreetingController < ApplicationController
+class GreetingsController < ApplicationController
def hello
@message = "Hello, how are you today? I am exuberant!"
end
@@ -150,7 +150,7 @@ class GreetingController < ApplicationController
end
</ruby>
-Then the view, to display our nice message (in +app/views/greeting/hello.html.erb+):
+Then the view, to display our nice message (in +app/views/greetings/hello.html.erb+):
<html>
<h1>A Greeting for You!</h1>
@@ -164,6 +164,8 @@ $ ./script/server
=> Booting WEBrick...
</shell>
+WARNING: Make sure that you do not have any "tilde backup" files in +app/views/(controller)+, or else WEBrick will _not_ show the expected output. This seems to be a *bug* in Rails 2.3.0.
+
The URL will be +http://localhost:3000/greetings/hello+. I'll wait for you to be suitably impressed.
INFO: With a normal, plain-old Rails application, your URLs will generally follow the pattern of http://(host)/(controller)/(action), and a URL like http://(host)/(controller) will hit the *index* action of that controller.
@@ -356,8 +358,8 @@ $ rails . --git --database=postgresql
add 'Rakefile'
create README
add 'README'
- create app/controllers/application.rb
-add 'app/controllers/application.rb'
+ create app/controllers/application_controller_.rb
+add 'app/controllers/application_controller_.rb'
create app/helpers/application_helper.rb
...
create log/test.log
@@ -464,7 +466,7 @@ We take whatever args are supplied, save them to an instance variable, and liter
* Check there's a *public* directory. You bet there is.
* Run the ERb template called "tutorial.erb".
* Save it into "RAILS_ROOT/public/tutorial.txt".
-* Pass in the args we saved through the +:assign+ parameter.
+* Pass in the arguments we saved through the +:assign+ parameter.
Next we'll build the template:
@@ -508,4 +510,77 @@ I got assigned some args:
:command=>:create}]
</shell>
-Tada! \ No newline at end of file
+Tada!
+
+h4. Rake is Ruby Make
+
+Rake is a standalone Ruby utility that replaces the Unix utility 'make', and uses a 'Rakefile' and +.rake+ files to build up a list of tasks. In Rails, Rake is used for common administration tasks, especially sophisticated ones that build off of each other.
+
+You can get a list of Rake tasks available to you, which will often depend on your current directory, by typing +rake --tasks+. Each task has a description, and should help you find the thing you need.
+
+<shell>
+ rake --tasks
+(in /home/developer/commandsapp)
+rake db:abort_if_pending_migrations # Raises an error if there are pending migrations
+rake db:charset # Retrieves the charset for the current environment's database
+rake db:collation # Retrieves the collation for the current environment's database
+rake db:create # Create the database defined in config/database.yml for the current RAILS_ENV
+...
+...
+rake tmp:pids:clear # Clears all files in tmp/pids
+rake tmp:sessions:clear # Clears all files in tmp/sessions
+rake tmp:sockets:clear # Clears all files in tmp/sockets
+</shell>
+
+Let's take a look at some of these 80 or so rake tasks.
+
+h5. db: Database
+
+The most common tasks of the +db:+ Rake namespace are +migrate+ and +create+, and it will pay off to try out all of the migration rake tasks (+up+, +down+, +redo+, +reset+). +rake db:version+ is useful when troubleshooting, telling you the current version of the database.
+
+h5. doc: Documentation
+
+If you want to strip out or rebuild any of the Rails documentation (including this guide!), the +doc:+ namespace has the tools. Stripping documentation is mainly useful for slimming your codebase, like if you're writing a Rails application for an embedded platform.
+
+h5. gems: Ruby gems
+
+You can specify which gems your application uses, and +rake gems:install+ will install them for you. Look at your environment.rb to learn how with the *config.gem* directive.
+
+NOTE: +gems:unpack+ will unpack, that is internalize your application's Gem dependencies by copying the Gem code into your vendor/gems directory. By doing this you increase your codebase size, but simplify installation on new hosts by eliminating the need to run +rake gems:install+, or finding and installing the gems your application uses.
+
+h5. notes: Code note enumeration
+
+These tasks will search through your code for commented lines beginning with "FIXME", "OPTIMIZE", "TODO", or any custom annotation (like XXX) and show you them.
+
+h5. rails: Rails-specific tasks
+
+In addition to the +gems:unpack+ task above, you can also unpack the Rails backend specific gems into vendor/rails by calling +rake rails:freeze:gems+, to unpack the version of Rails you are currently using, or +rake rails:freeze:edge+ to unpack the most recent (cutting, bleeding edge) version.
+
+When you have frozen the Rails gems, Rails will prefer to use the code in vendor/rails instead of the system Rails gems. You can "thaw" by running +rake rails:unfreeze+.
+
+After upgrading Rails, it is useful to run +rails:update+, which will update your config and scripts directories, and upgrade your Rails-specific javascript (like Scriptaculous).
+
+h5. test: Rails tests
+
+INFO: A good description of unit testing in Rails is given in "A Guide to Testing Rails Applications":testing.html
+
+Rails comes with a test suite called Test::Unit. It is through the use of tests that Rails itself is so stable, and the slew of people working on Rails can prove that everything works as it should.
+
+The +test:+ namespace helps in running the different tests you will (hopefully!) write.
+
+h5. time: Timezones
+
+You can list all the timezones Rails knows about with +rake time:zones:all+, which is useful just in day-to-day life.
+
+h5. tmp: Temporary files
+
+The tmp directory is, like in the *nix /tmp directory, the holding place for temporary files like sessions (if you're using a file store for files), process id files, and cached actions. The +tmp:+ namespace tasks will help you clear them if you need to if they've become overgrown, or create them in case of an +rm -rf *+ gone awry.
+
+h5. Miscellaneous tasks
+
+ +rake stats+ is great for looking at statistics on your code, displaying things like KLOCs (thousands of lines of code) and your code to test ratio.
+
+ +rake secret+ will give you a psuedo-random key to use for your session secret.
+
+ +rake routes+ will list all of your defined routes, which is useful for tracking down routing problems in your app, or giving you a good overview of the URLs in an app you're trying to get familiar with.
+
diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile
index c2b28ae6e0..d97ed56eaf 100644
--- a/railties/guides/source/configuring.textile
+++ b/railties/guides/source/configuring.textile
@@ -7,7 +7,6 @@ This guide covers the configuration and initialization features available to Rai
endprologue.
-NOTE: The first edition of this Guide was written from the Rails 2.3 source code. While the information it contains is broadly applicable to Rails 2.2, backwards compatibility is not guaranteed.
h3. Locations for Initialization Code
@@ -81,7 +80,9 @@ h4. Configuring Action Controller
* +consider_all_requests_local+ is generally set to +true+ during development and +false+ during production; if it is set to +true+, then any error will cause detailed debugging information to be dumped in the HTTP response. For finer-grained control, set this to +false+ and implement +local_request?+ to specify which requests should provide debugging information on errors.
-* +allow_concurrency+ should be set to +true+ to allow concurrent (threadsafe) action processing. Set to +false+ by default.
+* +allow_concurrency+ should be set to +true+ to allow concurrent (threadsafe) action processing. Set to +false+ by default. You probably don't want to call this one directly, though, because a series of other adjustments need to be made for threadsafe mode to work properly. Instead, you should simply call +config.threadsafe!+ inside your +production.rb+ file, which makes all the necessary adjustments.
+
+WARNING: Threadsafe operation in incompatible with the normal workings of development mode Rails. In particular, automatic dependency loading and class reloading are automatically disabled when you call +config.threadsafe!+.
* +param_parsers+ provides an array of handlers that can extract information from incoming HTTP requests and add it to the +params+ hash. By default, parsers for multipart forms, URL-encoded forms, XML, and JSON are active.
@@ -125,7 +126,7 @@ There are only a few configuration options for Action View, starting with four o
* +warn_cache_misses+ tells Rails to display a warning whenever an action results in a cache miss on your view paths. The default is +false+.
-* +field_error_proc+ provides an HTML generator for displaying errors that come from Active Record. The default is +Proc.new{ |html_tag, instance| "<div class=\"fieldWithErrors\">#{html_tag}</div>" }+
+* +field_error_proc+ provides an HTML generator for displaying errors that come from Active Record. The default is <tt>Proc.new{ |html_tag, instance| "<div class=\"fieldWithErrors\">#{html_tag}</div>" }</tt>
* +default_form_builder+ tells Rails which form builder to use by default. The default is +ActionView::Helpers::FormBuilder+.
@@ -166,8 +167,7 @@ There are a number of settings available on +ActionMailer::Base+:
* +default_mime_version+ is the default MIME version for the message. It defaults to +1.0+.
* +default_implicit_parts_order+ - When a message is built implicitly (i.e. multiple parts are assembled from templates
-which specify the content type in their filenames) this variable controls how the parts are ordered. Defaults to
-* +["text/html", "text/enriched", "text/plain"]+. Items that appear first in the array have higher priority in the mail client
+which specify the content type in their filenames) this variable controls how the parts are ordered. Defaults to +["text/html", "text/enriched", "text/plain"]+. Items that appear first in the array have higher priority in the mail client
and appear last in the mime encoded message.
h4. Configuring Active Resource
diff --git a/railties/guides/source/contribute.textile b/railties/guides/source/contribute.textile
new file mode 100644
index 0000000000..48f1a51d02
--- /dev/null
+++ b/railties/guides/source/contribute.textile
@@ -0,0 +1,77 @@
+h2. Contribute to the Rails Guides
+
+Rails Guides aim to improve the Rails documentation and to make the barrier to entry as low as possible. A reasonably experienced developer should be able to use the Guides to come up to speed on Rails quickly. You can track the overall effort at the "Rails Guides Lighthouse":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets. Our sponsors have contributed prizes for those who write an entire guide, but there are many other ways to contribute.
+
+endprologue.
+
+h3. How to Contribute?
+
+* We have an open commit policy: anyone is welcome to contribute, but you'll need to ask for commit access.
+* PM lifo at "GitHub":http://github.om asking for "docrails":http://github.com/lifo/docrails commit access.
+* Guides are written in Textile, and reside at railties/guides/source in the docrails project.
+* All images are in the railties/guides/images directory.
+* Sample format : "Active Record Associations":http://github.com/lifo/docrails/blob/3e56a3832415476fdd1cb963980d0ae390ac1ed3/railties/guides/source/association_basics.textile
+* Sample output : "Active Record Associations":http://guides.rails.info/association_basics.html
+* You can build the Guides during testing by running railties/guides/rails_guides.rb.
+
+h3. What to Contribute?
+
+* We need authors, editors, proofreaders, and translators. Adding a single paragraph of quality content to a guide is a good way to get started.
+* The easiest way to start is by improving an existing guide:
+** Improve the structure to make it more coherent
+** Add missing information
+** Correct any factual errors
+** Fix typos or improve style
+** Bring it up to date with the latest Edge Rails
+* We're also open to suggestions for entire new guides
+** Contact lifo or mikeg1a in IRC or via "email":mailto:MikeG1@larkfarm.com to get your idea approved
+** If you're the main author on a significant guide, you're eligible for the prizes
+
+h3. How to Commit
+
+* If you have a small change or typo fix, just ask lifo for commit access and commit it to the project.
+* If your change is more significant, post a patch or a message on Lighthouse, and commit after you get a +1 from lifo or mikeg1a.
+* If the guide is already marked done, you should get a +1 before pushing your changes.
+* Put [#&lt;ticket number&gt;] in your commit message to enable GitHub/Lighthouse integration.
+
+h3. Prizes
+
+For each completed guide, the lead contributor will receive all of the following prizes:
+
+* $200 from Caboose Rails Documentation Project.
+* 1 year of GitHub Micro account worth $84.
+* 1 year of RPM Basic (Production performance management) for up to 10 hosts worth 12 months x $40 per host x $10 hosts = $4800. And also, savings of $45 per host per month over list price to upgrade to advanced product.
+
+h3. Rules
+
+* Guides are licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 License.
+* If you're not sure whether a guide is actively being worked on, stop by IRC and ask.
+* If the same guide writer wants to write multiple guides, that's ideally the situation we'd love to be in! However, that guide writer will only receive the cash prize for all the subsequent guides (and not the GitHub or RPM prizes).
+* Our review team will have the final say on whether the guide is complete and of good enough quality.
+
+h3. Reviewers
+
+These are the main reviewers and editors for the guides:
+
+* Hongli Lai
+* Mike Gunderloy
+* Pratik Naik
+* Xavier Noria
+
+All authors should read and follow the "Rails Guides Conventions":http://wiki.github.com/lifo/docrails/rails-guides-conventions and the "Rails API Documentation Conventions":http://wiki.github.com/lifo/docrails/rails-api-documentation-conventions.
+
+h3. Translations
+
+The translation effort for the Rails Guides is just getting underway. We know about projects to translate the Guides into Spanish, Portuguese, Polish, and French. For more details or to get involved see the "Translating Rails Guides":http://wiki.github.com/lifo/docrails/translating-rails-guides page.
+
+h3. IRC Channel
+
+==#docrails @ irc.freenode.net==
+
+h3. Contact
+
+If you have any questions or need any clarification, feel free to contact:
+
+* IRC : lifo, mikeg1a, fxn, or FooBarWidget in #docrails
+* Email : pratiknaik aT gmail
+
diff --git a/railties/guides/source/credits.erb.textile b/railties/guides/source/credits.erb.textile
index 94c9da3f24..441ea60ffe 100644
--- a/railties/guides/source/credits.erb.textile
+++ b/railties/guides/source/credits.erb.textile
@@ -9,18 +9,6 @@ p. We'd like to thank the following people for their tireless contributions to t
Frederick Cheung is Chief Wizard at Texperts where he has been using Rails since 2006. He is based in Cambridge (UK) and when not consuming fine ales he blogs at "spacevatican.org":http://www.spacevatican.org.
<% end %>
-<% author('Mike Gunderloy', 'mgunderloy') do %>
- Mike Gunderloy is an independent consultant who brings 25 years of experience in a variety of languages to bear on his current work with Rails. His near-daily links and other blogging can be found at "A Fresh Cup":http://afreshcup.com.
-<% end %>
-
-<% author('Emilio Tagua', 'miloops') do %>
- Emilio Tagua -- a.k.a. miloops -- is an Argentinian entrepreneur, developer, open source contributor and Rails evangelist. Cofounder of "Eventioz":http://www.eventioz.com. He has been using Rails since 2006 and contributing since early 2008. Can be found at gmail, twitter, freenode, everywhere as miloops.
-<% end %>
-
-<% author('Heiko Webers', 'hawe') do %>
- Heiko Webers is the founder of "bauland42":http://www.bauland42.de, a German web application security consulting and development company focused on Ruby on Rails. He blogs at http://www.rorsecurity.info. After 10 years of desktop application development, Heiko has rarely looked back.
-<% end %>
-
<% author('Tore Darell', 'toretore') do %>
Tore Darell is an independent developer based in Menton, France who specialises in cruft-free web applications using Ruby, Rails and unobtrusive JavaScript. His home on the internet is his blog "Sneaky Abstractions":http://tore.darell.no.
<% end %>
@@ -29,10 +17,23 @@ p. We'd like to thank the following people for their tireless contributions to t
Jeff Dean is a software engineer with "Pivotal Labs":http://pivotallabs.com.
<% end %>
+<% author('Mike Gunderloy', 'mgunderloy') do %>
+ Mike Gunderloy is a consultant with "ActionRails":http://www.actionrails.com and also a member of the "Rails activism team":http://rubyonrails.org/activists . He brings 25 years of experience in a variety of languages to bear on his current work with Rails. His near-daily links and other blogging can be found at "A Fresh Cup":http://afreshcup.com and he "twitters":http://twitter.com/MikeG1 too much.
+<% end %>
+
<% author('Cássio Marques', 'cmarques') do %>
- Cássio Marques is a Brazilian software developer working with different programming languages such as Ruby, JavaScript, CPP and Java, as an independent consultant. He blogs at http://cassiomarques.wordpress.com, which is mainly written in portuguese, but will soon get a new section for posts with english translation.
+ Cássio Marques is a Brazilian software developer working with different programming languages such as Ruby, JavaScript, CPP and Java, as an independent consultant. He blogs at "/* CODIFICANDO */":http://cassiomarques.wordpress.com, which is mainly written in Portuguese, but will soon get a new section for posts with English translation.
<% end %>
<% author('Pratik Naik', 'lifo') do %>
- Pratik Naik is an independent Ruby on Rails consultant and also a member of the "Rails core team":http://rubyonrails.com/core. He blogs semi-regularly at "has_many :bugs, :through => :rails":http://m.onkey.org and has an active "twitter account":http://twitter.com/lifo.
+ Pratik Naik is a Ruby on Rails consultant with "ActionRails":http://www.actionrails.com and also a member of the "Rails core team":http://rubyonrails.com/core. He maintains a blog at "has_many :bugs, :through => :rails":http://m.onkey.org and has an active "twitter account":http://twitter.com/lifo.
+
+<% author('Emilio Tagua', 'miloops') do %>
+ Emilio Tagua -- a.k.a. miloops -- is an Argentinian entrepreneur, developer, open source contributor and Rails evangelist. Cofounder of "Eventioz":http://www.eventioz.com. He has been using Rails since 2006 and contributing since early 2008. Can be found at gmail, twitter, freenode, everywhere as miloops.
+<% end %>
+
+<% author('Heiko Webers', 'hawe') do %>
+ Heiko Webers is the founder of "bauland42":http://www.bauland42.de, a German web application security consulting and development company focused on Ruby on Rails. He blogs at the "Ruby on Rails Security Project":http://www.rorsecurity.info. After 10 years of desktop application development, Heiko has rarely looked back.
+<% end %>
+
<% end %>
diff --git a/railties/guides/source/debugging_rails_applications.textile b/railties/guides/source/debugging_rails_applications.textile
index dd89bef027..b1d6db2e55 100644
--- a/railties/guides/source/debugging_rails_applications.textile
+++ b/railties/guides/source/debugging_rails_applications.textile
@@ -627,7 +627,7 @@ To install it run:
sudo gem install bleak_house
</shell>
-Then setup you application for profiling. Then add the following at the bottom of config/environment.rb:
+Then setup your application for profiling. Then add the following at the bottom of config/environment.rb:
<ruby>
require 'bleak_house' if ENV['BLEAK_HOUSE']
diff --git a/railties/guides/source/form_helpers.textile b/railties/guides/source/form_helpers.textile
index 4e61bdcd26..41d8fba3dc 100644
--- a/railties/guides/source/form_helpers.textile
+++ b/railties/guides/source/form_helpers.textile
@@ -52,9 +52,9 @@ Probably the most minimal form often seen on the web is a search form with a sin
# a text input element, and
# a submit element.
-IMPORTANT: Always use "GET" as the method for search forms. This allows users are able to bookmark a specific search and get back to it, more generally Rails encourages you to use the right HTTP verb for an action.
+IMPORTANT: Always use "GET" as the method for search forms. This allows users to bookmark a specific search and get back to it. More generally Rails encourages you to use the right HTTP verb for an action.
-To create this form you will use +form_tag+, +label_tag+, +text_field_tag+ and +submit_tag+, respectively.
+To create this form you will use +form_tag+, +label_tag+, +text_field_tag+, and +submit_tag+, respectively.
A basic search form
@@ -86,18 +86,14 @@ h4. Multiple hashes in form helper calls
By now you've seen that the +form_tag+ helper accepts 2 arguments: the path for the action and an options hash. This hash specifies the method of form submission and HTML options such as the form element's class.
-As with the +link_to+ helper, the path argument doesn't have to be given a string. It can be a hash of URL parameters that Rails' routing mechanism will turn into a valid URL. Still, you cannot simply write this:
-
-A bad way to pass multiple hashes as method arguments:
+As with the +link_to+ helper, the path argument doesn't have to be given a string. It can be a hash of URL parameters that Rails' routing mechanism will turn into a valid URL. However, this is a bad way to pass multiple hashes as method arguments:
<ruby>
form_tag(:controller => "people", :action => "search", :method => "get", :class => "nifty_form")
# => <form action="/people/search?method=get&class=nifty_form" method="post">
</ruby>
-Here you wanted to pass two hashes, but the Ruby interpreter sees only one hash, so Rails will construct a URL with extraneous parameters. The solution is to delimit the first hash (or both hashes) with curly brackets:
-
-The correct way of passing multiple hashes as arguments:
+Here you wanted to pass two hashes, but the Ruby interpreter sees only one hash, so Rails will construct a URL with extraneous parameters. The correct way of passing multiple hashes as arguments is to delimit the first hash (or both hashes) with curly brackets:
<ruby>
form_tag({:controller => "people", :action => "search"}, :method => "get", :class => "nifty_form")
@@ -110,7 +106,7 @@ WARNING: Do not delimit the second hash without doing so with the first hash, ot
h4. Helpers for generating form elements
-Rails provides a series of helpers for generating form elements such as checkboxes, text fields, radio buttons and so. These basic helpers, with names ending in _tag such as +text_field_tag+, +check_box_tag+ just generate a single +&lt;input&gt;+ element. The first parameter to these is always the name of the input. In the controller, this name will be the key in the +params+ hash used to get the value entered by the user. For example if the form contains
+Rails provides a series of helpers for generating form elements such as checkboxes, text fields, radio buttons, and so on. These basic helpers, with names ending in <notextile>_tag</notextile> such as +text_field_tag+, +check_box_tag+, etc., generate just a single +&lt;input&gt;+ element. The first parameter to these is always the name of the input. In the controller this name will be the key in the +params+ hash used to get the value entered by the user. For example, if the form contains
<erb>
<%= text_field_tag(:query) %>
@@ -122,7 +118,7 @@ then the controller code should use
params[:query]
</ruby>
-to retrieve the value entered by the user. When naming inputs be aware that Rails uses certain conventions that control whether values are at the top level of the +params+ hash, inside an array or a nested hash and so on. You can read more about them in the parameter_names section. For details on the precise usage of these helpers, please refer to the "API documentation":http://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html.
+to retrieve the value entered by the user. When naming inputs, be aware that Rails uses certain conventions that control whether values are at the top level of the +params+ hash, inside an array or a nested hash and so on. You can read more about them in the parameter_names section. For details on the precise usage of these helpers, please refer to the "API documentation":http://api.rubyonrails.org/classes/ActionView/Helpers/FormTagHelper.html.
h5. Checkboxes
@@ -146,7 +142,7 @@ The second parameter to +check_box_tag+ is the value of the input. This is the v
h5. Radio buttons
-Radio buttons, while similar to checkboxes, are controls that specify a set of options in which they are mutually exclusive (user can only pick one):
+Radio buttons, while similar to checkboxes, are controls that specify a set of options in which they are mutually exclusive (i.e. the user can only pick one):
<erb>
<%= radio_button_tag(:age, "child") %>
@@ -213,7 +209,7 @@ Rails provides helpers for displaying the validation errors associated with a mo
h4. Binding a form to an object
-While this is an increase in comfort it is far from perfect. If Person has many attributes to edit then we would be repeating the name of the edited object many times. What we want to do is somehow bind a form to a model object which is exactly what +form_for+ does.
+While this is an increase in comfort it is far from perfect. If Person has many attributes to edit then we would be repeating the name of the edited object many times. What we want to do is somehow bind a form to a model object, which is exactly what +form_for+ does.
Assume we have a controller for dealing with articles:
@@ -240,7 +236,7 @@ articles/new.html.erb:
There are a few things to note here:
# +:article+ is the name of the model and +@article+ is the actual object being edited.
-# There is a single hash of options. Routing options are passed inside +:url+ hash, HTML options are passed in the +:html+ hash.
+# There is a single hash of options. Routing options are passed in the +:url+ hash, HTML options are passed in the +:html+ hash.
# The +form_for+ method yields a *form builder* object (the +f+ variable).
# Methods to create form controls are called *on* the form builder object +f+
@@ -254,7 +250,7 @@ The resulting HTML is:
</form>
</html>
-The name passed to +form_for+ controls the key used in +params+ to access the form's values. Here the name is +article+ and so all the inputs have names of the form +article[attribute_name]+. Accordingly, in the +create+ action +params[:article]+ will be a hash with keys +:title+ and +:body+. You can read more about the significance of input names in the <<parameter_names,parameter names>> section.
+The name passed to +form_for+ controls the key used in +params+ to access the form's values. Here the name is +article+ and so all the inputs have names of the form +article[<em>attribute_name</em>]+. Accordingly, in the +create+ action +params[:article]+ will be a hash with keys +:title+ and +:body+. You can read more about the significance of input names in the parameter_names section.
The helper methods called on the form builder are identical to the model object helpers except that it is not necessary to specify which object is being edited since this is already managed by the form builder.
@@ -302,13 +298,13 @@ form_for(@article)
Notice how the short-style +form_for+ invocation is conveniently the same, regardless of the record being new or existing. Record identification is smart enough to figure out if the record is new by asking +record.new_record?+. It also selects the correct path to submit to and the name based on the class of the object.
-Rails will also automatically set the +class+ and +id+ of the form appropriately: a form creating an article would have +id+ and +class+ +new_article+. If you were editing the article with id 23 the +class+ would be set to +edit_article+ and the id to +edit_article_23+. These attributes will be omitted for brevity in the rest of this guide.
+Rails will also automatically set the +class+ and +id+ of the form appropriately: a form creating an article would have +id+ and +class+ +new_article+. If you were editing the article with id 23, the +class+ would be set to +edit_article+ and the id to +edit_article_23+. These attributes will be omitted for brevity in the rest of this guide.
-WARNING: When you're using STI (single-table inheritance) with your models, you can't rely on record identification on a subclass if only their parent class is declared a resource. You will have to specify the model name, +:url+ and +:method+ explicitly.
+WARNING: When you're using STI (single-table inheritance) with your models, you can't rely on record identification on a subclass if only their parent class is declared a resource. You will have to specify the model name, +:url+, and +:method+ explicitly.
h5. Dealing with namespaces
-If you have created namespaced routes +form_for+ has a nifty shorthand for that too. If your application has an admin namespace then
+If you have created namespaced routes, +form_for+ has a nifty shorthand for that too. If your application has an admin namespace then
<ruby>
form_for [:admin, @article]
@@ -325,9 +321,9 @@ For more information on Rails' routing system and the associated conventions, pl
h4. How do forms with PUT or DELETE methods work?
-Rails framework encourages RESTful design of your applications, which means you'll be making a lot of "PUT" and "DELETE" requests (besides "GET" and "POST"). Still, most browsers _don't support_ methods other than "GET" and "POST" when it comes to submitting forms.
+The Rails framework encourages RESTful design of your applications, which means you'll be making a lot of "PUT" and "DELETE" requests (besides "GET" and "POST"). However, most browsers _don't support_ methods other than "GET" and "POST" when it comes to submitting forms.
-Rails works around this issue by emulating other methods over POST with a hidden input named +"_method"+ that is set to reflect the desired method:
+Rails works around this issue by emulating other methods over POST with a hidden input named +"_method"+, which is set to reflect the desired method:
<ruby>
form_tag(search_path, :method => "put")
@@ -410,7 +406,7 @@ TIP: The second argument to +options_for_select+ must be exactly equal to the de
h4. Select boxes for dealing with models
-In most cases form controls will be tied to a specific database model and as you might expect Rails provides helpers tailored for that purpose. Consistent with other form helpers, when dealing with models you drop the +<notextile>_tag</notextile>+ suffix from +select_tag+:
+In most cases form controls will be tied to a specific database model and as you might expect Rails provides helpers tailored for that purpose. Consistent with other form helpers, when dealing with models you drop the +_tag+ suffix from +select_tag+:
<ruby>
# controller:
@@ -424,7 +420,7 @@ In most cases form controls will be tied to a specific database model and as you
Notice that the third parameter, the options array, is the same kind of argument you pass to +options_for_select+. One advantage here is that you don't have to worry about pre-selecting the correct city if the user already has one -- Rails will do this for you by reading from the +@person.city_id+ attribute.
-As with other helpers, if you were to use +select+ helper on a form builder scoped to +@person+ object, the syntax would be:
+As with other helpers, if you were to use the +select+ helper on a form builder scoped to the +@person+ object, the syntax would be:
<erb>
# select on a form builder
@@ -468,7 +464,7 @@ To leverage time zone support in Rails, you have to ask your users what time zon
There is also +time_zone_options_for_select+ helper for a more manual (therefore more customizable) way of doing this. Read the API documentation to learn about the possible arguments for these two methods.
-Rails _used_ to have a +country_select+ helper for choosing countries but this has been extracted to the "country_select plugin":http://github.com/rails/country_select/tree/master. When using this do be aware that the exclusion or inclusion of certain names from the list can be somewhat controversial (and was the reason this functionality was extracted from rails).
+Rails _used_ to have a +country_select+ helper for choosing countries, but this has been extracted to the "country_select plugin":http://github.com/rails/country_select/tree/master. When using this, be aware that the exclusion or inclusion of certain names from the list can be somewhat controversial (and was the reason this functionality was extracted from rails).
h3. Using Date and Time Form Helpers
@@ -532,13 +528,13 @@ h4. Common options
Both families of helpers use the same core set of functions to generate the individual select tags and so both accept largely the same options. In particular, by default Rails will generate year options 5 years either side of the current year. If this is not an appropriate range, the +:start_year+ and +:end_year+ options override this. For an exhaustive list of the available options, refer to the "API documentation":http://api.rubyonrails.org/classes/ActionView/Helpers/DateHelper.html.
-As a rule of thumb you should be using +date_select+ when working with model objects and +select_date+ in others cases, such as a search form which filters results by date.
+As a rule of thumb you should be using +date_select+ when working with model objects and +select_date+ in other cases, such as a search form which filters results by date.
-NOTE: In many cases the built in date pickers are clumsy as they do not aid the user in working out the relationship between the date and the day of the week.
+NOTE: In many cases the built-in date pickers are clumsy as they do not aid the user in working out the relationship between the date and the day of the week.
h4. Individual components
-Occasionally you need to display just a single date component such as a year or a month. Rails provides a series of helpers for this, one for each component +select_year+, +select_month+, +select_day+, +select_hour+, +select_minute+, +select_second+. These helpers are fairly straightforward. By default they will generate a input named after the time component (for example "year" for +select_year+, "month" for +select_month+ etc.) although this can be overriden with the +:field_name+ option. The +:prefix+ option works in the same way that it does for +select_date+ and +select_time+ and has the same default value.
+Occasionally you need to display just a single date component such as a year or a month. Rails provides a series of helpers for this, one for each component +select_year+, +select_month+, +select_day+, +select_hour+, +select_minute+, +select_second+. These helpers are fairly straightforward. By default they will generate an input field named after the time component (for example "year" for +select_year+, "month" for +select_month+ etc.) although this can be overriden with the +:field_name+ option. The +:prefix+ option works in the same way that it does for +select_date+ and +select_time+ and has the same default value.
The first parameter specifies which value should be selected and can either be an instance of a Date, Time or DateTime, in which case the relevant component will be extracted, or a numerical value. For example
@@ -569,7 +565,7 @@ Rails provides the usual pair of helpers: the barebones +file_field_tag+ and the
h4. What gets uploaded
-The object in the +params+ hash is an instance of a subclass of IO. Depending on the size of the uploaded file it may in fact be a StringIO or an instance of File backed by a temporary file. In both cases the object will have an +original_filename+ attribute containing the name the file had on the user's computer and a +content_type+ attribute containing the MIME type of the uploaded file. The following snippet saves the uploaded content in +#\{Rails.root\}/public/uploads+ under the same name as the original file (assuming the form was the one in the previous example).
+The object in the +params+ hash is an instance of a subclass of IO. Depending on the size of the uploaded file it may in fact be a StringIO or an instance of File backed by a temporary file. In both cases the object will have an +original_filename+ attribute containing the name the file had on the user's computer and a +content_type+ attribute containing the MIME type of the uploaded file. The following snippet saves the uploaded content in +#{Rails.root}/public/uploads+ under the same name as the original file (assuming the form was the one in the previous example).
<ruby>
def upload
@@ -580,7 +576,7 @@ def upload
end
</ruby>
-Once a file has been uploaded there are a multitude of potential tasks, ranging from where to store the files (on disk, Amazon S3, etc) and associating them with models to resizing image files and generating thumbnails. The intricacies of this are beyond the scope of this guide, but there are several plugins designed to assist with these. Two of the better known ones are "Attachment-Fu":http://github.com/technoweenie/attachment_fu and "Paperclip":http://www.thoughtbot.com/projects/paperclip.
+Once a file has been uploaded, there are a multitude of potential tasks, ranging from where to store the files (on disk, Amazon S3, etc) and associating them with models to resizing image files and generating thumbnails. The intricacies of this are beyond the scope of this guide, but there are several plugins designed to assist with these. Two of the better known ones are "Attachment-Fu":http://github.com/technoweenie/attachment_fu and "Paperclip":http://www.thoughtbot.com/projects/paperclip.
NOTE: If the user has not selected a file the corresponding parameter will be an empty string.
@@ -631,7 +627,7 @@ h3. Understanding Parameter Naming Conventions
As you've seen in the previous sections, values from forms can be at the top level of the +params+ hash or nested in another hash. For example in a standard +create+
action for a Person model, +params[:model]+ would usually be a hash of all the attributes for the person to create. The +params+ hash can also contain arrays, arrays of hashes and so on.
-Fundamentally HTML forms don't know about any sort of structured data, all they generate is name-value pairs, where pairs are just plain strings. The arrays and hashes you see in your application are the result of some parameter naming conventions that Rails uses.
+Fundamentally HTML forms don't know about any sort of structured data, all they generate is name–value pairs, where pairs are just plain strings. The arrays and hashes you see in your application are the result of some parameter naming conventions that Rails uses.
TIP: You may find you can try out examples in this section faster by using the console to directly invoke Rails' parameter parser. For example <pre> ActionController::UrlEncodedPairParser.parse_query_parameters "name=fred&phone=0123456789" # => {"name"=>"fred", "phone"=>"0123456789"} </pre>
@@ -663,7 +659,7 @@ will result in the +params+ hash being
{'person' => {'address' => {'city' => 'New York'}}}
</ruby>
-Normally Rails ignores duplicate parameter names. If the parameter name contains [] then they will be accumulated in an array. If you wanted people to be able to input multiple phone numbers, your could place this in the form:
+Normally Rails ignores duplicate parameter names. If the parameter name contains an empty set of square brackets [] then they will be accumulated in an array. If you wanted people to be able to input multiple phone numbers, you could place this in the form:
<html>
<input name="person[phone_number][]" type="text"/>
diff --git a/railties/guides/source/getting_started.textile b/railties/guides/source/getting_started.textile
index cf5754c0d3..3d6c16f11c 100644
--- a/railties/guides/source/getting_started.textile
+++ b/railties/guides/source/getting_started.textile
@@ -21,7 +21,7 @@ This guide is designed for beginners who want to get started with a Rails applic
It is highly recommended that you *familiarize yourself with Ruby before diving into Rails*. You will find it much easier to follow what’s going on with a Rails application if you understand basic Ruby syntax. Rails isn’t going to magically revolutionize the way you write web applications if you have no experience with the language it uses. There are some good free resources on the internet for learning Ruby, including:
-* "Mr. Neigborly’s Humble Little Ruby Book":http://www.humblelittlerubybook.com
+* "Mr. Neighborly’s Humble Little Ruby Book":http://www.humblelittlerubybook.com
* "Programming Ruby":http://www.rubycentral.com/book
* "Why’s (Poignant) Guide to Ruby":http://poignantguide.net/ruby
@@ -163,7 +163,7 @@ $ cd blog
In any case, Rails will create a folder in your working directory called <tt>blog</tt>. Open up that folder and explore its contents. Most of the work in this tutorial will happen in the <tt>app/</tt> folder, but here’s a basic rundown on the function of each folder that Rails creates in a new application by default:
-|File/Folder|Purpose|
+|_.File/Folder|_.Purpose|
|README|This is a brief instruction manual for your application. Use it to tell others what your application does, how to set it up, and so on.|
|Rakefile|This file contains batch jobs that can be run from the terminal.|
|app/|Contains the controllers, models, and views for your application. You'll focus on this folder for the remainder of this guide.|
@@ -327,7 +327,7 @@ NOTE. While scaffolding will get you up and running quickly, the "one size fits
The scaffold generator will build 14 files in your application, along with some folders, and edit one more. Here's a quick overview of what it creates:
-|File |Purpose|
+|_.File |_.Purpose|
|app/models/post.rb |The Post model|
|db/migrate/20090113124235_create_posts.rb |Migration to create the posts table in your database (your name will include a different timestamp)|
|app/views/posts/index.html.erb |A view to display an index of all posts |
@@ -387,7 +387,6 @@ To hook the posts up to the home page you've already created, you can add a link
<code lang="ruby">
<h1>Hello, Rails!</h1>
<%= link_to "My Blog", posts_path %>
-
</code>
The +link_to+ method is one of Rails' built-in view helpers. It creates a hyperlink based on text to display and where to go - in this case, to the path for posts.
@@ -1294,4 +1293,4 @@ h3. Changelog
* October 16, 2008: Revised based on feedback from Pratik Naik by "Mike Gunderloy":credits.html#mgunderloy (not yet approved for publication)
* October 13, 2008: First complete draft by "Mike Gunderloy":credits.html#mgunderloy (not yet approved for publication)
* October 12, 2008: More detail, rearrangement, editing by "Mike Gunderloy":credits.html#mgunderloy (not yet approved for publication)
-* September 8, 2008: initial version by James Miller (not yet approved for publication) \ No newline at end of file
+* September 8, 2008: initial version by James Miller (not yet approved for publication)
diff --git a/railties/guides/source/i18n.textile b/railties/guides/source/i18n.textile
index c5025eb86b..bb445c0bf7 100644
--- a/railties/guides/source/i18n.textile
+++ b/railties/guides/source/i18n.textile
@@ -720,7 +720,7 @@ So, for example, instead of the default error message +"can not be blank"+ you c
* +count+, where available, can be used for pluralization if present:
-| validation | with option | message | interpolation|
+|_. validation |_.with option |_.message |_.interpolation|
| validates_confirmation_of | - | :confirmation | -|
| validates_acceptance_of | - | :accepted | -|
| validates_presence_of | - | :blank | -|
diff --git a/railties/guides/source/index.erb.textile b/railties/guides/source/index.erb.textile
index 0f6e942d78..4751c3a1f5 100644
--- a/railties/guides/source/index.erb.textile
+++ b/railties/guides/source/index.erb.textile
@@ -74,7 +74,7 @@ h3. Digging Deeper
<dl>
<% guide("Rails Internationalization API", 'i18n.html', :ticket => 23) do %>
- This guide covers how to build a plugin to extend the functionality of Rails.
+ This guide covers how to add internationalization to your applications. Your application will be able to translate content to different languages, change pluralization rules, use correct date formats for each country and so on.
<% end %>
<% guide("Action Mailer Basics", 'action_mailer_basics.html', :ticket => 25) do %>
@@ -97,7 +97,7 @@ h3. Digging Deeper
This guide covers the various ways of performance testing a Ruby on Rails application.
<% end %>
-<% guide("The Basics of Creating Rails Plugins", 'plugins.html') do %>
+<% guide("The Basics of Creating Rails Plugins", 'plugins.html', :ticket => 32) do %>
This guide covers how to build a plugin to extend the functionality of Rails.
<% end %>
@@ -105,8 +105,12 @@ h3. Digging Deeper
This guide covers the basic configuration settings for a Rails application.
<% end %>
-<% guide("Rails Command Line Tools and Rake tasks", 'command_line.html') do %>
+<% guide("Rails Command Line Tools and Rake tasks", 'command_line.html', :ticket => 29) do %>
This guide covers the command line tools and rake tasks provided by Rails.
<% end %>
+<% guide("Rails on Rack", 'rails_on_rack.html', :ticket => 58) do %>
+ This guide covers Rails integration with Rack and interfacing with other Rack components.
+<% end %>
+
</dl>
diff --git a/railties/guides/source/layout.html.erb b/railties/guides/source/layout.html.erb
index 141471a50f..cb02b90eb9 100644
--- a/railties/guides/source/layout.html.erb
+++ b/railties/guides/source/layout.html.erb
@@ -34,7 +34,7 @@
<p class="hide"><a href="#mainCol">Skip navigation</a>.</p>
<ul class="nav">
<li><a href="index.html">Home</a></li>
- <li class="index"><a href="#" onclick="guideMenu();" id="guidesMenu">Guides Index</a>
+ <li class="index"><a href="index.html" onclick="guideMenu(); return false;" id="guidesMenu">Guides Index</a>
<div id="guides" class="clearfix" style="display: none;">
<hr />
<dl class="L">
@@ -62,10 +62,11 @@
<dd><a href="performance_testing.html">Performance Testing Rails Applications</a></dd>
<dd><a href="plugins.html">The Basics of Creating Rails Plugins</a></dd>
<dd><a href="configuring.html">Configuring Rails Applications</a></dd>
+ <dd><a href="rails_on_rack.html">Rails on Rack</a></dd>
</dl>
</div>
</li>
- <li><a href="http://hackfest.rubyonrails.org">Contribute</a></li>
+ <li><a href="contribute.html">Contribute</a></li>
<li><a href="credits.html">Credits</a></li>
</ul>
</div>
@@ -91,7 +92,7 @@
<hr class="hide" />
<div id="footer">
<div class="wrapper">
- <p>Authors who have contributed to complete guides are listed <a href="credits.html">here</a>.<br />This work is licensed under a <a href="http://creativecommons.org/licenses/by-sa/3.0">Creative Commons Attribution-Share Alike 3.0</a> License</a></p>
+ <p>This work is licensed under a <a href="http://creativecommons.org/licenses/by-sa/3.0">Creative Commons Attribution-Share Alike 3.0</a> License</a></p>
<p>"Rails", "Ruby on Rails", and the Rails logo are trademarks of David Heinemeier Hansson. All rights reserved.</p>
</div>
</div>
diff --git a/railties/guides/source/layouts_and_rendering.textile b/railties/guides/source/layouts_and_rendering.textile
index a4ea200b02..d9bc605b84 100644
--- a/railties/guides/source/layouts_and_rendering.textile
+++ b/railties/guides/source/layouts_and_rendering.textile
@@ -130,7 +130,8 @@ render "/u/apps/warehouse_app/current/app/views/products/show"
Rails determines that this is a file render because of the leading slash character. To be explicit, you can use the +:file+ option (which was required on Rails 2.2 and earlier):
<ruby>
-render :file => "/u/apps/warehouse_app/current/app/views/products/show"
+render :file =>
+ "/u/apps/warehouse_app/current/app/views/products/show"
</ruby>
The +:file+ option takes an absolute file-system path. Of course, you need to have rights to the view that you're using to render the content.
@@ -144,7 +145,8 @@ h5. Using render with :inline
The +render+ method can do without a view completely, if you're willing to use the +:inline+ option to supply ERB as part of the method call. This is perfectly valid:
<ruby>
-render :inline => "<% products.each do |p| %><p><%= p.name %><p><% end %>"
+render :inline =>
+ "<% products.each do |p| %><p><%= p.name %><p><% end %>"
</ruby>
WARNING: There is seldom any good reason to use this option. Mixing ERB into your controllers defeats the MVC orientation of Rails and will make it harder for other developers to follow the logic of your project. Use a separate erb view instead.
@@ -152,7 +154,8 @@ WARNING: There is seldom any good reason to use this option. Mixing ERB into you
By default, inline rendering uses ERb. You can force it to use Builder instead with the +:type+ option:
<ruby>
-render :inline => "xml.p {'Horrid coding practice!'}", :type => :builder
+render :inline =>
+ "xml.p {'Horrid coding practice!'}", :type => :builder
</ruby>
h5. Using render with :update
@@ -165,7 +168,7 @@ render :update do |page|
end
</ruby>
-WARNING: Placing javascript updates in your controller may seem to streamline small updates, but it defeats the MVC orientation of Rails and will make it harder for other developers to follow the logic of your project. I recommend using a separate rjs template instead, no matter how small the update.
+WARNING: Placing javascript updates in your controller may seem to streamline small updates, but it defeats the MVC orientation of Rails and will make it harder for other developers to follow the logic of your project. We recommend using a separate rjs template instead, no matter how small the update.
h5. Rendering Text
@@ -251,7 +254,7 @@ render :status => 500
render :status => :forbidden
</ruby>
-Rails understands either numeric status codes or symbols for status codes. You can find its list of status codes in +actionpack/lib/action_controller/status_codes.rb+. You can also see there how it maps symbols to status codes in that file.
+Rails understands either numeric status codes or symbols for status codes. You can find its list of status codes in +actionpack/lib/action_controller/status_codes.rb+. You can also see there how Rails maps symbols to status codes.
h6. The :location Option
@@ -263,7 +266,7 @@ render :xml => photo, :location => photo_url(photo)
h5. Finding Layouts
-To find the current layout, Rails first looks for a file in +app/views/layouts+ with the same base name as the controller. For example, rendering actions from the +PhotosController+ class will use +/app/views/layouts/photos.html.erb+. If there is no such controller-specific layout, Rails will use +/app/views/layouts/application.html.erb+. If there is no +.erb+ layout, Rails will use a +.builder+ layout if one exists. Rails also provides several ways to more precisely assign specific layouts to individual controllers and actions.
+To find the current layout, Rails first looks for a file in +app/views/layouts+ with the same base name as the controller. For example, rendering actions from the +PhotosController+ class will use +/app/views/layouts/photos.html.erb+ (or +app/views/layouts/photos.builder+). If there is no such controller-specific layout, Rails will use +/app/views/layouts/application.html.erb+ ot +/app/views/layouts/application.builder+. If there is no +.erb+ layout, Rails will use a +.builder+ layout if one exists. Rails also provides several ways to more precisely assign specific layouts to individual controllers and actions.
h6. Specifying Layouts on a per-Controller Basis
@@ -416,7 +419,7 @@ end
h4. Using redirect_to
-Another way to handle returning responses to a HTTP request is with +redirect_to+. As you've seen, +render+ tells Rails which view (or other asset) to use in constructing a response. The +redirect_to+ method does something completely different: it tells the browser to send a new request for a different URL. For example, you could redirect from wherever you are in your code to the index of photos in your application with this call:
+Another way to handle returning responses to an HTTP request is with +redirect_to+. As you've seen, +render+ tells Rails which view (or other asset) to use in constructing a response. The +redirect_to+ method does something completely different: it tells the browser to send a new request for a different URL. For example, you could redirect from wherever you are in your code to the index of photos in your application with this call:
<ruby>
redirect_to photos_path
@@ -440,7 +443,7 @@ Just like the +:status+ option for +render+, +:status+ for +redirect_to+ accepts
h5. The Difference Between render and redirect
-Sometimes inexperienced developers conceive of +redirect_to+ as a sort of +goto+ command, moving execution from one place to another in your Rails code. This is _not_ correct. Your code stops running and waits for a new request for the browser. It just happens that you've told the browser what request it should make next, by sending back a HTTP 302 status code.
+Sometimes inexperienced developers conceive of +redirect_to+ as a sort of +goto+ command, moving execution from one place to another in your Rails code. This is _not_ correct. Your code stops running and waits for a new request for the browser. It just happens that you've told the browser what request it should make next, by sending back an HTTP 302 status code.
Consider these actions to see the difference:
@@ -507,17 +510,18 @@ Asset tags provide methods for generating HTML that links views to assets like i
* stylesheet_link_tag
* image_tag
-You can use these tags in layouts or other views, although the tags other than +image_tag+ are most commonly used in the +<head>+ section of a layout.
+You can use these tags in layouts or other views, although the tags other than +image_tag+ are most commonly used in the +&lt;head&gt;+ section of a layout.
WARNING: The asset tags do _not_ verify the existence of the assets at the specified locations; they simply assume that you know what you're doing and generate the link.
h5. Linking to Feeds with auto_discovery_link_tag
-The +auto_discovery_link_tag helper builds HTML that most browsers and newsreaders can use to detect the presences of RSS or ATOM feeds. It takes the type of the link (+:rss+ or +:atom+), a hash of options that are passed through to url_for, and a hash of options for the tag:
+The +auto_discovery_link_tag+ helper builds HTML that most browsers and newsreaders can use to detect the presences of RSS or ATOM feeds. It takes the type of the link (+:rss+ or +:atom+), a hash of options that are passed through to url_for, and a hash of options for the tag:
-<ruby>
-<%= auto_discovery_link_tag(:rss, {:action => "feed"}, {:title => "RSS Feed"}) %>
-</ruby>
+<erb>
+<%= auto_discovery_link_tag(:rss, {:action => "feed"},
+ {:title => "RSS Feed"}) %>
+</erb>
There are three tag options available for +auto_discovery_link_tag+:
@@ -529,137 +533,139 @@ h5. Linking to Javascript Files with javascript_include_tag
The +javascript_include_tag+ helper returns an HTML +script+ tag for each source provided. Rails looks in +public/javascripts+ for these files by default, but you can specify a full path relative to the document root, or a URL, if you prefer. For example, to include +public/javascripts/main.js+:
-<ruby>
+<erb>
<%= javascript_include_tag "main" %>
-</ruby>
+</erb>
To include +public/javascripts/main.js+ and +public/javascripts/columns.js+:
-<ruby>
+<erb>
<%= javascript_include_tag "main", "columns" %>
-</ruby>
+</erb>
To include +public/javascripts/main.js+ and +public/photos/columns.js+:
-<ruby>
+<erb>
<%= javascript_include_tag "main", "/photos/columns" %>
-</ruby>
+</erb>
To include +http://example.com/main.js+:
-<ruby>
+<erb>
<%= javascript_include_tag "http://example.com/main.js" %>
-</ruby>
+</erb>
The +defaults+ option loads the Prototype and Scriptaculous libraries:
-<ruby>
+<erb>
<%= javascript_include_tag :defaults %>
-</ruby>
+</erb>
The +all+ option loads every javascript file in +public/javascripts+, starting with the Prototype and Scriptaculous libraries:
-<ruby>
+<erb>
<%= javascript_include_tag :all %>
-</ruby>
+</erb>
You can supply the +:recursive+ option to load files in subfolders of +public/javascripts+ as well:
-<ruby>
+<erb>
<%= javascript_include_tag :all, :recursive => true %>
-</ruby>
+</erb>
If you're loading multiple javascript files, you can create a better user experience by combining multiple files into a single download. To make this happen in production, specify +:cache => true+ in your +javascript_include_tag+:
-<ruby>
+<erb>
<%= javascript_include_tag "main", "columns", :cache => true %>
-</ruby>
+</erb>
By default, the combined file will be delivered as +javascripts/all.js+. You can specify a location for the cached asset file instead:
-<ruby>
-<%= javascript_include_tag "main", "columns", :cache => 'cache/main/display' %>
-</ruby>
+<erb>
+<%= javascript_include_tag "main", "columns",
+ :cache => 'cache/main/display' %>
+</erb>
-You can even use dynamic paths such as "cache/#{current_site}/main/display"+.
+You can even use dynamic paths such as +cache/#{current_site}/main/display+.
h5. Linking to CSS Files with stylesheet_link_tag
The +stylesheet_link_tag+ helper returns an HTML +<link>+ tag for each source provided. Rails looks in +public/stylesheets+ for these files by default, but you can specify a full path relative to the document root, or a URL, if you prefer. For example, to include +public/stylesheets/main.cs+:
-<ruby>
+<erb>
<%= stylesheet_link_tag "main" %>
-</ruby>
+</erb>
To include +public/stylesheets/main.css+ and +public/stylesheets/columns.css+:
-<ruby>
+<erb>
<%= stylesheet_link_tag "main", "columns" %>
-</ruby>
+</erb>
To include +public/stylesheets/main.css+ and +public/photos/columns.css+:
-<ruby>
+<erb>
<%= stylesheet_link_tag "main", "/photos/columns" %>
-</ruby>
+</erb>
To include +http://example.com/main.cs+:
-<ruby>
+<erb>
<%= stylesheet_link_tag "http://example.com/main.cs" %>
-</ruby>
+</erb>
By default, +stylesheet_link_tag+ creates links with +media="screen" rel="stylesheet" type="text/css"+. You can override any of these defaults by specifying an appropriate option (:media, :rel, or :type):
-<ruby>
+<erb>
<%= stylesheet_link_tag "main_print", media => "print" %>
-</ruby>
+</erb>
The +all+ option links every CSS file in +public/stylesheets+:
-<ruby>
+<erb>
<%= stylesheet_link_tag :all %>
-</ruby>
+</erb>
You can supply the +:recursive+ option to link files in subfolders of +public/stylesheets+ as well:
-<ruby>
+<erb>
<%= stylesheet_link_tag :all, :recursive => true %>
-</ruby>
+</erb>
If you're loading multiple CSS files, you can create a better user experience by combining multiple files into a single download. To make this happen in production, specify +:cache => true+ in your +stylesheet_link_tag+:
-<ruby>
+<erb>
<%= stylesheet_link_tag "main", "columns", :cache => true %>
-</ruby>
+</erb>
By default, the combined file will be delivered as +stylesheets/all.css+. You can specify a location for the cached asset file instead:
-<ruby>
-<%= stylesheet_link_tag "main", "columns", :cache => 'cache/main/display' %>
-</ruby>
+<erb>
+<%= stylesheet_link_tag "main", "columns",
+ :cache => 'cache/main/display' %>
+</erb>
-You can even use dynamic paths such as "cache/#{current_site}/main/display"+.
+You can even use dynamic paths such as +cache/#{current_site}/main/display+.
h5. Linking to Images with image_tag
-The +image_tag+ helper builds an HTML +<image>+ tag to the specified file. By default, files are loaded from +public/images+. If you don't specify an extension, .png is assumed by default:
+The +image_tag+ helper builds an HTML +&lt;image&gt;+ tag to the specified file. By default, files are loaded from +public/images+. If you don't specify an extension, +.png+ is assumed by default:
-<ruby>
+<erb>
<%= image_tag "header" %>
-</ruby>
+</erb>
You can supply a path to the image if you like:
-<ruby>
+<erb>
<%= image_tag "icons/delete.gif" %>
-</ruby>
+</erb>
You can supply a hash of additional HTML options:
-<ruby>
+<erb>
<%= image_tag "icons/delete.gif", :height => 45 %>
-</ruby>
+</erb>
There are also three special options you can use with +image_tag+:
@@ -721,7 +727,7 @@ The result of rendering this page into the supplied layout would be this HTML:
</html>
</erb>
-The +content_for+ method is very helpful when your layout contains distinct regions such as sidebars and footers that should get their own blocks of content inserted. It's also useful for inserting tags that load page-specific javascript or css files into the header of an otherwise-generic layout.
+The +content_for+ method is very helpful when your layout contains distinct regions such as sidebars and footers that should get their own blocks of content inserted. It's also useful for inserting tags that load page-specific javascript or css files into the header of an otherwise generic layout.
h4. Using Partials
@@ -781,7 +787,8 @@ You can also pass local variables into partials, making them even more powerful
<erb>
<h1>New zone</h1>
<%= error_messages_for :zone %>
-<%= render :partial => "form", :locals => { :button_label => "Create zone", :zone => @zone } %>
+<%= render :partial => "form", :locals =>
+ { :button_label => "Create zone", :zone => @zone } %>
</erb>
* +edit.html.erb+
@@ -789,7 +796,8 @@ You can also pass local variables into partials, making them even more powerful
<erb>
<h1>Editing zone</h1>
<%= error_messages_for :zone %>
-<%= render :partial => "form", :locals => { :button_label => "Update zone", :zone => @zone } %>
+<%= render :partial => "form", :locals =>
+ { :button_label => "Update zone", :zone => @zone } %>
</erb>
* +_form.html.erb+
@@ -856,7 +864,8 @@ TIP: Rails also makes a counter variable available within a partial called by th
You can also specify a second partial to be rendered between instances of the main partial by using the +:spacer_template+ option:
<erb>
-<%= render :partial => "product", :collection => @products, :spacer_template => "product_ruler" %>
+<%= render :partial => "product", :collection => @products,
+ :spacer_template => "product_ruler" %>
</erb>
Rails will render the +_product_ruler+ partial (with no data passed in to it) between each pair of +_product+ partials.
@@ -882,7 +891,8 @@ Rails determines the name of the partial to use by looking at the model name in
<erb>
<h1>Contacts</h1>
-<%= render :partial => [customer1, employee1, customer2, employee2] %>
+<%= render :partial =>
+ [customer1, employee1, customer2, employee2] %>
</erb>
* +_customer.html.erb+
@@ -910,14 +920,14 @@ Suppose you have the follow +ApplicationController+ layout:
<erb>
<html>
<head>
- <title><%= @page_title %><title>
- <% stylesheet_tag 'layout' %>
+ <title><%= @page_title or 'Page Title' %></title>
+ <%= stylesheet_link_tag 'layout' %>
<style type="text/css"><%= yield :stylesheets %></style>
-<head>
+</head>
<body>
<div id="top_menu">Top menu items here</div>
<div id="menu">Menu items here</div>
- <div id="main"><%= yield %></div>
+ <div id="content"><%= yield(:content) or yield %></div>
</body>
</html>
</erb>
@@ -931,18 +941,16 @@ On pages generated by +NewsController+, you want to hide the top menu and add a
#top_menu {display: none}
#right_menu {float: right; background-color: yellow; color: black}
<% end -%>
-<% content_for :main %>
+<% content_for :content do %>
<div id="right_menu">Right menu items here</div>
- <%= yield %>
+ <%= yield(:news_content) or yield %>
<% end -%>
<% render :file => 'layouts/application' %>
</erb>
-NOTE: In versions of Rails before Rails 2.3, you should use +render 'layouts/applications'+ instead of +render :file => 'layouts/applications'+
-
That's it. The News views will use the new layout, hiding the top menu and adding a new right menu inside the "content" div.
-There are several ways of getting similar results with differents sub-templating schemes using this technique. Note that there is no limit in nesting levels. One can use the +ActionView::render+ method via +render 'layouts/news'+ to base a new layout on the News layout.
+There are several ways of getting similar results with different sub-templating schemes using this technique. Note that there is no limit in nesting levels. One can use the +ActionView::render+ method via +render :file => 'layouts/news'+ to base a new layout on the News layout. If one is sure she will not subtemplate the +News+ layout, she can ommit the +yield(:news_content) or + part.
h3. Changelog
@@ -954,4 +962,4 @@ h3. Changelog
* November 1, 2008: Added +:js+ option for +render+ by "Mike Gunderloy":credits.html#mgunderloy
* October 16, 2008: Ready for publication by "Mike Gunderloy":credits.html#mgunderloy
* October 4, 2008: Additional info on partials (+:object+, +:as+, and +:spacer_template+) by "Mike Gunderloy":credits.html#mgunderloy (not yet approved for publication)
-* September 28, 2008: First draft by "Mike Gunderloy":credits.html#mgunderloy (not yet approved for publication) \ No newline at end of file
+* September 28, 2008: First draft by "Mike Gunderloy":credits.html#mgunderloy (not yet approved for publication)
diff --git a/railties/guides/source/migrations.textile b/railties/guides/source/migrations.textile
index 4bc4092c3d..3f1ad51000 100644
--- a/railties/guides/source/migrations.textile
+++ b/railties/guides/source/migrations.textile
@@ -1,8 +1,10 @@
h2. Migrations
-Migrations are a convenient way for you to alter your database in a structured and organised manner. You could edit fragments of SQL by hand but you would then be responsible for telling other developers that they need to go and run it. You'd also have to keep track of which changes need to be run against the production machines next time you deploy. Active Record tracks which migrations have already been run so all you have to do is update your source and run +rake db:migrate+. Active Record will work out which migrations should be run. It will also update your db/schema.rb file to match the structure of your database.
+Migrations are a convenient way for you to alter your database in a structured and organised manner. You could edit fragments of SQL by hand but you would then be responsible for telling other developers that they need to go and run it. You'd also have to keep track of which changes need to be run against the production machines next time you deploy.
-Migrations also allow you to describe these transformations using Ruby. The great thing about this is that (like most of Active Record's functionality) it is database independent: you don't need to worry about the precise syntax of CREATE TABLE any more that you worry about variations on SELECT * (you can drop down to raw SQL for database specific features). For example you could use SQLite3 in development, but MySQL in production.
+Active Record tracks which migrations have already been run so all you have to do is update your source and run +rake db:migrate+. Active Record will work out which migrations should be run. It will also update your +db/schema.rb+ file to match the structure of your database.
+
+Migrations also allow you to describe these transformations using Ruby. The great thing about this is that (like most of Active Record's functionality) it is database independent: you don't need to worry about the precise syntax of +CREATE TABLE+ any more that you worry about variations on +SELECT *+ (you can drop down to raw SQL for database specific features). For example you could use SQLite3 in development, but MySQL in production.
You'll learn all about migrations including:
@@ -53,14 +55,14 @@ class AddReceiveNewsletterToUsers < ActiveRecord::Migration
end
</ruby>
-This migration adds an +receive_newsletter+ column to the +users+ table. We want it to default to +false+ for new users, but existing users are considered
+This migration adds a +receive_newsletter+ column to the +users+ table. We want it to default to +false+ for new users, but existing users are considered
to have already opted in, so we use the User model to set the flag to +true+ for existing users.
NOTE: Some "caveats":#using-models-in-your-migrations apply to using models in your migrations.
h4. Migrations are classes
-A migration is a subclass of ActiveRecord::Migration that implements two class methods: +up+ (perform the required transformations) and +down+ (revert them).
+A migration is a subclass of <tt>ActiveRecord::Migration</tt> that implements two class methods: +up+ (perform the required transformations) and +down+ (revert them).
Active Record provides methods that perform common data definition tasks in a database independent way (you'll read about them in detail later):
@@ -68,9 +70,9 @@ Active Record provides methods that perform common data definition tasks in a da
* +change_table+
* +drop_table+
* +add_column+
-* +remove_column+
* +change_column+
* +rename_column+
+* +remove_column+
* +add_index+
* +remove_index+
@@ -80,19 +82,19 @@ On databases that support transactions with statements that change the schema (s
h4. What's in a name
-Migrations are stored in files in +db/migrate+, one for each migration class. The name of the file is of the form +YYYYMMDDHHMMSS_create_products.rb+, that is to say a UTC timestamp identifying the migration followed by an underscore followed by the name of the migration. The migration class' name must match (the camelcased version of) the latter part of the file name. For example +20080906120000_create_products.rb+ should define CreateProducts and +20080906120001_add_details_to_products.rb+ should define AddDetailsToProducts. If you do feel the need to change the file name then you MUST update the name of the class inside or Rails will complain about a missing class.
+Migrations are stored in files in +db/migrate+, one for each migration class. The name of the file is of the form +YYYYMMDDHHMMSS_create_products.rb+, that is to say a UTC timestamp identifying the migration followed by an underscore followed by the name of the migration. The migration class' name must match (the camelcased version of) the latter part of the file name. For example +20080906120000_create_products.rb+ should define +CreateProducts+ and +20080906120001_add_details_to_products.rb+ should define +AddDetailsToProducts+. If you do feel the need to change the file name then you <em>have to</em> update the name of the class inside or Rails will complain about a missing class.
-Internally Rails only uses the migration's number (the timestamp) to identify them. Prior to Rails 2.1 the migration number started at 1 and was incremented each time a migration was generated. With multiple developers it was easy for these to clash requiring you to rollback migrations and renumber them. With Rails 2.1 this is largely avoided by using the creation time of the migration to identify them. You can revert to the old numbering scheme by setting +config.active_record.timestamped_migrations+ to +false+ in +environment.rb+.
+Internally Rails only uses the migration's number (the timestamp) to identify them. Prior to Rails 2.1 the migration number started at 1 and was incremented each time a migration was generated. With multiple developers it was easy for these to clash requiring you to rollback migrations and renumber them. With Rails 2.1 this is largely avoided by using the creation time of the migration to identify them. You can revert to the old numbering scheme by setting +config.active_record.timestamped_migrations+ to +false+ in +config/environment.rb+.
The combination of timestamps and recording which migrations have been run allows Rails to handle common situations that occur with multiple developers.
-For example Alice adds migrations +20080906120000+ and +20080906123000+ and Bob adds +20080906124500+ and runs it. Alice finishes her changes and checks in her migrations and Bob pulls down the latest changes. Rails knows that it has not run Alice's two migrations so +rake db:migrate+ would run them (even though Bob's migration with a later timestamp has been run), and similarly migrating down would not run their down methods.
+For example Alice adds migrations +20080906120000+ and +20080906123000+ and Bob adds +20080906124500+ and runs it. Alice finishes her changes and checks in her migrations and Bob pulls down the latest changes. Rails knows that it has not run Alice's two migrations so +rake db:migrate+ would run them (even though Bob's migration with a later timestamp has been run), and similarly migrating down would not run their +down+ methods.
-Of course this is no substitution for communication within the team, for example if Alice's migration removed a table that Bob's migration assumed the existence of then trouble will still occur.
+Of course this is no substitution for communication within the team. For example, if Alice's migration removed a table that Bob's migration assumed to exist, then trouble would certainly strike.
h4. Changing migrations
-Occasionally you will make a mistake while writing a migration. If you have already run the migration then you cannot just edit the migration and run the migration again: Rails thinks it has already run the migration and so will do nothing when you run +rake db:migrate+. You must rollback the migration (for example with +rake db:rollback+), edit your migration and then run +rake db:migrate+ to run the corrected version.
+Occasionally you will make a mistake when writing a migration. If you have already run the migration then you cannot just edit the migration and run the migration again: Rails thinks it has already run the migration and so will do nothing when you run +rake db:migrate+. You must rollback the migration (for example with +rake db:rollback+), edit your migration and then run +rake db:migrate+ to run the corrected version.
In general editing existing migrations is not a good idea: you will be creating extra work for yourself and your co-workers and cause major headaches if the existing version of the migration has already been run on production machines. Instead you should write a new migration that performs the changes you require. Editing a freshly generated migration that has not yet been committed to source control (or more generally which has not been propagated beyond your development machine) is relatively harmless. Just use some common sense.
@@ -102,7 +104,11 @@ h4. Creating a model
The model and scaffold generators will create migrations appropriate for adding a new model. This migration will already contain instructions for creating the relevant table. If you tell Rails what columns you want then statements for adding those will also be created. For example, running
-+ruby script/generate model Product name:string description:text+ will create a migration that looks like this
+<shell>
+ruby script/generate model Product name:string description:text
+</shell>
+
+will create a migration that looks like this
<ruby>
class CreateProducts < ActiveRecord::Migration
@@ -144,8 +150,7 @@ class AddPartNumberToProducts < ActiveRecord::Migration
end
</ruby>
-If the migration name is of the form AddXXXToYYY or RemoveXXXFromY and is followed by a list of column names and types then a migration containing
-the appropriate add and remove column statements will be created.
+If the migration name is of the form "AddXXXToYYY" or "RemoveXXXFromYYY" and is followed by a list of column names and types then a migration containing the appropriate +add_column+ and +remove_column+ statements will be created.
<shell>
ruby script/generate migration AddPartNumberToProducts part_number:string
@@ -225,7 +230,7 @@ end
which creates a +products+ table with a column called +name+ (and as discussed below, an implicit +id+ column).
-The object yielded to the block allows you create columns on the table. There are two ways of doing this. The first looks like
+The object yielded to the block allows you create columns on the table. There are two ways of doing this: The first (traditional) form looks like
<ruby>
create_table :products do |t|
@@ -233,7 +238,7 @@ create_table :products do |t|
end
</ruby>
-the second form, the so called "sexy" migrations, drops the somewhat redundant column method. Instead, the +string+, +integer+ etc. methods create a column of that type. Subsequent parameters are identical.
+the second form, the so called "sexy" migration, drops the somewhat redundant +column+ method. Instead, the +string+, +integer+, etc. methods create a column of that type. Subsequent parameters are the same.
<ruby>
create_table :products do |t|
@@ -241,18 +246,19 @@ create_table :products do |t|
end
</ruby>
-By default +create_table+ will create a primary key called +id+. You can change the name of the primary key with the +:primary_key+ option (don't forget to update the corresponding model) or if you don't want a primary key at all (for example for a HABTM join table) you can pass +:id => false+. If you need to pass database specific options you can place an sql fragment in the +:options+ option. For example
+By default +create_table+ will create a primary key called +id+. You can change the name of the primary key with the +:primary_key+ option (don't forget to update the corresponding model) or if you don't want a primary key at all (for example for a HABTM join table) you can pass +:id => false+. If you need to pass database specific options you can place an SQL fragment in the +:options+ option. For example
<ruby>
create_table :products, :options => "ENGINE=BLACKHOLE" do |t|
t.string :name, :null => false
end
</ruby>
-Will append +ENGINE=BLACKHOLE+ to the sql used to create the table (when using MySQL the default is "ENGINE=InnoDB").
-The types Active Record supports are +:primary_key+, +:string+, +:text+, +:integer+, +:float+, +:decimal+, +:datetime+, +:timestamp+, +:time+, +:date+, +:binary+, +:boolean+.
+will append +ENGINE=BLACKHOLE+ to the SQL statement used to create the table (when using MySQL the default is +ENGINE=InnoDB+).
+
+The types supported by Active Record are +:primary_key+, +:string+, +:text+, +:integer+, +:float+, +:decimal+, +:datetime+, +:timestamp+, +:time+, +:date+, +:binary+, +:boolean+.
-These will be mapped onto an appropriate underlying database type, for example with MySQL +:string+ is mapped to +VARCHAR(255)+. You can create columns of types not supported by Active Record when using the non sexy syntax, for example
+These will be mapped onto an appropriate underlying database type, for example with MySQL +:string+ is mapped to +VARCHAR(255)+. You can create columns of types not supported by Active Record when using the non-sexy syntax, for example
<ruby>
create_table :products do |t|
@@ -264,7 +270,7 @@ This may however hinder portability to other databases.
h4. Changing tables
-A close cousin of +create_table+ is +change_table+. Used for changing existing tables, it is used in a similar fashion to +create_table+ but the object yielded to the block knows more tricks. For example
+A close cousin of +create_table+ is +change_table+, used for changing existing tables. It is used in a similar fashion to +create_table+ but the object yielded to the block knows more tricks. For example
<ruby>
change_table :products do |t|
@@ -274,7 +280,7 @@ change_table :products do |t|
t.rename :upccode, :upc_code
end
</ruby>
-removes the +description+ column, creates a +part_number+ column and adds an index on it. Finally it renames the +upccode+ column. This is the same as doing
+removes the +description+ and +name+ columns, creates a +part_number+ column and adds an index on it. Finally it renames the +upccode+ column. This is the same as doing
<ruby>
remove_column :products, :description
@@ -295,7 +301,7 @@ create_table :products do |t|
t.timestamps
end
</ruby>
-will create a new products table with those two columns whereas
+will create a new products table with those two columns (plus the +id+ column) whereas
<ruby>
change_table :products do |t|
@@ -312,7 +318,7 @@ create_table :products do |t|
end
</ruby>
-will create a +category_id+ column of the appropriate type. Note that you pass the model name, not the column name. Active Record adds the +_id+ for you. If you have polymorphic belongs_to associations then +references+ will add both of the columns required:
+will create a +category_id+ column of the appropriate type. Note that you pass the model name, not the column name. Active Record adds the +_id+ for you. If you have polymorphic +belongs_to+ associations then +references+ will add both of the columns required:
<ruby>
create_table :products do |t|
@@ -325,11 +331,11 @@ NOTE: The +references+ helper does not actually create foreign key constraints f
If the helpers provided by Active Record aren't enough you can use the +execute+ function to execute arbitrary SQL.
-For more details and examples of individual methods check the API documentation, in particular the documentation for "ActiveRecord::ConnectionAdapters::SchemaStatements":http://api.rubyonrails.com/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html (which provides the methods available in the +up+ and +down+ methods), "ActiveRecord::ConnectionAdapters::TableDefinition":http://api.rubyonrails.com/classes/ActiveRecord/ConnectionAdapters/TableDefinition.html (which provides the methods available on the object yielded by +create_table+) and "ActiveRecord::ConnectionAdapters::Table":http://api.rubyonrails.com/classes/ActiveRecord/ConnectionAdapters/Table.html (which provides the methods available on the object yielded by +change_table+).
+For more details and examples of individual methods check the API documentation, in particular the documentation for "<tt>ActiveRecord::ConnectionAdapters::SchemaStatements</tt>":http://api.rubyonrails.com/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html (which provides the methods available in the +up+ and +down+ methods), "<tt>ActiveRecord::ConnectionAdapters::TableDefinition</tt>":http://api.rubyonrails.com/classes/ActiveRecord/ConnectionAdapters/TableDefinition.html (which provides the methods available on the object yielded by +create_table+) and "<tt>ActiveRecord::ConnectionAdapters::Table</tt>":http://api.rubyonrails.com/classes/ActiveRecord/ConnectionAdapters/Table.html (which provides the methods available on the object yielded by +change_table+).
h4. Writing your down method
-The +down+ method of your migration should revert the transformations done by the +up+ method. In other words the database should be unchanged if you do an +up+ followed by a +down+. For example if you create a table in the up you should drop it in the +down+ method. It is wise to do things in precisely the reverse order to in the +up+ method. For example
+The +down+ method of your migration should revert the transformations done by the +up+ method. In other words the database schema should be unchanged if you do an +up+ followed by a +down+. For example if you create a table in the +up+ method you should drop it in the +down+ method. It is wise to do things in precisely the reverse order to in the +up+ method. For example
<ruby>
class ExampleMigration < ActiveRecord::Migration
@@ -339,7 +345,12 @@ class ExampleMigration < ActiveRecord::Migration
t.references :category
end
#add a foreign key
- execute "ALTER TABLE products ADD CONSTRAINT fk_products_categories FOREIGN KEY (category_id) REFERENCES categories(id)"
+ execute <<-SQL
+ ALTER TABLE products
+ ADD CONSTRAINT fk_products_categories
+ FOREIGN KEY (category_id)
+ REFERENCES categories(id)
+ SQL
add_column :users, :home_page_url, :string
@@ -354,7 +365,7 @@ class ExampleMigration < ActiveRecord::Migration
end
end
</ruby>
-Sometimes your migration will do something which is just plain irreversible, for example it might destroy some data. In cases like those when you can't reverse the migration you can raise IrreversibleMigration from your +down+ method. If someone tries to revert your migration an error message will be
+Sometimes your migration will do something which is just plain irreversible, for example it might destroy some data. In cases like those when you can't reverse the migration you can raise +IrreversibleMigration+ from your +down+ method. If someone tries to revert your migration an error message will be
displayed saying that it can't be done.
@@ -475,7 +486,7 @@ h3. Using Models In Your Migrations
When creating or updating data in a migration it is often tempting to use one of your models. After all they exist to provide easy access to the underlying data. This can be done but some caution should be observed.
-Consider for example a migration that uses the Product model to update a row in the corresponding table. Alice later updates the Product model, adding a new column and a validation on it. Bob comes back from holiday, updates the source and runs outstanding migrations with +rake db:migrate+, including the one that used the Product model. When the migration runs the source is up to date and so the Product model has the validation added by Alice. The database however is still old and so does not have that column and an error ensues because that validation is on a column that does not yet exist.
+Consider for example a migration that uses the +Product+ model to update a row in the corresponding table. Alice later updates the +Product+ model, adding a new column and a validation on it. Bob comes back from holiday, updates the source and runs outstanding migrations with +rake db:migrate+, including the one that used the +Product+ model. When the migration runs the source is up to date and so the +Product+ model has the validation added by Alice. The database however is still old and so does not have that column and an error ensues because that validation is on a column that does not yet exist.
Frequently I just want to update rows in the database without writing out the SQL by hand: I'm not using anything specific to the model. One pattern for this is to define a copy of the model inside the migration itself, for example:
@@ -493,7 +504,7 @@ class AddPartNumberToProducts < ActiveRecord::Migration
end
end
</ruby>
-The migration has its own minimal copy of the Product model and no longer cares about the Product model defined in the application.
+The migration has its own minimal copy of the +Product+ model and no longer cares about the +Product+ model defined in the application.
h4. Dealing with changing models
@@ -521,11 +532,11 @@ h3. Schema dumping and you
h4. What are schema files for?
-Migrations, mighty as they may be, are not the authoritative source for your database schema. That role falls to either +schema.rb+ or an SQL file which Active Record generates by examining the database. They are not designed to be edited, they just represent the current state of the database.
+Migrations, mighty as they may be, are not the authoritative source for your database schema. That role falls to either +db/schema.rb+ or an SQL file which Active Record generates by examining the database. They are not designed to be edited, they just represent the current state of the database.
There is no need (and it is error prone) to deploy a new instance of an app by replaying the entire migration history. It is much simpler and faster to just load into the database a description of the current schema.
-For example, this is how the test database is created: the current development database is dumped (either to +schema.rb+ or +development.sql+) and then loaded into the test database.
+For example, this is how the test database is created: the current development database is dumped (either to +db/schema.rb+ or +db/development.sql+) and then loaded into the test database.
Schema files are also useful if you want a quick look at what attributes an Active Record object has. This information is not in the model's code and is frequently spread across several migrations but is all summed up in the schema file. The "annotate_models":http://agilewebdevelopment.com/plugins/annotate_models plugin, which automatically adds (and updates) comments at the top of each model summarising the schema, may also be of interest.
@@ -553,28 +564,28 @@ ActiveRecord::Schema.define(:version => 20080906171750) do
end
</ruby>
-In many ways this is exactly what it is. This file is created by inspecting the database and expressing its structure using +create_table+, +add_index+ and so on. Because this is database independent it could be loaded into any database that Active Record supports. This could be very useful if you were to distribute an application that is able to run against multiple databases.
+In many ways this is exactly what it is. This file is created by inspecting the database and expressing its structure using +create_table+, +add_index+, and so on. Because this is database independent it could be loaded into any database that Active Record supports. This could be very useful if you were to distribute an application that is able to run against multiple databases.
-There is however a trade-off: +schema.rb+ cannot express database specific items such as foreign key constraints, triggers or stored procedures. While in a migration you can execute custom SQL statements, the schema dumper cannot reconstitute those statements from the database. If you are using features like this then you should set the schema format to +:sql+.
+There is however a trade-off: +db/schema.rb+ cannot express database specific items such as foreign key constraints, triggers or stored procedures. While in a migration you can execute custom SQL statements, the schema dumper cannot reconstitute those statements from the database. If you are using features like this then you should set the schema format to +:sql+.
-Instead of using Active Record's schema dumper the database's structure will be dumped using a tool specific to that database (via the +db:structure:dump+ Rake task) into +db/#{RAILS_ENV}_structure.sql+. For example for PostgreSQL the +pg_dump+ utility is used and for MySQL this file will contain the output of SHOW CREATE TABLE for the various tables. Loading this schema is simply a question of executing the SQL statements contained inside.
+Instead of using Active Record's schema dumper the database's structure will be dumped using a tool specific to that database (via the +db:structure:dump+ Rake task) into +db/#{RAILS_ENV}_structure.sql+. For example for PostgreSQL the +pg_dump+ utility is used and for MySQL this file will contain the output of +SHOW CREATE TABLE+ for the various tables. Loading this schema is simply a question of executing the SQL statements contained inside.
By definition this will be a perfect copy of the database's structure but this will usually prevent loading the schema into a database other than the one used to create it.
h4. Schema dumps and source control
-Because they are the authoritative source for your database schema, it is strongly recommended that you check them into source control.
+Because schema dumps are the authoritative source for your database schema, it is strongly recommended that you check them into source control.
h3. Active Record and Referential Integrity
-The Active Record way is that intelligence belongs in your models, not in the database. As such, features such as triggers or foreign key constraints, which push some of that intelligence back into the database are not heavily used.
+The Active Record way claims that intelligence belongs in your models, not in the database. As such, features such as triggers or foreign key constraints, which push some of that intelligence back into the database, are not heavily used.
Validations such as +validates_uniqueness_of+ are one way in which models can enforce data integrity. The +:dependent+ option on associations allows models to automatically destroy child objects when the parent is destroyed. Like anything which operates at the application level these cannot guarantee referential integrity and so some people augment them with foreign key constraints.
-Although Active Record does not provide any tools for working directly with such features, the +execute+ method can be used to execute arbitrary SQL. There are also a number of plugins such as "redhillonrails":http://agilewebdevelopment.com/plugins/search?search=redhillonrails which add foreign key support to Active Record (including support for dumping foreign keys in +schema.rb+).
+Although Active Record does not provide any tools for working directly with such features, the +execute+ method can be used to execute arbitrary SQL. There are also a number of plugins such as "redhillonrails":http://agilewebdevelopment.com/plugins/search?search=redhillonrails which add foreign key support to Active Record (including support for dumping foreign keys in +db/schema.rb+).
h3. Changelog
"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/6
-* September 14, 2008: initial version by "Frederick Cheung":credits.html#fcheung \ No newline at end of file
+* September 14, 2008: initial version by "Frederick Cheung":credits.html#fcheung
diff --git a/railties/guides/source/performance_testing.textile b/railties/guides/source/performance_testing.textile
index 972b7aac50..c2bf36c893 100644
--- a/railties/guides/source/performance_testing.textile
+++ b/railties/guides/source/performance_testing.textile
@@ -95,7 +95,7 @@ class Post < ActiveRecord::Base
end
</ruby>
-h5. Controller Example
+h5. Controller example
Because performance tests are a special kind of integration test, you can use the +get+ and +post+ methods in them.
@@ -123,7 +123,7 @@ end
You can find more details about the +get+ and +post+ methods in the link:../testing_rails_applications.html#mgunderloy[Testing Rails Applications] guide.
-h5. Model Example
+h5. Model example
Even though the performance tests are integration tests and hence closer to the request/response cycle by nature, you can still performance test pure model code.
@@ -147,7 +147,7 @@ end
h4. Modes
-Performance tests can be run in two modes : Benchmarking and Profiling.
+Performance tests can be run in two modes: Benchmarking and Profiling.
h5. Benchmarking
@@ -173,41 +173,41 @@ h4. Metrics
Benchmarking and profiling run performance tests in various modes described below.
-h5. Wall Time
+h5. Wall time
Wall time measures the real world time elapsed during the test run. It is affected by any other processes concurrently running on the system.
-Mode : Benchmarking
+Mode: Benchmarking
-h5. Process Time
+h5. Process time
Process time measures the time taken by the process. It is unaffected by any other processes running concurrently on the same system. Hence, process time is likely to be constant for any given performance test, irrespective of the machine load.
-Mode : Profiling
+Mode: Profiling
h5. Memory
Memory measures the amount of memory used for the performance test case.
-Mode : Benchmarking, Profiling "Requires GC Patched Ruby":#installing-gc-patched-ruby
+Mode: Benchmarking, Profiling "Requires GC Patched Ruby":#installing-gc-patched-ruby
h5. Objects
Objects measures the number of objects allocated for the performance test case.
-Mode : Benchmarking, Profiling "Requires GC Patched Ruby":#installing-gc-patched-ruby
+Mode: Benchmarking, Profiling "Requires GC Patched Ruby":#installing-gc-patched-ruby
-h5. GC Runs
+h5. GC runs
GC Runs measures the number of times GC was invoked for the performance test case.
-Mode : Benchmarking "Requires GC Patched Ruby":#installing-gc-patched-ruby
+Mode: Benchmarking "Requires GC Patched Ruby":#installing-gc-patched-ruby
-h5. GC Time
+h5. GC time
GC Time measures the amount of time spent in GC for the performance test case.
-Mode : Benchmarking "Requires GC Patched Ruby":#installing-gc-patched-ruby
+Mode: Benchmarking "Requires GC Patched Ruby":#installing-gc-patched-ruby
h4. Understanding the output
@@ -215,11 +215,11 @@ Performance tests generate different outputs inside +tmp/performance+ directory
h5. Benchmarking
-In benchmarking mode, performance tests generate two types of outputs :
+In benchmarking mode, performance tests generate two types of outputs:
h6. Command line
-This is the primary form of output in benchmarking mode. Example :
+This is the primary form of output in benchmarking mode. Example:
<shell>
BrowsingTest#test_homepage (31 ms warmup)
@@ -232,7 +232,7 @@ BrowsingTest#test_homepage (31 ms warmup)
h6. CSV files
-Performance test results are also appended to +.csv+ files inside +tmp/performance+. For example, running the default +BrowsingTest#test_homepage+ will generate following five files :
+Performance test results are also appended to +.csv+ files inside +tmp/performance+. For example, running the default +BrowsingTest#test_homepage+ will generate following five files:
* BrowsingTest#test_homepage_gc_runs.csv
* BrowsingTest#test_homepage_gc_time.csv
@@ -285,13 +285,13 @@ h6. Tree
Tree output is profiling information in calltree format for use by http://kcachegrind.sourceforge.net/html/Home.html[kcachegrind] and similar tools.
-h4. Tuning Test Runs
+h4. Tuning test runs
By default, each performance test is run +4 times+ in benchmarking mode and +1 time+ in profiling. However, test runs can easily be configured.
WARNING: Performance test configurability is not yet enabled in Rails. But it will be soon.
-h4. Performance Test Environment
+h4. Performance test environment
Performance tests are run in the +development+ environment. But running performance tests will set the following configuration parameters:
@@ -303,17 +303,17 @@ Rails.logger.level = ActiveSupport::BufferedLogger::INFO
As +ActionController::Base.perform_caching+ is set to +true+, performance tests will behave much as they do in the +production+ environment.
-h4. Installing GC-Patched Ruby
+h4. Installing GC-patched Ruby
To get the best from Rails performance tests, you need to build a special Ruby binary with some super powers - "GC patch":http://rubyforge.org/tracker/download.php/1814/7062/17676/3291/ruby186gc.patch for measuring GC Runs/Time and memory/object allocation.
-The process is fairly straight forward. If you've never compiled a Ruby binary before, follow these steps to build a ruby binary inside your home directory:
+The process is fairly straightforward. If you've never compiled a Ruby binary before, follow these steps to build a ruby binary inside your home directory:
h5. Installation
Compile Ruby and apply this "GC Patch":http://rubyforge.org/tracker/download.php/1814/7062/17676/3291/ruby186gc.patch.
-h5. Download and Extract
+h5. Download and extract
<shell>
[lifo@null ~]$ mkdir rubygc
@@ -328,7 +328,7 @@ h5. Apply the patch
[lifo@null ruby-version]$ curl http://rubyforge.org/tracker/download.php/1814/7062/17676/3291/ruby186gc.patch | patch -p0
</shell>
-h5. Configure and Install
+h5. Configure and install
The following will install ruby in your home directory's +/rubygc+ directory. Make sure to replace +<homedir>+ with a full patch to your actual home directory.
@@ -349,11 +349,11 @@ alias gcirb='~/rubygc/bin/irb'
alias gcrails='~/rubygc/bin/rails'
</shell>
-h5. Install rubygems and dependency gems
+h5. Install Rubygems and dependency gems
Download "Rubygems":http://rubyforge.org/projects/rubygems and install it from source. Rubygem's README file should have necessary installation instructions.
-Additionally, install the following gems :
+Additionally, install the following gems:
* +rake+
* +rails+
@@ -374,9 +374,9 @@ h3. Command Line Tools
Writing performance test cases could be an overkill when you are looking for one time tests. Rails ships with two command line tools that enable quick and dirty performance testing:
-h4. benchmarker
+h4. +benchmarker+
-+benchmarker+ is a wrapper around Ruby's "Benchmark":http://ruby-doc.org/core/classes/Benchmark.html module.
++benchmarker+ is a wrapper around Ruby's "Benchmark":http://ruby-doc.org/core/classes/Benchmark.html standard library.
Usage:
@@ -396,7 +396,7 @@ If the +[times]+ argument is omitted, supplied methods are run just once:
$ script/performance/benchmarker 'Item.first' 'Item.last'
</shell>
-h4. profiler
+h4. +profiler+
+profiler+ is a wrapper around http://ruby-prof.rubyforge.org/[ruby-prof] gem.
@@ -426,7 +426,7 @@ If you want to store the output in a file:
$ script/performance/profiler 'Item.all' 10 graph 2> graph.txt
</shell>
-h3. Helper methods
+h3. Helper Methods
Rails provides various helper methods inside Active Record, Action Controller and Action View to measure the time taken by a given piece of code. The method is called +benchmark()+ in all the three components.
@@ -440,7 +440,7 @@ Project.benchmark("Creating project") do
end
</ruby>
-This benchmarks the code enclosed in the +Project.benchmark("Creating project") do..end+ block and prints the result to the log file:
+This benchmarks the code enclosed in the +Project.benchmark("Creating project") do...end+ block and prints the result to the log file:
<ruby>
Creating project (185.3ms)
@@ -490,27 +490,27 @@ For this section, we're only interested in the last line:
Completed in 5ms (View: 2, DB: 0) | 200 OK [http://0.0.0.0/items]
</shell>
-This data is fairly straightforward to understand. Rails uses millisecond(ms) as the metric to measures the time taken. The complete request spent 5 ms inside Rails, out of which 2 ms were spent rendering views and none was spent communication with the database. It's safe to assume that the remaining 3 ms were spent inside the controller.
+This data is fairly straightforward to understand. Rails uses millisecond(ms) as the metric to measure the time taken. The complete request spent 5 ms inside Rails, out of which 2 ms were spent rendering views and none was spent communication with the database. It's safe to assume that the remaining 3 ms were spent inside the controller.
Michael Koziarski has an "interesting blog post":http://www.therailsway.com/2009/1/6/requests-per-second explaining the importance of using milliseconds as the metric.
h3. Useful Links
-h4. Rails Plugins and Gems
+h4. Rails plugins and gems
* "Rails Analyzer":http://rails-analyzer.rubyforge.org
* "Palmist":http://www.flyingmachinestudios.com/projects
* "Rails Footnotes":http://github.com/josevalim/rails-footnotes/tree/master
* "Query Reviewer":http://github.com/dsboulder/query_reviewer/tree/master
-h4. Generic Tools
+h4. Generic tools
* "httperf":http://www.hpl.hp.com/research/linux/httperf
* "ab":http://httpd.apache.org/docs/2.2/programs/ab.html
* "JMeter":http://jakarta.apache.org/jmeter
* "kcachegrind":http://kcachegrind.sourceforge.net/html/Home.html
-h4. Tutorials and Documentation
+h4. Tutorials and documentation
* "ruby-prof API Documentation":http://ruby-prof.rubyforge.org
* "Request Profiling Railscast":http://railscasts.com/episodes/98-request-profiling - Outdated, but useful for understanding call graphs
diff --git a/railties/guides/source/plugins.textile b/railties/guides/source/plugins.textile
index c4fa7a88cd..d2fb157e35 100644
--- a/railties/guides/source/plugins.textile
+++ b/railties/guides/source/plugins.textile
@@ -51,7 +51,7 @@ NOTE: The aforementioned instructions will work for sqlite3. For more detailed
h4. Generate the plugin skeleton
-Rails ships with a plugin generator which creates a basic plugin skeleton. Pass the plugin name, either 'CamelCased' or 'under_scored', as an argument. Pass +\--with-generator+ to add an example generator also.
+Rails ships with a plugin generator which creates a basic plugin skeleton. Pass the plugin name, either 'CamelCased' or 'under_scored', as an argument. Pass +--with-generator+ to add an example generator also.
This creates a plugin in 'vendor/plugins' including an 'init.rb' and 'README' as well as standard 'lib', 'task', and 'test' directories.
@@ -63,7 +63,7 @@ Examples:
To get more detailed help on the plugin generator, type +./script/generate plugin+.
-Later on this guide will describe how to work with generators, so go ahead and generate your plugin with the +\--with-generator+ option now:
+Later on this guide will describe how to work with generators, so go ahead and generate your plugin with the +--with-generator+ option now:
<pre>
./script/generate plugin yaffle --with-generator
@@ -796,7 +796,7 @@ You can also see if your routes work by running +rake routes+ from your app dire
h3. Generators
-Many plugins ship with generators. When you created the plugin above, you specified the --with-generator option, so you already have the generator stubs in 'vendor/plugins/yaffle/generators/yaffle'.
+Many plugins ship with generators. When you created the plugin above, you specified the +--with-generator+ option, so you already have the generator stubs in 'vendor/plugins/yaffle/generators/yaffle'.
Building generators is a complex topic unto itself and this section will cover one small aspect of generators: generating a simple text file.
@@ -1504,3 +1504,9 @@ The final plugin should have a directory structure that looks something like thi
| `-- yaffle_test.rb
`-- uninstall.rb
</shell>
+
+h3. Changelog
+
+"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213/tickets/32-update-plugins-guide
+
+* November 17, 2008: Major revision by Jeff Dean
diff --git a/railties/guides/source/rails_on_rack.textile b/railties/guides/source/rails_on_rack.textile
index a0aec82d67..e300e047b4 100644
--- a/railties/guides/source/rails_on_rack.textile
+++ b/railties/guides/source/rails_on_rack.textile
@@ -5,8 +5,7 @@ This guide covers Rails integration with Rack and interfacing with other Rack co
* Create Rails Metal applications
* Use Rack Middlewares in your Rails applications
* Understand Action Pack's internal Middleware stack
-* Define custom internal Middleware stack
-* Understand the best practices for developing a middleware aimed at Rails applications
+* Define a custom Middleware stack
endprologue.
@@ -27,28 +26,31 @@ Explaining Rack is not really in the scope of this guide. In case you are not fa
h3. Rails on Rack
-h4. ActionController::Dispatcher.new
+h4. Rails Application's Rack Object
-+ActionController::Dispatcher.new+ is the primary Rack application object of a Rails application. It responds to +call+ method with a single +env+ argument and returns a Rack response. Any Rack compliant web server should be using +ActionController::Dispatcher.new+ object to serve a Rails application.
+<tt>ActionController::Dispatcher.new</tt> is the primary Rack application object of a Rails application. Any Rack compliant web server should be using +ActionController::Dispatcher.new+ object to serve a Rails application.</p>
h4. script/server
-+script/server+ does the basic job of creating a +Rack::Builder+ object and starting the webserver. This is Rails equivalent of Rack's +rackup+ script.
+<tt>script/server</tt> does the basic job of creating a +Rack::Builder+ object and starting the webserver. This is Rails' equivalent of Rack's +rackup+ script.
Here's how +script/server+ creates an instance of +Rack::Builder+
<ruby>
app = Rack::Builder.new {
use Rails::Rack::LogTailer unless options[:detach]
- use Rails::Rack::Static
use Rails::Rack::Debugger if options[:debugger]
- run ActionController::Dispatcher.new
+
+ map "/" do
+ use Rails::Rack::Static
+ run ActionController::Dispatcher.new
+ end
}.to_app
</ruby>
-Middlewares used in the code above are most useful in development envrionment. The following table explains their usage:
+Middlewares used in the code above are primarily useful only in the development envrionment. The following table explains their usage:
-|Middleware|Purpose|
+|_.Middleware|_.Purpose|
|Rails::Rack::LogTailer|Appends log file output to console|
|Rails::Rack::Static|Serves static files inside +RAILS_ROOT/public+ directory|
|Rails::Rack::Debugger|Starts Debugger|
@@ -82,7 +84,7 @@ h3. Action Controller Middleware Stack
Many of Action Controller's internal components are implemented as Rack middlewares. +ActionController::Dispatcher+ uses +ActionController::MiddlewareStack+ to combine various internal and external middlewares to form a complete Rails Rack application.
-NOTE: +ActionController::MiddlewareStack+ is Rails equivalent of +Rack::Builder+, but built for better flexibility and more features to meet Rails' requirements.
+NOTE: +ActionController::MiddlewareStack+ is Rails' equivalent of +Rack::Builder+, but built for better flexibility and more features to meet Rails' requirements.
h4. Inspecting Middleware Stack
@@ -92,90 +94,119 @@ Rails has a handy rake task for inspecting the middleware stack in use:
$ rake middleware
</shell>
-For a freshly generated Rails application, this will produce:
+For a freshly generated Rails application, this might produce something like:
<ruby>
-use ActionController::Lock
+use Rack::Lock
use ActionController::Failsafe
-use ActiveRecord::QueryCache
-use ActionController::Session::CookieStore, {:secret=>"<secret>", :session_key=>"_<app>_session"}
+use ActionController::Session::CookieStore, , {:secret=>"<secret>", :session_key=>"_<app>_session"}
use Rails::Rack::Metal
-use ActionController::VerbPiggybacking
+use ActionController::RewindableInput
+use ActionController::ParamsParser
+use Rack::MethodOverride
+use Rack::Head
+use ActiveRecord::QueryCache
run ActionController::Dispatcher.new
</ruby>
-h4. Adding Middlewares
+Purpose of each of this middlewares is explained in "Internal Middlewares":#internal-middleware-stack section.
+
+h4. Configuring Middleware Stack
+
+Rails provides a simple configuration interface +config.middleware+ for adding, removing and modifying the middlewares in the middleware stack via +environment.rb+ or the environment specific configuration file <tt>environments/&lt;environment&gt;.rb</tt>.
+
+h5. Adding a Middleware
+
+You can add a new middleware to the middleware stack using any of the following methods:
-Rails provides a very simple configuration interface for adding generic Rack middlewares to a Rails applications.
+* +config.middleware.add(new_middleware, args)+ - Adds the new middleware at the bottom of the middleware stack.
-Here's how you can add middlewares via +environment.rb+
+* +config.middleware.insert(index, new_middleware, args)+ - Adds the new middleware at the position specified by +index+ in the middleware stack.
+
+* +config.middleware.insert_before(existing_middleware, new_middleware, args)+ - Adds the new middleware before the specified existing middleware in the middleware stack.
+
+* +config.middleware.insert_after(existing_middleware, new_middleware, args)+ - Adds the new middleware after the specified existing middleware in the middleware stack.
+
+<strong>Example:</strong>
<ruby>
# environment.rb
+# Push Rack::BounceFavicon at the bottom
config.middleware.use Rack::BounceFavicon
+
+# Add Lifo::Cache after ActiveRecord::QueryCache.
+# Pass { :page_cache => false } argument to Lifo::Cache.
+config.middleware.insert_after ActiveRecord::QueryCache, Lifo::Cache, :page_cache => false
</ruby>
-h4. Internal Middleware Stack
+h5. Swapping a Middleware
+
+You can swap an existing middleware in the middleware stack using +config.middleware.swap+.
+
+<strong>Example:</strong>
<ruby>
-use "ActionController::Lock", :if => lambda {
- !ActionController::Base.allow_concurrency
-}
-
-use "ActionController::Failsafe"
-
-use "ActiveRecord::QueryCache", :if => lambda { defined?(ActiveRecord) }
-
-["ActionController::Session::CookieStore",
- "ActionController::Session::MemCacheStore",
- "ActiveRecord::SessionStore"].each do |store|
- use(store, ActionController::Base.session_options,
- :if => lambda {
- if session_store = ActionController::Base.session_store
- session_store.name == store
- end
- }
- )
-end
+# environment.rb
-use ActionController::VerbPiggybacking
+# Replace ActionController::Failsafe with Lifo::Failsafe
+config.middleware.swap ActionController::Failsafe, Lifo::Failsafe
</ruby>
-|Middleware|Purpose|
-|ActionController::Lock|Sets +env["rack.multithread"]+ flag to +true+ and wraps the application within a Mutex.|
+h4. Internal Middleware Stack
+
+Much of Action Controller's functionality is implemented as Middlewares. The following table explains the purpose of each of them:
+
+|_.Middleware|_.Purpose|
+|Rack::Lock|Sets +env["rack.multithread"]+ flag to +true+ and wraps the application within a Mutex.|
|ActionController::Failsafe|Returns HTTP Status +500+ to the client if an exception gets raised while dispatching.|
|ActiveRecord::QueryCache|Enable the Active Record query cache.|
|ActionController::Session::CookieStore|Uses the cookie based session store.|
|ActionController::Session::MemCacheStore|Uses the memcached based session store.|
|ActiveRecord::SessionStore|Uses the database based session store.|
-|ActionController::VerbPiggybacking|Sets HTTP method based on +_method+ parameter or +env["HTTP_X_HTTP_METHOD_OVERRIDE"]+.|
+|Rack::MethodOverride|Sets HTTP method based on +_method+ parameter or +env["HTTP_X_HTTP_METHOD_OVERRIDE"]+.|
+|Rack::Head|Discards the response body if the client sends a +HEAD+ request.|
+
+TIP: It's possible to use any of the above middlewares in your custom Rack stack.
h4. Customizing Internal Middleware Stack
-VERIFY THIS WORKS. Just a code dump at the moment.
+It's possible to replace the entire middleware stack with a custom stack using +ActionController::Dispatcher.middleware=+.
+
+<strong>Example:</strong>
+
+Put the following in an initializer:
-Put the following in an initializer.
<ruby>
+# config/initializers/stack.rb
ActionController::Dispatcher.middleware = ActionController::MiddlewareStack.new do |m|
- m.use ActionController::Lock
m.use ActionController::Failsafe
m.use ActiveRecord::QueryCache
- m.use ActionController::Session::CookieStore
- m.use ActionController::VerbPiggybacking
+ m.use Rack::Head
end
</ruby>
+And now inspecting the middleware stack:
+
+<shell>
+$ rake middleware
+(in /Users/lifo/Rails/blog)
+use ActionController::Failsafe
+use ActiveRecord::QueryCache
+use Rack::Head
+run ActionController::Dispatcher.new
+</shell>
+
h3. Rails Metal Applications
Rails Metal applications are minimal Rack applications specially designed for integrating with a typical Rails application. As Rails Metal Applications skip all of the Action Controller stack, serving a request has no overhead from the Rails framework itself. This is especially useful for infrequent cases where the performance of the full stack Rails framework is an issue.
h4. Generating a Metal Application
-Rails provides a generator called +performance_test+ for creating new performance tests:
+Rails provides a generator called +metal+ for creating a new Metal application:
<shell>
-script/generate metal poller
+$ script/generate metal poller
</shell>
This generates +poller.rb+ in the +app/metal+ directory:
@@ -217,10 +248,9 @@ In the code above, +@metals+ is an ordered ( alphabetical ) hash of metal applic
WARNING: Metal applications cannot return the HTTP Status +404+ to a client, as it is used for continuing the Metal chain execution. Please use normal Rails controllers or a custom middleware if returning +404+ is a requirement.
-h3. Middlewares and Rails
-
h3. Changelog
-"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/4
+"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/58
-* January 11, 2009: First version by "Pratik":credits.html#lifo \ No newline at end of file
+* February 7, 2009: Second version by "Pratik":credits.html#lifo
+* January 11, 2009: First version by "Pratik":credits.html#lifo
diff --git a/railties/guides/source/routing.textile b/railties/guides/source/routing.textile
index c31307e77f..c26a5cd6ee 100644
--- a/railties/guides/source/routing.textile
+++ b/railties/guides/source/routing.textile
@@ -131,7 +131,7 @@ map.resources :photos
creates seven different routes in your application:
-|_.HTTP verb |_.URL |_.controller |_.action |_.used for|
+|_.HTTP verb|_.URL |_.controller|_.action |_.used for|
|GET |/photos |Photos |index |display a list of all photos|
|GET |/photos/new |Photos |new |return an HTML form for creating a new photo|
|POST |/photos |Photos |create |create a new photo|
@@ -188,7 +188,7 @@ map.resource :geocoder
creates six different routes in your application:
-|_.HTTP verb |_.URL |_.controller |_.action |_.used for|
+|_.HTTP verb|_.URL |_.controller|_.action |_.used for|
|GET |/geocoder/new |Geocoders |new |return an HTML form for creating the new geocoder|
|POST |/geocoder |Geocoders |create |create the new geocoder|
|GET |/geocoder |Geocoders |show |display the one and only geocoder resource|
@@ -231,7 +231,7 @@ map.resources :photos, :controller => "images"
will recognize incoming URLs containing +photo+ but route the requests to the Images controller:
-|_.HTTP verb |_.URL |_.controller |_.action |_.used for|
+|_.HTTP verb|_.URL |_.controller|_.action |_.used for|
|GET |/photos |Images |index |display a list of all images|
|GET |/photos/new |Images |new |return an HTML form for creating a new image|
|POST |/photos |Images |create |create a new image|
@@ -250,9 +250,9 @@ Rails allows you to group your controllers into namespaces by saving them in fol
map.resources :adminphotos, :controller => "admin/photos"
</ruby>
-If you use controller namespaces, you need to be aware of a subtlety in the Rails routing code: it always tries to preserve as much of the namespace from the previous request as possible. For example, if you are on a view generated from the +adminphoto_path+ helper, and you follow a link generated with +<%= link_to "show", adminphoto(1) %>+ you will end up on the view generated by +admin/photos/show+ but you will also end up in the same place if you have +<%= link_to "show", {:controller => "photos", :action => "show"} %>+ because Rails will generate the show URL relative to the current URL.
+If you use controller namespaces, you need to be aware of a subtlety in the Rails routing code: it always tries to preserve as much of the namespace from the previous request as possible. For example, if you are on a view generated from the +adminphoto_path+ helper, and you follow a link generated with +&lt;%= link_to "show", adminphoto(1) %&gt;+ you will end up on the view generated by +admin/photos/show+, but you will also end up in the same place if you have +&lt;%= link_to "show", {:controller => "photos", :action => "show"} %&gt;+ because Rails will generate the show URL relative to the current URL.
-TIP: If you want to guarantee that a link goes to a top-level controller, use a preceding slash to anchor the controller name: +<%= link_to "show", {:controller => "/photos", :action => "show"} %>+
+TIP: If you want to guarantee that a link goes to a top-level controller, use a preceding slash to anchor the controller name: +&lt;%= link_to "show", {:controller => "/photos", :action => "show"} %&gt;+
You can also specify a controller namespace with the +:namespace+ option instead of a path:
@@ -304,7 +304,7 @@ map.resources :photos, :as => "images"
will recognize incoming URLs containing +image+ but route the requests to the Photos controller:
-|_.HTTP verb |_.URL |_.controller |_.action |_:used for|
+|_.HTTP verb|_.URL |_.controller|_.action |_:used for|
|GET |/images |Photos |index |display a list of all photos|
|GET |/images/new |Photos |new |return an HTML form for creating a new photo|
|POST |/images |Photos |create |create a new photo|
@@ -330,7 +330,7 @@ This would cause the routing to recognize URLs such as
/photos/1/change
</pre>
-NOTE: The actual action names aren't changed by this option; the two URLs show would still route to the new and edit actions.
+NOTE: The actual action names aren't changed by this option; the two URLs shown would still route to the new and edit actions.
TIP: If you find yourself wanting to change this option uniformly for all of your routes, you can set a default in your environment:
@@ -362,8 +362,10 @@ h5. Using :name_prefix
You can use the :name_prefix option to avoid collisions between routes. This is most useful when you have two resources with the same name that use +:path_prefix+ to map differently. For example:
<ruby>
-map.resources :photos, :path_prefix => '/photographers/:photographer_id', :name_prefix => 'photographer_'
-map.resources :photos, :path_prefix => '/agencies/:agency_id', :name_prefix => 'agency_'
+map.resources :photos, :path_prefix => '/photographers/:photographer_id',
+ :name_prefix => 'photographer_'
+map.resources :photos, :path_prefix => '/agencies/:agency_id',
+ :name_prefix => 'agency_'
</ruby>
This combination will give you route helpers such as +photographer_photos_path+ and +agency_edit_photo_path+ to use in your code.
@@ -386,7 +388,7 @@ The +:except+ option specifies a route or list of routes that should _not_ be ge
map.resources :photos, :except => :destroy
</ruby>
-In this case, all of the normal routes except the route for +destroy+ (a +DELETE+ request to +/photos/_id_+) will be generated.
+In this case, all of the normal routes except the route for +destroy+ (a +DELETE+ request to +/photos/<em>id</em>+) will be generated.
In addition to an action or a list of actions, you can also supply the special symbols +:all+ or +:none+ to the +:only+ and +:except+ options.
@@ -414,9 +416,11 @@ map.resources :magazines do |magazine|
end
</ruby>
+TIP: Further below you'll learn about a convenient shortcut for this construct:<br/>+map.resources :magazines, :has_many => :ads+.
+
In addition to the routes for magazines, this declaration will also create routes for ads, each of which requires the specification of a magazine in the URL:
-|_.HTTP verb |_.URL |_.controller |_.action |_.used for|
+|_.HTTP verb|_.URL |_.controller|_.action |_.used for|
|GET |/magazines/1/ads |Ads |index |display a list of all ads for a specific magazine|
|GET |/magazines/1/ads/new |Ads |new |return an HTML form for creating a new ad belonging to a specific magazine|
|POST |/magazines/1/ads |Ads |create |create a new ad belonging to a specific magazine|
@@ -491,7 +495,7 @@ However, without the use of +name_prefix => nil+, deeply-nested resources quickl
The corresponding route helper would be +publisher_magazine_photo_url+, requiring you to specify objects at all three levels. Indeed, this situation is confusing enough that a popular "article":http://weblog.jamisbuck.org/2007/2/5/nesting-resources by Jamis Buck proposes a rule of thumb for good Rails design:
-_Resources should never be nested more than 1 level deep._
+TIP: _Resources should never be nested more than 1 level deep._
h5. Shallow Nesting
@@ -612,13 +616,7 @@ map.resources :photos, :new => { :upload => :post }
This will enable Rails to recognize URLs such as +/photos/upload+ using the POST HTTP verb, and route them to the upload action of the Photos controller. It will also create a +upload_photos+ route helper.
-TIP: If you want to redefine the verbs accepted by one of the standard actions, you can do so by explicitly mapping that action. For example:
-
-<ruby>
-map.resources :photos, :new => { :new => :any }
-</ruby>
-
-This will allow the new action to be invoked by any request to +photos/new+, no matter what HTTP verb you use.
+TIP: If you want to redefine the verbs accepted by one of the standard actions, you can do so by explicitly mapping that action. For example:<br/>+map.resources :photos, :new => { :new => :any }+<br/>This will allow the new action to be invoked by any request to +photos/new+, no matter what HTTP verb you use.
h5. A Note of Caution
@@ -680,10 +678,11 @@ map.connect 'photos/:id', :controller => 'photos', :action => 'show'
With this route, an incoming URL of +/photos/12+ would be dispatched to the +show+ action within the +Photos+ controller.
-You an also define other defaults in a route by supplying a hash for the +:defaults+ option. This even applies to parameters that are not explicitly defined elsewhere in the route. For example:
+You can also define other defaults in a route by supplying a hash for the +:defaults+ option. This even applies to parameters that are not explicitly defined elsewhere in the route. For example:
<ruby>
-map.connect 'photos/:id', :controller => 'photos', :action => 'show', :defaults => { :format => 'jpg' }
+map.connect 'photos/:id', :controller => 'photos', :action => 'show',
+ :defaults => { :format => 'jpg' }
</ruby>
With this route, an incoming URL of +photos/12+ would be dispatched to the +show+ action within the +Photos+ controller, and +params[:format]+ will be set to +jpg+.
@@ -899,6 +898,6 @@ h3. Changelog
"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/3
-* October 4, 2008: Added additional detail on specifying verbs for resource member/collection routes , by "Mike Gunderloy":credits.html#mgunderloy
+* October 4, 2008: Added additional detail on specifying verbs for resource member/collection routes, by "Mike Gunderloy":credits.html#mgunderloy
* September 23, 2008: Added section on namespaced controllers and routing, by "Mike Gunderloy":credits.html#mgunderloy
* September 10, 2008: initial version by "Mike Gunderloy":credits.html#mgunderloy
diff --git a/railties/guides/source/security.textile b/railties/guides/source/security.textile
index a89cfaddbe..6b84ca1965 100644
--- a/railties/guides/source/security.textile
+++ b/railties/guides/source/security.textile
@@ -23,7 +23,7 @@ The Gartner Group however estimates that 75% of attacks are at the web applicati
The threats against web applications include user account hijacking, bypass of access control, reading or modifying sensitive data, or presenting fraudulent content. Or an attacker might be able to install a Trojan horse program or unsolicited e-mail sending software, aim at financial enrichment or cause brand name damage by modifying company resources. In order to prevent attacks, minimize their impact and remove points of attack, first of all, you have to fully understand the attack methods in order to find the correct countermeasures. That is what this guide aims at.
-In order to develop secure web applications you have to keep up to date on all layers and know your enemies. To keep up to date subscribe to security mailing lists, read security blogs and make updating and security checks a habit (check the Additional Resources chapter). I do it manually because that‘s how you find the nasty logical security problems.
+In order to develop secure web applications you have to keep up to date on all layers and know your enemies. To keep up to date subscribe to security mailing lists, read security blogs and make updating and security checks a habit (check the <a href="#additionalresources">Additional Resources</a> chapter). I do it manually because that‘s how you find the nasty logical security problems.
h3. Sessions
@@ -31,7 +31,7 @@ A good place to start looking at security is with sessions, which can be vulnera
h4. What are sessions?
--- _HTTP is a stateless protocol Sessions make it stateful._
+-- _HTTP is a stateless protocol. Sessions make it stateful._
Most applications need to keep track of certain state of a particular user. This could be the contents of a shopping basket or the user id of the currently logged in user. Without the idea of sessions, the user would have to identify, and probably authenticate, on every request.
Rails will create a new session automatically if a new user accesses the application. It will load an existing session if the user has already used the application.
@@ -61,11 +61,11 @@ Hence, the cookie serves as temporary authentication for the web application. Ev
* Most people don't clear out the cookies after working at a public terminal. So if the last user didn't log out of a web application, you would be able to use it as this user. Provide the user with a _(highlight)log-out button_ in the web application, and _(highlight)make it prominent_.
-* Many cross-site scripting (XSS) exploits aim at obtaining the user's cookie. You'll read more about XSS later.
+* Many cross-site scripting (XSS) exploits aim at obtaining the user's cookie. You'll read <a href="#cross-site-scripting-xss">more about XSS</a> later.
* Instead of stealing a cookie unknown to the attacker, he fixes a user's session identifier (in the cookie) known to him. Read more about this so-called session fixation later.
-The main objective of most attackers is to make money. The underground prices for stolen bank login accounts range from $10-$1000 (depending on the available amount of funds), $0.40-$20 for credit card numbers, $1-$8 for online auction site accounts and $4-$30 for email passwords, according to the "Symantec Global Internet Security Threat Report":http://eval.symantec.com/mktginfo/enterprise/white_papers/b-whitepaper_internet_security_threat_report_xiii_04-2008.en-us.pdf.
+The main objective of most attackers is to make money. The underground prices for stolen bank login accounts range from $10–$1000 (depending on the available amount of funds), $0.40–$20 for credit card numbers, $1–$8 for online auction site accounts and $4–$30 for email passwords, according to the "Symantec Global Internet Security Threat Report":http://eval.symantec.com/mktginfo/enterprise/white_papers/b-whitepaper_internet_security_threat_report_xiii_04-2008.en-us.pdf.
h4. Session guidelines
@@ -85,16 +85,16 @@ There are a number of session storages, i.e. where Rails saves the session hash
Rails 2 introduced a new default session storage, CookieStore. CookieStore saves the session hash directly in a cookie on the client-side. The server retrieves the session hash from the cookie and eliminates the need for a session id. That will greatly increase the speed of the application, but it is a controversial storage option and you have to think about the security implications of it:
-* Cookies imply a strict size limit of 4K. This is fine as you should not store large amounts of data in a session anyway, as described before. _(highlight)Storing the current user's database id in a session is usually ok_.
+* Cookies imply a strict size limit of 4kB. This is fine as you should not store large amounts of data in a session anyway, as described before. _(highlight)Storing the current user's database id in a session is usually ok_.
* The client can see everything you store in a session, because it is stored in clear-text (actually Base64-encoded, so not encrypted). So, of course, _(highlight)you don't want to store any secrets here_. To prevent session hash tampering, a digest is calculated from the session with a server-side secret and inserted into the end of the cookie.
-That means the security of this storage depends on this secret (and of the digest algorithm, which defaults to SHA512, which has not been compromised, yet). So _(highlight)don't use a trivial secret, i.e. a word from a dictionary, or one which is shorter than 30 characters_. Put the secret in your environment.rb:
+That means the security of this storage depends on this secret (and on the digest algorithm, which defaults to SHA512, which has not been compromised, yet). So _(highlight)don't use a trivial secret, i.e. a word from a dictionary, or one which is shorter than 30 characters_. Put the secret in your environment.rb:
<pre>
config.action_controller.session = {
- :key =&gt; ‘_app_session’,
- :secret =&gt; ‘0x0dkfj3927dkc7djdh36rkckdfzsg...’
+ :key => '_app_session',
+ :secret => '0x0dkfj3927dkc7djdh36rkckdfzsg...'
}
</pre>
@@ -106,7 +106,7 @@ h4. Replay attacks for CookieStore sessions
It works like this:
-* A user receives credits, the amount is stored in a session (which is bad idea, anyway, but we'll do this for demonstration purposes).
+* A user receives credits, the amount is stored in a session (which is a bad idea anyway, but we'll do this for demonstration purposes).
* The user buys something.
* His new, lower credit will be stored in the session.
* The dark side of the user forces him to take the cookie from the first step (which he copied) and replace the current cookie in the browser.
@@ -120,21 +120,15 @@ h4. Session fixation
-- _Apart from stealing a user's session id, the attacker may fix a session id known to him. This is called session fixation._
-image::images/session_fixation.png[Session fixation]
+!images/session_fixation.png(Session fixation)!
This attack focuses on fixing a user's session id known to the attacker, and forcing the user's browser into using this id. It is therefore not necessary for the attacker to steal the session id afterwards. Here is how this attack works:
# The attacker creates a valid session id: He loads the login page of the web application where he wants to fix the session, and takes the session id in the cookie from the response (see number 1 and 2 in the image).
-
# He possibly maintains the session. Expiring sessions, for example every 20 minutes, greatly reduces the time-frame for attack. Therefore he accesses the web application from time to time in order to keep the session alive.
-
-# Now the attacker will force the user's browser into using this session id (see number 3 in the image). As you may not change a cookie of another domain (because of the same origin policy), the attacker has to run a JavaScript from the domain of the target web application. Injecting the JavaScript code into the application by XSS accomplishes this attack. Here is an example: +&lt;script&gt;
document.cookie="_session_id=16d5b78abb28e3d6206b60f22a03c8d9";
&lt;/script&gt;+
-Read more about XSS and injection later on.
-
+# Now the attacker will force the user's browser into using this session id (see number 3 in the image). As you may not change a cookie of another domain (because of the same origin policy), the attacker has to run a JavaScript from the domain of the target web application. Injecting the JavaScript code into the application by XSS accomplishes this attack. Here is an example: +&lt;script&gt;
document.cookie="_session_id=16d5b78abb28e3d6206b60f22a03c8d9";
&lt;/script&gt;+. Read more about XSS and injection later on.
# The attacker lures the victim to the infected page with the JavaScript code. By viewing the page, the victim's browser will change the session id to the trap session id.
-
# As the new trap session is unused, the web application will require the user to authenticate.
-
# From now on, the victim and the attacker will co-use the web application with the same session: The session became valid and the victim didn't notice the attack.
h4. Session fixation – Countermeasures
@@ -155,7 +149,7 @@ h4. Session expiry
-- _Sessions that never expire extend the time-frame for attacks such as cross-site reference forgery (CSRF), session hijacking and session fixation._
-One possibility is to set the expiry time-stamp of the cookie with the session id. However the client can edit cookies that are stored in the web browser so expiring sessions on the server is safer. Here is an example of how to _(highlight)expire sessions in a database table_. Call Session.sweep("20m") to expire sessions that were used longer than 20 minutes ago.
+One possibility is to set the expiry time-stamp of the cookie with the session id. However the client can edit cookies that are stored in the web browser so expiring sessions on the server is safer. Here is an example of how to _(highlight)expire sessions in a database table_. Call +Session.sweep("20m")+ to expire sessions that were used longer than 20 minutes ago.
<ruby>
class Session < ActiveRecord::Base
@@ -174,7 +168,8 @@ class Session < ActiveRecord::Base
The section about session fixation introduced the problem of maintained sessions. An attacker maintaining a session every five minutes can keep the session alive forever, although you are expiring sessions. A simple solution for this would be to add a created_at column to the sessions table. Now you can delete sessions that were created a long time ago. Use this line in the sweep method above:
<ruby>
-self.delete_all "updated_at < '#{time.to_s(:db)}' OR created_at < '#{2.days.ago.to_s(:db)}'"
+self.delete_all "updated_at < '#{time.to_s(:db)}' OR
+ created_at < '#{2.days.ago.to_s(:db)}'"
</ruby>
h3. Cross-Site Reference Forgery (CSRF)
@@ -183,7 +178,7 @@ h3. Cross-Site Reference Forgery (CSRF)
!images/csrf.png!
-In the session chapter you have learned that most Rails applications use cookie-based sessions. Either they store the session id in the cookie and have a server-side session hash, or the entire session hash is on the client-side. In either case the browser will automatically send along the cookie on every request to a domain, if it can find a cookie for that domain. The controversial point is, that it will also send the cookie, if the request comes from a site of a different domain. Let's start with an example:
+In the <a href="#sessions">session chapter</a> you have learned that most Rails applications use cookie-based sessions. Either they store the session id in the cookie and have a server-side session hash, or the entire session hash is on the client-side. In either case the browser will automatically send along the cookie on every request to a domain, if it can find a cookie for that domain. The controversial point is, that it will also send the cookie, if the request comes from a site of a different domain. Let's start with an example:
* Bob browses a message board and views a post from a hacker where there is a crafted HTML image element. The element references a command in Bob's project management application, rather than an image file.
* +&lt;img src="http://www.webapp.com/project/1/destroy"&gt;+
@@ -222,7 +217,7 @@ verify :method => :post, :only => [:transfer], :redirect_to => {:action => :list
With this precaution, the attack from above will not work, because the browser sends a GET request for images, which will not be accepted by the web application.
-But this was only the first step, because _(highlight)POST requests can be send automatically, too_. Here is an example for a link which displays www.harmless.com as destination in the browser's status bar. In fact it dynamically creates a new form that sends a POST request.
+But this was only the first step, because _(highlight)POST requests can be sent automatically, too_. Here is an example for a link which displays www.harmless.com as destination in the browser's status bar. In fact it dynamically creates a new form that sends a POST request.
<html>
<a href="http://www.harmless.com/" onclick="
@@ -249,7 +244,7 @@ protect_from_forgery :secret => "123456789012345678901234567890..."
This will automatically include a security token, calculated from the current session and the server-side secret, in all forms and Ajax requests generated by Rails. You won't need the secret, if you use CookieStorage as session storage. It will raise an ActionController::InvalidAuthenticityToken error, if the security token doesn't match what was expected.
-Note that _(highlight)cross-site scripting (XSS) vulnerabilities bypass all CSRF protections_. XSS gives the attacker access to all elements on a page, so he can read the CSRF security token from a form or directly submit the form. Read more about XSS later.
+Note that _(highlight)cross-site scripting (XSS) vulnerabilities bypass all CSRF protections_. XSS gives the attacker access to all elements on a page, so he can read the CSRF security token from a form or directly submit the form. Read <a href="#cross-site-scripting-xss">more about XSS</a> later.
h3. Redirection and Files
@@ -269,7 +264,9 @@ end
This will redirect the user to the main action if he tried to access a legacy action. The intention was to preserve the URL parameters to the legacy action and pass them to the main action. However, it can exploited by an attacker if he includes a host key in the URL:
-+http://www.example.com/site/legacy?param1=xy&param2=23&host=www.attacker.com+
+<pre>
+http://www.example.com/site/legacy?param1=xy&param2=23&host=www.attacker.com
+</pre>
If it is at the end of the URL it will hardly be noticed and redirects the user to the attacker.com host. A simple countermeasure would be to _(highlight)include only the expected parameters in a legacy action_ (again a whitelist approach, as opposed to removing unexpected parameters). _(highlight)And if you redirect to an URL, check it with a whitelist or a regular expression_.
@@ -304,11 +301,11 @@ end
A significant disadvantage of synchronous processing of file uploads (as the attachment_fu plugin may do with images), is its _(highlight)vulnerability to denial-of-service attacks_. An attacker can synchronously start image file uploads from many computers which increases the server load and may eventually crash or stall the server.
-The solution to this, is best to _(highlight)process media files asynchronously_: Save the media file and schedule a processing request in the database. A second process will handle the processing of the file in the background.
+The solution to this is best to _(highlight)process media files asynchronously_: Save the media file and schedule a processing request in the database. A second process will handle the processing of the file in the background.
h4. Executable code in file uploads
--- _Source code in uploaded files may be executed when placed in specific directories. Do not place file uploads in Rails /public directory if it is Apache's home directory._
+-- _Source code in uploaded files may be executed when placed in specific directories. Do not place file uploads in Rails' /public directory if it is Apache's home directory._
The popular Apache web server has an option called DocumentRoot. This is the home directory of the web site, everything in this directory tree will be served by the web server. If there are files with a certain file name extension, the code in it will be executed when requested (might require some options to be set). Examples for this are PHP and CGI files. Now think of a situation where an attacker uploads a file “file.cgi” with code in it, which will be executed when someone downloads the file.
@@ -344,11 +341,11 @@ In 2007 there was the first tailor-made "Trojan":http://www.symantec.com/enterpr
*XSS* If your application re-displays malicious user input from the extranet, the application will be vulnerable to XSS. User names, comments, spam reports, order addresses are just a few uncommon examples, where there can be XSS.
-Having one single place in the admin interface or Intranet where the input has not been sanitized, makes the entire application vulnerable. Possible exploits include stealing the privileged administrator's cookie, injecting an iframe to steal the administrator's password or installing malicious software through browser security holes to take over the administrator's computer.
+Having one single place in the admin interface or Intranet, where the input has not been sanitized, makes the entire application vulnerable. Possible exploits include stealing the privileged administrator's cookie, injecting an iframe to steal the administrator's password or installing malicious software through browser security holes to take over the administrator's computer.
Refer to the Injection section for countermeasures against XSS. It is _(highlight)recommended to use the SafeErb plugin_ also in an Intranet or administration interface.
-*CSRF* Cross-Site Reference Forgery (CSRF) is a giant attack method, it allows the attacker to do everything the administrator or Intranet user may do. As you have already seen above how CSRF works, here are a few examples of what attackers can do in the Intranet or admin interface.
+*CSRF* Cross-Site Reference Forgery (CSRF) is a gigantic attack method, it allows the attacker to do everything the administrator or Intranet user may do. As you have already seen above how CSRF works, here are a few examples of what attackers can do in the Intranet or admin interface.
A real-world example is a "router reconfiguration by CSRF":http://www.symantec.com/enterprise/security_response/weblog/2008/01/driveby_pharming_in_the_
wild.html. The attackers sent a malicious e-mail, with CSRF in it, to Mexican users. The e-mail claimed there was an e-card waiting for them, but it also contained an image tag that resulted in a HTTP-GET request to reconfigure the user's router (which is a popular model in Mexico). The request changed the DNS-settings so that requests to a Mexico-based banking site would be mapped to the attacker's site. Everyone who accessed the banking site through that router saw the attacker's fake web site and had his credentials stolen.
@@ -372,7 +369,7 @@ h3. Mass assignment
-- _Without any precautions Model.new(params[:model]) allows attackers to set any database column's value._
-The mass-assignment feature may become a problem, as it allows an attacker to set any model's attribute by manipulating the hash passed to a model's new() method:
+The mass-assignment feature may become a problem, as it allows an attacker to set any model's attributes by manipulating the hash passed to a model's +new()+ method:
<ruby>
def signup
@@ -384,7 +381,7 @@ end
Mass-assignment saves you much work, because you don't have to set each value individually. Simply pass a hash to the new() method, or assign attributes=(attributes) a hash value, to set the model's attributes to the values in the hash. The problem is that it is often used in conjunction with the parameters (params) hash available in the controller, which may be manipulated by an attacker. He may do so by changing the URL like this:
<pre>
-"name":http://www.example.com/user/signup?user=ow3ned&amp;user[admin]=1
+"name":http://www.example.com/user/signup?user=ow3ned&user[admin]=1
</pre>
This will set the following parameters in the controller:
@@ -403,7 +400,7 @@ To avoid this, Rails provides two class methods in your ActiveRecord class to co
attr_protected :admin
</ruby>
-A much better way, because it follows the whitelist-principle, is the _(highlight)attr_accessible method_. It is the exact opposite of attr_protected, because _(highlight)it takes a list of attributes that will be accessible_. All other attributes will be protected. This way you won't forget to protect attributes when adding new ones in the course of development. Here is an example:
+A much better way, because it follows the whitelist-principle, is the +attr_accessible+ method. It is the exact opposite of attr_protected, because _(highlight)it takes a list of attributes that will be accessible_. All other attributes will be protected. This way you won't forget to protect attributes when adding new ones in the course of development. Here is an example:
<ruby>
attr_accessible :name
@@ -423,7 +420,7 @@ h3. User management
-- _Almost every web application has to deal with authorization and authentication. Instead of rolling your own, it is advisable to use common plug-ins. But keep them up-to-date, too. A few additional precautions can make your application even more secure._
-There are some authorization and authentication plug-ins for Rails available. A good one saves only encrypted passwords, not plain-text passwords. The most popular plug-in is _(highlight)restful_authentication_ which protects from session fixation, too. However, earlier versions allowed you to login without user name and password in certain circumstances.
+There are some authorization and authentication plug-ins for Rails available. A good one saves only encrypted passwords, not plain-text passwords. The most popular plug-in is +restful_authentication+ which protects from session fixation, too. However, earlier versions allowed you to login without user name and password in certain circumstances.
Every new user gets an activation code to activate his account when he gets an e-mail with a link in it. After activating the account, the activation_code columns will be set to NULL in the database. If someone requested an URL like these, he would be logged in as the first activated user found in the database (and chances are that this is the administrator):
@@ -441,7 +438,7 @@ User.find_by_activation_code(params[:id])
If the parameter was nil, the resulting SQL query will be
<pre>
-SELECT * FROM users WHERE (users.+activation_code+ IS NULL) LIMIT 1
+SELECT * FROM users WHERE (users.activation_code IS NULL) LIMIT 1
</pre>
And thus it found the first user in the database, returned it and logged him in. You can find out more about it in "my blog post":http://www.rorsecurity.info/2007/10/28/restful_authentication-login-security/. _(highlight)It is advisable to update your plug-ins from time to time_. Moreover, you can review your application to find more flaws like this.
@@ -499,13 +496,13 @@ You can find more sophisticated negative CAPTCHAs in Ned Batchelder's "blog post
* Randomize the field names
* Include more than one honeypot field of all types, including submission buttons
-Note that this protects you only from automatic bots, targeted tailor-made bots cannot be stopped by this. So negative CAPTCHAs might not be good to protect login forms.
+Note that this protects you only from automatic bots, targeted tailor-made bots cannot be stopped by this. So _(highlight)negative CAPTCHAs might not be good to protect login forms_.
h4. Logging
-- _Tell Rails not to put passwords in the log files._
-By default, Rails logs all requests being made to the web application. But log files can be a huge security issue, as they may contain login credentials, credit card numbers etcetera. When designing a web application security concept, you should also think about what will happen if an attacker got (full) access to the web server. Encrypting secrets and passwords in the database will be quite useless, if the log files list them in clear text. You can _(highlight)filter certain request parameters from your log files_ by the filter_parameter_logging method in a controller. These parameters will be marked [FILTERED] in the log.
+By default, Rails logs all requests being made to the web application. But log files can be a huge security issue, as they may contain login credentials, credit card numbers et cetera. When designing a web application security concept, you should also think about what will happen if an attacker got (full) access to the web server. Encrypting secrets and passwords in the database will be quite useless, if the log files list them in clear text. You can _(highlight)filter certain request parameters from your log files_ by the filter_parameter_logging method in a controller. These parameters will be marked [FILTERED] in the log.
<ruby>
filter_parameter_logging :password
@@ -517,7 +514,7 @@ h4. Good passwords
Bruce Schneier, a security technologist, "has analysed":http://www.schneier.com/blog/archives/2006/12/realworld_passw.html 34,000 real-world user names and passwords from the MySpace phishing attack mentioned earlier. It turns out that most of the passwords are quite easy to crack. The 20 most common passwords are:
-password1, abc123, myspace1, password, blink182, qwerty1, ****you, 123abc, baseball1, football1, 123456, soccer, monkey1, liverpool1, princess1, jordan23, slipknot1, superman1, iloveyou1 and monkey.
+password1, abc123, myspace1, password, blink182, qwerty1, ****you, 123abc, baseball1, football1, 123456, soccer, monkey1, liverpool1, princess1, jordan23, slipknot1, superman1, iloveyou1, and monkey.
It is interesting that only 4% of these passwords were dictionary words and the great majority is actually alphanumeric. However, password cracker dictionaries contain a large number of today's passwords, and they try out all kinds of (alphanumerical) combinations. If an attacker knows your user name and you use a weak password, your account will be easily cracked.
@@ -651,7 +648,7 @@ Also, the second query renames some columns with the AS statement so that the we
h5. Countermeasures
-Ruby on Rails has a built in filter for special SQL characters, which will escape ' , " , NULL character and line breaks. _(highlight)Using Model.find(id) or Model.find_by_some thing(something) automatically applies this countermeasure_. But in SQL fragments, especially _(highlight)in conditions fragments (:conditions => "..."), the connection.execute() or Model.find_by_sql() methods, it has to be applied manually_.
+Ruby on Rails has a built in filter for special SQL characters, which will escape ' , " , NULL character and line breaks. <em class="highlight">Using +Model.find(id)+ or +Model.find_by_some thing(something)+ automatically applies this countermeasure</em>. But in SQL fragments, especially <em class="highlight">in conditions fragments (+:conditions => "..."+), the +connection.execute()+ or +Model.find_by_sql()+ methods, it has to be applied manually</em>.
Instead of passing a string to the conditions option, you can pass an array to sanitize tainted strings like this:
@@ -677,9 +674,9 @@ An entry point is a vulnerable URL and its parameters where an attacker can star
The most common entry points are message posts, user comments, and guest books, but project titles, document names and search result pages have also been vulnerable - just about everywhere where the user can input data. But the input does not necessarily have to come from input boxes on web sites, it can be in any URL parameter – obvious, hidden or internal. Remember that the user may intercept any traffic. Applications, such as the "Live HTTP Headers Firefox plugin":http://livehttpheaders.mozdev.org/, or client-site proxies make it easy to change requests.
-XSS attacks work like this: An attacker injects some code, the web application saves it and displays it on a page, later presented to a victim. Most XSS examples simply display an alert box, but it is more powerful than that. XSS can steal the cookie, hijack the session; redirect the victim to a fake website, display advertisements for the benefit of the attacker, change elements on the web site to get confidential information or install malicious software through security holes in the web browser.
+XSS attacks work like this: An attacker injects some code, the web application saves it and displays it on a page, later presented to a victim. Most XSS examples simply display an alert box, but it is more powerful than that. XSS can steal the cookie, hijack the session, redirect the victim to a fake website, display advertisements for the benefit of the attacker, change elements on the web site to get confidential information or install malicious software through security holes in the web browser.
-During the second half of 2007, there were 88 vulnerabilities reported in Mozilla browsers, 22 in Safari, 18 in IE, and 12 in Opera. The "Symantec Global Internet Security threat report":http://eval.symantec.com/mktginfo/enterprise/white_papers/b-whitepaper_internet_security_threat_report_xiii_04-2008.en-us.pdf also documented 239 browser plug-in vulnerabilities in the last six months of 2007. "Mpack":http://pandalabs.pandasecurity.com/archive/MPack-uncovered_2100_.aspx is a very active and up-to-date attack framework which exploits these vulnerabilities. For criminal hackers, it is very attractive to exploit an SQL-Injection vulnerability in a web application framework and insert malicious code in every textual table column. In April 2008 more than 510,000 sites "were hacked":http://www.0x000000.com/?i=556 like this, among them the British government, United Nations and many more high targets.
+During the second half of 2007, there were 88 vulnerabilities reported in Mozilla browsers, 22 in Safari, 18 in IE, and 12 in Opera. The "Symantec Global Internet Security threat report":http://eval.symantec.com/mktginfo/enterprise/white_papers/b-whitepaper_internet_security_threat_report_xiii_04-2008.en-us.pdf also documented 239 browser plug-in vulnerabilities in the last six months of 2007. "Mpack":http://pandalabs.pandasecurity.com/archive/MPack-uncovered_2100_.aspx is a very active and up-to-date attack framework which exploits these vulnerabilities. For criminal hackers, it is very attractive to exploit an SQL-Injection vulnerability in a web application framework and insert malicious code in every textual table column. In April 2008 more than 510,000 sites "were hacked":http://www.0x000000.com/?i=556 like this, among them the British government, United Nations, and many more high targets.
A relatively new, and unusual, form of entry points are banner advertisements. In earlier 2008, malicious code appeared in banner ads on popular sites, such as MySpace and Excite, according to "Trend Micro":http://blog.trendmicro.com/myspace-excite-and-blick-serve-up-malicious-banner-ads/.
@@ -762,7 +759,7 @@ s = sanitize(user_input, :tags => tags, :attributes => %w(href title))
This allows only the given tags and does a good job, even against all kinds of tricks and malformed tags.
-As a second step, _(highlight)it is good practice to escape all output of the application_, especially when re-displaying user input, which hasn't been input filtered (as in the search form example earlier on). _(highlight)Use escapeHTML() (or its alias h()) method_ to replace the HTML input characters &amp;,",&lt;,&gt; by its uninterpreted representations in HTML (&amp;, &quot;, &lt; and &gt;). However, it can easily happen that the programmer forgets to use it, so _(highlight)it is recommended to use the "SafeErb":http://safe-erb.rubyforge.org/svn/plugins/safe_erb/ plugin_. SafeErb reminds you to escape strings from external sources.
+As a second step, _(highlight)it is good practice to escape all output of the application_, especially when re-displaying user input, which hasn't been input-filtered (as in the search form example earlier on). _(highlight)Use +escapeHTML()+ (or its alias +h()+) method_ to replace the HTML input characters &amp;, &quot;, &lt;, &gt; by their uninterpreted representations in HTML (+&amp;amp;+, +&amp;quot;+, +&amp;lt+;, and +&amp;gt;+). However, it can easily happen that the programmer forgets to use it, so <em class="highlight">it is recommended to use the "SafeErb":http://safe-erb.rubyforge.org/svn/plugins/safe_erb/ plugin</em>. SafeErb reminds you to escape strings from external sources.
h6. Obfuscation and Encoding Injection
@@ -787,7 +784,7 @@ The following is an excerpt from the "Js.Yamanner@m":http://www.symantec.com/sec
var IDList = ''; var CRumb = ''; function makeRequest(url, Func, Method,Param) { ...
</pre>
-The worms exploits a hole in Yahoo's HTML/JavaScript filter, it usually filters all target and onload attributes from tags (because there can be JavaScript). The filter is applied only once, however, so the onload attribute with the worm code stays in place. This is a good example why blacklist filters are never complete and why it is hard to allow HTML/JavaScript in a web application.
+The worms exploits a hole in Yahoo's HTML/JavaScript filter, which usually filters all target and onload attributes from tags (because there can be JavaScript). The filter is applied only once, however, so the onload attribute with the worm code stays in place. This is a good example why blacklist filters are never complete and why it is hard to allow HTML/JavaScript in a web application.
Another proof-of-concept webmail worm is Nduja, a cross-domain worm for four Italian webmail services. Find more details and a video demonstration on "Rosario Valotta's website":http://rosario.valotta.googlepages.com/home. Both webmail worms have the goal to harvest email addresses, something a criminal hacker could make money with.
@@ -825,20 +822,21 @@ The next problem was MySpace filtering the word “javascript”, so the author
<div id="mycode" expr="alert('hah!')" style="background:url('java↵
script:eval(document.all.mycode.expr)')">
</pre>
-Another problem for the worm's author were CSRF security tokens. Without them he couldn't send a friend request over POST. He got around it by sending a GET to the page right before adding a the user and parsing the result for the CSRF token.
+Another problem for the worm's author were CSRF security tokens. Without them he couldn't send a friend request over POST. He got around it by sending a GET to the page right before adding a user and parsing the result for the CSRF token.
In the end, he got a 4 KB worm, which he injected into his profile page.
The "moz-binding":http://www.securiteam.com/securitynews/5LP051FHPE.html CSS property proved to be another way to introduce JavaScript in CSS in Gecko-based browsers (Firefox, for example).
h5. Countermeasures
+
This example, again, showed that a blacklist filter is never complete. However, as custom CSS in web applications is a quite rare feature, I am not aware of a whitelist CSS filter. _(highlight)If you want to allow custom colours or images, you can allow the user to choose them and build the CSS in the web application_. Use Rails' +sanitize()+ method as a model for a whitelist CSS filter, if you really need one.
h4. Textile Injection
-- _If you want to provide text formatting other than HTML (due to security), use a mark-up language which is converted to HTML on the server-side. "RedCloth":http://whytheluckystiff.net/ruby/redcloth/ is such a language for Ruby, but without precautions, it is also vulnerable to XSS._
- For example, RedCloth translates _test_ to &lt;em&gt;test&lt;em&gt, which makes the text italic. However, up to the current version 3.0.4, it is still vulnerable to XSS. Get the "all-new version 4":http://www.redcloth.org that removed serious bugs. However, even that version has "some security bugs":http://www.rorsecurity.info/journal/2008/10/13/new-redcloth-security.html, so the countermeasures still apply. Here is an example for version 3.0.4:
+For example, RedCloth translates +_test_+ to &lt;em&gt;test&lt;em&gt;, which makes the text italic. However, up to the current version 3.0.4, it is still vulnerable to XSS. Get the "all-new version 4":http://www.redcloth.org that removed serious bugs. However, even that version has "some security bugs":http://www.rorsecurity.info/journal/2008/10/13/new-redcloth-security.html, so the countermeasures still apply. Here is an example for version 3.0.4:
<pre>
@@ -862,7 +860,7 @@ However, this does not filter all HTML, a few tags will be left (by design), for
h5. Countermeasures
-It is recommended to _(highlight)use RedCloth in combination with a whitelist input filter_, as described in the countermeasures against XSS.
+It is recommended to _(highlight)use RedCloth in combination with a whitelist input filter_, as described in the countermeasures against XSS section.
h4. Ajax Injection
@@ -874,7 +872,7 @@ h4. RJS Injection
-- _Don't forget to escape in JavaScript (RJS) templates, too._
-The RJS API generates blocks of JavaScript code based on Ruby code, thus allowing you to manipulate a view or parts of a view from the server side. _(highlight)If you allow user input in RJS templates, do escape it using escape_javascript() within JavaScript functions, and in HTML parts using h()_. Otherwise an attacker could execute arbitrary JavaScript.
+The RJS API generates blocks of JavaScript code based on Ruby code, thus allowing you to manipulate a view or parts of a view from the server side. <em class="highlight">If you allow user input in RJS templates, do escape it using +escape_javascript()+ within JavaScript functions, and in HTML parts using +h()+</em>. Otherwise an attacker could execute arbitrary JavaScript.
h4. Command Line Injection
@@ -891,9 +889,10 @@ system("/bin/echo","hello; rm *")
h4. Header Injection
+
-- _HTTP headers are dynamically generated and under certain circumstances user input may be injected. This can lead to false redirection, XSS or HTTP response splitting._
-HTTP request headers have a Referer, User-Agent (client software) and Cookie field, among others. Response headers for example have a status code, Cookie and Location (redirection target URL) field. All of them are user-supplied and may be manipulated with more or less effort. _(highlight)Remember to escape these header fields, too._ For example when you display the user agent in an administration area.
+HTTP request headers have a Referer, User-Agent (client software), and Cookie field, among others. Response headers for example have a status code, Cookie and Location (redirection target URL) field. All of them are user-supplied and may be manipulated with more or less effort. _(highlight)Remember to escape these header fields, too._ For example when you display the user agent in an administration area.
Besides that, it is _(highlight)important to know what you are doing when building response headers partly based on user input._ For example you want to redirect the user back to a specific page. To do that you introduced a “referer“ field in a form to redirect to the given address:
@@ -922,9 +921,10 @@ HTTP/1.1 302 Moved Temporarily
Location: http://www.malicious.tld
</pre>
-So _(highlight)attack vectors for Header Injection are based on the injection of CRLF characters in a header field._ And what could an attacker do with a false redirection? He could redirect to a phishing site that looks the same as yours, but asks to login again (and sends the login credentials to the attacker). Or he could install malicious software through browser security holes on that site. _(highlight)Rails 2.1.2 escapes these characters for the Location field in the redirect_to method. Make sure you do it yourself when you build other header fields with user input._
+So _(highlight)attack vectors for Header Injection are based on the injection of CRLF characters in a header field._ And what could an attacker do with a false redirection? He could redirect to a phishing site that looks the same as yours, but asks to login again (and sends the login credentials to the attacker). Or he could install malicious software through browser security holes on that site. Rails 2.1.2 escapes these characters for the Location field in the +redirect_to+ method. _(highlight)Make sure you do it yourself when you build other header fields with user input._
h5. Response Splitting
+
If Header Injection was possible, Response Splitting might be, too. In HTTP, the header block is followed by two CRLFs and the actual data (usually HTML). The idea of Response Splitting is to inject two CRLFs into a header field, followed by another response with malicious HTML. The response will be:
<pre>
diff --git a/railties/guides/source/testing.textile b/railties/guides/source/testing.textile
index 8129dc34c2..9b764806a8 100644
--- a/railties/guides/source/testing.textile
+++ b/railties/guides/source/testing.textile
@@ -201,7 +201,7 @@ This line of code is called an _assertion_. An assertion is a line of code that
Every test contains one or more assertions. Only when all the assertions are successful the test passes.
-h4. Preparing you Application for Testing
+h4. Preparing your Application for Testing
Before you can run your tests you need to ensure that the test database structure is current. For this you can use the following rake commands:
@@ -217,14 +217,14 @@ NOTE: +db:test:prepare+ will fail with an error if db/schema.rb doesn't exists.
h5. Rake Tasks for Preparing your Application for Testing
-|_.Tasks |_.Description|
+|_.Tasks |_.Description|
|+rake db:test:clone+ |Recreate the test database from the current environment's database schema|
|+rake db:test:clone_structure+ |Recreate the test databases from the development structure|
|+rake db:test:load+ |Recreate the test database from the current +schema.rb+|
|+rake db:test:prepare+ |Check for pending migrations and load the test schema|
|+rake db:test:purge+ |Empty the test database.|
-TIP: You can see all these rake tasks and their descriptions by running +rake \-\-tasks \-\-describe+
+TIP: You can see all these rake tasks and their descriptions by running +rake --tasks --describe+
h4. Running Tests
@@ -384,7 +384,7 @@ By now you've caught a glimpse of some of the assertions that are available. Ass
There are a bunch of different types of assertions you can use. Here's the complete list of assertions that ship with +test/unit+, the testing library used by Rails. The +[msg]+ parameter is an optional string message you can specify to make your test failure messages clearer. It's not required.
-|_.Assertion |_.Purpose|
+|_.Assertion |_.Purpose|
|+assert( boolean, [msg] )+ |Ensures that the object/expression is true.|
|+assert_equal( obj1, obj2, [msg] )+ |Ensures that +obj1 == obj2+ is true.|
|+assert_not_equal( obj1, obj2, [msg] )+ |Ensures that +obj1 == obj2+ is false.|
@@ -413,7 +413,7 @@ h4. Rails Specific Assertions
Rails adds some custom assertions of its own to the +test/unit+ framework:
-|_.Assertion |_.Purpose|
+|_.Assertion |_.Purpose|
|+assert_valid(record)+ |Ensures that the passed record is valid by Active Record standards and returns any error messages if it is not.|
|+assert_difference(expressions, difference = 1, message = nil) {...}+ |Test numeric difference between the return value of an expression as a result of what is evaluated in the yielded block.|
|+assert_no_difference(expressions, message = nil, &block)+ |Asserts that the numeric result of evaluating an expression is not changed before and after invoking the passed in block.|
@@ -550,7 +550,7 @@ NOTE: You may find references to +assert_tag+ in other documentation, but this i
There are two forms of +assert_select+:
-+assert_select(selector, [equality], [message])`+ ensures that the equality condition is met on the selected elements through the selector. The selector may be a CSS selector expression (String), an expression with substitution values, or an +HTML::Selector+ object.
++assert_select(selector, [equality], [message])+ ensures that the equality condition is met on the selected elements through the selector. The selector may be a CSS selector expression (String), an expression with substitution values, or an +HTML::Selector+ object.
+assert_select(element, selector, [equality], [message])+ ensures that the equality condition is met on all the selected elements through the selector starting from the _element_ (instance of +HTML::Node+) and its descendants.
@@ -588,7 +588,7 @@ h5. Additional View-based Assertions
There are more assertions that are primarily used in testing views:
-|_.Assertion |_.Purpose|
+|_.Assertion |_.Purpose|
|+assert_select_email+ |Allows you to make assertions on the body of an e-mail. |
|+assert_select_rjs+ |Allows you to make assertions on RJS response. +assert_select_rjs+ has variants which allow you to narrow down on the updated element or even a particular operation on an element.|
|+assert_select_encoded+ |Allows you to make assertions on encoded HTML. It does this by un-encoding the contents of each element and then calling the block with all the un-encoded elements.|
@@ -635,7 +635,7 @@ h4. Helpers Available for Integration tests
In addition to the standard testing helpers, there are some additional helpers available to integration tests:
-|_.Helper |_.Purpose|
+|_.Helper |_.Purpose|
|+https?+ |Returns +true+ if the session is mimicking a secure HTTPS request.|
|+https!+ |Allows you to mimic a secure HTTPS request.|
|+host!+ |Allows you to set the host name to use in the next request.|
@@ -732,7 +732,7 @@ h3. Rake Tasks for Running your Tests
You don't need to set up and run your tests by hand on a test-by-test basis. Rails comes with a number of rake tasks to help in testing. The table below lists all rake tasks that come along in the default Rakefile when you initiate a Rail project.
-|_.Tasks |_.Description|
+|_.Tasks |_.Description|
|+rake test+ |Runs all unit, functional and integration tests. You can also simply run +rake+ as the _test_ target is the default.|
|+rake test:units+ |Runs all the unit tests from +test/unit+|
|+rake test:functionals+ |Runs all the functional tests from +test/functional+|