diff options
Diffstat (limited to 'guides')
95 files changed, 2130 insertions, 1099 deletions
diff --git a/guides/.document b/guides/.document new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/guides/.document diff --git a/guides/CHANGELOG.md b/guides/CHANGELOG.md index e9f7ff9d68..766f7f6f56 100644 --- a/guides/CHANGELOG.md +++ b/guides/CHANGELOG.md @@ -1,9 +1,3 @@ -## Rails 4.0.0 (unreleased) ## +* No changes. -* Split Validations and Callbacks guide into two. *Steve Klabnik* - -* New guide _Working with JavaScript in Rails_. *Steve Klabnik* - -* Guides updated to reflect new test locations. *Mike Moore* - -* Guides have a responsive design. *Joe Fiorini* +Please check [4-0-stable](https://github.com/rails/rails/blob/4-0-stable/guides/CHANGELOG.md) for previous changes. diff --git a/guides/assets/images/challenge.png b/guides/assets/images/challenge.png Binary files differdeleted file mode 100644 index 30be3d7028..0000000000 --- a/guides/assets/images/challenge.png +++ /dev/null diff --git a/guides/assets/images/edge_badge.png b/guides/assets/images/edge_badge.png Binary files differindex a35dc9f8ee..a3c1843d1d 100644 --- a/guides/assets/images/edge_badge.png +++ b/guides/assets/images/edge_badge.png diff --git a/guides/assets/images/feature_tile.gif b/guides/assets/images/feature_tile.gif Binary files differindex 75469361db..5268ef8373 100644 --- a/guides/assets/images/feature_tile.gif +++ b/guides/assets/images/feature_tile.gif diff --git a/guides/assets/images/footer_tile.gif b/guides/assets/images/footer_tile.gif Binary files differindex bb33fc1ff0..3fe21a8275 100644 --- a/guides/assets/images/footer_tile.gif +++ b/guides/assets/images/footer_tile.gif diff --git a/guides/assets/images/fxn.png b/guides/assets/images/fxn.png Binary files differindex 9b531ee584..733d380cba 100644 --- a/guides/assets/images/fxn.png +++ b/guides/assets/images/fxn.png diff --git a/guides/assets/images/getting_started/challenge.png b/guides/assets/images/getting_started/challenge.png Binary files differnew file mode 100644 index 0000000000..4a30e49e6d --- /dev/null +++ b/guides/assets/images/getting_started/challenge.png diff --git a/guides/assets/images/getting_started/forbidden_attributes_for_new_post.png b/guides/assets/images/getting_started/forbidden_attributes_for_new_post.png Binary files differindex 500dfc2c02..6c78e52173 100644 --- a/guides/assets/images/getting_started/forbidden_attributes_for_new_post.png +++ b/guides/assets/images/getting_started/forbidden_attributes_for_new_post.png diff --git a/guides/assets/images/getting_started/new_post.png b/guides/assets/images/getting_started/new_post.png Binary files differindex b573cb164c..b20b0192d4 100644 --- a/guides/assets/images/getting_started/new_post.png +++ b/guides/assets/images/getting_started/new_post.png diff --git a/guides/assets/images/getting_started/rails_welcome.png b/guides/assets/images/getting_started/rails_welcome.png Binary files differnew file mode 100644 index 0000000000..569dd846a8 --- /dev/null +++ b/guides/assets/images/getting_started/rails_welcome.png diff --git a/guides/assets/images/getting_started/routing_error_no_controller.png b/guides/assets/images/getting_started/routing_error_no_controller.png Binary files differindex 43ccd25252..35ee4f348f 100644 --- a/guides/assets/images/getting_started/routing_error_no_controller.png +++ b/guides/assets/images/getting_started/routing_error_no_controller.png diff --git a/guides/assets/images/getting_started/routing_error_no_route_matches.png b/guides/assets/images/getting_started/routing_error_no_route_matches.png Binary files differindex 1b8c0ea57e..1cbddfa0f1 100644 --- a/guides/assets/images/getting_started/routing_error_no_route_matches.png +++ b/guides/assets/images/getting_started/routing_error_no_route_matches.png diff --git a/guides/assets/images/getting_started/template_is_missing_posts_new.png b/guides/assets/images/getting_started/template_is_missing_posts_new.png Binary files differindex 75980432b2..f03db05fb8 100644 --- a/guides/assets/images/getting_started/template_is_missing_posts_new.png +++ b/guides/assets/images/getting_started/template_is_missing_posts_new.png diff --git a/guides/assets/images/getting_started/unknown_action_create_for_posts.png b/guides/assets/images/getting_started/unknown_action_create_for_posts.png Binary files differindex c6750e1ae1..8fdd4c574a 100644 --- a/guides/assets/images/getting_started/unknown_action_create_for_posts.png +++ b/guides/assets/images/getting_started/unknown_action_create_for_posts.png diff --git a/guides/assets/images/getting_started/unknown_action_new_for_posts.png b/guides/assets/images/getting_started/unknown_action_new_for_posts.png Binary files differindex f4b3eff9dc..7e72feee38 100644 --- a/guides/assets/images/getting_started/unknown_action_new_for_posts.png +++ b/guides/assets/images/getting_started/unknown_action_new_for_posts.png diff --git a/guides/assets/images/header_tile.gif b/guides/assets/images/header_tile.gif Binary files differindex e2c878d492..6b1af15eab 100644 --- a/guides/assets/images/header_tile.gif +++ b/guides/assets/images/header_tile.gif diff --git a/guides/assets/images/icons/README b/guides/assets/images/icons/README index f12b2a730c..09da77fc86 100644 --- a/guides/assets/images/icons/README +++ b/guides/assets/images/icons/README @@ -1,5 +1,5 @@ Replaced the plain DocBook XSL admonition icons with Jimmac's DocBook icons (http://jimmac.musichall.cz/ikony.php3). I dropped transparency -from the Jimmac icons to get round MS IE and FOP PNG incompatibilies. +from the Jimmac icons to get round MS IE and FOP PNG incompatibilities. Stuart Rackham diff --git a/guides/assets/images/icons/callouts/11.png b/guides/assets/images/icons/callouts/11.png Binary files differindex 9244a1ac4b..3b7b9318e7 100644 --- a/guides/assets/images/icons/callouts/11.png +++ b/guides/assets/images/icons/callouts/11.png diff --git a/guides/assets/images/icons/callouts/12.png b/guides/assets/images/icons/callouts/12.png Binary files differindex ae56459f4c..7b95925e9d 100644 --- a/guides/assets/images/icons/callouts/12.png +++ b/guides/assets/images/icons/callouts/12.png diff --git a/guides/assets/images/icons/callouts/13.png b/guides/assets/images/icons/callouts/13.png Binary files differindex 1181f9f892..4b99fe8efc 100644 --- a/guides/assets/images/icons/callouts/13.png +++ b/guides/assets/images/icons/callouts/13.png diff --git a/guides/assets/images/icons/callouts/15.png b/guides/assets/images/icons/callouts/15.png Binary files differindex 39304de94f..70e4bba615 100644 --- a/guides/assets/images/icons/callouts/15.png +++ b/guides/assets/images/icons/callouts/15.png diff --git a/guides/assets/images/icons/caution.png b/guides/assets/images/icons/caution.png Binary files differindex 031e19c776..7227b54b32 100644 --- a/guides/assets/images/icons/caution.png +++ b/guides/assets/images/icons/caution.png diff --git a/guides/assets/images/icons/example.png b/guides/assets/images/icons/example.png Binary files differindex 1b0e482059..de23c0aa87 100644 --- a/guides/assets/images/icons/example.png +++ b/guides/assets/images/icons/example.png diff --git a/guides/assets/images/jaimeiniesta.jpg b/guides/assets/images/jaimeiniesta.jpg Binary files differdeleted file mode 100644 index 445f048d92..0000000000 --- a/guides/assets/images/jaimeiniesta.jpg +++ /dev/null diff --git a/guides/assets/images/radar.png b/guides/assets/images/radar.png Binary files differindex f61e08763f..421b62b623 100644 --- a/guides/assets/images/radar.png +++ b/guides/assets/images/radar.png diff --git a/guides/assets/images/rails4_features.png b/guides/assets/images/rails4_features.png Binary files differindex a979f02207..b3bd5ef69e 100644 --- a/guides/assets/images/rails4_features.png +++ b/guides/assets/images/rails4_features.png diff --git a/guides/assets/images/rails_guides_kindle_cover.jpg b/guides/assets/images/rails_guides_kindle_cover.jpg Binary files differindex 9eb16720a9..f068bd9a04 100644 --- a/guides/assets/images/rails_guides_kindle_cover.jpg +++ b/guides/assets/images/rails_guides_kindle_cover.jpg diff --git a/guides/assets/images/rails_welcome.png b/guides/assets/images/rails_welcome.png Binary files differdeleted file mode 100644 index 8ad2d351de..0000000000 --- a/guides/assets/images/rails_welcome.png +++ /dev/null diff --git a/guides/assets/images/vijaydev.jpg b/guides/assets/images/vijaydev.jpg Binary files differindex e21d3cabfc..fe5e4f1cb4 100644 --- a/guides/assets/images/vijaydev.jpg +++ b/guides/assets/images/vijaydev.jpg diff --git a/guides/bug_report_templates/active_record_gem.rb b/guides/bug_report_templates/active_record_gem.rb new file mode 100644 index 0000000000..2e604bec47 --- /dev/null +++ b/guides/bug_report_templates/active_record_gem.rb @@ -0,0 +1,37 @@ +# Activate the gem you are reporting the issue against. +gem 'activerecord', '3.2.13' +require 'active_record' +require 'minitest/autorun' +require 'logger' + +# This connection will do for database-independent bug reports. +ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:') +ActiveRecord::Base.logger = Logger.new(STDOUT) + +ActiveRecord::Schema.define do + create_table :posts do |t| + end + + create_table :comments do |t| + t.integer :post_id + end +end + +class Post < ActiveRecord::Base + has_many :comments +end + +class Comment < ActiveRecord::Base + belongs_to :post +end + +class BugTest < MiniTest::Unit::TestCase + def test_association_stuff + post = Post.create! + post.comments << Comment.create! + + assert_equal 1, post.comments.count + assert_equal 1, Comment.count + assert_equal post.id, Comment.first.post.id + end +end diff --git a/guides/bug_report_templates/active_record_master.rb b/guides/bug_report_templates/active_record_master.rb new file mode 100644 index 0000000000..68069cdd8d --- /dev/null +++ b/guides/bug_report_templates/active_record_master.rb @@ -0,0 +1,48 @@ +unless File.exists?('Gemfile') + File.write('Gemfile', <<-GEMFILE) + source 'https://rubygems.org' + gem 'rails', github: 'rails/rails' + gem 'sqlite3' + GEMFILE + + system 'bundle' +end + +require 'bundler' +Bundler.setup(:default) + +require 'active_record' +require 'minitest/autorun' +require 'logger' + +# This connection will do for database-independent bug reports. +ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:') +ActiveRecord::Base.logger = Logger.new(STDOUT) + +ActiveRecord::Schema.define do + create_table :posts do |t| + end + + create_table :comments do |t| + t.integer :post_id + end +end + +class Post < ActiveRecord::Base + has_many :comments +end + +class Comment < ActiveRecord::Base + belongs_to :post +end + +class BugTest < MiniTest::Unit::TestCase + def test_association_stuff + post = Post.create! + post.comments << Comment.create! + + assert_equal 1, post.comments.count + assert_equal 1, Comment.count + assert_equal post.id, Comment.first.post.id + end +end diff --git a/guides/code/getting_started/.gitignore b/guides/code/getting_started/.gitignore index 25a742dff0..6a502e997f 100644 --- a/guides/code/getting_started/.gitignore +++ b/guides/code/getting_started/.gitignore @@ -1,4 +1,4 @@ -# See http://help.github.com/ignore-files/ for more about ignoring files. +# See https://help.github.com/articles/ignoring-files for more about ignoring files. # # If you find yourself ignoring temporary files generated by your text editor # or operating system, you probably want to add a global ignore instead: diff --git a/guides/code/getting_started/Gemfile b/guides/code/getting_started/Gemfile index b355c7d91a..acd2ed5160 100644 --- a/guides/code/getting_started/Gemfile +++ b/guides/code/getting_started/Gemfile @@ -2,28 +2,33 @@ source 'https://rubygems.org' gem 'rails', '4.0.0' +# Use sqlite3 as the database for Active Record gem 'sqlite3' -# Gems used only for assets and not required -# in production environments by default. -group :assets do - gem 'sprockets-rails' - gem 'sass-rails' - gem 'coffee-rails' +# Use SCSS for stylesheets +gem 'sass-rails' - # See https://github.com/sstephenson/execjs#readme for more supported runtimes - # gem 'therubyracer', platforms: :ruby +# Use CoffeeScript for .js.coffee assets and views +gem 'coffee-rails' - gem 'uglifier', '>= 1.0.3' -end +# See https://github.com/sstephenson/execjs#readme for more supported runtimes +# gem 'therubyracer', platforms: :ruby + +# Use Uglifier as compressor for JavaScript assets +gem 'uglifier', '>= 1.0.3' gem 'jquery-rails' # Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks gem 'turbolinks' +group :doc do + # bundle exec rake doc:rails generates the API under doc/api. + gem 'sdoc', require: false +end + # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder -gem 'jbuilder', '~> 1.0.1' +gem 'jbuilder', '~> 1.2' # To use ActiveModel has_secure_password # gem 'bcrypt-ruby', '~> 3.0.0' diff --git a/guides/code/getting_started/Gemfile.lock b/guides/code/getting_started/Gemfile.lock index 823fac5ff7..888a6b30e2 100644 --- a/guides/code/getting_started/Gemfile.lock +++ b/guides/code/getting_started/Gemfile.lock @@ -92,7 +92,7 @@ GEM multi_json (~> 1.0) hike (1.2.1) i18n (0.6.1) - jbuilder (1.0.2) + jbuilder (1.3.0) activesupport (>= 3.0.0) jquery-rails (2.2.0) railties (>= 3.0, < 5.0) diff --git a/guides/code/getting_started/app/assets/images/rails.png b/guides/code/getting_started/app/assets/images/rails.png Binary files differdeleted file mode 100644 index d5edc04e65..0000000000 --- a/guides/code/getting_started/app/assets/images/rails.png +++ /dev/null diff --git a/guides/code/getting_started/app/assets/javascripts/application.js b/guides/code/getting_started/app/assets/javascripts/application.js index 9e83eb5e7e..5a4fbaa370 100644 --- a/guides/code/getting_started/app/assets/javascripts/application.js +++ b/guides/code/getting_started/app/assets/javascripts/application.js @@ -7,8 +7,7 @@ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the // compiled file. // -// WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD -// GO AFTER THE REQUIRES BELOW. +// stub path allows dependency to be excluded from the asset bundle. // //= require jquery //= require jquery_ujs diff --git a/guides/code/getting_started/app/controllers/comments_controller.rb b/guides/code/getting_started/app/controllers/comments_controller.rb index 0082e9c8ec..0e3d2a6dde 100644 --- a/guides/code/getting_started/app/controllers/comments_controller.rb +++ b/guides/code/getting_started/app/controllers/comments_controller.rb @@ -1,7 +1,7 @@ class CommentsController < ApplicationController - http_basic_authenticate_with name: "dhh", password: "secret", only: :destroy - + http_basic_authenticate_with name: "dhh", password: "secret", only: :destroy + def create @post = Post.find(params[:post_id]) @comment = @post.comments.create(params[:comment].permit(:commenter, :body)) diff --git a/guides/code/getting_started/app/controllers/posts_controller.rb b/guides/code/getting_started/app/controllers/posts_controller.rb index 0398395200..6aa1409170 100644 --- a/guides/code/getting_started/app/controllers/posts_controller.rb +++ b/guides/code/getting_started/app/controllers/posts_controller.rb @@ -1,7 +1,7 @@ class PostsController < ApplicationController - http_basic_authenticate_with name: "dhh", password: "secret", except: [:index, :show] - + http_basic_authenticate_with name: "dhh", password: "secret", except: [:index, :show] + def index @posts = Post.all end diff --git a/guides/code/getting_started/config/application.rb b/guides/code/getting_started/config/application.rb index 526a782b5c..3d7604b659 100644 --- a/guides/code/getting_started/config/application.rb +++ b/guides/code/getting_started/config/application.rb @@ -2,8 +2,9 @@ require File.expand_path('../boot', __FILE__) require 'rails/all' -# Assets should be precompiled for production (so we don't need the gems loaded then) -Bundler.require(*Rails.groups(assets: %w(development test))) +# Require the gems listed in Gemfile, including any gems +# you've limited to :test, :development, or :production. +Bundler.require(:default, Rails.env) module Blog class Application < Rails::Application diff --git a/guides/code/getting_started/config/environment.rb b/guides/code/getting_started/config/environment.rb index 2d65111004..e7e341c960 100644 --- a/guides/code/getting_started/config/environment.rb +++ b/guides/code/getting_started/config/environment.rb @@ -1,5 +1,5 @@ -# Load the rails application. +# Load the Rails application. require File.expand_path('../application', __FILE__) -# Initialize the rails application. +# Initialize the Rails application. Blog::Application.initialize! diff --git a/guides/code/getting_started/config/environments/development.rb b/guides/code/getting_started/config/environments/development.rb index ed2667ac58..d169e9452c 100644 --- a/guides/code/getting_started/config/environments/development.rb +++ b/guides/code/getting_started/config/environments/development.rb @@ -22,10 +22,6 @@ Blog::Application.configure do # Only use best-standards-support built into browsers. config.action_dispatch.best_standards_support = :builtin - # Log the query plan for queries taking more than this (works - # with SQLite, MySQL, and PostgreSQL). - config.active_record.auto_explain_threshold_in_seconds = 0.5 - # Raise an error on page load if there are pending migrations config.active_record.migration_error = :page_load diff --git a/guides/code/getting_started/config/environments/production.rb b/guides/code/getting_started/config/environments/production.rb index 58dab2a319..368a735122 100644 --- a/guides/code/getting_started/config/environments/production.rb +++ b/guides/code/getting_started/config/environments/production.rb @@ -72,10 +72,6 @@ Blog::Application.configure do # Send deprecation notices to registered listeners. config.active_support.deprecation = :notify - # Log the query plan for queries taking more than this (works - # with SQLite, MySQL, and PostgreSQL). - # config.active_record.auto_explain_threshold_in_seconds = 0.5 - # Disable automatic flushing of the log to improve performance. # config.autoflush_log = false diff --git a/guides/code/getting_started/config/initializers/session_store.rb b/guides/code/getting_started/config/initializers/session_store.rb index 2e37d93799..3b2ca93ab9 100644 --- a/guides/code/getting_started/config/initializers/session_store.rb +++ b/guides/code/getting_started/config/initializers/session_store.rb @@ -1,3 +1,3 @@ # Be sure to restart your server when you modify this file. -Blog::Application.config.session_store :encrypted_cookie_store, key: '_blog_session' +Blog::Application.config.session_store :cookie_store, key: '_blog_session' diff --git a/guides/code/getting_started/config/routes.rb b/guides/code/getting_started/config/routes.rb index 9950568629..0155b613a3 100644 --- a/guides/code/getting_started/config/routes.rb +++ b/guides/code/getting_started/config/routes.rb @@ -2,6 +2,6 @@ Blog::Application.routes.draw do resources :posts do resources :comments end - - root to: "welcome#index" + + root "welcome#index" end diff --git a/guides/code/getting_started/public/404.html b/guides/code/getting_started/public/404.html index 3d875c342e..3d287b135d 100644 --- a/guides/code/getting_started/public/404.html +++ b/guides/code/getting_started/public/404.html @@ -3,16 +3,47 @@ <head> <title>The page you were looking for doesn't exist (404)</title> <style> - body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; } - div.dialog { - width: 25em; - padding: 0 4em; - margin: 4em auto 0 auto; - border: 1px solid #ccc; - border-right-color: #999; - border-bottom-color: #999; - } - h1 { font-size: 100%; color: #f00; line-height: 1.5em; } + body { + background-color: #EFEFEF; + color: #2E2F30; + text-align: center; + font-family: arial, sans-serif; + } + + div.dialog { + width: 25em; + margin: 4em auto 0 auto; + border: 1px solid #CCC; + border-right-color: #999; + border-left-color: #999; + border-bottom-color: #BBB; + border-top: #B00100 solid 4px; + border-top-left-radius: 9px; + border-top-right-radius: 9px; + background-color: white; + padding: 7px 4em 0 4em; + } + + h1 { + font-size: 100%; + color: #730E15; + line-height: 1.5em; + } + + body > p { + width: 33em; + margin: 0 auto 1em; + padding: 1em 0; + background-color: #F7F7F7; + border: 1px solid #CCC; + border-right-color: #999; + border-bottom-color: #999; + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + border-top-color: #DADADA; + color: #666; + box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17); + } </style> </head> diff --git a/guides/code/getting_started/public/422.html b/guides/code/getting_started/public/422.html index 3f1bfb3417..3b946bf4a4 100644 --- a/guides/code/getting_started/public/422.html +++ b/guides/code/getting_started/public/422.html @@ -3,16 +3,47 @@ <head> <title>The change you wanted was rejected (422)</title> <style> - body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; } - div.dialog { - width: 25em; - padding: 0 4em; - margin: 4em auto 0 auto; - border: 1px solid #ccc; - border-right-color: #999; - border-bottom-color: #999; - } - h1 { font-size: 100%; color: #f00; line-height: 1.5em; } + body { + background-color: #EFEFEF; + color: #2E2F30; + text-align: center; + font-family: arial, sans-serif; + } + + div.dialog { + width: 25em; + margin: 4em auto 0 auto; + border: 1px solid #CCC; + border-right-color: #999; + border-left-color: #999; + border-bottom-color: #BBB; + border-top: #B00100 solid 4px; + border-top-left-radius: 9px; + border-top-right-radius: 9px; + background-color: white; + padding: 7px 4em 0 4em; + } + + h1 { + font-size: 100%; + color: #730E15; + line-height: 1.5em; + } + + body > p { + width: 33em; + margin: 0 auto 1em; + padding: 1em 0; + background-color: #F7F7F7; + border: 1px solid #CCC; + border-right-color: #999; + border-bottom-color: #999; + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + border-top-color: #DADADA; + color: #666; + box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17); + } </style> </head> @@ -22,5 +53,6 @@ <h1>The change you wanted was rejected.</h1> <p>Maybe you tried to change something you didn't have access to.</p> </div> + <p>If you are the application owner check the logs for more information.</p> </body> </html> diff --git a/guides/code/getting_started/public/500.html b/guides/code/getting_started/public/500.html index 012977d3d2..ccc4ad5656 100644 --- a/guides/code/getting_started/public/500.html +++ b/guides/code/getting_started/public/500.html @@ -3,16 +3,47 @@ <head> <title>We're sorry, but something went wrong (500)</title> <style> - body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; } - div.dialog { - width: 25em; - padding: 0 4em; - margin: 4em auto 0 auto; - border: 1px solid #ccc; - border-right-color: #999; - border-bottom-color: #999; - } - h1 { font-size: 100%; color: #f00; line-height: 1.5em; } + body { + background-color: #EFEFEF; + color: #2E2F30; + text-align: center; + font-family: arial, sans-serif; + } + + div.dialog { + width: 25em; + margin: 4em auto 0 auto; + border: 1px solid #CCC; + border-right-color: #999; + border-left-color: #999; + border-bottom-color: #BBB; + border-top: #B00100 solid 4px; + border-top-left-radius: 9px; + border-top-right-radius: 9px; + background-color: white; + padding: 7px 4em 0 4em; + } + + h1 { + font-size: 100%; + color: #730E15; + line-height: 1.5em; + } + + body > p { + width: 33em; + margin: 0 auto 1em; + padding: 1em 0; + background-color: #F7F7F7; + border: 1px solid #CCC; + border-right-color: #999; + border-bottom-color: #999; + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + border-top-color: #DADADA; + color: #666; + box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17); + } </style> </head> diff --git a/guides/rails_guides.rb b/guides/rails_guides.rb index ab890f202c..ce409868ca 100644 --- a/guides/rails_guides.rb +++ b/guides/rails_guides.rb @@ -22,7 +22,7 @@ end begin require 'redcarpet' -rescue Gem::LoadError +rescue LoadError # This can happen if doc:guides is executed in an application. $stderr.puts('Generating guides requires Redcarpet 2.1.1+.') $stderr.puts(<<ERROR) if bundler? diff --git a/guides/rails_guides/markdown/renderer.rb b/guides/rails_guides/markdown/renderer.rb index c3fe5b8799..2eb7ca17a3 100644 --- a/guides/rails_guides/markdown/renderer.rb +++ b/guides/rails_guides/markdown/renderer.rb @@ -65,7 +65,7 @@ HTML # if a bulleted list follows the first item is not rendered # as a list item, but as a paragraph starting with a plain # asterisk. - body.gsub(/^(TIP|IMPORTANT|CAUTION|WARNING|NOTE|INFO|TODO)[.:](.*?)(\n(?=\n)|\Z)/m) do |m| + body.gsub(/^(TIP|IMPORTANT|CAUTION|WARNING|NOTE|INFO|TODO)[.:](.*?)(\n(?=\n)|\Z)/m) do css_class = case $1 when 'CAUTION', 'IMPORTANT' 'warning' diff --git a/guides/source/2_2_release_notes.md b/guides/source/2_2_release_notes.md index cef82f3784..7db4cf07e7 100644 --- a/guides/source/2_2_release_notes.md +++ b/guides/source/2_2_release_notes.md @@ -31,20 +31,20 @@ Documentation The internal documentation of Rails, in the form of code comments, has been improved in numerous places. In addition, the [Ruby on Rails Guides](http://guides.rubyonrails.org/) project is the definitive source for information on major Rails components. In its first official release, the Guides page includes: -* [Getting Started with Rails](http://guides.rubyonrails.org/getting_started.html) -* [Rails Database Migrations](http://guides.rubyonrails.org/migrations.html) -* [Active Record Associations](http://guides.rubyonrails.org/association_basics.html) -* [Active Record Query Interface](http://guides.rubyonrails.org/active_record_querying.html) -* [Layouts and Rendering in Rails](http://guides.rubyonrails.org/layouts_and_rendering.html) -* [Action View Form Helpers](http://guides.rubyonrails.org/form_helpers.html) -* [Rails Routing from the Outside In](http://guides.rubyonrails.org/routing.html) -* [Action Controller Overview](http://guides.rubyonrails.org/action_controller_overview.html) -* [Rails Caching](http://guides.rubyonrails.org/caching_with_rails.html) -* [A Guide to Testing Rails Applications](http://guides.rubyonrails.org/testing.html) -* [Securing Rails Applications](http://guides.rubyonrails.org/security.html) -* [Debugging Rails Applications](http://guides.rubyonrails.org/debugging_rails_applications.html) -* [Performance Testing Rails Applications](http://guides.rubyonrails.org/performance_testing.html) -* [The Basics of Creating Rails Plugins](http://guides.rubyonrails.org/plugins.html) +* [Getting Started with Rails](getting_started.html) +* [Rails Database Migrations](migrations.html) +* [Active Record Associations](association_basics.html) +* [Active Record Query Interface](active_record_querying.html) +* [Layouts and Rendering in Rails](layouts_and_rendering.html) +* [Action View Form Helpers](form_helpers.html) +* [Rails Routing from the Outside In](routing.html) +* [Action Controller Overview](action_controller_overview.html) +* [Rails Caching](caching_with_rails.html) +* [A Guide to Testing Rails Applications](testing.html) +* [Securing Rails Applications](security.html) +* [Debugging Rails Applications](debugging_rails_applications.html) +* [Performance Testing Rails Applications](performance_testing.html) +* [The Basics of Creating Rails Plugins](plugins.html) All told, the Guides provide tens of thousands of words of guidance for beginning and intermediate Rails developers. @@ -200,7 +200,7 @@ Active Record association proxies now respect the scope of methods on the proxie * More information: * [Rails 2.2 Change: Private Methods on Association Proxies are Private](http://afreshcup.com/2008/10/24/rails-22-change-private-methods-on-association-proxies-are-private/) -### Other ActiveRecord Changes +### Other Active Record Changes * `rake db:migrate:redo` now accepts an optional VERSION to target that specific migration to redo * Set `config.active_record.timestamped_migrations = false` to have migrations with numeric prefix instead of UTC timestamp. @@ -236,7 +236,7 @@ This will enable recognition of (among others) these routes: * Lead Contributor: [S. Brent Faulkner](http://www.unwwwired.net/) * More information: - * [Rails Routing from the Outside In](http://guides.rubyonrails.org/routing.html#nested-resources) + * [Rails Routing from the Outside In](routing.html#nested-resources) * [What's New in Edge Rails: Shallow Routes](http://ryandaigle.com/articles/2008/9/7/what-s-new-in-edge-rails-shallow-routes) ### Method Arrays for Member or Collection Routes diff --git a/guides/source/2_3_release_notes.md b/guides/source/2_3_release_notes.md index 7aef566e40..0f05cc6b85 100644 --- a/guides/source/2_3_release_notes.md +++ b/guides/source/2_3_release_notes.md @@ -40,7 +40,7 @@ Here's a summary of the rack-related changes: * `ActiveRecord::QueryCache` middleware is automatically inserted onto the middleware stack if `ActiveRecord` has been loaded. This middleware sets up and flushes the per-request Active Record query cache. * The Rails router and controller classes follow the Rack spec. You can call a controller directly with `SomeController.call(env)`. The router stores the routing parameters in `rack.routing_args`. * `ActionController::Request` inherits from `Rack::Request`. -* Instead of `config.action_controller.session = { :session_key => 'foo', ...` use `config.action_controller.session = { :key => 'foo', ...`. +* Instead of `config.action_controller.session = { :session_key => 'foo', ...` use `config.action_controller.session = { :key => 'foo', ...`. * Using the `ParamsParser` middleware preprocesses any XML, JSON, or YAML requests so they can be read normally with any `Rack::Request` object after it. ### Renewed Support for Rails Engines @@ -134,7 +134,7 @@ Rails 2.3 will introduce the notion of _default scopes_ similar to named scopes, ### Batch Processing -You can now process large numbers of records from an ActiveRecord model with less pressure on memory by using `find_in_batches`: +You can now process large numbers of records from an Active Record model with less pressure on memory by using `find_in_batches`: ```ruby Customer.find_in_batches(:conditions => {:active => true}) do |customer_group| @@ -173,8 +173,8 @@ before_save :update_credit_rating, :if => :active, Rails now has a `:having` option on find (as well as on `has_many` and `has_and_belongs_to_many` associations) for filtering records in grouped finds. As those with heavy SQL backgrounds know, this allows filtering based on grouped results: ```ruby -developers = Developer.find(:all, :group => "salary", - :having => "sum(salary) > 10000", :select => "salary") +developers = Developer.find(:all, :group => "salary", + :having => "sum(salary) > 10000", :select => "salary") ``` * Lead Contributor: [Emilio Tagua](http://github.com/miloops) @@ -504,7 +504,7 @@ A lot of folks have adopted the notion of using try() to attempt operations on o ### Swappable Parsers for XMLmini -The support for XML parsing in ActiveSupport has been made more flexible by allowing you to swap in different parsers. By default, it uses the standard REXML implementation, but you can easily specify the faster LibXML or Nokogiri implementations for your own applications, provided you have the appropriate gems installed: +The support for XML parsing in Active Support has been made more flexible by allowing you to swap in different parsers. By default, it uses the standard REXML implementation, but you can easily specify the faster LibXML or Nokogiri implementations for your own applications, provided you have the appropriate gems installed: ```ruby XmlMini.backend = 'LibXML' diff --git a/guides/source/3_0_release_notes.md b/guides/source/3_0_release_notes.md index 388ba3fa30..d398cd680c 100644 --- a/guides/source/3_0_release_notes.md +++ b/guides/source/3_0_release_notes.md @@ -79,7 +79,7 @@ Creating a Rails 3.0 application -------------------------------- ```bash -# You should have the 'rails' rubygem installed +# You should have the 'rails' RubyGem installed $ rails new myapp $ cd myapp ``` @@ -342,7 +342,7 @@ Helpers that do something else, like `cache` or `content_for`, are not affected * Helpers now output HTML 5 by default. * Form label helper now pulls values from I18n with a single value, so `f.label :name` will pull the `:name` translation. * I18n select label on should now be :en.helpers.select instead of :en.support.select. -* You no longer need to place a minus sign at the end of a ruby interpolation inside an ERb template to remove the trailing carriage return in the HTML output. +* You no longer need to place a minus sign at the end of a Ruby interpolation inside an ERB template to remove the trailing carriage return in the HTML output. * Added `grouped_collection_select` helper to Action View. * `content_for?` has been added allowing you to check for the existence of content in a view before rendering. * passing `:value => nil` to form helpers will set the field's `value` attribute to nil as opposed to using the default value @@ -475,7 +475,7 @@ As well as the following deprecations: * `named_scope` in an Active Record class is deprecated and has been renamed to just `scope`. * In `scope` methods, you should move to using the relation methods, instead of a `:conditions => {}` finder method, for example `scope :since, lambda {|time| where("created_at > ?", time) }`. * `save(false)` is deprecated, in favor of `save(:validate => false)`. -* I18n error messages for ActiveRecord should be changed from :en.activerecord.errors.template to `:en.errors.template`. +* I18n error messages for Active Record should be changed from :en.activerecord.errors.template to `:en.errors.template`. * `model.errors.on` is deprecated in favor of `model.errors[]` * validates_presence_of => validates... :presence => true * `ActiveRecord::Base.colorize_logging` and `config.active_record.colorize_logging` are deprecated in favor of `Rails::LogSubscriber.colorize_logging` or `config.colorize_logging` @@ -580,7 +580,7 @@ Action Mailer has been given a new API with TMail being replaced out with the ne * All mailers are now in `app/mailers` by default. * Can now send email using new API with three methods: `attachments`, `headers` and `mail`. -* ActionMailer now has native support for inline attachments using the `attachments.inline` method. +* Action Mailer now has native support for inline attachments using the `attachments.inline` method. * Action Mailer emailing methods now return `Mail::Message` objects, which can then be sent the `deliver` message to send itself. * All delivery methods are now abstracted out to the Mail gem. * The mail delivery method can accept a hash of all valid mail header fields with their value pair. @@ -611,4 +611,3 @@ Credits See the [full list of contributors to Rails](http://contributors.rubyonrails.org/) for the many people who spent many hours making Rails 3. Kudos to all of them. Rails 3.0 Release Notes were compiled by [Mikel Lindsaar](http://lindsaar.net.) - diff --git a/guides/source/3_1_release_notes.md b/guides/source/3_1_release_notes.md index d3f8abe0c8..5c99892e39 100644 --- a/guides/source/3_1_release_notes.md +++ b/guides/source/3_1_release_notes.md @@ -137,7 +137,7 @@ Creating a Rails 3.1 application -------------------------------- ```bash -# You should have the 'rails' rubygem installed +# You should have the 'rails' RubyGem installed $ rails new myapp $ cd myapp ``` diff --git a/guides/source/3_2_release_notes.md b/guides/source/3_2_release_notes.md index 68a47be14f..e036860de2 100644 --- a/guides/source/3_2_release_notes.md +++ b/guides/source/3_2_release_notes.md @@ -67,7 +67,7 @@ Creating a Rails 3.2 application -------------------------------- ```bash -# You should have the 'rails' rubygem installed +# You should have the 'rails' RubyGem installed $ rails new myapp $ cd myapp ``` @@ -101,7 +101,7 @@ Rails 3.2 comes with a development mode that's noticeably faster. Inspired by [A ### Automatic Query Explains -Rails 3.2 comes with a nice feature that explains queries generated by ARel by defining an `explain` method in `ActiveRecord::Relation`. For example, you can run something like `puts Person.active.limit(5).explain` and the query ARel produces is explained. This allows to check for the proper indexes and further optimizations. +Rails 3.2 comes with a nice feature that explains queries generated by Arel by defining an `explain` method in `ActiveRecord::Relation`. For example, you can run something like `puts Person.active.limit(5).explain` and the query Arel produces is explained. This allows to check for the proper indexes and further optimizations. Queries that take more than half a second to run are *automatically* explained in the development mode. This threshold, of course, can be changed. @@ -189,7 +189,7 @@ Action Pack * form\_for is changed to use "#{action}\_#{as}" as the css class and id if `:as` option is provided. Earlier versions used "#{as}\_#{action}". -* `ActionController::ParamsWrapper` on ActiveRecord models now only wrap `attr_accessible` attributes if they were set. If not, only the attributes returned by the class method `attribute_names` will be wrapped. This fixes the wrapping of nested attributes by adding them to `attr_accessible`. +* `ActionController::ParamsWrapper` on Active Record models now only wrap `attr_accessible` attributes if they were set. If not, only the attributes returned by the class method `attribute_names` will be wrapped. This fixes the wrapping of nested attributes by adding them to `attr_accessible`. * Log "Filter chain halted as CALLBACKNAME rendered or redirected" every time a before callback halts. diff --git a/guides/source/4_0_release_notes.md b/guides/source/4_0_release_notes.md index 9c157ec0b3..6ebeeed0bf 100644 --- a/guides/source/4_0_release_notes.md +++ b/guides/source/4_0_release_notes.md @@ -22,7 +22,7 @@ Creating a Rails 4.0 application -------------------------------- ``` - You should have the 'rails' rubygem installed + You should have the 'rails' RubyGem installed $ rails new myapp $ cd myapp ``` @@ -59,15 +59,15 @@ Extraction of features to gems In Rails 4.0, several features have been extracted into gems. You can simply add the extracted gems to your `Gemfile` to bring the functionality back. -* Hash-based & Dynamic finder methods ([Github](https://github.com/rails/activerecord-deprecated_finders)) -* Mass assignment protection in Active Record models ([Github](https://github.com/rails/protected_attributes), [Pull Request](https://github.com/rails/rails/pull/7251)) -* ActiveRecord::SessionStore ([Github](https://github.com/rails/activerecord-session_store), [Pull Request](https://github.com/rails/rails/pull/7436)) -* Active Record Observers ([Github](https://github.com/rails/rails-observers), [Commit](https://github.com/rails/rails/commit/39e85b3b90c58449164673909a6f1893cba290b2)) -* Active Resource ([Github](https://github.com/rails/activeresource), [Pull Request](https://github.com/rails/rails/pull/572), [Blog](http://yetimedia.tumblr.com/post/35233051627/activeresource-is-dead-long-live-activeresource)) -* Action Caching ([Github](https://github.com/rails/actionpack-action_caching), [Pull Request](https://github.com/rails/rails/pull/7833)) -* Page Caching ([Github](https://github.com/rails/actionpack-page_caching), [Pull Request](https://github.com/rails/rails/pull/7833)) -* Sprockets ([Github](https://github.com/rails/sprockets-rails)) -* Performance tests ([Github](https://github.com/rails/rails-perftest), [Pull Request](https://github.com/rails/rails/pull/8876)) +* Hash-based & Dynamic finder methods ([GitHub](https://github.com/rails/activerecord-deprecated_finders)) +* Mass assignment protection in Active Record models ([GitHub](https://github.com/rails/protected_attributes), [Pull Request](https://github.com/rails/rails/pull/7251)) +* ActiveRecord::SessionStore ([GitHub](https://github.com/rails/activerecord-session_store), [Pull Request](https://github.com/rails/rails/pull/7436)) +* Active Record Observers ([GitHub](https://github.com/rails/rails-observers), [Commit](https://github.com/rails/rails/commit/39e85b3b90c58449164673909a6f1893cba290b2)) +* Active Resource ([GitHub](https://github.com/rails/activeresource), [Pull Request](https://github.com/rails/rails/pull/572), [Blog](http://yetimedia.tumblr.com/post/35233051627/activeresource-is-dead-long-live-activeresource)) +* Action Caching ([GitHub](https://github.com/rails/actionpack-action_caching), [Pull Request](https://github.com/rails/rails/pull/7833)) +* Page Caching ([GitHub](https://github.com/rails/actionpack-page_caching), [Pull Request](https://github.com/rails/rails/pull/7833)) +* Sprockets ([GitHub](https://github.com/rails/sprockets-rails)) +* Performance tests ([GitHub](https://github.com/rails/rails-perftest), [Pull Request](https://github.com/rails/rails/pull/8876)) Documentation ------------- @@ -83,9 +83,9 @@ Please refer to the [Changelog](https://github.com/rails/rails/blob/master/railt ### Notable changes -* New test locations `test/models`, `test/helpers`, `test/controllers`, and `test/mailers`. Corresponding rake tasks added as well. ([Pull Request](https://github.com/rails/rails/pull/7878)) +* New test locations `test/models`, `test/helpers`, `test/controllers`, and `test/mailers`. Corresponding rake tasks added as well. ([Pull Request](https://github.com/rails/rails/pull/7878)) -* Your app's executables now live in the `bin/` dir. Run `rake rails:update:bin` to get `bin/bundle`, `bin/rails`, and `bin/rake`. +* Your app's executables now live in the `bin/` directory. Run `rake rails:update:bin` to get `bin/bundle`, `bin/rails`, and `bin/rake`. * Threadsafe on by default @@ -111,9 +111,9 @@ Please refer to the [Changelog](https://github.com/rails/rails/blob/master/activ ### Notable changes -* Add `ActiveModel::ForbiddenAttributesProtection`, a simple module to protect attributes from mass assignment when non-permitted attributes are passed. +* Add `ActiveModel::ForbiddenAttributesProtection`, a simple module to protect attributes from mass assignment when non-permitted attributes are passed. -* Added `ActiveModel::Model`, a mixin to make Ruby objects work with AP out of box. +* Added `ActiveModel::Model`, a mixin to make Ruby objects work with Action Pack out of box. ### Deprecations @@ -124,27 +124,27 @@ Please refer to the [Changelog](https://github.com/rails/rails/blob/master/activ ### Notable changes -* Replace deprecated `memcache-client` gem with `dalli` in ActiveSupport::Cache::MemCacheStore. +* Replace deprecated `memcache-client` gem with `dalli` in ActiveSupport::Cache::MemCacheStore. -* Optimize ActiveSupport::Cache::Entry to reduce memory and processing overhead. +* Optimize ActiveSupport::Cache::Entry to reduce memory and processing overhead. -* Inflections can now be defined per locale. `singularize` and `pluralize` accept locale as an extra argument. +* Inflections can now be defined per locale. `singularize` and `pluralize` accept locale as an extra argument. -* `Object#try` will now return nil instead of raise a NoMethodError if the receiving object does not implement the method, but you can still get the old behavior by using the new `Object#try!`. +* `Object#try` will now return nil instead of raise a NoMethodError if the receiving object does not implement the method, but you can still get the old behavior by using the new `Object#try!`. ### Deprecations -* Deprecate `ActiveSupport::TestCase#pending` method, use `skip` from MiniTest instead. +* Deprecate `ActiveSupport::TestCase#pending` method, use `skip` from MiniTest instead. -* ActiveSupport::Benchmarkable#silence has been deprecated due to its lack of thread safety. It will be removed without replacement in Rails 4.1. +* ActiveSupport::Benchmarkable#silence has been deprecated due to its lack of thread safety. It will be removed without replacement in Rails 4.1. -* `ActiveSupport::JSON::Variable` is deprecated. Define your own `#as_json` and `#encode_json` methods for custom JSON string literals. +* `ActiveSupport::JSON::Variable` is deprecated. Define your own `#as_json` and `#encode_json` methods for custom JSON string literals. -* Deprecates the compatibility method Module#local_constant_names, use Module#local_constants instead (which returns symbols). +* Deprecates the compatibility method Module#local_constant_names, use Module#local_constants instead (which returns symbols). -* BufferedLogger is deprecated. Use ActiveSupport::Logger, or the logger from Ruby stdlib. +* BufferedLogger is deprecated. Use ActiveSupport::Logger, or the logger from Ruby standard library. -* Deprecate `assert_present` and `assert_blank` in favor of `assert object.blank?` and `assert object.present?` +* Deprecate `assert_present` and `assert_blank` in favor of `assert object.blank?` and `assert object.present?` Action Pack ----------- @@ -165,7 +165,7 @@ Please refer to the [Changelog](https://github.com/rails/rails/blob/master/activ ### Notable changes -* Improve ways to write `change` migrations, making the old `up` & `down` methods no longer necessary. +* Improve ways to write `change` migrations, making the old `up` & `down` methods no longer necessary. * The methods `drop_table` and `remove_column` are now reversible, as long as the necessary information is given. The method `remove_column` used to accept multiple column names; instead use `remove_columns` (which is not revertible). @@ -178,40 +178,36 @@ Please refer to the [Changelog](https://github.com/rails/rails/blob/master/activ If migrating down, the given migration / block is run normally. See the [Guide on Migration](https://github.com/rails/rails/blob/master/guides/source/migrations.md#reverting-previous-migrations) -* Adds some metadata columns to `schema_migrations` table. +* Adds PostgreSQL array type support. Any datatype can be used to create an array column, with full migration and schema dumper support. - * `migrated_at` - * `fingerprint` - an md5 hash of the migration. - * `name` - the filename minus version and extension. +* Add `Relation#load` to explicitly load the record and return `self`. -* Adds PostgreSQL array type support. Any datatype can be used to create an array column, with full migration and schema dumper support. +* `Model.all` now returns an `ActiveRecord::Relation`, rather than an array of records. Use `Relation#to_a` if you really want an array. In some specific cases, this may cause breakage when upgrading. -* Add `Relation#load` to explicitly load the record and return `self`. +* Added `ActiveRecord::Migration.check_pending!` that raises an error if migrations are pending. -* `Model.all` now returns an `ActiveRecord::Relation`, rather than an array of records. Use `Relation#to_a` if you really want an array. In some specific cases, this may cause breakage when upgrading. - -* Added `ActiveRecord::Migration.check_pending!` that raises an error if migrations are pending. - -* Added custom coders support for `ActiveRecord::Store`. Now you can set your custom coder like this: +* Added custom coders support for `ActiveRecord::Store`. Now you can set your custom coder like this: store :settings, accessors: [ :color, :homepage ], coder: JSON -* `mysql` and `mysql2` connections will set `SQL_MODE=STRICT_ALL_TABLES` by default to avoid silent data loss. This can be disabled by specifying `strict: false` in your `database.yml`. +* `mysql` and `mysql2` connections will set `SQL_MODE=STRICT_ALL_TABLES` by default to avoid silent data loss. This can be disabled by specifying `strict: false` in your `database.yml`. + +* Remove IdentityMap. -* Remove IdentityMap. +* Remove automatic execution of EXPLAIN queries. The option `active_record.auto_explain_threshold_in_seconds` is no longer used and should be removed. -* Adds `ActiveRecord::NullRelation` and `ActiveRecord::Relation#none` implementing the null object pattern for the Relation class. +* Adds `ActiveRecord::NullRelation` and `ActiveRecord::Relation#none` implementing the null object pattern for the Relation class. -* Added `create_join_table` migration helper to create HABTM join tables. +* Added `create_join_table` migration helper to create HABTM join tables. -* Allows PostgreSQL hstore records to be created. +* Allows PostgreSQL hstore records to be created. ### Deprecations -* Deprecated the old-style hash based finder API. This means that methods which previously accepted "finder options" no longer do. +* Deprecated the old-style hash based finder API. This means that methods which previously accepted "finder options" no longer do. -* All dynamic methods except for `find_by_...` and `find_by_...!` are deprecated. Here's - how you can rewrite the code: +* All dynamic methods except for `find_by_...` and `find_by_...!` are deprecated. Here's + how you can rewrite the code: * `find_all_by_...` can be rewritten using `where(...)`. * `find_last_by_...` can be rewritten using `where(...).last`. diff --git a/guides/source/action_controller_overview.md b/guides/source/action_controller_overview.md index 7260a48c8c..2701f5bb72 100644 --- a/guides/source/action_controller_overview.md +++ b/guides/source/action_controller_overview.md @@ -6,6 +6,7 @@ In this guide you will learn how controllers work and how they fit into the requ After reading this guide, you will know: * How to follow the flow of a request through a controller. +* How to restrict parameters passed to your controller. * Why and how to store data in the session or cookies. * How to work with filters to execute code during request processing. * How to use Action Controller's built-in HTTP authentication. @@ -26,6 +27,16 @@ A controller can thus be thought of as a middle man between models and views. It NOTE: For more details on the routing process, see [Rails Routing from the Outside In](routing.html). +Controller Naming Convention +---------------------------- + +The naming convention of controllers in Rails favors pluralization of the last word in the controller's name, although it is not strictly required (e.g. `ApplicationController`). For example, `ClientsController` is preferable to `ClientController`, `SiteAdminsController` is preferable to `SiteAdminController` or `SitesAdminsController`, and so on. + +Following this convention will allow you to use the default route generators (e.g. `resources`, etc) without needing to qualify each `:path` or `:controller`, and keeps URL and path helpers' usage consistent throughout your application. See [Layouts & Rendering Guide](layouts_and_rendering.html) for more details. + +NOTE: The controller naming convention differs from the naming convention of models, which expected to be named in singular form. + + Methods and Actions ------------------- @@ -38,7 +49,7 @@ class ClientsController < ApplicationController end ``` -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 would 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 @@ -112,23 +123,23 @@ To send a hash you include the key name inside the brackets: </form> ``` -When this form is submitted, the value of `params[:client]` 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 `{ "name" => "Acme", "phone" => "12345", "address" => { "postcode" => "12345", "city" => "Carrot City" } }`. Note the nested hash in `params[:client][:address]`. -Note that the `params` hash is actually an instance of `ActiveSupport::HashWithIndifferentAccess`, which acts like a hash that lets you use symbols and strings interchangeably as keys. +Note that the `params` hash is actually an instance of `ActiveSupport::HashWithIndifferentAccess`, which acts like a hash but lets you use symbols and strings interchangeably as keys. -### JSON/XML parameters +### JSON parameters -If you're writing a web service application, you might find yourself more comfortable on accepting parameters in JSON or XML format. Rails will automatically convert your parameters into `params` hash, which you'll be able to access like you would normally do with form data. +If you're writing a web service application, you might find yourself more comfortable accepting parameters in JSON format. Rails will automatically convert your parameters into the `params` hash, which you can access as you would normally. -So for example, if you are sending this JSON parameter: +So for example, if you are sending this JSON content: ```json { "company": { "name": "acme", "address": "123 Carrot Street" } } ``` -You'll get `params[:company]` as `{ :name => "acme", "address" => "123 Carrot Street" }`. +You'll get `params[:company]` as `{ "name" => "acme", "address" => "123 Carrot Street" }`. -Also, if you've turned on `config.wrap_parameters` in your initializer or calling `wrap_parameters` in your controller, you can safely omit the root element in the JSON/XML parameter. The parameters will be cloned and wrapped in the key according to your controller's name by default. So the above parameter can be written as: +Also, if you've turned on `config.wrap_parameters` in your initializer or calling `wrap_parameters` in your controller, you can safely omit the root element in the JSON parameter. The parameters will be cloned and wrapped in the key according to your controller's name by default. So the above parameter can be written as: ```json { "name": "acme", "address": "123 Carrot Street" } @@ -137,17 +148,19 @@ Also, if you've turned on `config.wrap_parameters` in your initializer or callin And assume that you're sending the data to `CompaniesController`, it would then be wrapped in `:company` key like this: ```ruby -{ :name => "acme", :address => "123 Carrot Street", :company => { :name => "acme", :address => "123 Carrot Street" }} +{ name: "acme", address: "123 Carrot Street", company: { name: "acme", address: "123 Carrot Street" } } ``` You can customize the name of the key or specific parameters you want to wrap by consulting the [API documentation](http://api.rubyonrails.org/classes/ActionController/ParamsWrapper.html) +NOTE: Support for parsing XML parameters has been extracted into a gem named `actionpack-xml_parser` + ### 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 -match '/clients/:status' => 'clients#index', foo: "bar" +match '/clients/:status' => 'clients#index', foo: 'bar' ``` 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". @@ -168,6 +181,158 @@ These options will be used as a starting point when generating URLs, so it's pos If you define `default_url_options` in `ApplicationController`, as in the example above, it would be used for all URL generation. The method can also be defined in one specific controller, in which case it only affects URLs generated there. +### Strong Parameters + +With strong parameters, Action Controller parameters are forbidden to +be used in Active Model mass assignments until they have been +whitelisted. This means you'll have to make a conscious choice about +which attributes to allow for mass updating and thus prevent +accidentally exposing that which shouldn't be exposed. + +In addition, parameters can be marked as required and flow through a +predefined raise/rescue flow to end up as a 400 Bad Request with no +effort. + +```ruby +class PeopleController < ActionController::Base + # This will raise an ActiveModel::ForbiddenAttributes exception + # because it's using mass assignment without an explicit permit + # step. + def create + Person.create(params[:person]) + end + + # This will pass with flying colors as long as there's a person key + # in the parameters, otherwise it'll raise a + # ActionController::ParameterMissing exception, which will get + # caught by ActionController::Base and turned into that 400 Bad + # Request reply. + def update + person = current_account.people.find(params[:id]) + person.update_attributes!(person_params) + redirect_to person + end + + private + # Using a private method to encapsulate the permissible parameters + # is just a good pattern since you'll be able to reuse the same + # permit list between create and update. Also, you can specialize + # this method with per-user checking of permissible attributes. + def person_params + params.require(:person).permit(:name, :age) + end +end +``` + +#### Permitted Scalar Values + +Given + +```ruby +params.permit(:id) +``` + +the key `:id` will pass the whitelisting if it appears in `params` and +it has a permitted scalar value associated. Otherwise the key is going +to be filtered out, so arrays, hashes, or any other objects cannot be +injected. + +The permitted scalar types are `String`, `Symbol`, `NilClass`, +`Numeric`, `TrueClass`, `FalseClass`, `Date`, `Time`, `DateTime`, +`StringIO`, `IO`, `ActionDispatch::Http::UploadedFile` and +`Rack::Test::UploadedFile`. + +To declare that the value in `params` must be an array of permitted +scalar values map the key to an empty array: + +```ruby +params.permit(id: []) +``` + +To whitelist an entire hash of parameters, the `permit!` method can be +used: + +```ruby +params.require(:log_entry).permit! +``` + +This will mark the `:log_entry` parameters hash and any subhash of it +permitted. Extreme care should be taken when using `permit!` as it +will allow all current and future model attributes to be +mass-assigned. + +#### Nested Parameters + +You can also use permit on nested parameters, like: + +```ruby +params.permit(:name, { emails: [] }, + friends: [ :name, + { family: [ :name ], hobbies: [] }]) +``` + +This declaration whitelists the `name`, `emails` and `friends` +attributes. It is expected that `emails` will be an array of permitted +scalar values and that `friends` will be an array of resources with +specific attributes : they should have a `name` attribute (any +permitted scalar values allowed), a `hobbies` attribute as an array of +permitted scalar values, and a `family` attribute which is restricted +to having a `name` (any permitted scalar values allowed, too). + +#### More Examples + +You want to also use the permitted attributes in the `new` +action. This raises the problem that you can't use `require` on the +root key because normally it does not exist when calling `new`: + +```ruby +# using `fetch` you can supply a default and use +# the Strong Parameters API from there. +params.fetch(:blog, {}).permit(:title, :author) +``` + +`accepts_nested_attributes_for` allows you to update and destroy +associated records. This is based on the `id` and `_destroy` +parameters: + +```ruby +# permit :id and :_destroy +params.require(:author).permit(:name, books_attributes: [:title, :id, :_destroy]) +``` + +Hashes with integer keys are treated differently and you can declare +the attributes as if they were direct children. You get these kinds of +parameters when you use `accepts_nested_attributes_for` in combination +with a `has_many` association: + +```ruby +# To whitelist the following data: +# {"book" => {"title" => "Some Book", +# "chapters_attributes" => { "1" => {"title" => "First Chapter"}, +# "2" => {"title" => "Second Chapter"}}}} + +params.require(:book).permit(:title, chapters_attributes: [:title]) +``` + +#### Outside the Scope of Strong Parameters + +The strong parameter API was designed with the most common use cases +in mind. It is not meant as a silver bullet to handle all your +whitelisting problems. However you can easily mix the API with your +own code to adapt to your situation. + +Imagine a scenario where you want to whitelist an attribute +containing a hash with any keys. Using strong parameters you can't +allow a hash with any keys but you can use a simple assignment to get +the job done: + +```ruby +def product_params + params.require(:product).permit(:name).tap do |whitelisted| + whitelisted[:data] = params[:product][:data] + end +end +``` Session ------- @@ -403,10 +568,10 @@ end Note that while for session values you set the key to `nil`, to delete a cookie value you should use `cookies.delete(:key)`. -Rendering xml and json data +Rendering XML and JSON data --------------------------- -ActionController makes it extremely easy to render `xml` or `json` data. If you generate a controller using scaffolding then it would look something like this: +ActionController makes it extremely easy to render `XML` or `JSON` data. If you've generated a controller using scaffolding, it would look something like this: ```ruby class UsersController < ApplicationController @@ -421,7 +586,7 @@ class UsersController < ApplicationController end ``` -Notice that in the above case code is `render xml: @users` and not `render xml: @users.to_xml`. That is because if the input is not string then rails automatically invokes `to_xml` . +You may notice in the above code that we're using `render xml: @users`, not `render xml: @users.to_xml`. If the object is not a String, then Rails will automatically invoke `to_xml` for us. Filters ------- @@ -444,15 +609,6 @@ class ApplicationController < ActionController::Base 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. - def logged_in? - !!current_user - end end ``` @@ -633,7 +789,7 @@ Rails comes with two built-in HTTP authentication mechanisms: 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, `http_basic_authenticate_with`. ```ruby -class AdminController < ApplicationController +class AdminsController < ApplicationController http_basic_authenticate_with name: "humbaba", password: "5baa61e4" end ``` @@ -645,7 +801,7 @@ With this in place, you can create namespaced controllers that inherit from `Adm 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 +class AdminsController < ApplicationController USERS = { "lifo" => "world" } before_action :authenticate diff --git a/guides/source/action_mailer_basics.md b/guides/source/action_mailer_basics.md index 513ae1272f..d1dd231cf6 100644 --- a/guides/source/action_mailer_basics.md +++ b/guides/source/action_mailer_basics.md @@ -1,7 +1,9 @@ Action Mailer Basics ==================== -This guide should provide you with all you need to get started in sending and receiving emails from and to your application, and many internals of Action Mailer. It also covers how to test your mailers. +This guide provides you with all you need to get started in sending and +receiving emails from and to your application, and many internals of Action +Mailer. It also covers how to test your mailers. After reading this guide, you will know: @@ -9,17 +11,19 @@ After reading this guide, you will know: * How to generate and edit an Action Mailer class and mailer view. * How to configure Action Mailer for your environment. * How to test your Action Mailer classes. + -------------------------------------------------------------------------------- Introduction ------------ -Action Mailer allows you to send emails from your application using a mailer model and views. So, in Rails, emails are used by creating mailers that inherit from `ActionMailer::Base` and live in `app/mailers`. Those mailers have associated views that appear alongside controller views in `app/views`. +Action Mailer allows you to send emails from your application using mailer classes and views. Mailers work very similarly to controllers. They inherit from `ActionMailer::Base` and live in `app/mailers`, and they have associated views that appear in `app/views`. Sending Emails -------------- -This section will provide a step-by-step guide to creating a mailer and its views. +This section will provide a step-by-step guide to creating a mailer and its +views. ### Walkthrough to Generating a Mailer @@ -34,10 +38,25 @@ invoke test_unit create test/mailers/user_mailer_test.rb ``` -So we got the mailer, the views, and the tests. +As you can see, you can generate mailers just like you use other generators with +Rails. Mailers are conceptually similar to controllers, and so we get a mailer, +a directory for views, and a test. + +If you didn't want to use a generator, you could create your own file inside of +app/mailers, just make sure that it inherits from `ActionMailer::Base`: + +```ruby +class MyMailer < ActionMailer::Base +end +``` #### Edit the Mailer +Mailers are very similar to Rails controllers. They also have methods called +"actions" and use views to structure the content. Where a controller generates +content like HTML to send back to the client, a Mailer creates a message to be +delivered via email. + `app/mailers/user_mailer.rb` contains an empty mailer: ```ruby @@ -46,7 +65,8 @@ class UserMailer < ActionMailer::Base end ``` -Let's 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 @@ -55,21 +75,25 @@ class UserMailer < ActionMailer::Base def welcome_email(user) @user = user @url = 'http://example.com/login' - mail(to: user.email, subject: 'Welcome to My Awesome Site') + mail(to: @user.email, subject: 'Welcome to My Awesome Site') end end ``` -Here is a quick explanation of the items presented in the preceding method. For a full list of all available options, please have a look further down at the Complete List of Action Mailer user-settable attributes section. +Here is a quick explanation of the items presented in the preceding method. For +a full list of all available options, please have a look further down at the +Complete List of Action Mailer user-settable attributes section. -* `default Hash` - This is a hash of default values for any email you send, in this case we are setting the `:from` header to a value for all messages in this class, this can be overridden on a per email basis +* `default Hash` - This is a hash of default values for any email you send from this mailer. In this case we are setting the `:from` header to a value for all messages in this class. This can be overridden on a per-email basis. * `mail` - The actual email message, we are passing the `:to` and `:subject` headers in. -Just like controllers, any instance variables we define in the method become available for use in the views. +Just like controllers, any instance variables we define in the method become +available for use in the views. #### Create a Mailer View -Create a file called `welcome_email.html.erb` in `app/views/user_mailer/`. This will be the template used for the email, formatted in HTML: +Create a file called `welcome_email.html.erb` in `app/views/user_mailer/`. This +will be the template used for the email, formatted in HTML: ```html+erb <!DOCTYPE html> @@ -91,7 +115,9 @@ Create a file called `welcome_email.html.erb` in `app/views/user_mailer/`. This </html> ``` -It is also a good idea to make a text part for this email. To do this, create a file called `welcome_email.text.erb` in `app/views/user_mailer/`: +Let's also make a text part for this email. Not all clients prefer HTML emails, +and so sending both is best practice. To do this, create a file called +`welcome_email.text.erb` in `app/views/user_mailer/`: ```erb Welcome to example.com, <%= @user.name %> @@ -105,22 +131,29 @@ To login to the site, just follow this link: <%= @url %>. Thanks for joining and have a great day! ``` -When you call the `mail` method now, Action Mailer will detect the two templates (text and HTML) and automatically generate a `multipart/alternative` email. +When you call the `mail` method now, Action Mailer will detect the two templates +(text and HTML) and automatically generate a `multipart/alternative` email. -#### Wire It Up So That the System Sends the Email When a User Signs Up +#### Calling the Mailer -There are several ways to do this, some people create Rails Observers to fire off emails, others do it inside of the User Model. However, mailers are really just another way to render a view. Instead of rendering a view and sending out the HTTP protocol, they are just sending it out through the Email protocols instead. Due to this, it makes sense to just have your controller tell the mailer to send an email when a user is successfully created. +Mailers are really just another way to render a view. Instead of rendering a +view and sending out the HTTP protocol, they are just sending it out through the +Email protocols instead. Due to this, it makes sense to just have your +controller tell the Mailer to send an email when a user is successfully created. Setting this up is painfully simple. -First off, we need to create a simple `User` scaffold: +First, let's create a simple `User` scaffold: ```bash -$ rails generate scaffold user name:string email:string login:string +$ rails generate scaffold user name email login $ rake db:migrate ``` -Now that we have a user model to play with, we will just edit the `app/controllers/users_controller.rb` make it instruct the UserMailer to deliver an email to the newly created user by editing the create action and inserting a call to `UserMailer.welcome_email` right after the user is successfully saved: +Now that we have a user model to play with, we will just edit the +`app/controllers/users_controller.rb` make it instruct the UserMailer to deliver +an email to the newly created user by editing the create action and inserting a +call to `UserMailer.welcome_email` right after the user is successfully saved: ```ruby class UsersController < ApplicationController @@ -145,63 +178,50 @@ class UsersController < ApplicationController end ``` -This provides a much simpler implementation that does not require the registering of observers and the like. - -The method `welcome_email` returns a `Mail::Message` object which can then just be told `deliver` to send itself out. +The method `welcome_email` returns a `Mail::Message` object which can then just +be told `deliver` to send itself out. ### Auto encoding header values -Action Mailer now handles the auto encoding of multibyte characters inside of headers and bodies. - -If you are using UTF-8 as your character set, you do not have to do anything special, just go ahead and send in UTF-8 data to the address fields, subject, keywords, filenames or body of the email and Action Mailer will auto encode it into quoted printable for you in the case of a header field or Base64 encode any body parts that are non US-ASCII. +Action Mailer handles the auto encoding of multibyte characters inside of +headers and bodies. -For more complex examples such as defining alternate character sets or self-encoding text first, please refer to the Mail library. +For more complex examples such as defining alternate character sets or +self-encoding text first, please refer to the +[Mail](https://github.com/mikel/mail) library. ### Complete List of Action Mailer Methods -There are just three methods that you need to send pretty much any email message: +There are just three methods that you need to send pretty much any email +message: -* `headers` - Specifies any header on the email you want. You can pass a hash of header field names and value pairs, or you can call `headers[:field_name] = 'value'`. -* `attachments` - Allows you to add attachments to your email. For example, `attachments['file-name.jpg'] = File.read('file-name.jpg')`. -* `mail` - Sends the actual email itself. You can pass in headers as a hash to the mail method as a parameter, mail will then create an email, either plain text, or multipart, depending on what email templates you have defined. - -#### Custom Headers - -Defining custom headers are simple, you can do it one of three ways: - -* Defining a header field as a parameter to the `mail` method: - - ```ruby - mail('X-Spam' => value) - ``` - -* Passing in a key value assignment to the `headers` method: - - ```ruby - headers['X-Spam'] = value - ``` - -* Passing a hash of key value pairs to the `headers` method: - - ```ruby - headers {'X-Spam' => value, 'X-Special' => another_value} - ``` - -TIP: All `X-Value` headers per the RFC2822 can appear more than once. If you want to delete an `X-Value` header, you need to assign it a value of `nil`. +* `headers` - Specifies any header on the email you want. You can pass a hash of + header field names and value pairs, or you can call `headers[:field_name] = + 'value'`. +* `attachments` - Allows you to add attachments to your email. For example, + `attachments['file-name.jpg'] = File.read('file-name.jpg')`. +* `mail` - Sends the actual email itself. You can pass in headers as a hash to + the mail method as a parameter, mail will then create an email, either plain + text, or multipart, depending on what email templates you have defined. #### Adding Attachments -Adding attachments has been simplified in Action Mailer 3.0. +Action Mailer makes it very easy to add attachments. -* Pass the file name and content and Action Mailer and the Mail gem will automatically guess the mime_type, set the encoding and create the attachment. +* Pass the file name and content and Action Mailer and the + [Mail gem](https://github.com/mikel/mail) will automatically guess the + mime_type, set the encoding and create the attachment. ```ruby attachments['filename.jpg'] = File.read('/path/to/filename.jpg') ``` -NOTE: Mail will automatically Base64 encode an attachment. If you want something different, pre-encode your content and pass in the encoded content and encoding in a `Hash` to the `attachments` method. +NOTE: Mail will automatically Base64 encode an attachment. If you want something +different, encode your content and pass in the encoded content and encoding in a +`Hash` to the `attachments` method. -* Pass the file name and specify headers and content and Action Mailer and Mail will use the settings you pass in. +* Pass the file name and specify headers and content and Action Mailer and Mail + will use the settings you pass in. ```ruby encoded_content = SpecialEncode(File.read('/path/to/filename.jpg')) @@ -210,13 +230,14 @@ NOTE: Mail will automatically Base64 encode an attachment. If you want something content: encoded_content } ``` -NOTE: If you specify an encoding, Mail will assume that your content is already encoded and not try to Base64 encode it. +NOTE: If you specify an encoding, Mail will assume that your content is already +encoded and not try to Base64 encode it. #### Making Inline Attachments Action Mailer 3.0 makes inline attachments, which involved a lot of hacking in pre 3.0 versions, much simpler and trivial as they should be. -* Firstly, to tell Mail to turn an attachment into an inline attachment, you just call `#inline` on the attachments method within your Mailer: +* First, to tell Mail to turn an attachment into an inline attachment, you just call `#inline` on the attachments method within your Mailer: ```ruby def welcome @@ -224,7 +245,9 @@ Action Mailer 3.0 makes inline attachments, which involved a lot of hacking in p end ``` -* Then in your view, you can just reference `attachments[]` as a hash and specify which attachment you want to show, calling `url` on it and then passing the result into the `image_tag` method: +* Then in your view, you can just reference `attachments` as a hash and specify + which attachment you want to show, calling `url` on it and then passing the + result into the `image_tag` method: ```html+erb <p>Hello there, this is our image</p> @@ -232,7 +255,8 @@ Action Mailer 3.0 makes inline attachments, which involved a lot of hacking in p <%= image_tag attachments['image.jpg'].url %> ``` -* As this is a standard call to `image_tag` you can pass in an options hash after the attachment URL as you could for any other image: +* As this is a standard call to `image_tag` you can pass in an options hash + after the attachment URL as you could for any other image: ```html+erb <p>Hello there, this is our image</p> @@ -243,7 +267,10 @@ Action Mailer 3.0 makes inline attachments, which involved a lot of hacking in p #### Sending Email To Multiple Recipients -It is possible to send email to one or more recipients in one email (e.g., informing all admins of a new signup) by setting the list of emails to the `:to` key. The list of emails can be an array of email addresses or a single string with the addresses separated by commas. +It is possible to send email to one or more recipients in one email (e.g., +informing all admins of a new signup) by setting the list of emails to the `:to` +key. The list of emails can be an array of email addresses or a single string +with the addresses separated by commas. ```ruby class AdminMailer < ActionMailer::Base @@ -257,12 +284,14 @@ class AdminMailer < ActionMailer::Base end ``` -The same format can be used to set carbon copy (Cc:) and blind carbon copy (Bcc:) recipients, by using the `:cc` and `:bcc` keys respectively. +The same format can be used to set carbon copy (Cc:) and blind carbon copy +(Bcc:) recipients, by using the `:cc` and `:bcc` keys respectively. #### Sending Email With Name -Sometimes you wish to show the name of the person instead of just their email address when they receive the email. The trick to doing that is -to format the email address in the format `"Name <email>"`. +Sometimes you wish to show the name of the person instead of just their email +address when they receive the email. The trick to doing that is to format the +email address in the format `"Full Name <email>"`. ```ruby def welcome_email(user) @@ -274,7 +303,11 @@ end ### Mailer Views -Mailer views are located in the `app/views/name_of_mailer_class` directory. The specific mailer view is known to the class because its name is the same as the mailer method. 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.text.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 its name is the same as the +mailer method. 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.text.erb` for the plain text version. To change the default mailer view for your action you do something like: @@ -285,7 +318,7 @@ class UserMailer < ActionMailer::Base def welcome_email(user) @user = user @url = 'http://example.com/login' - mail(to: user.email, + mail(to: @user.email, subject: 'Welcome to My Awesome Site', template_path: 'notifications', template_name: 'another') @@ -293,9 +326,12 @@ class UserMailer < ActionMailer::Base end ``` -In this case it will look for templates at `app/views/notifications` with name `another`. You can also specify an array of paths for `template_path`, and they will be searched in order. +In this case it will look for templates at `app/views/notifications` with name +`another`. You can also specify an array of paths for `template_path`, and they +will be searched in order. -If you want more flexibility you can also pass a block and render specific templates or even render inline or text without using a template file: +If you want more flexibility you can also pass a block and render specific +templates or even render inline or text without using a template file: ```ruby class UserMailer < ActionMailer::Base @@ -304,23 +340,28 @@ class UserMailer < ActionMailer::Base def welcome_email(user) @user = user @url = 'http://example.com/login' - mail(to: user.email, + mail(to: @user.email, subject: 'Welcome to My Awesome Site') do |format| format.html { render 'another_template' } format.text { render text: 'Render text' } end end - end ``` -This will render the template 'another_template.html.erb' for the HTML part and use the rendered text for the text part. The render command is the same one used inside of Action Controller, so you can use all the same options, such as `:text`, `:inline` etc. +This will render the template 'another_template.html.erb' for the HTML part and +use the rendered text for the text part. The render command is the same one used +inside of Action Controller, so you can use all the same options, such as +`:text`, `:inline` etc. ### Action Mailer Layouts -Just like controller views, you can also have mailer layouts. The layout name needs to be the same as your mailer, such as `user_mailer.html.erb` and `user_mailer.text.erb` to be automatically recognized by your mailer as a layout. +Just like controller views, you can also have mailer layouts. The layout name +needs to be the same as your mailer, such as `user_mailer.html.erb` and +`user_mailer.text.erb` to be automatically recognized by your mailer as a +layout. -In order to use a different file just use: +In order to use a different file, call `layout` in your mailer: ```ruby class UserMailer < ActionMailer::Base @@ -328,9 +369,11 @@ class UserMailer < ActionMailer::Base end ``` -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. -You can also pass in a `layout: 'layout_name'` option to the render call inside the format block to specify different layouts for different actions: +You can also pass in a `layout: 'layout_name'` option to the render call inside +the format block to specify different layouts for different actions: ```ruby class UserMailer < ActionMailer::Base @@ -343,13 +386,37 @@ class UserMailer < ActionMailer::Base end ``` -Will render the HTML part using the `my_layout.html.erb` file and the text part with the usual `user_mailer.text.erb` file if it exists. +Will render the HTML part using the `my_layout.html.erb` file and the text part +with the usual `user_mailer.text.erb` file if it exists. ### Generating URLs in Action Mailer Views -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` parameter yourself. + +As the `:host` usually is consistent across the application you can configure it +globally in `config/application.rb`: + +```ruby +config.action_mailer.default_url_options = { host: 'example.com' } +``` + +#### generating URLs with `url_for` + +You need to pass 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. + +```erb +<%= url_for(controller: 'welcome', + action: 'greeting', + only_path: false) %> +``` + +If you did not configure the `:host` option globally make sure to pass it to +`url_for`. -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', @@ -357,27 +424,32 @@ Unlike controllers, the mailer instance doesn't have any context about the incom action: 'greeting') %> ``` -When using named routes you only need to supply the `:host`: +NOTE: When you explicitly pass the `:host` Rails will always generate absolute +URLs, so there is no need to pass `only_path: false`. -```erb -<%= user_url(@user, host: 'example.com') %> -``` +#### generating URLs with named routes -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. +Email clients have no web context and so paths have no base URL to form complete +web addresses. Thus, you should always use the "_url" variant of named route +helpers. -It is also possible to set a default host that will be used in all mailers by setting the `:host` option as a configuration option in `config/application.rb`: +If you did not configure the `:host` option globally make sure to pass it to the +url helper. -```ruby -config.action_mailer.default_url_options = { host: 'example.com' } +```erb +<%= user_url(@user, host: 'example.com') %> ``` -If you use this setting, you should pass 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. - ### 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.text.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.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. -The order of the parts getting inserted is determined by the `:parts_order` inside of the `ActionMailer::Base.default` method. +The order of the parts getting inserted is determined by the `:parts_order` +inside of the `ActionMailer::Base.default` method. ### Sending Emails with Attachments @@ -389,25 +461,51 @@ class UserMailer < ActionMailer::Base @user = user @url = user_url(@user) attachments['terms.pdf'] = File.read('/path/terms.pdf') - mail(to: user.email, + mail(to: @user.email, subject: 'Please see the Terms and Conditions attached') end end ``` -The above will send a multipart email with an attachment, properly nested with the top level being `multipart/mixed` and the first part being a `multipart/alternative` containing the plain text and HTML email messages. +The above will send a multipart email with an attachment, properly nested with +the top level being `multipart/mixed` and the first part being a +`multipart/alternative` containing the plain text and HTML email messages. ### Sending Emails with Dynamic Delivery Options -If you wish to override the default delivery options (e.g. SMTP credentials) while delivering emails, you can do this using `delivery_method_options` in the mailer action. +If you wish to override the default delivery options (e.g. SMTP credentials) +while delivering emails, you can do this using `delivery_method_options` in the +mailer action. ```ruby class UserMailer < ActionMailer::Base - def welcome_email(user,company) + def welcome_email(user, company) @user = user @url = user_url(@user) - delivery_options = { user_name: company.smtp_user, password: company.smtp_password, address: company.smtp_host } - mail(to: user.email, subject: "Please see the Terms and Conditions attached", delivery_method_options: delivery_options) + delivery_options = { user_name: company.smtp_user, + password: company.smtp_password, + address: company.smtp_host } + mail(to: @user.email, + subject: "Please see the Terms and Conditions attached", + delivery_method_options: delivery_options) + end +end +``` + +### Sending Emails without Template Rendering + +There may be cases in which you want to skip the template rendering step and +supply the email body as a string. You can achieve this using the `:body` +option. In such cases don't forget to add the `:content_type` option. Rails +will default to `text/plain` otherwise. + +```ruby +class UserMailer < ActionMailer::Base + def welcome_email(user, email_body) + mail(to: user.email, + body: email_body, + content_type: "text/html", + subject: "Already rendered!") end end ``` @@ -415,13 +513,21 @@ end Receiving Emails ---------------- -Receiving and parsing emails with Action Mailer can be a rather complex endeavor. 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 to: +Receiving and parsing emails with Action Mailer can be a rather complex +endeavor. 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 to: * Implement a `receive` method in your mailer. -* Configure your email server to forward emails from the address(es) you would like your app to receive to `/path/to/app/bin/rails runner 'UserMailer.receive(STDIN.read)'`. +* Configure your email server to forward emails from the address(es) you would + like your app to receive to `/path/to/app/bin/rails 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 `receive` instance 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 @@ -447,17 +553,23 @@ end Action Mailer Callbacks --------------------------- -Action Mailer allows for you to specify a `before_action`, `after_action` and 'around_action'. +Action Mailer allows for you to specify a `before_action`, `after_action` and +`around_action`. -* Filters can be specified with a block or a symbol to a method in the mailer class similar to controllers. +* Filters can be specified with a block or a symbol to a method in the mailer + class similar to controllers. -* You could use a `before_action` to prepopulate the mail object with defaults, delivery_method_options or insert default headers and attachments. +* You could use a `before_action` to populate the mail object with defaults, + delivery_method_options or insert default headers and attachments. -* You could use an `after_action` to do similar setup as a `before_action` but using instance variables set in your mailer action. +* You could use an `after_action` to do similar setup as a `before_action` but + using instance variables set in your mailer action. ```ruby class UserMailer < ActionMailer::Base - after_action :set_delivery_options, :prevent_delivery_to_guests, :set_business_headers + after_action :set_delivery_options, + :prevent_delivery_to_guests, + :set_business_headers def feedback_message(business, user) @business = business @@ -473,7 +585,8 @@ class UserMailer < ActionMailer::Base private def set_delivery_options - # You have access to the mail instance and @business and @user instance variables here + # You have access to the mail instance, + # @business and @user instance variables here if @business && @business.has_smtp_settings? mail.delivery_method.settings.merge!(@business.smtp_settings) end @@ -498,18 +611,19 @@ end Using Action Mailer Helpers --------------------------- -Action Mailer now just inherits from Abstract Controller, so you have access to the same generic helpers as you do in Action Controller. +Action Mailer now just inherits from `AbstractController`, so you have access to +the same generic helpers as you do in Action Controller. Action Mailer Configuration --------------------------- -The following configuration options are best made in one of the environment files (environment.rb, production.rb, etc...) +The following configuration options are best made in one of the environment +files (environment.rb, production.rb, etc...) | Configuration | Description | |---------------|-------------| -|`template_root`|Determines the base from which template references will be made.| |`logger`|Generates information on the mailing run if available. Can be set to `nil` for no logging. Compatible with both Ruby's own `Logger` and `Log4r` loggers.| -|`smtp_settings`|Allows detailed configuration for `:smtp` delivery method:<ul><li>`:address` - Allows you to use a remote mail server. Just change it from its default "localhost" setting.</li><li>`:port` - On the off chance that your mail server doesn't run on port 25, you can change it.</li><li>`:domain` - If you need to specify a HELO domain, you can do it here.</li><li>`:user_name` - If your mail server requires authentication, set the username in this setting.</li><li>`:password` - If your mail server requires authentication, set the password in this setting.</li><li>`:authentication` - If your mail server requires authentication, you need to specify the authentication type here. This is a symbol and one of `:plain`, `:login`, `:cram_md5`.</li><li>`:enable_starttls_auto` - Set this to `false` if there is a problem with your server certificate that you cannot resolve.</li></ul>| +|`smtp_settings`|Allows detailed configuration for `:smtp` delivery method:<ul><li>`:address` - Allows you to use a remote mail server. Just change it from its default "localhost" setting.</li><li>`:port` - On the off chance that your mail server doesn't run on port 25, you can change it.</li><li>`:domain` - If you need to specify a HELO domain, you can do it here.</li><li>`:user_name` - If your mail server requires authentication, set the username in this setting.</li><li>`:password` - If your mail server requires authentication, set the password in this setting.</li><li>`:authentication` - If your mail server requires authentication, you need to specify the authentication type here. This is a symbol and one of `:plain`, `:login`, `:cram_md5`.</li><li>`:enable_starttls_auto` - Set this to `false` if there is a problem with your server certificate that you cannot resolve.</li></ul>| |`sendmail_settings`|Allows you to override options for the `:sendmail` delivery method.<ul><li>`:location` - The location of the sendmail executable. Defaults to `/usr/sbin/sendmail`.</li><li>`:arguments` - The command line arguments to be passed to sendmail. Defaults to `-i -t`.</li></ul>| |`raise_delivery_errors`|Whether or not errors should be raised if the email fails to be delivered. This only works if the external email server is configured for immediate delivery.| |`delivery_method`|Defines a delivery method. Possible values are `:smtp` (default), `:sendmail`, `:file` and `:test`.| @@ -517,9 +631,14 @@ The following configuration options are best made in one of the environment file |`deliveries`|Keeps an array of all the emails sent out through the Action Mailer with delivery_method :test. Most useful for unit and functional testing.| |`default_options`|Allows you to set default values for the `mail` method options (`:from`, `:reply_to`, etc.).| +For a complete writeup of possible configurations see the +[Action Mailer section](configuring.html#configuring-action-mailer) in +our Configuring Rails Applications guide. + ### Example Action Mailer Configuration -An example would be adding the following to your appropriate `config/environments/$RAILS_ENV.rb` file: +An example would be adding the following to your appropriate +`config/environments/$RAILS_ENV.rb` file: ```ruby config.action_mailer.delivery_method = :sendmail @@ -530,19 +649,20 @@ config.action_mailer.delivery_method = :sendmail # } config.action_mailer.perform_deliveries = true config.action_mailer.raise_delivery_errors = true -config.action_mailer.default_options = {from: 'no-replay@example.org'} +config.action_mailer.default_options = {from: 'no-reply@example.com'} ``` -### Action Mailer Configuration for GMail +### Action Mailer Configuration for Gmail -As Action Mailer now uses the Mail gem, this becomes as simple as adding to your `config/environments/$RAILS_ENV.rb` file: +As Action Mailer now uses the Mail gem, this becomes as simple as adding to your +`config/environments/$RAILS_ENV.rb` file: ```ruby config.action_mailer.delivery_method = :smtp config.action_mailer.smtp_settings = { address: 'smtp.gmail.com', port: 587, - domain: 'baci.lindsaar.net', + domain: 'example.com', user_name: '<username>', password: '<password>', authentication: 'plain', @@ -552,35 +672,15 @@ config.action_mailer.smtp_settings = { Mailer Testing -------------- -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 - def test_welcome_email - user = users(:some_user_in_your_fixtures) - - # Send the email, then test that it got queued - email = UserMailer.welcome_email(user).deliver - 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_match "<h1>Welcome to example.com, #{user.name}</h1>", email.body.to_s - assert_match 'you have joined to example.com community', email.body.to_s - end -end -``` - -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 what we expect. - -NOTE: The `ActionMailer::Base.deliveries` array is only reset automatically in `ActionMailer::TestCase` tests. If you want to have a clean slate outside Action Mailer tests, you can reset it manually with: `ActionMailer::Base.deliveries.clear` +You can find detailed instructions on how to test your mailers in the +[testing guide](testing.html#testing-your-mailers). Intercepting Emails ------------------- -There are situations where you need to edit an email before it's delivered. Fortunately Action Mailer provides hooks to intercept every email. You can register an interceptor to make modifications to mail messages right before they are handed to the delivery agents. +There are situations where you need to edit an email before it's +delivered. Fortunately Action Mailer provides hooks to intercept every +email. You can register an interceptor to make modifications to mail messages +right before they are handed to the delivery agents. ```ruby class SandboxEmailInterceptor @@ -590,10 +690,15 @@ class SandboxEmailInterceptor end ``` -Before the interceptor can do its job you need to register it with the Action Mailer framework. You can do this in an initializer file `config/initializers/sandbox_email_interceptor.rb` +Before the interceptor can do its job you need to register it with the Action +Mailer framework. You can do this in an initializer file +`config/initializers/sandbox_email_interceptor.rb` ```ruby ActionMailer::Base.register_interceptor(SandboxEmailInterceptor) if Rails.env.staging? ``` -NOTE: The example above uses a custom environment called "staging" for a production like server but for testing purposes. You can read [Creating Rails environments](./configuring.html#creating-rails-environments) for more information about custom Rails environments. +NOTE: The example above uses a custom environment called "staging" for a +production like server but for testing purposes. You can read +[Creating Rails environments](./configuring.html#creating-rails-environments) +for more information about custom Rails environments. diff --git a/guides/source/action_view_overview.md b/guides/source/action_view_overview.md index 4cdac43a7e..dea1ddef71 100644 --- a/guides/source/action_view_overview.md +++ b/guides/source/action_view_overview.md @@ -172,7 +172,7 @@ That code will pull in the partial from `app/views/shared/_menu.html.erb`. #### Using Partials to simplify Views -One way to use partials is to treat them as the equivalent of subroutines: as a way to move details out of a view so that you can grasp what's going on more easily. For example, you might have a view that looked like this: +One way to use partials is to treat them as the equivalent of subroutines; a way to move details out of a view so that you can grasp what's going on more easily. For example, you might have a view that looks like this: ```html+erb <%= render "shared/ad_banner" %> @@ -269,12 +269,7 @@ Rails will render the `_product_ruler` partial (with no data passed to it) betwe ### Layouts -TODO... - -Using Templates, Partials and Layouts "The Rails Way" --------------------------------------------------------- - -TODO... +Layouts can be used to render a common view template around the results of Rails controller actions. Typically, every Rails has a couple of overall layouts that most pages are rendered within. For example, a site might have a layout for a logged in user, and a layout for the marketing or sales side of the site. The logged in user layout might include top-level navigation that should be present across many controller actions. The sales layout for a SaaS app might include top-level navigation for things like "Pricing" and "Contact Us." You would expect each layout to have a different look and feel. You can read more details about Layouts in the [Layouts and Rendering in Rails](layouts_and_rendering.html) guide. Partial Layouts --------------- @@ -492,7 +487,7 @@ image_path("edit.png") # => /assets/edit-2d1a2db63fc738690021fedb5a65b68e.png #### image_url -Computes the url to an image asset in the `app/asset/images` directory. This will call `image_path` internally and merge with your current host or your asset host. +Computes the url to an image asset in the `app/assets/images` directory. This will call `image_path` internally and merge with your current host or your asset host. ```ruby image_url("edit.png") # => http://www.example.com/assets/edit.png diff --git a/guides/source/active_model_basics.md b/guides/source/active_model_basics.md index 68ac26c681..1d87646e49 100644 --- a/guides/source/active_model_basics.md +++ b/guides/source/active_model_basics.md @@ -14,7 +14,7 @@ Active Model is a library containing various modules used in developing framewor ### AttributeMethods -The AttributeMethods module can add custom prefixes and suffixes on methods of a class. It is used by defining the prefixes and suffixes, which methods on the object will use them. +The AttributeMethods module can add custom prefixes and suffixes on methods of a class. It is used by defining the prefixes and suffixes and which methods on the object will use them. ```ruby class Person @@ -45,7 +45,7 @@ person.age_highest? # false ### Callbacks -Callbacks gives Active Record style callbacks. This provides the ability to define the callbacks and those will run at appropriate time. After defining a callbacks you can wrap with before, after and around custom methods. +Callbacks gives Active Record style callbacks. This provides an ability to define callbacks which run at appropriate times. After defining callbacks, you can wrap them with before, after and around custom methods. ```ruby class Person @@ -57,19 +57,19 @@ class Person def update run_callbacks(:update) do - # This will call when we are trying to call update on object. + # This method is called when update is called on an object. end end def reset_me - # This method will call when you are calling update on object as a before_update callback as defined. + # This method is called when update is called on an object as a before_update callback is defined. end end ``` ### Conversion -If a class defines `persisted?` and `id` methods then you can include `Conversion` module in that class and you can able to call Rails conversion methods to objects of that class. +If a class defines `persisted?` and `id` methods, then you can include the `Conversion` module in that class and call the Rails conversion methods on objects of that class. ```ruby class Person diff --git a/guides/source/active_record_basics.md b/guides/source/active_record_basics.md index 69d7333e6f..1f25c6ae95 100644 --- a/guides/source/active_record_basics.md +++ b/guides/source/active_record_basics.md @@ -1,14 +1,14 @@ Active Record Basics ==================== - + This guide is an introduction to Active Record. After reading this guide, you will know: -* What Object Relational Mapping and Active Record are and how they are used in +* What Object Relational Mapping and Active Record are and how they are used in Rails. * How Active Record fits into the Model-View-Controller paradigm. -* How to use Active Record models to manipulate data stored in a relational +* How to use Active Record models to manipulate data stored in a relational database. * Active Record schema naming conventions. * The concepts of database migrations, validations and callbacks. @@ -18,33 +18,34 @@ After reading this guide, you will know: What is Active Record? ---------------------- -Active Record is the M in [MVC](getting_started.html#the-mvc-architecture) - the -model - which is the layer of the system responsible for representing business -data and logic. Active Record facilitates the creation and use of business -objects whose data requires persistent storage to a database. It is an -implementation of the Active Record pattern which itself is a description of an +Active Record is the M in [MVC](getting_started.html#the-mvc-architecture) - the +model - which is the layer of the system responsible for representing business +data and logic. Active Record facilitates the creation and use of business +objects whose data requires persistent storage to a database. It is an +implementation of the Active Record pattern which itself is a description of an Object Relational Mapping system. ### The Active Record Pattern -Active Record was described by Martin Fowler in his book _Patterns of Enterprise -Application Architecture_. In Active Record, objects carry both persistent data -and behavior which operates on that data. Active Record takes the opinion that -ensuring data access logic is part of the object will educate users of that +[Active Record was described by Martin Fowler](http://www.martinfowler.com/eaaCatalog/activeRecord.html) +in his book _Patterns of Enterprise Application Architecture_. In +Active Record, objects carry both persistent data and behavior which +operates on that data. Active Record takes the opinion that ensuring +data access logic is part of the object will educate users of that object on how to write to and read from the database. ### Object Relational Mapping -Object-Relational Mapping, commonly referred to as its abbreviation ORM, is -a technique that connects the rich objects of an application to tables in -a relational database management system. Using ORM, the properties and -relationships of the objects in an application can be easily stored and -retrieved from a database without writing SQL statements directly and with less +Object-Relational Mapping, commonly referred to as its abbreviation ORM, is +a technique that connects the rich objects of an application to tables in +a relational database management system. Using ORM, the properties and +relationships of the objects in an application can be easily stored and +retrieved from a database without writing SQL statements directly and with less overall database access code. ### Active Record as an ORM Framework -Active Record gives us several mechanisms, the most important being the ability +Active Record gives us several mechanisms, the most important being the ability to: * Represent models and their data @@ -56,29 +57,29 @@ to: Convention over Configuration in Active Record ---------------------------------------------- -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 Active Record 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. +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 Active Record models. The idea is that if +you configure your applications in the very same way most of the time then this +should be the default way. Thus, explicit configuration would be needed +only in those cases where you can't follow the standard convention. ### Naming Conventions -By default, Active Record uses some naming conventions to find out how the -mapping between models and database tables should be created. Rails will -pluralize your class names to find the respective database table. So, for -a class `Book`, you should have a database table called **books**. The Rails -pluralization mechanisms are very powerful, being capable to pluralize (and -singularize) both regular and irregular words. When using class names composed -of two or more words, the model class name should follow the Ruby conventions, -using the CamelCase form, while the table name must contain the words separated +By default, Active Record uses some naming conventions to find out how the +mapping between models and database tables should be created. Rails will +pluralize your class names to find the respective database table. So, for +a class `Book`, you should have a database table called **books**. The Rails +pluralization mechanisms are very powerful, being capable to pluralize (and +singularize) both regular and irregular words. When using class names composed +of two or more words, the model class name should follow the Ruby conventions, +using the CamelCase form, while the table name must contain the words separated by underscores. Examples: * Database Table - Plural with underscores separating words (e.g., `book_clubs`) -* Model Class - Singular with the first letter of each word capitalized (e.g., +* Model Class - Singular with the first letter of each word capitalized (e.g., `BookClub`) | Model / Class | Table / Schema | @@ -92,33 +93,35 @@ by underscores. Examples: ### Schema Conventions -Active Record uses naming conventions for the columns in database tables, +Active Record 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 - `singularized_table_name_id` (e.g., `item_id`, `order_id`). These are the - fields that Active Record will look for when you create associations between +* **Foreign keys** - These fields should be named following the pattern + `singularized_table_name_id` (e.g., `item_id`, `order_id`). These are the + fields that Active Record will look for when you create associations between your models. -* **Primary keys** - By default, Active Record will use an integer column named - `id` as the table's primary key. When using [Rails - Migrations](migrations.html) to create your tables, this column will be +* **Primary keys** - By default, Active Record will use an integer column named + `id` as the table's primary key. When using [Rails + Migrations](migrations.html) to create your tables, this column will be automatically created. -There are also some optional column names that will create additional features +There are also some optional column names that will create additional features to Active Record instances: -* `created_at` - Automatically gets set to the current date and time when the +* `created_at` - Automatically gets set to the current date and time when the record is first created. -* `updated_at` - Automatically gets set to the current date and time whenever +* `updated_at` - Automatically gets set to the current date and time whenever the record is updated. -* `lock_version` - Adds [optimistic - locking](http://api.rubyonrails.org/classes/ActiveRecord/Locking.html) to +* `lock_version` - Adds [optimistic + locking](http://api.rubyonrails.org/classes/ActiveRecord/Locking.html) to a model. -* `type` - Specifies that the model uses [Single Table +* `type` - Specifies that the model uses [Single Table Inheritance](http://api.rubyonrails.org/classes/ActiveRecord/Base.html) -* `(table_name)_count` - Used to cache the number of belonging objects on - associations. For example, a `comments_count` column in a `Post` class that - has many instances of `Comment` will cache the number of existent comments +* `(association_name)_type` - Stores the type for + [polymorphic associations](association_basics.html#polymorphic-associations). +* `(table_name)_count` - Used to cache the number of belonging objects on + associations. For example, a `comments_count` column in a `Post` class that + has many instances of `Comment` will cache the number of existent comments for each post. NOTE: While these column names are optional, they are in fact reserved by Active Record. Steer clear of reserved keywords unless you want the extra functionality. For example, `type` is a reserved keyword used to designate a table using Single Table Inheritance (STI). If you are not using STI, try an analogous keyword like "context", that may still accurately describe the data you are modeling. @@ -126,7 +129,7 @@ NOTE: While these column names are optional, they are in fact reserved by Active Creating Active Record Models ----------------------------- -It is very easy to create Active Record models. All you have to do is to +It is very easy to create Active Record models. All you have to do is to subclass the `ActiveRecord::Base` class and you're good to go: ```ruby @@ -134,9 +137,9 @@ class Product < ActiveRecord::Base end ``` -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. Suppose +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. Suppose that the `products` table was created using an SQL sentence like: ```sql @@ -147,7 +150,7 @@ CREATE TABLE products ( ); ``` -Following the table schema above, you would be able to write code like the +Following the table schema above, you would be able to write code like the following: ```ruby @@ -159,11 +162,11 @@ puts p.name # "Some Book" Overriding the Naming Conventions --------------------------------- -What if you need to follow a different naming convention or need to use your -Rails application with a legacy database? No problem, you can easily override +What if you need to follow a different naming convention or need to use your +Rails application with a legacy database? No problem, you can easily override the default conventions. -You can use the `ActiveRecord::Base.table_name=` method to specify the table +You can use the `ActiveRecord::Base.table_name=` method to specify the table name that should be used: ```ruby @@ -172,8 +175,8 @@ class Product < ActiveRecord::Base end ``` -If you do so, you will have to define manually the class name that is hosting -the fixtures (class_name.yml) using the `set_fixture_class` method in your test +If you do so, you will have to define manually the class name that is hosting +the fixtures (class_name.yml) using the `set_fixture_class` method in your test definition: ```ruby @@ -184,7 +187,7 @@ class FunnyJoke < ActiveSupport::TestCase end ``` -It's also possible to override the column that should be used as the table's +It's also possible to override the column that should be used as the table's primary key using the `ActiveRecord::Base.set_primary_key` method: ```ruby @@ -196,17 +199,17 @@ end CRUD: Reading and Writing Data ------------------------------ -CRUD is an acronym for the four verbs we use to operate on data: **C**reate, -**R**ead, **U**pdate and **D**elete. Active Record automatically creates methods +CRUD is an acronym for the four verbs we use to operate on data: **C**reate, +**R**ead, **U**pdate and **D**elete. Active Record automatically creates methods to allow an application to read and manipulate data stored within its tables. ### Create -Active Record objects can be created from a hash, a block or have their -attributes manually set after creation. The `new` method will return a new +Active Record objects can be created from a hash, a block or have their +attributes manually set after creation. The `new` method will return a new object while `create` will return the object and save it to the database. -For example, given a model `User` with attributes of `name` and `occupation`, +For example, given a model `User` with attributes of `name` and `occupation`, the `create` method call will create and save a new record into the database: ```ruby @@ -223,7 +226,7 @@ user.occupation = "Code Artist" A call to `user.save` will commit the record to the database. -Finally, if a block is provided, both `create` and `new` will yield the new +Finally, if a block is provided, both `create` and `new` will yield the new object to that block for initialization: ```ruby @@ -235,7 +238,7 @@ end ### Read -Active Record provides a rich API for accessing data within a database. Below +Active Record provides a rich API for accessing data within a database. Below are a few examples of different data access methods provided by Active Record. ```ruby @@ -258,12 +261,12 @@ david = User.find_by_name('David') users = User.where(name: 'David', occupation: 'Code Artist').order('created_at DESC') ``` -You can learn more about querying an Active Record model in the [Active Record +You can learn more about querying an Active Record model in the [Active Record Query Interface](active_record_querying.html) guide. ### Update -Once an Active Record object has been retrieved, its attributes can be modified +Once an Active Record object has been retrieved, its attributes can be modified and it can be saved to the database. ```ruby @@ -272,7 +275,7 @@ user.name = 'Dave' user.save ``` -A shorthand for this is to use a hash mapping attribute names to the desired +A shorthand for this is to use a hash mapping attribute names to the desired value, like so: ```ruby @@ -280,8 +283,8 @@ user = User.find_by_name('David') user.update(name: 'Dave') ``` -This is most useful when updating several attributes at once. If, on the other -hand, you'd like to update several records in bulk, you may find the +This is most useful when updating several attributes at once. If, on the other +hand, you'd like to update several records in bulk, you may find the `update_all` class method useful: ```ruby @@ -290,7 +293,7 @@ User.update_all "max_login_attempts = 3, must_change_password = 'true'" ### Delete -Likewise, once retrieved an Active Record object can be destroyed which removes +Likewise, once retrieved an Active Record object can be destroyed which removes it from the database. ```ruby @@ -301,46 +304,46 @@ user.destroy Validations ----------- -Active Record allows you to validate the state of a model before it gets written -into the database. There are several methods that you can use to check your -models and validate that an attribute value is not empty, is unique and not +Active Record allows you to validate the state of a model before it gets written +into the database. There are several methods that you can use to check your +models and validate that an attribute value is not empty, is unique and not already in the database, follows a specific format and many more. -Validation is a very important issue to consider when persisting to database, so -the methods `create`, `save` and `update` take it into account when -running: they return `false` when validation fails and they didn't actually -perform any operation on database. All of these have a bang counterpart (that -is, `create!`, `save!` and `update!`), which are stricter in that -they raise the exception `ActiveRecord::RecordInvalid` if validation fails. +Validation is a very important issue to consider when persisting to database, so +the methods `create`, `save` and `update` take it into account when +running: they return `false` when validation fails and they didn't actually +perform any operation on database. All of these have a bang counterpart (that +is, `create!`, `save!` and `update!`), which are stricter in that +they raise the exception `ActiveRecord::RecordInvalid` if validation fails. A quick example to illustrate: ```ruby class User < ActiveRecord::Base - validates_presence_of :name + validates :name, presence: true end User.create # => false User.create! # => ActiveRecord::RecordInvalid: Validation failed: Name can't be blank ``` -You can learn more about validations in the [Active Record Validations +You can learn more about validations in the [Active Record Validations guide](active_record_validations.html). Callbacks --------- -Active Record callbacks allow you to attach code to certain events in the -life-cycle of your models. This enables you to 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 +Active Record callbacks allow you to attach code to certain events in the +life-cycle of your models. This enables you to 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 Callbacks guide](active_record_callbacks.html). Migrations ---------- -Rails provides a domain-specific language for managing a database schema called -migrations. Migrations are stored in files which are executed against any -database that Active Record support using `rake`. Here's a migration that +Rails provides a domain-specific language for managing a database schema called +migrations. Migrations are stored in files which are executed against any +database that Active Record support using `rake`. Here's a migration that creates a table: ```ruby @@ -361,10 +364,10 @@ class CreatePublications < ActiveRecord::Migration end ``` -Rails keeps track of which files have been committed to the database and +Rails keeps track of which files have been committed to the database and provides rollback features. To actually create the table, you'd run `rake db:migrate` and to roll it back, `rake db:rollback`. -Note that the above code is database-agnostic: it will run in MySQL, postgresql, -Oracle and others. You can learn more about migrations in the [Active Record +Note that the above code is database-agnostic: it will run in MySQL, postgresql, +Oracle and others. You can learn more about migrations in the [Active Record Migrations guide](migrations.html) diff --git a/guides/source/active_record_callbacks.md b/guides/source/active_record_callbacks.md index 516457bcd3..bb42fab101 100644 --- a/guides/source/active_record_callbacks.md +++ b/guides/source/active_record_callbacks.md @@ -342,19 +342,17 @@ By using the `after_commit` callback we can account for this case. ```ruby class PictureFile < ActiveRecord::Base - attr_accessor :delete_file + after_commit :delete_picture_file_from_disk, :on => [:destroy] - after_destroy do |picture_file| - picture_file.delete_file = picture_file.filepath - end - - after_commit do |picture_file| - if picture_file.delete_file && File.exist?(picture_file.delete_file) - File.delete(picture_file.delete_file) - picture_file.delete_file = nil + def delete_picture_file_from_disk + if File.exist?(filepath) + File.delete(filepath) end end end ``` +NOTE: the `:on` option specifies when a callback will be fired. If you +don't supply the `:on` option the callback will fire for every action. + The `after_commit` and `after_rollback` callbacks are guaranteed to be called for all models created, updated, or destroyed within a transaction block. If any exceptions are raised within one of these callbacks, they will be ignored so that they don't interfere with the other callbacks. As such, if your callback code could raise an exception, you'll need to rescue it and handle it appropriately within the callback. diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md index 62d6294ae5..2555927d15 100644 --- a/guides/source/active_record_querying.md +++ b/guides/source/active_record_querying.md @@ -76,6 +76,7 @@ The methods are: * `reorder` * `reverse_order` * `select` +* `distinct` * `uniq` * `where` @@ -90,7 +91,7 @@ The primary operation of `Model.find(options)` can be summarized as: ### Retrieving a Single Object -Active Record provides five different ways of retrieving a single object. +Active Record provides several different ways of retrieving a single object. #### Using a Primary Key @@ -299,7 +300,7 @@ Client.first(2) The SQL equivalent of the above is: ```sql -SELECT * FROM clients LIMIT 2 +SELECT * FROM clients ORDER BY id ASC LIMIT 2 ``` #### last @@ -315,7 +316,7 @@ Client.last(2) The SQL equivalent of the above is: ```sql -SELECT * FROM clients ORDER By id DESC LIMIT 2 +SELECT * FROM clients ORDER BY id DESC LIMIT 2 ``` ### Retrieving Multiple Objects in Batches @@ -505,19 +506,15 @@ This code will generate SQL like this: SELECT * FROM clients WHERE (clients.orders_count IN (1,3,5)) ``` -### NOT, LIKE, and NOT LIKE Conditions +### NOT Conditions -`NOT`, `LIKE`, and `NOT LIKE` SQL queries can be built by `where.not`, `where.like`, and `where.not_like` respectively. +`NOT` SQL queries can be built by `where.not`. ```ruby Post.where.not(author: author) - -Author.where.like(name: 'Nari%') - -Developer.where.not_like(name: 'Tenderl%') ``` -In other words, these sort of queries can be generated by calling `where` with no argument, then immediately chain with `not`, `like`, or `not_like` passing `where` conditions. +In other words, this query can be generated by calling `where` with no argument, then immediately chain with `not` passing `where` conditions. Ordering -------- @@ -580,10 +577,10 @@ ActiveModel::MissingAttributeError: missing attribute: <attribute> 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. -If you would like to only grab a single record per unique value in a certain field, you can use `uniq`: +If you would like to only grab a single record per unique value in a certain field, you can use `distinct`: ```ruby -Client.select(:name).uniq +Client.select(:name).distinct ``` This would generate SQL like: @@ -595,10 +592,10 @@ SELECT DISTINCT name FROM clients You can also remove the uniqueness constraint: ```ruby -query = Client.select(:name).uniq +query = Client.select(:name).distinct # => Returns unique names -query.uniq(false) +query.distinct(false) # => Returns all names, even if there are duplicates ``` @@ -690,6 +687,31 @@ The SQL that would be executed: ```sql SELECT * FROM posts WHERE id > 10 LIMIT 20 + +# Original query without `except` +SELECT * FROM posts WHERE id > 10 ORDER BY id asc LIMIT 20 + +``` + +### `unscope` + +The `except` method does not work when the relation is merged. For example: + +```ruby +Post.comments.except(:order) +``` + +will still have an order if the order comes from a default scope on Comment. In order to remove all ordering, even from relations which are merged in, use unscope as follows: + +```ruby +Post.order('id DESC').limit(20).unscope(:order) = Post.limit(20) +Post.order('id DESC').limit(20).unscope(:order, :limit) = Post.all +``` + +You can additionally unscope specific where clauses. For example: + +```ruby +Post.where(:id => 10).limit(1).unscope(where: :id, :limit).order('id DESC') = Post.order('id DESC') ``` ### `only` @@ -704,6 +726,10 @@ The SQL that would be executed: ```sql SELECT * FROM posts WHERE id > 10 ORDER BY id DESC + +# Original query without `only` +SELECT "posts".* FROM "posts" WHERE (id > 10) ORDER BY id desc LIMIT 20 + ``` ### `reorder` @@ -714,7 +740,7 @@ The `reorder` method overrides the default scope order. For example: class Post < ActiveRecord::Base .. .. - has_many :comments, order: 'posted_at DESC' + has_many :comments, -> { order('posted_at DESC') } end Post.find(10).comments.reorder('name') @@ -949,7 +975,7 @@ SELECT categories.* FROM categories INNER JOIN posts ON posts.category_id = categories.id ``` -Or, in English: "return a Category object for all categories with posts". Note that you will see duplicate categories if more than one post has the same category. If you want unique categories, you can use `Category.joins(:posts).select("distinct(categories.id)")`. +Or, in English: "return a Category object for all categories with posts". Note that you will see duplicate categories if more than one post has the same category. If you want unique categories, you can use `Category.joins(:posts).uniq`. #### Joining Multiple Associations @@ -1175,6 +1201,62 @@ Using a class method is the preferred way to accept arguments for scopes. These category.posts.created_before(time) ``` +### Merging of scopes + +Just like `where` clauses scopes are merged using `AND` conditions. + +```ruby +class User < ActiveRecord::Base + scope :active, -> { where state: 'active' } + scope :inactive, -> { where state: 'inactive' } +end +``` + +```ruby +User.active.inactive +# => SELECT "users".* FROM "users" WHERE "users"."state" = 'active' AND "users"."state" = 'inactive' +``` + +We can mix and match `scope` and `where` conditions and the final sql +will have all conditions joined with `AND` . + +```ruby +User.active.where(state: 'finished') +# => SELECT "users".* FROM "users" WHERE "users"."state" = 'active' AND "users"."state" = 'finished' +``` + +If we do want the `last where clause` to win then `Relation#merge` can +be used . + +```ruby +User.active.merge(User.inactive) +# => SELECT "users".* FROM "users" WHERE "users"."state" = 'inactive' +``` + +One important caveat is that `default_scope` will be overridden by +`scope` and `where` conditions. + +```ruby +class User < ActiveRecord::Base + default_scope { where state: 'pending' } + scope :active, -> { where state: 'active' } + scope :inactive, -> { where state: 'inactive' } +end + +User.all +# => SELECT "users".* FROM "users" WHERE "users"."state" = 'pending' + +User.active +# => SELECT "users".* FROM "users" WHERE "users"."state" = 'active' + +User.where(state: 'inactive') +# => SELECT "users".* FROM "users" WHERE "users"."state" = 'inactive' +``` + +As you can see above the `default_scope` is being overridden by both +`scope` and `where` conditions. + + ### Applying a default scope If we wish for a scope to be applied across all queries to the model we can use the @@ -1221,13 +1303,18 @@ recommended that you use the block form of `unscoped`: ```ruby Client.unscoped { - Client.created_before(Time.zome.now) + Client.created_before(Time.zone.now) } ``` Dynamic Finders --------------- +NOTE: Dynamic finders have been deprecated in Rails 4.0 and will be +removed in Rails 4.1. The best practice is to use Active Record scopes +instead. You can find the deprecation gem at +https://github.com/rails/activerecord-deprecated_finders + 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 `first_name` on your `Client` model for example, you get `find_by_first_name` for free from Active Record. If you have a `locked` field on the `Client` model, you also get `find_by_locked` and methods. 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")` @@ -1362,7 +1449,7 @@ Client.where(active: true).pluck(:id) # SELECT id FROM clients WHERE active = 1 # => [1, 2, 3] -Client.uniq.pluck(:role) +Client.distinct.pluck(:role) # SELECT DISTINCT role FROM clients # => ['admin', 'member', 'guest'] @@ -1378,7 +1465,7 @@ Client.select(:id).map { |c| c.id } # or Client.select(:id).map(&:id) # or -Client.select(:id).map { |c| [c.id, c.name] } +Client.select(:id, :name).map { |c| [c.id, c.name] } ``` with @@ -1609,45 +1696,6 @@ EXPLAIN for: SELECT `posts`.* FROM `posts` WHERE `posts`.`user_id` IN (1) under MySQL. -### Automatic EXPLAIN - -Active Record is able to run EXPLAIN automatically on slow queries and log its -output. This feature is controlled by the configuration parameter - -```ruby -config.active_record.auto_explain_threshold_in_seconds -``` - -If set to a number, any query exceeding those many seconds will have its EXPLAIN -automatically triggered and logged. In the case of relations, the threshold is -compared to the total time needed to fetch records. So, a relation is seen as a -unit of work, no matter whether the implementation of eager loading involves -several queries under the hood. - -A threshold of `nil` disables automatic EXPLAINs. - -The default threshold in development mode is 0.5 seconds, and `nil` in test and -production modes. - -INFO. Automatic EXPLAIN gets disabled if Active Record has no logger, regardless -of the value of the threshold. - -#### Disabling Automatic EXPLAIN - -Automatic EXPLAIN can be selectively silenced with `ActiveRecord::Base.silence_auto_explain`: - -```ruby -ActiveRecord::Base.silence_auto_explain do - # no automatic EXPLAIN is triggered here -end -``` - -That may be useful for queries you know are slow but fine, like a heavyweight -report of an admin interface. - -As its name suggests, `silence_auto_explain` only silences automatic EXPLAINs. -Explicit calls to `ActiveRecord::Relation#explain` run. - ### Interpreting EXPLAIN Interpretation of the output of EXPLAIN is beyond the scope of this guide. The diff --git a/guides/source/active_record_validations.md b/guides/source/active_record_validations.md index 32641d04c1..37790c62b1 100644 --- a/guides/source/active_record_validations.md +++ b/guides/source/active_record_validations.md @@ -162,8 +162,8 @@ Person.create(name: nil).valid? # => false ``` After Active Record has performed validations, any errors found can be accessed -through the `errors` instance method, which returns a collection of errors. By -definition, an object is valid if this collection is empty after running +through the `errors.messages` instance method, which returns a collection of errors. +By definition, an object is valid if this collection is empty after running validations. Note that an object instantiated with `new` will not report errors even if it's @@ -176,17 +176,17 @@ end >> p = Person.new #=> #<Person id: nil, name: nil> ->> p.errors +>> p.errors.messages #=> {} >> p.valid? #=> false ->> p.errors +>> p.errors.messages #=> {name:["can't be blank"]} >> p = Person.create #=> #<Person id: nil, name: nil> ->> p.errors +>> p.errors.messages #=> {name:["can't be blank"]} >> p.save @@ -243,7 +243,7 @@ line of code you can add the same kind of validation to several attributes. All of them accept the `:on` and `:message` options, which define when the validation should be run and what message should be added to the `errors` collection if it fails, respectively. The `:on` option takes one of the values -`:save` (the default), `:create` or `:update`. There is a default error +`:save` (the default), `:create` or `:update`. There is a default error message for each one of the validation helpers. These messages are used when the `:message` option isn't specified. Let's take a look at each one of the available helpers. @@ -357,7 +357,7 @@ given regular expression, which is specified using the `:with` option. ```ruby class Product < ActiveRecord::Base validates :legacy_code, format: { with: /\A[a-zA-Z]+\z/, - message: "Only letters allowed" } + message: "only allows letters" } end ``` @@ -434,7 +434,7 @@ end Note that the default error messages are plural (e.g., "is too short (minimum is %{count} characters)"). For this reason, when `:minimum` is 1 you should -provide a personalized message or use `validates_presence_of` instead. When +provide a personalized message or use `presence: true` instead. When `:in` or `:within` have a lower limit of 1, you should either provide a personalized message or call `presence` prior to `length`. @@ -530,6 +530,47 @@ field you should use `validates :field_name, inclusion: { in: [true, false] }`. The default error message is _"can't be empty"_. +### `absence` + +This helper validates that the specified attributes are absent. It uses the +`present?` method to check if the value is not either nil or a blank string, that +is, a string that is either empty or consists of whitespace. + +```ruby +class Person < ActiveRecord::Base + validates :name, :login, :email, absence: true +end +``` + +If you want to be sure that an association is absent, you'll need to test +whether the associated object itself is absent, and not the foreign key used +to map the association. + +```ruby +class LineItem < ActiveRecord::Base + belongs_to :order + validates :order, absence: true +end +``` + +In order to validate associated records whose absence is required, you must +specify the `:inverse_of` option for the association: + +```ruby +class Order < ActiveRecord::Base + has_many :line_items, inverse_of: :order +end +``` + +If you validate the absence of an object associated via a `has_one` or +`has_many` relationship, it will check that the object is neither `present?` nor +`marked_for_destruction?`. + +Since `false.present?` is false, if you want to validate the absence of a boolean +field you should use `validates :field_name, exclusion: { in: [true, false] }`. + +The default error message is _"must be blank"_. + ### `uniqueness` This helper validates that the attribute's value is unique right before the @@ -636,13 +677,13 @@ class GoodnessValidator def initialize(person) @person = person end - + def validate if some_complex_condition_involving_ivars_and_private_methods? @person.errors[:base] << "This person is evil" end end - + # … end ``` @@ -695,8 +736,8 @@ class Topic < ActiveRecord::Base validates :title, length: { is: 5 }, allow_blank: true end -Topic.create("title" => "").valid? # => true -Topic.create("title" => nil).valid? # => true +Topic.create(title: "").valid? # => true +Topic.create(title: nil).valid? # => true ``` ### `:message` @@ -727,6 +768,7 @@ class Person < ActiveRecord::Base validates :name, presence: true, on: :save end ``` +The last line is in review state and as of now, it is not running in any version of Rails 3.2.x as discussed in this [issue](https://github.com/rails/rails/issues/10248) Strict Validations ------------------ @@ -951,12 +993,12 @@ end person = Person.new person.valid? # => false -person.errors +person.errors.messages # => {:name=>["can't be blank", "is too short (minimum is 3 characters)"]} person = Person.new(name: "John Doe") person.valid? # => true -person.errors # => [] +person.errors.messages # => {} ``` ### `errors[]` diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md index f02b377832..7f65d920df 100644 --- a/guides/source/active_support_core_extensions.md +++ b/guides/source/active_support_core_extensions.md @@ -10,7 +10,7 @@ After reading this guide, you will know: * What Core Extensions are. * How to load all extensions. * How to cherry-pick just the extensions you want. -* What extensions ActiveSupport provides. +* What extensions Active Support provides. -------------------------------------------------------------------------------- @@ -166,7 +166,7 @@ NOTE: Defined in `active_support/core_ext/object/duplicable.rb`. ### `deep_dup` -The `deep_dup` method returns deep copy of a given object. Normally, when you `dup` an object that contains other objects, ruby does not `dup` them, so it creates a shallow copy of the object. If you have an array with a string, for example, it will look like this: +The `deep_dup` method returns deep copy of a given object. Normally, when you `dup` an object that contains other objects, Ruby does not `dup` them, so it creates a shallow copy of the object. If you have an array with a string, for example, it will look like this: ```ruby array = ['string'] @@ -418,6 +418,14 @@ TIP: Since `with_options` forwards calls to its receiver they can be nested. Eac NOTE: Defined in `active_support/core_ext/object/with_options.rb`. +### JSON support + +Active Support provides a better implemention of `to_json` than the +json+ gem ordinarily provides for Ruby objects. This is because some classes, like +Hash+ and +OrderedHash+ needs special handling in order to provide a proper JSON representation. + +Active Support also provides an implementation of `as_json` for the <tt>Process::Status</tt> class. + +NOTE: Defined in `active_support/core_ext/object/to_json.rb`. + ### Instance Variables Active Support provides several methods to ease access to instance variables. @@ -439,6 +447,22 @@ C.new(0, 1).instance_values # => {"x" => 0, "y" => 1} NOTE: Defined in `active_support/core_ext/object/instance_variables.rb`. +#### `instance_variable_names` + +The method `instance_variable_names` returns an array. Each name includes the "@" sign. + +```ruby +class C + def initialize(x, y) + @x, @y = x, y + end +end + +C.new(0, 1).instance_variable_names # => ["@x", "@y"] +``` + +NOTE: Defined in `active_support/core_ext/object/instance_variables.rb`. + ### Silencing Warnings, Streams, and Exceptions The methods `silence_warnings` and `enable_warnings` change the value of `$VERBOSE` accordingly for the duration of their block, and reset it afterwards: @@ -476,12 +500,11 @@ NOTE: Defined in `active_support/core_ext/kernel/reporting.rb`. ### `in?` -The predicate `in?` tests if an object is included in another object or a list of objects. An `ArgumentError` exception will be raised if a single argument is passed and it does not respond to `include?`. +The predicate `in?` tests if an object is included in another object. An `ArgumentError` exception will be raised if the argument passed does not respond to `include?`. Examples of `in?`: ```ruby -1.in?(1,2) # => true 1.in?([1,2]) # => true "lo".in?("hello") # => true 25.in?(30..50) # => false @@ -1039,6 +1062,8 @@ For convenience `class_attribute` also defines an instance predicate which is th When `:instance_reader` is `false`, the instance predicate returns a `NoMethodError` just like the reader method. +If you do not want the instance predicate, pass `instance_predicate: false` and it will not be defined. + NOTE: Defined in `active_support/core_ext/class/attribute.rb` #### `cattr_reader`, `cattr_writer`, and `cattr_accessor` @@ -1344,7 +1369,7 @@ The second argument, `indent_string`, specifies which indent string to use. The "foo".indent(2, "\t") # => "\t\tfoo" ``` -While `indent_string` is tipically one space or tab, it may be any string. +While `indent_string` is typically one space or tab, it may be any string. The third argument, `indent_empty_lines`, is a flag that says whether empty lines should be indented. Default is false. @@ -1422,7 +1447,7 @@ The method `pluralize` returns the plural of its receiver: As the previous example shows, Active Support knows some irregular plurals and uncountable nouns. Built-in rules can be extended in `config/initializers/inflections.rb`. That file is generated by the `rails` command and has instructions in comments. -`pluralize` can also take an optional `count` parameter. If `count == 1` the singular form will be returned. For any other value of `count` the plural form will be returned: +`pluralize` can also take an optional `count` parameter. If `count == 1` the singular form will be returned. For any other value of `count` the plural form will be returned: ```ruby "dude".pluralize(0) # => "dudes" @@ -2010,8 +2035,33 @@ NOTE: Defined in `active_support/core_ext/integer/inflections.rb`. Extensions to `BigDecimal` -------------------------- +### `to_s` -... +The method `to_s` is aliased to `to_formatted_s`. This provides a convenient way to display a BigDecimal value in floating-point notation: + +```ruby +BigDecimal.new(5.00, 6).to_s # => "5.0" +``` + +### `to_formatted_s` + +Te method `to_formatted_s` provides a default specifier of "F". This means that a simple call to `to_formatted_s` or `to_s` will result in floating point representation instead of engineering notation: + +```ruby +BigDecimal.new(5.00, 6).to_formatted_s # => "5.0" +``` + +and that symbol specifiers are also supported: + +```ruby +BigDecimal.new(5.00, 6).to_formatted_s(:db) # => "5.0" +``` + +Engineering notation is still supported: + +```ruby +BigDecimal.new(5.00, 6).to_formatted_s("e") # => "0.5E1" +``` Extensions to `Enumerable` -------------------------- @@ -2198,7 +2248,7 @@ This method accepts three options: * `:words_connector`: What is used to join the elements of arrays with 3 or more elements, except for the last two. Default is ", ". * `:last_word_connector`: What is used to join the last items of an array with 3 or more elements. Default is ", and ". -The defaults for these options can be localised, their keys are: +The defaults for these options can be localized, their keys are: | Option | I18n key | | ---------------------- | ----------------------------------- | @@ -2214,7 +2264,9 @@ NOTE: Defined in `active_support/core_ext/array/conversions.rb`. The method `to_formatted_s` acts like `to_s` by default. -If the array contains items that respond to `id`, however, it may be passed the symbol `:db` as argument. That's typically used with collections of ARs. Returned strings are: +If the array contains items that respond to `id`, however, the symbol +`:db` may be passed as argument. That's typically used with +collections of Active Record objects. Returned strings are: ```ruby [].to_formatted_s(:db) # => "null" @@ -2370,7 +2422,8 @@ NOTE: Defined in `active_support/core_ext/array/wrap.rb`. ### Duplicating -The method `Array.deep_dup` duplicates itself and all objects inside recursively with ActiveSupport method `Object#deep_dup`. It works like `Array#map` with sending `deep_dup` method to each object inside. +The method `Array.deep_dup` duplicates itself and all objects inside +recursively with Active Support method `Object#deep_dup`. It works like `Array#map` with sending `deep_dup` method to each object inside. ```ruby array = [1, [2, 3]] @@ -2591,7 +2644,8 @@ NOTE: Defined in `active_support/core_ext/hash/deep_merge.rb`. ### Deep duplicating -The method `Hash.deep_dup` duplicates itself and all keys and values inside recursively with ActiveSupport method `Object#deep_dup`. It works like `Enumerator#each_with_object` with sending `deep_dup` method to each pair inside. +The method `Hash.deep_dup` duplicates itself and all keys and values +inside recursively with Active Support method `Object#deep_dup`. It works like `Enumerator#each_with_object` with sending `deep_dup` method to each pair inside. ```ruby hash = { a: 1, b: { c: 2, d: [3, 4] } } @@ -3320,7 +3374,25 @@ date.end_of_hour # => Mon Jun 07 19:59:59 +0200 2010 `beginning_of_hour` is aliased to `at_beginning_of_hour`. -INFO: `beginning_of_hour` and `end_of_hour` are implemented for `Time` and `DateTime` but **not** `Date` as it does not make sense to request the beginning or end of an hour on a `Date` instance. +##### `beginning_of_minute`, `end_of_minute` + +The method `beginning_of_minute` returns a timestamp at the beginning of the minute (hh:mm:00): + +```ruby +date = DateTime.new(2010, 6, 7, 19, 55, 25) +date.beginning_of_minute # => Mon Jun 07 19:55:00 +0200 2010 +``` + +The method `end_of_minute` returns a timestamp at the end of the minute (hh:mm:59): + +```ruby +date = DateTime.new(2010, 6, 7, 19, 55, 25) +date.end_of_minute # => Mon Jun 07 19:55:59 +0200 2010 +``` + +`beginning_of_minute` is aliased to `at_beginning_of_minute`. + +INFO: `beginning_of_hour`, `end_of_hour`, `beginning_of_minute` and `end_of_minute` are implemented for `Time` and `DateTime` but **not** `Date` as it does not make sense to request the beginning or end of an hour or minute on a `Date` instance. ##### `ago`, `since` diff --git a/guides/source/active_support_instrumentation.md b/guides/source/active_support_instrumentation.md index 6b3be69942..38dbfd3152 100644 --- a/guides/source/active_support_instrumentation.md +++ b/guides/source/active_support_instrumentation.md @@ -273,7 +273,7 @@ Action Mailer to: ["users@rails.com", "ddh@rails.com"], from: ["me@rails.com"], date: Sat, 10 Mar 2012 14:18:09 +0100, - mail: "..." # ommitted for beverity + mail: "..." # omitted for brevity } ``` @@ -299,7 +299,7 @@ Action Mailer to: ["users@rails.com", "ddh@rails.com"], from: ["me@rails.com"], date: Sat, 10 Mar 2012 14:18:09 +0100, - mail: "..." # ommitted for beverity + mail: "..." # omitted for brevity } ``` @@ -428,7 +428,7 @@ end ``` Defining all those block arguments each time can be tedious. You can easily create an `ActiveSupport::Notifications::Event` -from block args like this: +from block arguments like this: ```ruby ActiveSupport::Notifications.subscribe "process_action.action_controller" do |*args| @@ -442,7 +442,7 @@ ActiveSupport::Notifications.subscribe "process_action.action_controller" do |*a end ``` -Most times you only care about the data itself. Here is a shortuct to just get the data. +Most times you only care about the data itself. Here is a shortcut to just get the data. ```ruby ActiveSupport::Notifications.subscribe "process_action.action_controller" do |*args| @@ -450,7 +450,7 @@ ActiveSupport::Notifications.subscribe "process_action.action_controller" do |*a data # { extra: :information } ``` -You may also subscribe to events matching a regular expresssion. This enables you to subscribe to +You may also subscribe to events matching a regular expression. This enables you to subscribe to multiple events at once. Here's you could subscribe to everything from `ActionController`. ```ruby @@ -465,7 +465,7 @@ Creating custom events Adding your own events is easy as well. `ActiveSupport::Notifications` will take care of all the heavy lifting for you. Simply call `instrument` with a `name`, `payload` and a block. The notification will be sent after the block returns. `ActiveSupport` will generate the start and end times -as well as the unique ID. All data passed into the `insturment` call will make it into the payload. +as well as the unique ID. All data passed into the `instrument` call will make it into the payload. Here's an example: diff --git a/guides/source/api_documentation_guidelines.md b/guides/source/api_documentation_guidelines.md index d0499878da..7e056d970c 100644 --- a/guides/source/api_documentation_guidelines.md +++ b/guides/source/api_documentation_guidelines.md @@ -25,7 +25,7 @@ Write in present tense: "Returns a hash that...", rather than "Returned a hash t Start comments in upper case. Follow regular punctuation rules: ```ruby -# Declares an attribute reader backed by an internally-named +# Declares an attribute reader backed by an internally-named # instance variable. def attr_internal_reader(*attrs) ... @@ -57,7 +57,7 @@ Use two spaces to indent chunks of code--that is, for markup purposes, two space Short docs do not need an explicit "Examples" label to introduce snippets; they just follow paragraphs: ```ruby -# Converts a collection of elements into a formatted string by +# Converts a collection of elements into a formatted string by # calling +to_s+ on all elements and joining them. # # Blog.all.to_formatted_s # => "First PostSecond PostThird Post" @@ -141,7 +141,7 @@ class Array end ``` -WARNING: Using a pair of `+...+` for fixed-width font only works with **words**; that is: anything matching `\A\w+\z`. For anything else use `<tt>...</tt>`, notably symbols, setters, inline snippets, etc. +WARNING: Using a pair of `+...+` for fixed-width font only works with **words**; that is: anything matching `\A\w+\z`. For anything else use `<tt>...</tt>`, notably symbols, setters, inline snippets, etc. ### Regular Font diff --git a/guides/source/asset_pipeline.md b/guides/source/asset_pipeline.md index e939606c88..b68c24acfd 100644 --- a/guides/source/asset_pipeline.md +++ b/guides/source/asset_pipeline.md @@ -75,7 +75,7 @@ The query string strategy has several disadvantages: 2. **The file name can change between nodes in multi-server environments.**<br /> The default query string in Rails 2.x is based on the modification time of the files. When assets are deployed to a cluster, there is no guarantee that the timestamps will be the same, resulting in different values being used depending on which server handles the request. 3. **Too much cache invalidation**<br /> - When static assets are deployed with each new release of code, the mtime of _all_ these files changes, forcing all remote clients to fetch them again, even when the content of those assets has not changed. + When static assets are deployed with each new release of code, the mtime(time of last modification) of _all_ these files changes, forcing all remote clients to fetch them again, even when the content of those assets has not changed. Fingerprinting fixes these problems by avoiding query strings, and by ensuring that filenames are consistent based on their content. @@ -416,21 +416,10 @@ You can call this task on the server during deployment to create compiled versio The rake task is: ```bash -$ bundle exec rake assets:precompile +$ RAILS_ENV=production bundle exec rake assets:precompile ``` -For faster asset precompiles, you can partially load your application by setting -`config.assets.initialize_on_precompile` to false in `config/application.rb`, though in that case templates -cannot see application objects or methods. **Heroku requires this to be false.** - -WARNING: If you set `config.assets.initialize_on_precompile` to false, be sure to -test `rake assets:precompile` locally before deploying. It may expose bugs where -your assets reference application objects or methods, since those are still -in scope in development mode regardless of the value of this flag. Changing this flag also affects -engines. Engines can define assets for precompilation as well. Since the complete environment is not loaded, -engines (or other gems) will not be loaded, which can cause missing assets. - -Capistrano (v2.8.0 and above) includes a recipe to handle this in deployment. Add the following line to `Capfile`: +Capistrano (v2.15.1 and above) includes a recipe to handle this in deployment. Add the following line to `Capfile`: ```ruby load 'deploy/assets' @@ -450,7 +439,7 @@ The default matcher for compiling files includes `application.js`, `application. NOTE. The matcher (and other members of the precompile array; see below) is applied to final compiled file names. This means that anything that compiles to JS/CSS is excluded, as well as raw JS/CSS files; for example, `.coffee` and `.scss` files are **not** automatically included as they compile to JS/CSS. -If you have other manifests or individual stylesheets and JavaScript files to include, you can add them to the `precompile` array: +If you have other manifests or individual stylesheets and JavaScript files to include, you can add them to the `precompile` array in `config/application.rb`: ```ruby config.assets.precompile += ['admin.js', 'admin.css', 'swfObject.js'] @@ -459,7 +448,7 @@ config.assets.precompile += ['admin.js', 'admin.css', 'swfObject.js'] Or you can opt to precompile all assets with something like this: ```ruby -# config/environments/production.rb +# config/application.rb config.assets.precompile << Proc.new do |path| if path =~ /\.(css|js)\z/ full_path = Rails.application.assets.resolve(path).to_path @@ -570,16 +559,8 @@ In `config/environments/development.rb`, place the following line: config.assets.prefix = "/dev-assets" ``` -You will also need this in application.rb: - -```ruby -config.assets.initialize_on_precompile = false -``` - The `prefix` change makes Rails use a different URL for serving assets in development mode, and pass all requests to Sprockets. The prefix is still set to `/assets` in the production environment. Without this change, the application would serve the precompiled assets from `public/assets` in development, and you would not see any local changes until you compile assets again. -The `initialize_on_precompile` change tells the precompile task to run without invoking Rails. This is because the precompile task runs in production mode by default, and will attempt to connect to your specified production database. Please note that you cannot have code in pipeline files that relies on Rails resources (such as the database) when compiling locally with this option. - You will also need to ensure that any compressors or minifiers are available on your development system. In practice, this will allow you to precompile locally, have those files in your working tree, and commit those files to source control when needed. Development mode will work as expected. @@ -707,7 +688,7 @@ config.assets.cache_store = :memory_store The options accepted by the assets cache store are the same as the application's cache store. ```ruby -config.assets.cache_store = :memory_store, { :size => 32.megabytes } +config.assets.cache_store = :memory_store, { size: 32.megabytes } ``` Adding Assets to Your Gems @@ -740,7 +721,7 @@ end ``` Now that you have a `Template` class, it's time to associate it with an -extenstion for template files: +extension for template files: ```ruby Sprockets.register_engine '.bang', BangBang::Template @@ -815,18 +796,16 @@ end If you use the `assets` group with Bundler, please make sure that your `config/application.rb` has the following Bundler require statement: ```ruby -if defined?(Bundler) - # If you precompile assets before deploying to production, use this line - Bundler.require *Rails.groups(:assets => %w(development test)) - # If you want your assets lazily compiled in production, use this line - # Bundler.require(:default, :assets, Rails.env) -end +# If you precompile assets before deploying to production, use this line +Bundler.require *Rails.groups(:assets => %w(development test)) +# If you want your assets lazily compiled in production, use this line +# Bundler.require(:default, :assets, Rails.env) ``` -Instead of the old Rails 3.0 version: +Instead of the generated version: ```ruby -# If you have a Gemfile, require the gems listed there, including any gems +# Require the gems listed in Gemfile, including any gems # you've limited to :test, :development, or :production. -Bundler.require(:default, Rails.env) if defined?(Bundler) +Bundler.require(:default, Rails.env) ``` diff --git a/guides/source/association_basics.md b/guides/source/association_basics.md index dd59e2a8df..d7277b487f 100644 --- a/guides/source/association_basics.md +++ b/guides/source/association_basics.md @@ -572,7 +572,7 @@ end These need to be backed up by a migration to create the `assemblies_parts` table. This table should be created without a primary key: ```ruby -class CreateAssemblyPartJoinTable < ActiveRecord::Migration +class CreateAssembliesPartsJoinTable < ActiveRecord::Migration def change create_table :assemblies_parts, id: false do |t| t.integer :assembly_id @@ -693,6 +693,17 @@ There are a few limitations to `inverse_of` support: * They do not work with `:as` associations. * For `belongs_to` associations, `has_many` inverse associations are ignored. +Every association will attempt to automatically find the inverse association +and set the `:inverse_of` option heuristically (based on the association name). +Most associations with standard names will be supported. However, associations +that contain the following options will not have their inverses set +automatically: + +* :conditions +* :through +* :polymorphic +* :foreign_key + Detailed Association Reference ------------------------------ @@ -710,6 +721,7 @@ When you declare a `belongs_to` association, the declaring class automatically g * `association=(associate)` * `build_association(attributes = {})` * `create_association(attributes = {})` +* `create_association!(attributes = {})` In all of these methods, `association` is replaced with the symbol passed as the first argument to `belongs_to`. For example, given the declaration: @@ -726,6 +738,7 @@ customer customer= build_customer create_customer +create_customer! ``` NOTE: When initializing a new `has_one` or `belongs_to` association you must use the `build_` prefix to build the association, rather than the `association.build` method that would be used for `has_many` or `has_and_belongs_to_many` associations. To create one, use the `create_` prefix. @@ -766,6 +779,10 @@ The `create_association` method returns a new object of the associated type. Thi customer_name: "John Doe") ``` +##### `create_association!(attributes = {})` + +Does the same as <tt>create_association</tt> above, but raises <tt>ActiveRecord::RecordInvalid</tt> if the record is invalid. + #### Options for `belongs_to` @@ -845,7 +862,7 @@ Counter cache columns are added to the containing model's list of read-only attr ##### `:dependent` -If you set the `:dependent` option to `:destroy`, then deleting this object will call the `destroy` method on the associated object to delete that object. If you set the `:dependent` option to `:delete`, then deleting this object will delete the associated object _without_ calling its `destroy` method. +If you set the `:dependent` option to `:destroy`, then deleting this object will call the `destroy` method on the associated object to delete that object. If you set the `:dependent` option to `:delete`, then deleting this object will delete the associated object _without_ calling its `destroy` method. If you set the `:dependent` option to `:restrict`, then attempting to delete this object will result in a `ActiveRecord::DeleteRestrictionError` if there are any associated objects. WARNING: You should not specify this option on a `belongs_to` association that is connected with a `has_many` association on the other class. Doing so can lead to orphaned records in your database. @@ -1008,6 +1025,7 @@ When you declare a `has_one` association, the declaring class automatically gain * `association=(associate)` * `build_association(attributes = {})` * `create_association(attributes = {})` +* `create_association!(attributes = {})` In all of these methods, `association` is replaced with the symbol passed as the first argument to `has_one`. For example, given the declaration: @@ -1024,6 +1042,7 @@ account account= build_account create_account +create_account! ``` NOTE: When initializing a new `has_one` or `belongs_to` association you must use the `build_` prefix to build the association, rather than the `association.build` method that would be used for `has_many` or `has_and_belongs_to_many` associations. To create one, use the `create_` prefix. @@ -1062,6 +1081,10 @@ The `create_association` method returns a new object of the associated type. Thi @account = @supplier.create_account(terms: "Net 30") ``` +##### `create_association!(attributes = {})` + +Does the same as <tt>create_association</tt> above, but raises <tt>ActiveRecord::RecordInvalid</tt> if the record is invalid. + #### Options for `has_one` While Rails uses intelligent defaults that will work well in most situations, there may be times when you want to customize the behavior of the `has_one` association reference. Such customizations can easily be accomplished by passing options when you create the association. For example, this association uses two such options: @@ -1109,7 +1132,7 @@ end Controls what happens to the associated object when its owner is destroyed: * `:destroy` causes the associated object to also be destroyed -* `:delete` causes the asssociated object to be deleted directly from the database (so callbacks will not execute) +* `:delete` causes the associated object to be deleted directly from the database (so callbacks will not execute) * `:nullify` causes the foreign key to be set to `NULL`. Callbacks are not executed. * `:restrict_with_exception` causes an exception to be raised if there is an associated record * `:restrict_with_error` causes an error to be added to the owner if there is an associated object @@ -1274,6 +1297,7 @@ When you declare a `has_many` association, the declaring class automatically gai * `collection.exists?(...)` * `collection.build(attributes = {}, ...)` * `collection.create(attributes = {})` +* `collection.create!(attributes = {})` In all of these methods, `collection` is replaced with the symbol passed as the first argument to `has_many`, and `collection_singular` is replaced with the singularized version of that symbol. For example, given the declaration: @@ -1301,6 +1325,7 @@ orders.where(...) orders.exists?(...) orders.build(attributes = {}, ...) orders.create(attributes = {}) +orders.create!(attributes = {}) ``` ##### `collection(force_reload = false)` @@ -1416,6 +1441,10 @@ The `collection.create` method returns a new object of the associated type. This order_number: "A12345") ``` +##### `collection.create!(attributes = {})` + +Does the same as <tt>collection.create</tt> above, but raises <tt>ActiveRecord::RecordInvalid</tt> if the record is invalid. + #### Options for `has_many` While Rails uses intelligent defaults that will work well in most situations, there may be times when you want to customize the behavior of the `has_many` association reference. Such customizations can easily be accomplished by passing options when you create the association. For example, this association uses two such options: @@ -1463,7 +1492,7 @@ end Controls what happens to the associated objects when their owner is destroyed: * `:destroy` causes all the associated objects to also be destroyed -* `:delete_all` causes all the asssociated objects to be deleted directly from the database (so callbacks will not execute) +* `:delete_all` causes all the associated objects to be deleted directly from the database (so callbacks will not execute) * `:nullify` causes the foreign keys to be set to `NULL`. Callbacks are not executed. * `:restrict_with_exception` causes an exception to be raised if there are any associated records * `:restrict_with_error` causes an error to be added to the owner if there are any associated objects @@ -1500,6 +1529,20 @@ end By convention, Rails assumes that the column used to hold the primary key of the association is `id`. You can override this and explicitly specify the primary key with the `:primary_key` option. +Let's say that `users` table has `id` as the primary_key but it also has +`guid` column. And the requirement is that `todos` table should hold +`guid` column value and not `id` value. This can be achieved like this + +```ruby +class User < ActiveRecord::Base + has_many :todos, primary_key: :guid +end +``` + +Now if we execute `@user.todos.create` then `@todo` record will have +`user_id` value as the `guid` value of `@user`. + + ##### `:source` The `:source` option specifies the source association name for a `has_many :through` association. You only need to use this option if the name of the source association cannot be automatically inferred from the association name. @@ -1648,9 +1691,10 @@ The `select` method lets you override the SQL `SELECT` clause that is used to re WARNING: If you specify your own `select`, be sure to include the primary key and foreign key columns of the associated model. If you do not, Rails will throw an error. -##### `uniq` +##### `distinct` -Use the `uniq` method to keep the collection free of duplicates. This is mostly useful together with the `:through` option. +Use the `distinct` method to keep the collection free of duplicates. This is +mostly useful together with the `:through` option. ```ruby class Person < ActiveRecord::Base @@ -1666,14 +1710,15 @@ person.posts.inspect # => [#<Post id: 5, name: "a1">, #<Post id: 5, name: "a1">] Reading.all.inspect # => [#<Reading id: 12, person_id: 5, post_id: 5>, #<Reading id: 13, person_id: 5, post_id: 5>] ``` -In the above case there are two readings and `person.posts` brings out both of them even though these records are pointing to the same post. +In the above case there are two readings and `person.posts` brings out both of +them even though these records are pointing to the same post. -Now let's set `uniq`: +Now let's set `distinct`: ```ruby class Person has_many :readings - has_many :posts, -> { uniq }, through: :readings + has_many :posts, -> { distinct }, through: :readings end person = Person.create(name: 'Honda') @@ -1684,7 +1729,29 @@ person.posts.inspect # => [#<Post id: 7, name: "a1">] Reading.all.inspect # => [#<Reading id: 16, person_id: 7, post_id: 7>, #<Reading id: 17, person_id: 7, post_id: 7>] ``` -In the above case there are still two readings. However `person.posts` shows only one post because the collection loads only unique records. +In the above case there are still two readings. However `person.posts` shows +only one post because the collection loads only unique records. + +If you want to make sure that, upon insertion, all of the records in the +persisted association are distinct (so that you can be sure that when you +inspect the association that you will never find duplicate records), you should +add a unique index on the table itself. For example, if you have a table named +``person_posts`` and you want to make sure all the posts are unique, you could +add the following in a migration: + +```ruby +add_index :person_posts, :post, :unique => true +``` + +Note that checking for uniqueness using something like ``include?`` is subject +to race conditions. Do not attempt to use ``include?`` to enforce distinctness +in an association. For instance, using the post example from above, the +following code would be racy because multiple users could be attempting this +at the same time: + +```ruby +person.posts << post unless person.posts.include?(post) +``` #### When are Objects Saved? @@ -1719,6 +1786,7 @@ When you declare a `has_and_belongs_to_many` association, the declaring class au * `collection.exists?(...)` * `collection.build(attributes = {})` * `collection.create(attributes = {})` +* `collection.create!(attributes = {})` In all of these methods, `collection` is replaced with the symbol passed as the first argument to `has_and_belongs_to_many`, and `collection_singular` is replaced with the singularized version of that symbol. For example, given the declaration: @@ -1746,6 +1814,7 @@ assemblies.where(...) assemblies.exists?(...) assemblies.build(attributes = {}, ...) assemblies.create(attributes = {}) +assemblies.create!(attributes = {}) ``` ##### Additional Column Methods @@ -1865,6 +1934,10 @@ The `collection.create` method returns a new object of the associated type. This @assembly = @part.assemblies.create({assembly_name: "Transmission housing"}) ``` +##### `collection.create!(attributes = {})` + +Does the same as <tt>collection.create</tt>, but raises <tt>ActiveRecord::RecordInvalid</tt> if the record is invalid. + #### Options for `has_and_belongs_to_many` While Rails uses intelligent defaults that will work well in most situations, there may be times when you want to customize the behavior of the `has_and_belongs_to_many` association reference. Such customizations can easily be accomplished by passing options when you create the association. For example, this association uses two such options: @@ -1893,7 +1966,7 @@ TIP: The `:foreign_key` and `:association_foreign_key` options are useful when s ```ruby class User < ActiveRecord::Base - has_and_belongs_to_many :friends, + has_and_belongs_to_many :friends, class_name: "User", foreign_key: "this_user_id", association_foreign_key: "other_user_id" @@ -2126,4 +2199,4 @@ Extensions can refer to the internals of the association proxy using these three * `proxy_association.owner` returns the object that the association is a part of. * `proxy_association.reflection` returns the reflection object that describes the association. -* `proxy_association.target` returns the associated object for `belongs_to` or `has_one`, or the collection of associated objects for `has_many` or `has_and_belongs_to_many`. +* `proxy_association.target` returns the associated object for `belongs_to` or `has_one`, or the collection of associated objects for `has_many` or `has_and_belongs_to_many`.
\ No newline at end of file diff --git a/guides/source/caching_with_rails.md b/guides/source/caching_with_rails.md index a270ec7a7e..1e196b0e42 100644 --- a/guides/source/caching_with_rails.md +++ b/guides/source/caching_with_rails.md @@ -5,8 +5,8 @@ This guide will teach you what you need to know about avoiding that expensive ro After reading this guide, you will know: -* Page, action, and fragment caching. -* Sweepers. +* Page and action caching (moved to separate gems as of Rails 4). +* Fragment caching. * Alternative cache stores. * Conditional GET support. @@ -30,13 +30,13 @@ config.action_controller.perform_caching = true Page caching is a Rails mechanism which allows the request for a generated page to be fulfilled by the webserver (i.e. Apache or nginx), without ever having to go through the Rails stack at all. Obviously, this is super-fast. Unfortunately, it can't be applied to every situation (such as pages that need authentication) and since the webserver is literally just serving a file from the filesystem, cache expiration is an issue that needs to be dealt with. -INFO: Page Caching has been removed from Rails 4. See the [actionpack-page_caching gem](https://github.com/rails/actionpack-page_caching) +INFO: Page Caching has been removed from Rails 4. See the [actionpack-page_caching gem](https://github.com/rails/actionpack-page_caching). See [DHH's key-based cache expiration overview](http://37signals.com/svn/posts/3113-how-key-based-cache-expiration-works) for the newly-preferred method. ### Action Caching Page Caching cannot be used for actions that have before filters - for example, pages that require authentication. This is where Action Caching comes in. Action Caching works like Page Caching except the incoming web request hits the Rails stack so that before filters can be run on it before the cache is served. This allows authentication and other restrictions to be run while still serving the result of the output from a cached copy. -INFO: Action Caching has been removed from Rails 4. See the [actionpack-action_caching gem](https://github.com/rails/actionpack-action_caching) +INFO: Action Caching has been removed from Rails 4. See the [actionpack-action_caching gem](https://github.com/rails/actionpack-action_caching). See [DHH's key-based cache expiration overview](http://37signals.com/svn/posts/3113-how-key-based-cache-expiration-works) for the newly-preferred method. ### Fragment Caching @@ -104,6 +104,15 @@ This method generates a cache key that depends on all products and can be used i All available products: <% end %> ``` + +If you want to cache a fragment under certain condition you can use `cache_if` or `cache_unless` + +```erb +<% cache_if (condition, cache_key_for_products) do %> + All available products: +<% end %> +``` + You can also use an Active Record model as the cache key: ```erb @@ -236,7 +245,7 @@ config.cache_store = :ehcache_store When initializing the cache, you may use the `:ehcache_config` option to specify the Ehcache config file to use (where the default is "ehcache.xml" in your Rails config directory), and the :cache_name option to provide a custom name for your cache (the default is rails_cache). -In addition to the standard `:expires_in` option, the `write` method on this cache can also accept the additional `:unless_exist` option, which will cause the cache store to use Ehcache's `putIfAbsent` method instead of `put`, and therefore will not overwrite an existing entry. Additionally, the `write` method supports all of the properties exposed by the [Ehcache Element class](http://ehcache.org/apidocs/net/sf/ehcache/Element.html) , including: +In addition to the standard `:expires_in` option, the `write` method on this cache can also accept the additional `:unless_exist` option, which will cause the cache store to use Ehcache's `putIfAbsent` method instead of `put`, and therefore will not overwrite an existing entry. Additionally, the `write` method supports all of the properties exposed by the [Ehcache Element class](http://ehcache.org/apidocs/net/sf/ehcache/Element.html) , including: | Property | Argument Type | Description | | --------------------------- | ------------------- | ----------------------------------------------------------- | @@ -343,8 +352,3 @@ class ProductsController < ApplicationController end end ``` - -Further reading ---------------- - -* [Scaling Rails Screencasts](http://railslab.newrelic.com/scaling-rails) diff --git a/guides/source/command_line.md b/guides/source/command_line.md index 9d1fb03fab..e0b44bbf93 100644 --- a/guides/source/command_line.md +++ b/guides/source/command_line.md @@ -82,7 +82,7 @@ The server can be run on a different port using the `-p` option. The default dev $ rails server -e production -p 4000 ``` -The `-b` option binds Rails to the specified ip, by default it is 0.0.0.0. You can run a server as a daemon by passing a `-d` option. +The `-b` option binds Rails to the specified IP, by default it is 0.0.0.0. You can run a server as a daemon by passing a `-d` option. ### `rails generate` @@ -201,7 +201,7 @@ Usage: ... -ActiveRecord options: +Active Record options: [--migration] # Indicates when to generate migration # Default: true @@ -414,7 +414,7 @@ app/controllers/admin/users_controller.rb: * [ 20] [TODO] any other way to do this? * [132] [FIXME] high priority for next deploy -app/model/school.rb: +app/models/school.rb: * [ 13] [OPTIMIZE] refactor this code to make it faster * [ 17] [FIXME] ``` @@ -427,7 +427,7 @@ $ rake notes:fixme app/controllers/admin/users_controller.rb: * [132] high priority for next deploy -app/model/school.rb: +app/models/school.rb: * [ 17] ``` @@ -436,7 +436,7 @@ You can also use custom annotations in your code and list them using `rake notes ```bash $ rake notes:custom ANNOTATION=BUG (in /home/foobar/commandsapp) -app/model/post.rb: +app/models/post.rb: * [ 23] Have to fix this one before pushing! ``` @@ -445,12 +445,12 @@ NOTE. When using specific annotations and custom annotations, the annotation nam By default, `rake notes` will look in the `app`, `config`, `lib`, `bin` and `test` directories. If you would like to search other directories, you can provide them as a comma separated list in an environment variable `SOURCE_ANNOTATION_DIRECTORIES`. ```bash -$ export SOURCE_ANNOTATION_DIRECTORIES='rspec,vendor' +$ export SOURCE_ANNOTATION_DIRECTORIES='spec,vendor' $ rake notes (in /home/foobar/commandsapp) -app/model/user.rb: +app/models/user.rb: * [ 35] [FIXME] User should have a subscription at this point -rspec/model/user_spec.rb: +spec/models/user_spec.rb: * [122] [TODO] Verify the user that has a subscription works ``` diff --git a/guides/source/configuring.md b/guides/source/configuring.md index be46e15078..ee0d373287 100644 --- a/guides/source/configuring.md +++ b/guides/source/configuring.md @@ -30,10 +30,10 @@ Configuring Rails Components In general, the work of configuring Rails means configuring the components of Rails, as well as configuring Rails itself. The configuration file `config/application.rb` and environment-specific configuration files (such as `config/environments/production.rb`) allow you to specify the various settings that you want to pass down to all of the components. -For example, the default `config/application.rb` file includes this setting: +For example, the `config/application.rb` file includes this setting: ```ruby -config.filter_parameters += [:password] +config.autoload_paths += %W(#{config.root}/extras) ``` This is a setting for Rails itself. If you want to pass settings to individual Rails components, you can do so via the same `config` object in `config/application.rb`: @@ -97,7 +97,9 @@ These configuration methods are to be called on a `Rails::Railtie` object, such * `config.file_watcher` the class used to detect file updates in the filesystem when `config.reload_classes_only_on_change` is true. Must conform to `ActiveSupport::FileUpdateChecker` API. -* `config.filter_parameters` used for filtering out the parameters that you don't want shown in the logs, such as passwords or credit card numbers. +* `config.filter_parameters` used for filtering out the parameters that +you don't want shown in the logs, such as passwords or credit card +numbers. New applications filter out passwords by adding the following `config.filter_parameters+=[:password]` in `config/initializers/filter_parameter_logging.rb`. * `config.force_ssl` forces all requests to be under HTTPS protocol by using `ActionDispatch::SSL` middleware. @@ -131,7 +133,8 @@ These configuration methods are to be called on a `Rails::Railtie` object, such ### Configuring Assets -* `config.assets.enabled` a flag that controls whether the asset pipeline is enabled. It is explicitly initialized in `config/application.rb`. +* `config.assets.enabled` a flag that controls whether the asset +pipeline is enabled. It is set to true by default. * `config.assets.compress` a flag that enables the compression of compiled assets. It is explicitly set to true in `config/production.rb`. @@ -268,9 +271,7 @@ config.middleware.delete "Rack::MethodOverride" * `config.active_record.lock_optimistically` controls whether Active Record will use optimistic locking and is true by default. -* `config.active_record.auto_explain_threshold_in_seconds` configures the threshold for automatic EXPLAINs (`nil` disables this feature). Queries exceeding the threshold get their query plan logged. Default is 0.5 in development mode. - -* +config.active_record.cache_timestamp_format+ controls the format of the timestamp value in the cache key. Default is +:number+. +* `config.active_record.cache_timestamp_format` controls the format of the timestamp value in the cache key. Default is `:number`. The MySQL adapter adds one additional configuration option: @@ -423,13 +424,13 @@ There are a few configuration options available in Active Support: ### Configuring a Database -Just about every Rails application will interact with a database. The database to use is specified in a configuration file called `config/database.yml`. If you open this file in a new Rails application, you'll see a default database configured to use SQLite3. The file contains sections for three different environments in which Rails can run by default: +Just about every Rails application will interact with a database. The database to use is specified in a configuration file called `config/database.yml`. If you open this file in a new Rails application, you'll see a default database configured to use SQLite3. The file contains sections for three different environments in which Rails can run by default: * The `development` environment is used on your development/local computer as you interact manually with the application. * The `test` environment is used when running automated tests. * The `production` environment is used when you deploy your application for the world to use. -TIP: You don't have to update the database configurations manually. If you look at the options of the application generator, you will see that one of the options is named `--database`. This option allows you to choose an adapter from a list of the most used relational databases. You can even run the generator repeatedly: `cd .. && rails new blog --database=mysql`. When you confirm the overwriting of the `config/database.yml` file, your application will be configured for MySQL instead of SQLite. Detailed examples of the common database connections are below. +TIP: You don't have to update the database configurations manually. If you look at the options of the application generator, you will see that one of the options is named `--database`. This option allows you to choose an adapter from a list of the most used relational databases. You can even run the generator repeatedly: `cd .. && rails new blog --database=mysql`. When you confirm the overwriting of the `config/database.yml` file, your application will be configured for MySQL instead of SQLite. Detailed examples of the common database connections are below. #### Configuring an SQLite3 Database @@ -529,7 +530,7 @@ By default Rails ships with three environments: "development", "test", and "prod Imagine you have a server which mirrors the production environment but is only used for testing. Such a server is commonly called a "staging server". To define an environment called "staging" for this server just by create a file called `config/environments/staging.rb`. Please use the contents of any existing file in `config/environments` as a starting point and make the necessary changes from there. -That environment is no different than the default ones, start a server with `rails server -e staging`, a console with `rails console staging`, `Rails.env.staging?` works, etc. +That environment is no different than the default ones, start a server with `rails server -e staging`, a console with `rails console staging`, `Rails.env.staging?` works, etc. Rails Environment Settings @@ -648,7 +649,7 @@ Below is a comprehensive list of all the initializers found in Rails in the orde * `active_support.initialize_time_zone` Sets the default time zone for the application based on the `config.time_zone` setting, which defaults to "UTC". -* `active_support.initialize_beginning_of_week` Sets the default beginnig of week for the application based on `config.beginning_of_week` setting, which defaults to `:monday`. +* `active_support.initialize_beginning_of_week` Sets the default beginning of week for the application based on `config.beginning_of_week` setting, which defaults to `:monday`. * `action_dispatch.configure` Configures the `ActionDispatch::Http::URL.tld_length` to be set to the value of `config.action_dispatch.tld_length`. @@ -700,7 +701,7 @@ Below is a comprehensive list of all the initializers found in Rails in the orde * `engines_blank_point` Provides a point-in-initialization to hook into if you wish to do anything before engines are loaded. After this point, all railtie and engine initializers are run. -* `add_generator_templates` Finds templates for generators at `lib/templates` for the application, railities and engines and adds these to the `config.generators.templates` setting, which will make the templates available for all generators to reference. +* `add_generator_templates` Finds templates for generators at `lib/templates` for the application, railties and engines and adds these to the `config.generators.templates` setting, which will make the templates available for all generators to reference. * `ensure_autoload_once_paths_as_subset` Ensures that the `config.autoload_once_paths` only contains paths from `config.autoload_paths`. If it contains extra paths, then an exception will be raised. @@ -731,7 +732,7 @@ development: timeout: 5000 ``` -Since the connection pooling is handled inside of ActiveRecord by default, all application servers (Thin, mongrel, Unicorn etc.) should behave the same. Initially, the database connection pool is empty and it will create additional connections as the demand for them increases, until it reaches the connection pool limit. +Since the connection pooling is handled inside of Active Record by default, all application servers (Thin, mongrel, Unicorn etc.) should behave the same. Initially, the database connection pool is empty and it will create additional connections as the demand for them increases, until it reaches the connection pool limit. Any one request will check out a connection the first time it requires access to the database, after which it will check the connection back in, at the end of the request, meaning that the additional connection slot will be available again for the next request in the queue. diff --git a/guides/source/contributing_to_ruby_on_rails.md b/guides/source/contributing_to_ruby_on_rails.md index 7909a00c47..0bfa646b81 100644 --- a/guides/source/contributing_to_ruby_on_rails.md +++ b/guides/source/contributing_to_ruby_on_rails.md @@ -24,12 +24,20 @@ NOTE: Bugs in the most recent released version of Ruby on Rails are likely to ge ### Creating a Bug Report -If you've found a problem in Ruby on Rails which is not a security risk, do a search in GitHub under [Issues](https://github.com/rails/rails/issues) in case it was already reported. If you find no issue addressing it you can [add a new one](https://github.com/rails/rails/issues/new). (See the next section for reporting security issues). +If you've found a problem in Ruby on Rails which is not a security risk, do a search in GitHub under [Issues](https://github.com/rails/rails/issues) in case it was already reported. If you find no issue addressing it you can [add a new one](https://github.com/rails/rails/issues/new). (See the next section for reporting security issues.) At the minimum, your issue report needs a title and descriptive text. But that's only a minimum. You should include as much relevant information as possible. You need at least to post the code sample that has the issue. Even better is to include a unit test that shows how the expected behavior is not occurring. Your goal should be to make it easy for yourself — and others — to replicate the bug and figure out a fix. Then, don't get your hopes up! Unless you have a "Code Red, Mission Critical, the World is Coming to an End" kind of bug, you're creating this issue report in the hope that others with the same problem will be able to collaborate with you on solving it. Do not expect that the issue report will automatically see any activity or that others will jump to fix it. Creating an issue like this is mostly to help yourself start on the path of fixing the problem and for others to confirm it with an "I'm having this problem too" comment. +### Create a Self-Contained gist for Active Record Issues + +If you are filing a bug report for Active Record, please use +[this template for gems](https://github.com/rails/rails/blob/master/guides/bug_report_templates/active_record_gem.rb) +if the bug is found in a published gem, and +[this template for master](https://github.com/rails/rails/blob/master/guides/bug_report_templates/active_record_master.rb) +if the bug happens in the master branch. + ### Special Treatment for Security Issues WARNING: Please do not report security vulnerabilities with public GitHub issue reports. The [Rails security policy page](http://rubyonrails.org/security) details the procedure to follow for security issues. @@ -53,6 +61,22 @@ The easiest and recommended way to get a development environment ready to hack i In case you can't use the Rails development box, see section above, check [this other guide](development_dependencies_install.html). + +Running an Application Against Your Local Branch +------------------------------------------------ + +The `--dev` flag of `rails new` generates an application that uses your local +branch: + +```bash +$ cd rails +$ bundle exec rails new ~/my-test-app --dev +``` + +The application generated in `~/my-test-app` runs against your local branch +and in particular sees any modifications upon server reboot. + + Testing Active Record --------------------- @@ -158,9 +182,9 @@ Contributing to the Rails Documentation Ruby on Rails has two main sets of documentation: the guides help you in learning about Ruby on Rails, and the API is a reference. -You can help improve the Rails guides by making them more coherent, consistent or readable, adding missing information, correcting factual errors, fixing typos, or bringing it up to date with the latest edge Rails. To get involved in the translation of Rails guides, please see [Translating Rails Guides](https://wiki.github.com/lifo/docrails/translating-rails-guides). +You can help improve the Rails guides by making them more coherent, consistent or readable, adding missing information, correcting factual errors, fixing typos, or bringing it up to date with the latest edge Rails. To get involved in the translation of Rails guides, please see [Translating Rails Guides](https://wiki.github.com/rails/docrails/translating-rails-guides). -If you're confident about your changes, you can push them directly yourself via [docrails](https://github.com/lifo/docrails). Docrails is a branch with an **open commit policy** and public write access. Commits to docrails are still reviewed, but this happens after they are pushed. Docrails is merged with master regularly, so you are effectively editing the Ruby on Rails documentation. +If you're confident about your changes, you can push them directly yourself via [docrails](https://github.com/rails/docrails). Docrails is a branch with an **open commit policy** and public write access. Commits to docrails are still reviewed, but this happens after they are pushed. Docrails is merged with master regularly, so you are effectively editing the Ruby on Rails documentation. If you are unsure of the documentation changes, you can create an issue in the [Rails](https://github.com/rails/rails/issues) issues tracker on GitHub. @@ -190,7 +214,7 @@ $ cd rails $ git checkout -b my_new_branch ``` -It doesn’t matter much what name you use, because this branch will only exist on your local computer and your personal repository on Github. It won't be part of the Rails Git repository. +It doesn’t matter much what name you use, because this branch will only exist on your local computer and your personal repository on GitHub. It won't be part of the Rails Git repository. ### Write Your Code @@ -201,6 +225,17 @@ Now get busy and add or edit code. You’re on your branch now, so you can write * Include tests that fail without your code, and pass with it. * Update the (surrounding) documentation, examples elsewhere, and the guides: whatever is affected by your contribution. +It is not customary in Rails to run the full test suite before pushing +changes. The railties test suite in particular takes a long time, and even +more if the source code is mounted in `/vagrant` as happens in the recommended +workflow with the [rails-dev-box](https://github.com/rails/rails-dev-box). + +As a compromise, test what your code obviously affects, and if the change is +not in railties run the whole test suite of the affected component. If all is +green that's enough to propose your contribution. We have [Travis CI](https +://travis-ci.org/) as a safety net for catching unexpected breakages +elsewhere. + TIP: Changes that are cosmetic in nature and do not add anything substantial to the stability, functionality, or testability of Rails will generally not be accepted. ### Follow the Coding Conventions @@ -225,7 +260,7 @@ The above are guidelines — please use your best judgment in using them. The CHANGELOG is an important part of every release. It keeps the list of changes for every Rails version. -You should add an entry to the CHANGELOG of the framework that you modified if you're adding or removing a feature, commiting a bug fix or adding deprecation notices. Refactorings and documentation changes generally should not go to the CHANGELOG. +You should add an entry to the CHANGELOG of the framework that you modified if you're adding or removing a feature, committing a bug fix or adding deprecation notices. Refactorings and documentation changes generally should not go to the CHANGELOG. A CHANGELOG entry should summarize what was changed and should end with author's name. You can use multiple lines if you need more space and you can attach code examples indented with 4 spaces. If a change is related to a specific issue, you should attach issue's number. Here is an example CHANGELOG entry: @@ -250,8 +285,6 @@ Your name can be added directly after the last word if you don't provide any cod You should not be the only person who looks at the code before you submit it. You know at least one other Rails developer, right? Show them what you’re doing and ask for feedback. Doing this in private before you push a patch out publicly is the “smoke test” for a patch: if you can’t convince one other developer of the beauty of your code, you’re unlikely to convince the core team either. -You might want also to check out the [RailsBridge BugMash](http://wiki.railsbridge.org/projects/railsbridge/wiki/BugMash) as a way to get involved in a group effort to improve Rails. This can help you get started and help you check your code when you're writing your first patches. - ### Commit Your Changes When you're happy with the code on your computer, you need to commit the changes to Git: diff --git a/guides/source/credits.html.erb b/guides/source/credits.html.erb index ff76fa2b85..10dd8178fb 100644 --- a/guides/source/credits.html.erb +++ b/guides/source/credits.html.erb @@ -28,11 +28,11 @@ Ruby on Rails Guides: Credits <h3 class="section">Rails Guides Authors</h3> <%= author('Ryan Bigg', 'radar', 'radar.png') do %> -Ryan Bigg works as a consultant at <a href="http://rubyx.com">RubyX</a> and has been working with Rails since 2006. He's co-authoring a book called <a href="http://manning.com/katz">Rails 3 in Action</a> and he's written many gems which can be seen on <a href="https://github.com/radar">his GitHub page</a> and he also tweets prolifically as <a href="http://twitter.com/ryanbigg">@ryanbigg</a>. + Ryan Bigg works as the Community Manager at <a href="http://spreecommerce.com">Spree Commerce</a> and has been working with Rails since 2006. He's the author of <a href="https://leanpub.com/multi-tenancy-rails">Multi Tenancy With Rails</a> and co-author of <a href="http://manning.com/bigg2">Rails 4 in Action</a>. He's written many gems which can be seen on <a href="https://github.com/radar">his GitHub page</a> and he also tweets prolifically as <a href="http://twitter.com/ryanbigg">@ryanbigg</a>. <% end %> <%= author('Oscar Del Ben', 'oscardelben', 'oscardelben.jpg') do %> -Oscar Del Ben is a software engineer at <a href="http://www.wildfireapp.com/">Wildfire</a>. He's a regular open source contributor (<a href="https://github.com/oscardelben">Github account</a>) and tweets regularly at <a href="https://twitter.com/oscardelben">@oscardelben</a>. +Oscar Del Ben is a software engineer at <a href="http://www.wildfireapp.com/">Wildfire</a>. He's a regular open source contributor (<a href="https://github.com/oscardelben">GitHub account</a>) and tweets regularly at <a href="https://twitter.com/oscardelben">@oscardelben</a>. <% end %> <%= author('Frederick Cheung', 'fcheung') do %> @@ -40,7 +40,7 @@ Oscar Del Ben is a software engineer at <a href="http://www.wildfireapp.com/">Wi <% 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 <a href="http://tore.darell.no">Sneaky Abstractions</a>. + 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 <a href="http://tore.darell.no">Sneaky Abstractions</a>. <% end %> <%= author('Jeff Dean', 'zilkey') do %> diff --git a/guides/source/debugging_rails_applications.md b/guides/source/debugging_rails_applications.md index 5531dee343..70055c1d7d 100644 --- a/guides/source/debugging_rails_applications.md +++ b/guides/source/debugging_rails_applications.md @@ -23,7 +23,7 @@ One common task is to inspect the contents of a variable. In Rails, you can do t ### `debug` -The `debug` helper will return a \<pre>-tag that renders the object using the YAML format. This will generate human-readable data from any object. For example, if you have this code in a view: +The `debug` helper will return a \<pre> tag that renders the object using the YAML format. This will generate human-readable data from any object. For example, if you have this code in a view: ```html+erb <%= debug @post %> @@ -174,7 +174,7 @@ class PostsController < ApplicationController end ``` -Here's an example of the log generated by this method: +Here's an example of the log generated when this controller action is executed: ``` Processing PostsController#create (for 127.0.0.1 at 2008-09-08 11:52:54) [POST] @@ -194,11 +194,13 @@ Redirected to #<Post:0x20af760> Completed in 0.01224 (81 reqs/sec) | DB: 0.00044 (3%) | 302 Found [http://localhost/posts] ``` -Adding extra logging like this makes it easy to search for unexpected or unusual behavior in your logs. If you add extra logging, be sure to make sensible use of log levels, to avoid filling your production logs with useless trivia. +Adding extra logging like this makes it easy to search for unexpected or unusual behavior in your logs. If you add extra logging, be sure to make sensible use of log levels to avoid filling your production logs with useless trivia. ### Tagged Logging -When running multi-user, multi-account applications, it’s often useful to be able to filter the logs using some custom rules. `TaggedLogging` in Active Support helps in doing exactly that by stamping log lines with subdomains, request ids, and anything else to aid debugging such applications. +When running multi-user, multi-account applications, it’s often useful +to be able to filter the logs using some custom rules. `TaggedLogging` +in Active Support helps in doing exactly that by stamping log lines with subdomains, request ids, and anything else to aid debugging such applications. ```ruby logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT)) @@ -216,7 +218,7 @@ The debugger can also help you if you want to learn about the Rails source code ### Setup -Rails uses the `debugger` gem to set breakpoints and step through live code. To install it, just run: +You can use the `debugger` gem to set breakpoints and step through live code in Rails. To install it, just run: ```bash $ gem install debugger @@ -235,7 +237,7 @@ class PeopleController < ApplicationController end ``` -If you see the message in the console or logs: +If you see this message in the console or logs: ``` ***** Debugger requested, but was not available: Start server with --debugger to enable ***** @@ -246,12 +248,12 @@ Make sure you have started your web server with the option `--debugger`: ```bash $ rails server --debugger => Booting WEBrick -=> Rails 3.0.0 application starting on http://0.0.0.0:3000 +=> Rails 3.2.13 application starting on http://0.0.0.0:3000 => Debugger enabled ... ``` -TIP: In development mode, you can dynamically `require \'debugger\'` instead of restarting the server, if it was started without `--debugger`. +TIP: In development mode, you can dynamically `require \'debugger\'` instead of restarting the server, even if it was started without `--debugger`. ### The Shell @@ -266,7 +268,7 @@ For example: (rdb:7) ``` -Now it's time to explore and dig into your application. A good place to start is by asking the debugger for help... so type: `help` (You didn't see that coming, right?) +Now it's time to explore and dig into your application. A good place to start is by asking the debugger for help. Type: `help` ``` (rdb:7) help @@ -281,7 +283,7 @@ condition down finish list ps save thread var continue edit frame method putl set tmate where ``` -TIP: To view the help menu for any command use `help <command-name>` in active debug mode. For example: _`help var`_ +TIP: To view the help menu for any command use `help <command-name>` at the debugger prompt. For example: _`help var`_ The next command to learn is one of the most useful: `list`. You can abbreviate any debugging command by supplying just enough letters to distinguish them from other commands, so you can also use `l` for the `list` command. @@ -289,7 +291,7 @@ This command shows you where you are in the code by printing 10 lines centered a ``` (rdb:7) list -[1, 10] in /PathToProject/posts_controller.rb +[1, 10] in /PathTo/project/app/controllers/posts_controller.rb 1 class PostsController < ApplicationController 2 # GET /posts 3 # GET /posts.json @@ -325,7 +327,7 @@ On the other hand, to see the previous ten lines you should type `list-` (or `l- ``` (rdb:7) l- -[1, 10] in /PathToProject/posts_controller.rb +[1, 10] in /PathTo/project/app/controllers/posts_controller.rb 1 class PostsController < ApplicationController 2 # GET /posts 3 # GET /posts.json @@ -343,7 +345,7 @@ Finally, to see where you are in the code again you can type `list=` ``` (rdb:7) list= -[1, 10] in /PathToProject/posts_controller.rb +[1, 10] in /PathTo/project/app/controllers/posts_controller.rb 1 class PostsController < ApplicationController 2 # GET /posts 3 # GET /posts.json @@ -502,7 +504,7 @@ TIP: You can use the debugger while using `rails console`. Just remember to `req ``` $ rails console -Loading development environment (Rails 3.1.0) +Loading development environment (Rails 3.2.13) >> require "debugger" => [] >> author = Author.first @@ -655,21 +657,18 @@ Plugins for Debugging There are some Rails plugins to help you to find errors and debug your application. Here is a list of useful plugins for debugging: -* [Footnotes](https://github.com/josevalim/rails-footnotes:) Every Rails page has footnotes that give request information and link back to your source via TextMate. -* [Query Trace](https://github.com/ntalbott/query_trace/tree/master:) Adds query origin tracing to your logs. -* [Query Reviewer](https://github.com/nesquena/query_reviewer:) This rails plugin not only runs "EXPLAIN" before each of your select queries in development, but provides a small DIV in the rendered output of each page with the summary of warnings for each query that it analyzed. -* [Exception Notifier](https://github.com/smartinez87/exception_notification/tree/master:) Provides a mailer object and a default set of templates for sending email notifications when errors occur in a Rails application. +* [Footnotes](https://github.com/josevalim/rails-footnotes) Every Rails page has footnotes that give request information and link back to your source via TextMate. +* [Query Trace](https://github.com/ntalbott/query_trace/tree/master) Adds query origin tracing to your logs. +* [Query Reviewer](https://github.com/nesquena/query_reviewer) This rails plugin not only runs "EXPLAIN" before each of your select queries in development, but provides a small DIV in the rendered output of each page with the summary of warnings for each query that it analyzed. +* [Exception Notifier](https://github.com/smartinez87/exception_notification/tree/master) Provides a mailer object and a default set of templates for sending email notifications when errors occur in a Rails application. References ---------- * [ruby-debug Homepage](http://bashdb.sourceforge.net/ruby-debug/home-page.html) * [debugger Homepage](https://github.com/cldwalker/debugger) -* [Article: Debugging a Rails application with ruby-debug](http://www.sitepoint.com/article/debug-rails-app-ruby-debug/) -* [ruby-debug Basics screencast](http://brian.maybeyoureinsane.net/blog/2007/05/07/ruby-debug-basics-screencast/) +* [Article: Debugging a Rails application with ruby-debug](http://www.sitepoint.com/debug-rails-app-ruby-debug/) * [Ryan Bates' debugging ruby (revised) screencast](http://railscasts.com/episodes/54-debugging-ruby-revised) * [Ryan Bates' stack trace screencast](http://railscasts.com/episodes/24-the-stack-trace) * [Ryan Bates' logger screencast](http://railscasts.com/episodes/56-the-logger) * [Debugging with ruby-debug](http://bashdb.sourceforge.net/ruby-debug.html) -* [ruby-debug cheat sheet](http://cheat.errtheblog.com/s/rdebug/) -* [Ruby on Rails Wiki: How to Configure Logging](http://wiki.rubyonrails.org/rails/pages/HowtoConfigureLogging) diff --git a/guides/source/development_dependencies_install.md b/guides/source/development_dependencies_install.md index 6493c1e1ec..5647a4c1b7 100644 --- a/guides/source/development_dependencies_install.md +++ b/guides/source/development_dependencies_install.md @@ -5,6 +5,10 @@ This guide covers how to setup an environment for Ruby on Rails core development After reading this guide, you will know: +* How to set up your machine for Rails development +* How to run specific groups of unit tests from the Rails test suite +* How the ActiveRecord portion of the Rails test suite operates + -------------------------------------------------------------------------------- The Easy Way @@ -21,10 +25,10 @@ In case you can't use the Rails development box, see section above, these are th Ruby on Rails uses Git for source code control. The [Git homepage](http://git-scm.com/) has installation instructions. There are a variety of resources on the net that will help you get familiar with Git: -* [Try Git course](http://try.github.com/) is an interactive course that will teach you the basics. +* [Try Git course](http://try.github.io/) is an interactive course that will teach you the basics. * The [official Documentation](http://git-scm.com/documentation) is pretty comprehensive and also contains some videos with the basics of Git -* [Everyday Git](http://schacon.github.com/git/everyday.html) will teach you just enough about Git to get by. -* The [PeepCode screencast](https://peepcode.com/products/git) on Git ($9) is easier to follow. +* [Everyday Git](http://schacon.github.io/git/everyday.html) will teach you just enough about Git to get by. +* The [PeepCode screencast](https://peepcode.com/products/git) on Git is easier to follow. * [GitHub](http://help.github.com) offers links to a variety of Git resources. * [Pro Git](http://git-scm.com/book) is an entire book about Git with a Creative Commons license. @@ -53,7 +57,7 @@ If you are on Fedora or CentOS, you can run $ sudo yum install libxml2 libxml2-devel libxslt libxslt-devel ``` -If you have any problems with these libraries, you should install them manually compiling the source code. Just follow the instructions at the [Red Hat/CentOS section of the Nokogiri tutorials](http://nokogiri.org/tutorials/installing_nokogiri.html#red_hat__centos) . +If you have any problems with these libraries, you can install them manually by compiling the source code. Just follow the instructions at the [Red Hat/CentOS section of the Nokogiri tutorials](http://nokogiri.org/tutorials/installing_nokogiri.html#red_hat__centos) . Also, SQLite3 and its development files for the `sqlite3-ruby` gem — in Ubuntu you're done with just @@ -93,7 +97,7 @@ $ cd actionpack $ bundle exec rake test ``` -If you want to run the tests located in a specific directory use the `TEST_DIR` environment variable. For example, this will run the tests of the `railties/test/generators` directory only: +If you want to run the tests located in a specific directory use the `TEST_DIR` environment variable. For example, this will run the tests in the `railties/test/generators` directory only: ```bash $ cd railties @@ -133,14 +137,14 @@ $ sudo yum install mysql-server mysql-devel $ sudo yum install postgresql-server postgresql-devel ``` -After that run: +After that, run: ```bash $ rm .bundle/config $ bundle install ``` -We need first to delete `.bundle/config` because Bundler remembers in that file that we didn't want to install the "db" group (alternatively you can edit the file). +First, we need to delete `.bundle/config` because Bundler remembers in that file that we didn't want to install the "db" group (alternatively you can edit the file). In order to be able to run the test suite against MySQL you need to create a user named `rails` with privileges on the test databases: diff --git a/guides/source/documents.yaml b/guides/source/documents.yaml index c73bbeb90d..1b16f4e516 100644 --- a/guides/source/documents.yaml +++ b/guides/source/documents.yaml @@ -36,6 +36,11 @@ name: Views documents: - + name: Action View Overview + url: action_view_overview.html + description: This guide provides an introduction to Action View and introduces a few of the more common view helpers. + work_in_progress: true + - name: Layouts and Rendering in Rails url: layouts_and_rendering.html description: This guide covers the basic layout features of Action Controller and Action View, including rendering and redirecting, using content_for blocks, and working with partials. @@ -68,7 +73,6 @@ - name: Action Mailer Basics url: action_mailer_basics.html - work_in_progress: true description: This guide describes how to use Action Mailer to send and receive emails. - name: Testing Rails Applications diff --git a/guides/source/engines.md b/guides/source/engines.md index 00939c4ff2..bc66ed256e 100644 --- a/guides/source/engines.md +++ b/guides/source/engines.md @@ -461,7 +461,7 @@ NOTE: Other engines, such as Devise, handle this a little differently by making The engine contains migrations for the `blorgh_posts` and `blorgh_comments` table which need to be created in the application's database so that the engine's models can query them correctly. To copy these migrations into the application use this command: ```bash -$ rake blorgh:install:migrations +$ rake blorgh_engine:install:migrations ``` If you have multiple engines that need migrations copied over, use `railties:install:migrations` instead: @@ -612,50 +612,50 @@ This section covers how to make the `User` class configurable, followed by gener #### Setting configuration settings in the application -The next step is to make the class that represents a `User` in the application customizable for the engine. This is because, as explained before, that class may not always be `User`. To make this customizable, the engine will have a configuration setting called `user_class` that will be used to specify what the class representing users is inside the application. +The next step is to make the class that represents a `User` in the application customizable for the engine. This is because, as explained before, that class may not always be `User`. To make this customizable, the engine will have a configuration setting called `author_class` that will be used to specify what the class representing users is inside the application. To define this configuration setting, you should use a `mattr_accessor` inside the `Blorgh` module for the engine, located at `lib/blorgh.rb` inside the engine. Inside this module, put this line: ```ruby -mattr_accessor :user_class +mattr_accessor :author_class ``` -This method works like its brothers `attr_accessor` and `cattr_accessor`, but provides a setter and getter method on the module with the specified name. To use it, it must be referenced using `Blorgh.user_class`. +This method works like its brothers `attr_accessor` and `cattr_accessor`, but provides a setter and getter method on the module with the specified name. To use it, it must be referenced using `Blorgh.author_class`. The next step is switching the `Blorgh::Post` model over to this new setting. For the `belongs_to` association inside this model (`app/models/blorgh/post.rb`), it will now become this: ```ruby -belongs_to :author, class_name: Blorgh.user_class +belongs_to :author, class_name: Blorgh.author_class ``` The `set_author` method also located in this class should also use this class: ```ruby -self.author = Blorgh.user_class.constantize.find_or_create_by(name: author_name) +self.author = Blorgh.author_class.constantize.find_or_create_by(name: author_name) ``` -To save having to call `constantize` on the `user_class` result all the time, you could instead just override the `user_class` getter method inside the `Blorgh` module in the `lib/blorgh.rb` file to always call `constantize` on the saved value before returning the result: +To save having to call `constantize` on the `author_class` result all the time, you could instead just override the `author_class` getter method inside the `Blorgh` module in the `lib/blorgh.rb` file to always call `constantize` on the saved value before returning the result: ```ruby -def self.user_class - @@user_class.constantize +def self.author_class + @@author_class.constantize end ``` This would then turn the above code for `set_author` into this: ```ruby -self.author = Blorgh.user_class.find_or_create_by(name: author_name) +self.author = Blorgh.author_class.find_or_create_by(name: author_name) ``` -Resulting in something a little shorter, and more implicit in its behavior. The `user_class` method should always return a `Class` object. +Resulting in something a little shorter, and more implicit in its behavior. The `author_class` method should always return a `Class` object. -Since we changed the `user_class` method to no longer return a +Since we changed the `author_class` method to no longer return a `String` but a `Class` we must also modify our `belongs_to` definition in the `Blorgh::Post` model: ```ruby -belongs_to :author, class_name: Blorgh.user_class.to_s +belongs_to :author, class_name: Blorgh.author_class.to_s ``` To set this configuration setting within the application, an initializer should be used. By using an initializer, the configuration will be set up before the application starts and calls the engine's models which may depend on this configuration setting existing. @@ -663,7 +663,7 @@ To set this configuration setting within the application, an initializer should Create a new initializer at `config/initializers/blorgh.rb` inside the application where the `blorgh` engine is installed and put this content in it: ```ruby -Blorgh.user_class = "User" +Blorgh.author_class = "User" ``` WARNING: It's very important here to use the `String` version of the class, rather than the class itself. If you were to use the class, Rails would attempt to load that class and then reference the related table, which could lead to problems if the table wasn't already existing. Therefore, a `String` should be used and then converted to a class using `constantize` in the engine later on. @@ -676,7 +676,12 @@ There are now no strict dependencies on what the class is, only what the API for Within an engine, there may come a time where you wish to use things such as initializers, internationalization or other configuration options. The great news is that these things are entirely possible because a Rails engine shares much the same functionality as a Rails application. In fact, a Rails application's functionality is actually a superset of what is provided by engines! -If you wish to use an initializer — code that should run before the engine is loaded — the place for it is the `config/initializers` folder. This directory's functionality is explained in the [Initializers section](http://guides.rubyonrails.org/configuring.html#initializers) of the Configuring guide, and works precisely the same way as the `config/initializers` directory inside an application. Same goes for if you want to use a standard initializer. +If you wish to use an initializer — code that should run before the engine is +loaded — the place for it is the `config/initializers` folder. This directory's +functionality is explained in the +[Initializers section](configuring.html#initializers) of the Configuring guide, +and works precisely the same way as the `config/initializers` directory inside +an application. Same goes for if you want to use a standard initializer. For locales, simply place the locale files in the `config/locales` directory, just like you would in an application. @@ -714,6 +719,32 @@ Engine model and controller classes can be extended by open classing them in the For simple class modifications use `Class#class_eval`, and for complex class modifications, consider using `ActiveSupport::Concern`. +#### A note on Decorators and loading code + +Because these decorators are not referenced by your Rails application itself, +Rails' autoloading system will not kick in and load your decorators. This +means that you need to require them yourself. + +Here is some sample code to do this: + +```ruby +# lib/blorgh/engine.rb +module Blorgh + class Engine < ::Rails::Engine + isolate_namespace Blorgh + + config.to_prepare do + Dir.glob(Rails.root + "app/decorators/**/*_decorator*.rb").each do |c| + require_dependency(c) + end + end + end +end +``` + +This doesn't apply to just Decorators, but anything that you add in an engine +that isn't referenced by your main application. + #### Implementing Decorator Pattern Using Class#class_eval **Adding** `Post#time_since_created`, @@ -918,7 +949,7 @@ initializer "blorgh.assets.precompile" do |app| end ``` -For more information, read the [Asset Pipeline guide](http://guides.rubyonrails.org/asset_pipeline.html) +For more information, read the [Asset Pipeline guide](asset_pipeline.html) ### Other gem dependencies diff --git a/guides/source/form_helpers.md b/guides/source/form_helpers.md index b8681d493a..b409534cb0 100644 --- a/guides/source/form_helpers.md +++ b/guides/source/form_helpers.md @@ -221,7 +221,7 @@ Upon form submission the value entered by the user will be stored in `params[:pe WARNING: You must pass the name of an instance variable, i.e. `:person` or `"person"`, not an actual instance of your model object. -Rails provides helpers for displaying the validation errors associated with a model object. These are covered in detail by the [Active Record Validations and Callbacks](./active_record_validations_callbacks.html#displaying-validation-errors-in-the-view) guide. +Rails provides helpers for displaying the validation errors associated with a model object. These are covered in detail by the [Active Record Validations](./active_record_validations.html#displaying-validation-errors-in-views) guide. ### Binding a Form to an Object @@ -423,7 +423,7 @@ Whenever Rails sees that the internal value of an option being generated matches TIP: The second argument to `options_for_select` must be exactly equal to the desired internal value. In particular if the value is the integer 2 you cannot pass "2" to `options_for_select` — you must pass 2. Be aware of values extracted from the `params` hash as they are all strings. -WARNING: when `:inlude_blank` or `:prompt:` are not present, `:include_blank` is forced true if the select attribute `required` is true, display `size` is one and `multiple` is not true. +WARNING: when `:include_blank` or `:prompt` are not present, `:include_blank` is forced true if the select attribute `required` is true, display `size` is one and `multiple` is not true. You can add arbitrary attributes to the options using hashes: @@ -568,7 +568,7 @@ NOTE: In many cases the built-in date pickers are clumsy as they do not aid the ### 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 an input field named after the time component (for example "year" for `select_year`, "month" for `select_month` etc.) although this can be overridden 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 overridden 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 @@ -830,23 +830,20 @@ Many apps grow beyond simple forms editing a single object. For example when cre ### Configuring the Model -Active Record provides model level support via the `accepts_nested_attributes_for` method: +Active Record provides model level support via the `accepts_nested_attributes_for` method: ```ruby class Person < ActiveRecord::Base has_many :addresses accepts_nested_attributes_for :addresses - - attr_accessible :name, :addresses_attributes end class Address < ActiveRecord::Base belongs_to :person - attr_accessible :kind, :street end ``` -This creates an `addresses_attributes=` method on `Person` that allows you to create, update and (optionally) destroy addresses. When using `attr_accessible` or `attr_protected` you must mark `addresses_attributes` as accessible as well as the other attributes of `Person` and `Address` that should be mass assigned. +This creates an `addresses_attributes=` method on `Person` that allows you to create, update and (optionally) destroy addresses. ### Building the Form @@ -888,7 +885,7 @@ end :name => 'John Doe', :addresses_attributes => { '0' => { - :kind => 'Home', + :kind => 'Home', :street => '221b Baker Street', }, '1' => { @@ -906,7 +903,21 @@ If the associated object is already saved, `fields_for` autogenerates a hidden i ### The Controller -You do not need to write any specific controller code to use nested attributes. Create and update records as you would with a simple form. +As usual you need to +[whitelist the parameters](action_controller_overview.html#strong-parameters) in +the controller before you pass them to the model: + +```ruby +def create + @person = Person.new(person_params) + # ... +end + +private +def person_params + params.require(:person).permit(:name, addresses_attributes: [:id, :kind, :street]) +end +``` ### Removing Objects @@ -937,6 +948,16 @@ If the hash of attributes for an object contains the key `_destroy` with a value <% end %> ``` +Don't forget to update the whitelisted params in your controller to also include +the `_destroy` field: + +```ruby +def person_params + params.require(:person). + permit(:name, addresses_attributes: [:id, :kind, :street, :_destroy]) +end +``` + ### Preventing Empty Records It is often useful to ignore sets of fields that the user has not filled in. You can control this by passing a `:reject_if` proc to `accepts_nested_attributes_for`. This proc will be called with each hash of attributes submitted by the form. If the proc returns `false` then Active Record will not build an associated object for that hash. The example below only tries to build an address if the `kind` attribute is set. diff --git a/guides/source/generators.md b/guides/source/generators.md index 1a08eb420a..a8a34d0ac4 100644 --- a/guides/source/generators.md +++ b/guides/source/generators.md @@ -412,7 +412,7 @@ This command will generate the `Thud` application, and then apply the template t Templates don't have to be stored on the local system, the `-m` option also supports online templates: ```bash -$ rails new thud -m https://gist.github.com/722911.txt +$ rails new thud -m https://gist.github.com/radar/722911/raw/ ``` Whilst the final section of this guide doesn't cover how to generate the most awesome template known to man, it will take you through the methods available at your disposal so that you can develop it yourself. These same methods are also available for generators. @@ -589,11 +589,11 @@ Creates an initializer in the `config/initializers` directory of the application initializer "begin.rb", "puts 'this is the beginning'" ``` -This method also takes a block: +This method also takes a block, expected to return a string: ```ruby initializer "begin.rb" do - puts "Almost done!" + "puts 'this is the beginning'" end ``` diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md index 87f5e43157..2fb0cd7c72 100644 --- a/guides/source/getting_started.md +++ b/guides/source/getting_started.md @@ -29,7 +29,7 @@ prerequisites installed: Rails is a web application framework running on the Ruby programming language. If you have no prior experience with Ruby, you will find a very steep learning curve diving straight into Rails. There are some good free resources on the -internet for learning Ruby, including: +Internet for learning Ruby, including: * [Mr. Neighborly's Humble Little Ruby Book](http://www.humblelittlerubybook.com) * [Programming Ruby](http://www.ruby-doc.org/docs/ProgrammingRuby/) @@ -64,7 +64,7 @@ Creating a New Rails Project The best way to use this guide is to follow each step as it happens, no code or step needed to make this example application has been left out, so you can literally follow along step by step. You can get the complete code -[here](https://github.com/lifo/docrails/tree/master/guides/code/getting_started). +[here](https://github.com/rails/docrails/tree/master/guides/code/getting_started). By following along with this guide, you'll create a Rails project called `blog`, a @@ -103,7 +103,7 @@ To verify that you have everything installed correctly, you should be able to ru $ rails --version ``` -If it says something like "Rails 3.2.9", you are ready to continue. +If it says something like "Rails 4.0.0", you are ready to continue. ### Creating the Blog Application @@ -135,7 +135,7 @@ application. Most of the work in this tutorial will happen in the `app/` folder, | ----------- | ------- | |app/|Contains the controllers, models, views, helpers, mailers and assets for your application. You'll focus on this folder for the remainder of this guide.| |bin/|Contains the rails script that starts your app and can contain other scripts you use to deploy or run your application.| -|config/|Configure your application's runtime rules, routes, database, and more. This is covered in more detail in [Configuring Rails Applications](configuring.html)| +|config/|Configure your application's runtime rules, routes, database, and more. This is covered in more detail in [Configuring Rails Applications](configuring.html)| |config.ru|Rack configuration for Rack based servers used to start the application.| |db/|Contains your current database schema, as well as the database migrations.| |Gemfile<br />Gemfile.lock|These files allow you to specify what gem dependencies are needed for your Rails application. These files are used by the Bundler gem. For more information about Bundler, see [the Bundler website](http://gembundler.com) | @@ -165,7 +165,7 @@ TIP: Compiling CoffeeScript to JavaScript requires a JavaScript runtime and the This will fire up WEBrick, a webserver built into Ruby by default. To see your application in action, open a browser window and navigate to <http://localhost:3000>. You should see the Rails default information page: -![Welcome Aboard screenshot](images/rails_welcome.png) +![Welcome Aboard screenshot](images/getting_started/rails_welcome.png) TIP: To stop the web server, hit Ctrl+C in the terminal window where it's running. To verify the server has stopped you should see your command prompt cursor again. For most UNIX-like systems including Mac OS X this will be a dollar sign `$`. In development mode, Rails does not generally require you to restart the server; changes you make in files will be automatically picked up by the server. @@ -208,7 +208,7 @@ create app/assets/stylesheets/welcome.css.scss Most important of these are of course the controller, located at `app/controllers/welcome_controller.rb` and the view, located at `app/views/welcome/index.html.erb`. -Open the `app/views/welcome/index.html.erb` file in your text editor and edit it to contain a single line of code: +Open the `app/views/welcome/index.html.erb` file in your text editor. Delete all of the existing code in the file, and replace it with the following single line of code: ```html <h1>Hello, Rails!</h1> @@ -252,33 +252,47 @@ Now that you've seen how to create a controller, an action and a view, let's cre In the Blog application, you will now create a new _resource_. A resource is the term used for a collection of similar objects, such as posts, people or animals. You can create, read, update and destroy items for a resource and these operations are referred to as _CRUD_ operations. -In the next section, you will add the ability to create new posts in your application and be able to view them. This is the "C" and the "R" from CRUD: creation and reading. The form for doing this will look like this: +Rails provides a `resources` method which can be used to declare a +standard REST resource. Here's how `config/routes.rb` will look like. -![The new post form](images/getting_started/new_post.png) +```ruby +Blog::Application.routes.draw do -It will look a little basic for now, but that's ok. We'll look at improving the styling for it afterwards. + resources :posts -### Laying down the ground work + root to: "welcome#index" +end +``` -The first thing that you are going to need to create a new post within the application is a place to do that. A great place for that would be at `/posts/new`. If you attempt to navigate to that now — by visiting <http://localhost:3000/posts/new> — Rails will give you a routing error: +If you run `rake routes`, you'll see that all the routes for the +standard RESTful actions. -![A routing error, no route matches /posts/new](images/getting_started/routing_error_no_route_matches.png) +```bash +$ rake routes + posts GET /posts(.:format) posts#index + POST /posts(.:format) posts#create + new_post GET /posts/new(.:format) posts#new +edit_post GET /posts/:id/edit(.:format) posts#edit + post GET /posts/:id(.:format) posts#show + PATCH /posts/:id(.:format) posts#update + PUT /posts/:id(.:format) posts#update + DELETE /posts/:id(.:format) posts#destroy + root / welcome#index +``` -This is because there is nowhere inside the routes for the application — defined inside `config/routes.rb` — that defines this route. By default, Rails has no routes configured at all, besides the root route you defined earlier, and so you must define your routes as you need them. +In the next section, you will add the ability to create new posts in your application and be able to view them. This is the "C" and the "R" from CRUD: creation and reading. The form for doing this will look like this: - To do this, you're going to need to create a route inside `config/routes.rb` file, on a new line between the `do` and the `end` for the `draw` method: +![The new post form](images/getting_started/new_post.png) -```ruby -get "posts/new" -``` +It will look a little basic for now, but that's ok. We'll look at improving the styling for it afterwards. -This route is a super-simple route: it defines a new route that only responds to `GET` requests, and that the route is at `posts/new`. But how does it know where to go without the use of the `:to` option? Well, Rails uses a sensible default here: Rails will assume that you want this route to go to the new action inside the posts controller. +### Laying down the ground work -With the route defined, requests can now be made to `/posts/new` in the application. Navigate to <http://localhost:3000/posts/new> and you'll see another routing error: +The first thing that you are going to need to create a new post within the application is a place to do that. A great place for that would be at `/posts/new`. With the route already defined, requests can now be made to `/posts/new` in the application. Navigate to <http://localhost:3000/posts/new> and you'll see a routing error: ![Another routing error, uninitialized constant PostsController](images/getting_started/routing_error_no_controller.png) -This error is happening because this route need a controller to be defined. The route is attempting to find that controller so it can serve the request, but with the controller undefined, it just can't do that. The solution to this particular problem is simple: you need to create a controller called `PostsController`. You can do this by running this command: +This error occurs because the route needs to have a controller defined in order to serve the request. The solution to this particular problem is simple: create a controller called `PostsController`. You can do this by running this command: ```bash $ rails g controller posts @@ -377,14 +391,10 @@ like this is called "create", and so the form should be pointed to that action. Edit the `form_for` line inside `app/views/posts/new.html.erb` to look like this: ```html+erb -<%= form_for :post, url: { action: :create } do |f| %> +<%= form_for :post, url: posts_path do |f| %> ``` -In this example, a `Hash` object is passed to the `:url` option. What Rails will do with this is that it will point the form to the `create` action of the current controller, the `PostsController`, and will send a `POST` request to that route. For this to work, you will need to add a route to `config/routes.rb`, right underneath the one for "posts/new": - -```ruby -post "posts" => "posts#create" -``` +In this example, the `posts_path` helper is passed to the `:url` option. What Rails will do with this is that it will point the form to the `create` action of the current controller, the `PostsController`, and will send a `POST` request to that route. By using the `post` method rather than the `get` method, Rails will define a route that will only respond to POST methods. The POST method is the typical method used by forms all over the web. @@ -521,21 +531,28 @@ and change the `create` action to look like this: ```ruby def create - @post = Post.new(params[:post]) - + @post = Post.new(post_params) + @post.save - redirect_to action: :show, id: @post.id + redirect_to @post end + +private + def post_params + params.require(:post).permit(:title, :text) + end ``` Here's what's going on: every Rails model can be initialized with its respective attributes, which are automatically mapped to the respective database columns. In the first line we do just that (remember that -`params[:post]` contains the attributes we're interested in). Then, +`post_params` contains the attributes we're interested in). Then, `@post.save` is responsible for saving the model in the database. Finally, we redirect the user to the `show` action, which we'll define later. +TIP: Note that `def post_params` is private. This new approach prevents an attacker from setting the model's attributes by manipulating the hash passed to the model. For more information, refer to [this blog post about Strong Parameters](http://weblog.rubyonrails.org/2012/3/21/strong-parameters/). + TIP: As we'll see later, `@post.save` returns a boolean indicating whether the model was saved or not. @@ -543,16 +560,14 @@ whether the model was saved or not. If you submit the form again now, Rails will complain about not finding the `show` action. That's not very useful though, so let's add the -`show` action before proceeding. Open `config/routes.rb` and add the following route: +`show` action before proceeding. ```ruby -get "posts/:id" => "posts#show" +post GET /posts/:id(.:format) posts#show ``` The special syntax `:id` tells rails that this route expects an `:id` -parameter, which in our case will be the id of the post. Note that this -time we had to specify the actual mapping, `posts#show` because -otherwise Rails would not know which action to render. +parameter, which in our case will be the id of the post. As we did before, we need to add the `show` action in `app/controllers/posts_controller.rb` and its respective view. @@ -568,7 +583,7 @@ interested in. We also use an instance variable (prefixed by `@`) to hold a reference to the post object. We do this because Rails will pass all instance variables to the view. -Now, create a new file `app/view/posts/show.html.erb` with the following +Now, create a new file `app/views/posts/show.html.erb` with the following content: ```html+erb @@ -601,7 +616,7 @@ look like this: @post = Post.new(params[:post].permit(:title, :text)) @post.save - redirect_to action: :show, id: @post.id + redirect_to @post end ``` @@ -613,11 +628,11 @@ Visit <http://localhost:3000/posts/new> and give it a try! ### Listing all posts -We still need a way to list all our posts, so let's do that. As usual, -we'll need a route placed into `config/routes.rb`: +We still need a way to list all our posts, so let's do that. +We'll use a specific route from `config/routes.rb`: ```ruby -get "posts" => "posts#index" +posts GET /posts(.:format) posts#index ``` And an action for that route inside the `PostsController` in the `app/controllers/posts_controller.rb` file: @@ -669,7 +684,7 @@ for posts. Let's add links to the other views as well, starting with adding this "New Post" link to `app/views/posts/index.html.erb`, placing it above the `<table>` tag: ```erb -<%= link_to 'New post', action: :new %> +<%= link_to 'New post', new_post_path %> ``` This link will allow you to bring up the form that lets you create a new post. You should also add a link to this template — `app/views/posts/new.html.erb` — to go back to the `index` action. Do this by adding this underneath the form in this template: @@ -679,7 +694,7 @@ This link will allow you to bring up the form that lets you create a new post. Y ... <% end %> -<%= link_to 'Back', action: :index %> +<%= link_to 'Back', posts_path %> ``` Finally, add another link to the `app/views/posts/show.html.erb` template to go back to the `index` action as well, so that people who are viewing a single post can go back and view the whole list again: @@ -695,7 +710,7 @@ Finally, add another link to the `app/views/posts/show.html.erb` template to go <%= @post.text %> </p> -<%= link_to 'Back', action: :index %> +<%= link_to 'Back', posts_path %> ``` TIP: If you want to link to an action in the same controller, you don't @@ -734,7 +749,7 @@ end ``` These changes will ensure that all posts have a title that is at least five -characters long. Rails can validate a variety of conditions in a model, +characters long. Rails can validate a variety of conditions in a model, including the presence or uniqueness of columns, their format, and the existence of associated objects. Validations are covered in detail in [Active Record Validations](active_record_validations.html) @@ -755,7 +770,7 @@ def create @post = Post.new(params[:post].permit(:title, :text)) if @post.save - redirect_to action: :show, id: @post.id + redirect_to @post else render 'new' end @@ -776,7 +791,7 @@ something went wrong. To do that, you'll modify `app/views/posts/new.html.erb` to check for error messages: ```html+erb -<%= form_for :post, url: { action: :create } do |f| %> +<%= form_for :post, url: posts_path do |f| %> <% if @post.errors.any? %> <div id="errorExplanation"> <h2><%= pluralize(@post.errors.count, "error") %> prohibited @@ -803,7 +818,7 @@ something went wrong. To do that, you'll modify </p> <% end %> -<%= link_to 'Back', action: :index %> +<%= link_to 'Back', posts_path %> ``` A few things are going on. We check if there are any errors with @@ -832,14 +847,6 @@ We've covered the "CR" part of CRUD. Now let's focus on the "U" part, updating p The first step we'll take is adding an `edit` action to `posts_controller`. -Start by adding a route to `config/routes.rb`: - -```ruby -get "posts/:id/edit" => "posts#edit" -``` - -And then add the controller action: - ```ruby def edit @post = Post.find(params[:id]) @@ -853,8 +860,7 @@ it look as follows: ```html+erb <h1>Editing post</h1> -<%= form_for :post, url: { action: :update, id: @post.id }, -method: :patch do |f| %> +<%= form_for :post, url: post_path(@post.id), method: :patch do |f| %> <% if @post.errors.any? %> <div id="errorExplanation"> <h2><%= pluralize(@post.errors.count, "error") %> prohibited @@ -881,7 +887,7 @@ method: :patch do |f| %> </p> <% end %> -<%= link_to 'Back', action: :index %> +<%= link_to 'Back', posts_path %> ``` This time we point the form to the `update` action, which is not defined yet @@ -893,21 +899,14 @@ via the `PATCH` HTTP method which is the HTTP method you're expected to use to TIP: By default forms built with the _form_for_ helper are sent via `POST`. -Next, we need to add the `update` action. The file -`config/routes.rb` will need just one more line: - -```ruby -patch "posts/:id" => "posts#update" -``` - -And then create the `update` action in `app/controllers/posts_controller.rb`: +Next we need to create the `update` action in `app/controllers/posts_controller.rb`: ```ruby def update @post = Post.find(params[:id]) if @post.update(params[:post].permit(:title, :text)) - redirect_to action: :show, id: @post.id + redirect_to @post else render 'edit' end @@ -941,8 +940,8 @@ appear next to the "Show" link: <tr> <td><%= post.title %></td> <td><%= post.text %></td> - <td><%= link_to 'Show', action: :show, id: post.id %></td> - <td><%= link_to 'Edit', action: :edit, id: post.id %></td> + <td><%= link_to 'Show', post_path(post) %></td> + <td><%= link_to 'Edit', edit_post_path(post) %></td> </tr> <% end %> </table> @@ -955,8 +954,8 @@ the template: ```html+erb ... -<%= link_to 'Back', action: :index %> -| <%= link_to 'Edit', action: :edit, id: @post.id %> +<%= link_to 'Back', posts_path %> +| <%= link_to 'Edit', edit_post_path(@post) %> ``` And here's how our app looks so far: @@ -1016,7 +1015,7 @@ completely: <%= render 'form' %> -<%= link_to 'Back', action: :index %> +<%= link_to 'Back', posts_path %> ``` Then do the same for the `app/views/posts/edit.html.erb` view: @@ -1026,66 +1025,17 @@ Then do the same for the `app/views/posts/edit.html.erb` view: <%= render 'form' %> -<%= link_to 'Back', action: :index %> +<%= link_to 'Back', posts_path %> ``` -Point your browser to <http://localhost:3000/posts/new> and -try creating a new post. Everything still works. Now try editing the -post and you'll receive the following error: - -![Undefined method post_path](images/getting_started/undefined_method_post_path.png) - -To understand this error, you need to understand how `form_for` works. -When you pass an object to `form_for` and you don't specify a `:url` -option, Rails will try to guess the `action` and `method` options by -checking if the passed object is a new record or not. Rails follows the -REST convention, so to create a new `Post` object it will look for a -route named `posts_path`, and to update a `Post` object it will look for -a route named `post_path` and pass the current object. Similarly, rails -knows that it should create new objects via POST and update them via -PUT. - -If you run `rake routes` from the console you'll see that we already -have a `posts_path` route, which was created automatically by Rails when we -defined the route for the index action. -However, we don't have a `post_path` yet, which is the reason why we -received an error before. With your server running you can view your routes by visiting [localhost:3000/rails/info/routes](http://localhost:3000/rails/info/routes), or you can generate them from the command line by running `rake routes`: - -```bash -$ rake routes - - posts GET /posts(.:format) posts#index -posts_new GET /posts/new(.:format) posts#new - POST /posts(.:format) posts#create - GET /posts/:id(.:format) posts#show - GET /posts/:id/edit(.:format) posts#edit - PUT /posts/:id(.:format) posts#update - root / welcome#index -``` - -To fix this, open `config/routes.rb` and modify the `get "posts/:id"` -line like this: - -```ruby -get "posts/:id" => "posts#show", as: :post -``` - -The `:as` option tells the `get` method that we want to make routing helpers -called `post_url` and `post_path` available to our application. These are -precisely the methods that the `form_for` needs when editing a post, and so now -you'll be able to update posts again. - -NOTE: The `:as` option is available on the `post`, `patch`, `put`, `delete` and `match` -routing methods also. - ### Deleting Posts We're now ready to cover the "D" part of CRUD, deleting posts from the -database. Following the REST convention, we're going to add a route for -deleting posts to `config/routes.rb`: +database. Following the REST convention, the route for +deleting posts in the `config/routes.rb` is: ```ruby -delete "posts/:id" => "posts#destroy" +DELETE /posts/:id(.:format) posts#destroy ``` The `delete` routing method should be used for routes that destroy @@ -1105,7 +1055,7 @@ def destroy @post = Post.find(params[:id]) @post.destroy - redirect_to action: :index + redirect_to posts_path end ``` @@ -1132,18 +1082,17 @@ together. <tr> <td><%= post.title %></td> <td><%= post.text %></td> - <td><%= link_to 'Show', action: :show, id: post.id %></td> - <td><%= link_to 'Edit', action: :edit, id: post.id %></td> - <td><%= link_to 'Destroy', { action: :destroy, id: post.id }, + <td><%= link_to 'Show', post_path(post) %></td> + <td><%= link_to 'Edit', edit_post_path(post) %></td> + <td><%= link_to 'Destroy', post_path(post), method: :delete, data: { confirm: 'Are you sure?' } %></td> </tr> <% end %> </table> ``` -Here we're using `link_to` in a different way. We wrap the -`:action` and `:id` attributes in a hash so that we can pass those two keys in -first as one argument, and then the final two keys as another argument. The `:method` and `:'data-confirm'` +Here we're using `link_to` in a different way. We pass the named route as the first argument, +and then the final two keys as another argument. The `:method` and `:'data-confirm'` options are used as HTML5 attributes so that when the link is clicked, Rails will first show a confirm dialog to the user, and then submit the link with method `delete`. This is done via the JavaScript file `jquery_ujs` which is automatically included @@ -1153,61 +1102,11 @@ generated the application. Without this file, the confirmation dialog box wouldn ![Confirm Dialog](images/getting_started/confirm_dialog.png) Congratulations, you can now create, show, list, update and destroy -posts. In the next section will see how Rails can aid us when creating -REST applications, and how we can refactor our Blog app to take -advantage of it. - -### Going Deeper into REST - -We've now covered all the CRUD actions of a REST app. We did so by -declaring separate routes with the appropriate verbs into -`config/routes.rb`. Here's how that file looks so far: - -```ruby -get "posts" => "posts#index" -get "posts/new" -post "posts" => "posts#create" -get "posts/:id" => "posts#show", as: :post -get "posts/:id/edit" => "posts#edit" -patch "posts/:id" => "posts#update" -delete "posts/:id" => "posts#destroy" -``` - -That's a lot to type for covering a single **resource**. Fortunately, -Rails provides a `resources` method which can be used to declare a -standard REST resource. Here's how `config/routes.rb` looks after the -cleanup: - -```ruby -Blog::Application.routes.draw do - - resources :posts - - root to: "welcome#index" -end -``` - -If you run `rake routes`, you'll see that all the routes that we -declared before are still available: - -```bash -$ rake routes - posts GET /posts(.:format) posts#index - POST /posts(.:format) posts#create - new_post GET /posts/new(.:format) posts#new -edit_post GET /posts/:id/edit(.:format) posts#edit - post GET /posts/:id(.:format) posts#show - PUT /posts/:id(.:format) posts#update - DELETE /posts/:id(.:format) posts#destroy - root / welcome#index -``` - -Also, if you go through the motions of creating, updating and deleting -posts the app still works as before. +posts. TIP: In general, Rails encourages the use of resources objects in place -of declaring routes manually. It was only done in this guide as a learning -exercise. For more information about routing, see +of declaring routes manually. +For more information about routing, see [Rails Routing from the Outside In](routing.html). Adding a Second Model @@ -1256,19 +1155,17 @@ class CreateComments < ActiveRecord::Migration create_table :comments do |t| t.string :commenter t.text :body - t.references :post + t.references :post, index: true t.timestamps end - - add_index :comments, :post_id end end ``` The `t.references` line sets up a foreign key column for the association between -the two models. And the `add_index` line sets up an index for this association -column. Go ahead and run the migration: +the two models. An index for this association is also created on this column. +Go ahead and run the migration: ```bash $ rake db:migrate @@ -1280,10 +1177,8 @@ run against the current database, so in this case you will just see: ```bash == CreateComments: migrating ================================================= -- create_table(:comments) - -> 0.0008s --- add_index(:comments, :post_id) - -> 0.0003s -== CreateComments: migrated (0.0012s) ======================================== + -> 0.0115s +== CreateComments: migrated (0.0119s) ======================================== ``` ### Associating Models @@ -1721,7 +1616,7 @@ class CommentsController < ApplicationController Now if you try to create a new post, you will be greeted with a basic HTTP Authentication challenge -![Basic HTTP Authentication Challenge](images/challenge.png) +![Basic HTTP Authentication Challenge](images/getting_started/challenge.png) What's Next? ------------ @@ -1761,7 +1656,7 @@ cannot be automatically detected by Rails and corrected. Two very common sources of data that are not UTF-8: -* Your text editor: Most text editors (such as Textmate), default to saving files as +* Your text editor: Most text editors (such as TextMate), default to saving files as UTF-8. If your text editor does not, this can result in special characters that you enter in your templates (such as é) to appear as a diamond with a question mark inside in the browser. This also applies to your i18n translation files. diff --git a/guides/source/i18n.md b/guides/source/i18n.md index 5304ca4285..b248c7645a 100644 --- a/guides/source/i18n.md +++ b/guides/source/i18n.md @@ -174,7 +174,7 @@ end # in your /etc/hosts file to try this out locally def extract_locale_from_tld parsed_locale = request.host.split('.').last - I18n.available_locales.include?(parsed_locale.to_sym) ? parsed_locale : nil + I18n.available_locales.include?(parsed_locale.to_sym) ? parsed_locale : nil end ``` @@ -258,7 +258,7 @@ match '/:locale' => 'dashboard#index' Do take special care about the **order of your routes**, so this route declaration does not "eat" other ones. (You may want to add it directly before the `root :to` declaration.) -NOTE: Have a look at two plugins which simplify work with routes in this way: Sven Fuchs's [routing_filter](https://github.com/svenfuchs/routing-filter/tree/master and Raul Murciano's [translate_routes](https://github.com/raul/translate_routes/tree/master). +NOTE: Have a look at two plugins which simplify work with routes in this way: Sven Fuchs's [routing_filter](https://github.com/svenfuchs/routing-filter/tree/master) and Raul Murciano's [translate_routes](https://github.com/raul/translate_routes/tree/master). ### Setting the Locale from the Client Supplied Information @@ -417,7 +417,7 @@ So that would give you: ![rails i18n demo localized time to pirate](images/i18n/demo_localized_pirate.png) -TIP: Right now you might need to add some more date/time formats in order to make the I18n backend work as expected (at least for the 'pirate' locale). Of course, there's a great chance that somebody already did all the work by **translating Rails' defaults for your locale**. See the [rails-i18n repository at Github](https://github.com/svenfuchs/rails-i18n/tree/master/rails/locale) for an archive of various locale files. When you put such file(s) in `config/locales/` directory, they will automatically be ready for use. +TIP: Right now you might need to add some more date/time formats in order to make the I18n backend work as expected (at least for the 'pirate' locale). Of course, there's a great chance that somebody already did all the work by **translating Rails' defaults for your locale**. See the [rails-i18n repository at GitHub](https://github.com/svenfuchs/rails-i18n/tree/master/rails/locale) for an archive of various locale files. When you put such file(s) in `config/locales/` directory, they will automatically be ready for use. ### Inflection Rules For Other Locales @@ -837,6 +837,28 @@ en: NOTE: In order to use this helper, you need to install [DynamicForm](https://github.com/joelmoss/dynamic_form) gem by adding this line to your Gemfile: `gem 'dynamic_form'`. +### Translations for Action Mailer E-Mail Subjects + +If you don't pass a subject to the `mail` method, Action Mailer will try to find +it in your translations. The performed lookup will use the pattern +`<mailer_scope>.<action_name>.subject` to construct the key. + +```ruby +# user_mailer.rb +class UserMailer < ActionMailer::Base + def welcome(user) + #... + end +end +``` + +```yaml +en: + user_mailer: + welcome: + subject: "Welcome to Rails Guides!" +``` + ### Overview of Other Built-In Methods that Provide I18n Support Rails uses fixed strings and other localizations, such as format strings and other format information in a couple of helpers. Here's a brief overview. @@ -958,8 +980,8 @@ Resources * [rails-i18n.org](http://rails-i18n.org) - Homepage of the rails-i18n project. You can find lots of useful resources on the [wiki](http://rails-i18n.org/wiki). * [Google group: rails-i18n](http://groups.google.com/group/rails-i18n) - The project's mailing list. -* [Github: rails-i18n](https://github.com/svenfuchs/rails-i18n/tree/master) - Code repository for the rails-i18n project. Most importantly you can find lots of [example translations](https://github.com/svenfuchs/rails-i18n/tree/master/rails/locale) for Rails that should work for your application in most cases. -* [Github: i18n](https://github.com/svenfuchs/i18n/tree/master) - Code repository for the i18n gem. +* [GitHub: rails-i18n](https://github.com/svenfuchs/rails-i18n/tree/master) - Code repository for the rails-i18n project. Most importantly you can find lots of [example translations](https://github.com/svenfuchs/rails-i18n/tree/master/rails/locale) for Rails that should work for your application in most cases. +* [GitHub: i18n](https://github.com/svenfuchs/i18n/tree/master) - Code repository for the i18n gem. * [Lighthouse: rails-i18n](http://i18n.lighthouseapp.com/projects/14948-rails-i18n/overview) - Issue tracker for the rails-i18n project. * [Lighthouse: i18n](http://i18n.lighthouseapp.com/projects/14947-ruby-i18n/overview) - Issue tracker for the i18n gem. diff --git a/guides/source/initialization.md b/guides/source/initialization.md index 8ba5fa4601..738591659d 100644 --- a/guides/source/initialization.md +++ b/guides/source/initialization.md @@ -36,8 +36,8 @@ This file is as follows: ```ruby #!/usr/bin/env ruby -APP_PATH = File.expand_path('../../config/application', __FILE__) -require File.expand_path('../../config/boot', __FILE__) +APP_PATH = File.expand_path('../../config/application', __FILE__) +require File.expand_path('../../config/boot', __FILE__) require 'rails/commands' ``` @@ -59,35 +59,33 @@ dependencies of the application. `config/boot.rb` sets `ENV['BUNDLE_GEMFILE']` to the location of this file. If the Gemfile exists, `bundler/setup` is then required. -The gems that a Rails 4 application depends on are as follows: - -TODO: change these when the Rails 4 release is near. - -* abstract (1.0.0) -* actionmailer (4.0.0.beta) -* actionpack (4.0.0.beta) -* activemodel (4.0.0.beta) -* activerecord (4.0.0.beta) -* activesupport (4.0.0.beta) -* arel (2.0.7) -* builder (3.0.0) -* bundler (1.0.6) -* erubis (2.6.6) -* i18n (0.5.0) -* mail (2.2.12) -* mime-types (1.16) -* polyglot (0.3.1) -* rack (1.2.1) -* rack-cache (0.5.3) -* rack-mount (0.6.13) -* rack-test (0.5.6) -* rails (4.0.0.beta) -* railties (4.0.0.beta) -* rake (0.8.7) -* sqlite3-ruby (1.3.2) -* thor (0.14.6) -* treetop (1.4.9) -* tzinfo (0.3.23) +A standard Rails application depends on several gems, specifically: + +* abstract +* actionmailer +* actionpack +* activemodel +* activerecord +* activesupport +* arel +* builder +* bundler +* erubis +* i18n +* mail +* mime-types +* polyglot +* rack +* rack-cache +* rack-mount +* rack-test +* rails +* railties +* rake +* sqlite3-ruby +* thor +* treetop +* tzinfo ### `rails/commands.rb` @@ -116,7 +114,7 @@ If we used `s` rather than `server`, Rails will use the `aliases` defined in the ```ruby when 'server' - # Change to the application's path if there is no config.ru file in current dir. + # Change to the application's path if there is no config.ru file in current directory. # This allows us to run `rails server` from other directories, but still get # the main config.ru and properly set the tmp directory. Dir.chdir(File.expand_path('../../', APP_PATH)) unless File.exists?(File.expand_path("config.ru")) @@ -131,7 +129,7 @@ when 'server' end ``` -This file will change into the root of the directory (a path two directories back from `APP_PATH` which points at `config/application.rb`), but only if the `config.ru` file isn't found. This then requires `rails/commands/server` which sets up the `Rails::Server` class. +This file will change into the Rails root directory (a path two directories up from `APP_PATH` which points at `config/application.rb`), but only if the `config.ru` file isn't found. This then requires `rails/commands/server` which sets up the `Rails::Server` class. ```ruby require 'fileutils' @@ -147,11 +145,11 @@ module Rails ### `actionpack/lib/action_dispatch.rb` Action Dispatch is the routing component of the Rails framework. -It adds functionalities like routing, session, and common middlewares. +It adds functionality like routing, session, and common middlewares. ### `rails/commands/server.rb` -The `Rails::Server` class is defined in this file as inheriting from `Rack::Server`. When `Rails::Server.new` is called, this calls the `initialize` method in `rails/commands/server.rb`: +The `Rails::Server` class is defined in this file by inheriting from `Rack::Server`. When `Rails::Server.new` is called, this calls the `initialize` method in `rails/commands/server.rb`: ```ruby def initialize(*) @@ -266,7 +264,7 @@ def start url = "#{options[:SSLEnable] ? 'https' : 'http'}://#{options[:Host]}:#{options[:Port]}" puts "=> Booting #{ActiveSupport::Inflector.demodulize(server)}" puts "=> Rails #{Rails.version} application starting in #{Rails.env} on #{url}" - puts "=> Call with -d to detach" unless options[:daemonize] + puts "=> Run `rails server -h` for more startup options" trap(:INT) { exit } puts "=> Ctrl-C to shutdown server" unless options[:daemonize] @@ -375,7 +373,7 @@ The `options[:config]` value defaults to `config.ru` which contains this: ```ruby # This file is used by Rack-based servers to start the application. -require ::File.expand_path('../config/environment', __FILE__) +require ::File.expand_path('../config/environment', __FILE__) run <%= app_const %> ``` @@ -390,7 +388,7 @@ app = eval "Rack::Builder.new {( " + cfgfile + "\n )}.to_app", The `initialize` method of `Rack::Builder` will take the block here and execute it within an instance of `Rack::Builder`. This is where the majority of the initialization process of Rails happens. The `require` line for `config/environment.rb` in `config.ru` is the first to run: ```ruby -require ::File.expand_path('../config/environment', __FILE__) +require ::File.expand_path('../config/environment', __FILE__) ``` ### `config/environment.rb` @@ -441,14 +439,14 @@ inside each of those frameworks, but you're encouraged to try and explore them on your own. For now, just keep in mind that common functionality like Rails engines, -I18n and Rails configuration is all being defined here. +I18n and Rails configuration are all being defined here. ### Back to `config/environment.rb` When `config/application.rb` has finished loading Rails, and defined -your application namespace, you go back to `config/environment.rb`, -where your application is initialized. For example, if you application was called -`Blog`, here you would find `Blog::Application.initialize!`, which is +the application namespace, we go back to `config/environment.rb`, +where the application is initialized. For example, if the application was called +`Blog`, here we would find `Blog::Application.initialize!`, which is defined in `rails/application.rb` ### `railties/lib/rails/application.rb` @@ -548,7 +546,7 @@ def self.run(app, options={}) else server.register('/', Rack::Handler::Mongrel.new(app)) end - yield server if block_given? + yield server if block_given? server.run.join end ``` diff --git a/guides/source/kindle/KINDLE.md b/guides/source/kindle/KINDLE.md index 08937e053e..8c4fad18aa 100644 --- a/guides/source/kindle/KINDLE.md +++ b/guides/source/kindle/KINDLE.md @@ -10,7 +10,7 @@ ## Resources * [Stack Overflow: Kindle Periodical Format](http://stackoverflow.com/questions/5379565/kindle-periodical-format) - * Example Periodical [.ncx](https://gist.github.com/808c971ed087b839d462) and [.opf](https://gist.github.com/d6349aa8488eca2ee6d0) + * Example Periodical [.ncx](https://gist.github.com/mipearson/808c971ed087b839d462) and [.opf](https://gist.github.com/mipearson/d6349aa8488eca2ee6d0) * [Kindle Publishing Guidelines](http://kindlegen.s3.amazonaws.com/AmazonKindlePublishingGuidelines.pdf) * [KindleGen & Kindle Previewer](http://www.amazon.com/gp/feature.html?ie=UTF8&docId=1000234621) diff --git a/guides/source/layout.html.erb b/guides/source/layout.html.erb index 397dd62638..ef2bdf0753 100644 --- a/guides/source/layout.html.erb +++ b/guides/source/layout.html.erb @@ -102,10 +102,10 @@ </p> <p> If you see any typos or factual errors you are confident to - patch, please clone <%= link_to 'docrails', 'https://github.com/lifo/docrails' %> + patch, please clone <%= link_to 'docrails', 'https://github.com/rails/docrails' %> and push the change yourself. That branch of Rails has public write access. Commits are still reviewed, but that happens after you've submitted your - contribution. <%= link_to 'docrails', 'https://github.com/lifo/docrails' %> is + contribution. <%= link_to 'docrails', 'https://github.com/rails/docrails' %> is cross-merged with master periodically. </p> <p> diff --git a/guides/source/layouts_and_rendering.md b/guides/source/layouts_and_rendering.md index 339008ab9e..1ab841b137 100644 --- a/guides/source/layouts_and_rendering.md +++ b/guides/source/layouts_and_rendering.md @@ -1,7 +1,7 @@ Layouts and Rendering in Rails ============================== -This guide covers the basic layout features of Action Controller and Action View. By referring to this guide, you will be able to: +This guide covers the basic layout features of Action Controller and Action View. After reading this guide, you will know: @@ -283,8 +283,8 @@ Calls to the `render` method generally accept four options: * `:content_type` * `:layout` -* `:status` * `:location` +* `:status` ##### The `:content_type` Option @@ -310,25 +310,86 @@ You can also tell Rails to render with no layout at all: render layout: false ``` -##### The `:status` Option +##### The `:location` Option -Rails will automatically generate a response with the correct HTTP status code (in most cases, this is `200 OK`). You can use the `:status` option to change this: +You can use the `:location` option to set the HTTP `Location` header: ```ruby -render status: 500 -render status: :forbidden +render xml: photo, location: photo_url(photo) ``` -Rails understands both numeric and symbolic status codes. - -##### The `:location` Option +##### The `:status` Option -You can use the `:location` option to set the HTTP `Location` header: +Rails will automatically generate a response with the correct HTTP status code (in most cases, this is `200 OK`). You can use the `:status` option to change this: ```ruby -render xml: photo, location: photo_url(photo) +render status: 500 +render status: :forbidden ``` +Rails understands both numeric status codes and the corresponding symbols shown below. + +| Response Class | HTTP Status Code | Symbol | +| ------------------- | ---------------- | -------------------------------- | +| **Informational** | 100 | :continue | +| | 101 | :switching_protocols | +| | 102 | :processing | +| **Success** | 200 | :ok | +| | 201 | :created | +| | 202 | :accepted | +| | 203 | :non_authoritative_information | +| | 204 | :no_content | +| | 205 | :reset_content | +| | 206 | :partial_content | +| | 207 | :multi_status | +| | 208 | :already_reported | +| | 226 | :im_used | +| **Redirection** | 300 | :multiple_choices | +| | 301 | :moved_permanently | +| | 302 | :found | +| | 303 | :see_other | +| | 304 | :not_modified | +| | 305 | :use_proxy | +| | 306 | :reserved | +| | 307 | :temporary_redirect | +| | 308 | :permanent_redirect | +| **Client Error** | 400 | :bad_request | +| | 401 | :unauthorized | +| | 402 | :payment_required | +| | 403 | :forbidden | +| | 404 | :not_found | +| | 405 | :method_not_allowed | +| | 406 | :not_acceptable | +| | 407 | :proxy_authentication_required | +| | 408 | :request_timeout | +| | 409 | :conflict | +| | 410 | :gone | +| | 411 | :length_required | +| | 412 | :precondition_failed | +| | 413 | :request_entity_too_large | +| | 414 | :request_uri_too_long | +| | 415 | :unsupported_media_type | +| | 416 | :requested_range_not_satisfiable | +| | 417 | :expectation_failed | +| | 422 | :unprocessable_entity | +| | 423 | :locked | +| | 424 | :failed_dependency | +| | 426 | :upgrade_required | +| | 423 | :precondition_required | +| | 424 | :too_many_requests | +| | 426 | :request_header_fields_too_large | +| **Server Error** | 500 | :internal_server_error | +| | 501 | :not_implemented | +| | 502 | :bad_gateway | +| | 503 | :service_unavailable | +| | 504 | :gateway_timeout | +| | 505 | :http_version_not_supported | +| | 506 | :variant_also_negotiates | +| | 507 | :insufficient_storage | +| | 508 | :loop_detected | +| | 510 | :not_extended | +| | 511 | :network_authentication_required | + #### 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` (or `app/views/layouts/photos.builder`). If there is no such controller-specific layout, Rails will use `app/views/layouts/application.html.erb` or `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. @@ -363,7 +424,7 @@ You can use a symbol to defer the choice of layout until a request is processed: ```ruby class ProductsController < ApplicationController - layout "products_layout" + layout :products_layout def show @product = Product.find(params[:id]) @@ -568,7 +629,8 @@ def show @book = Book.find_by_id(params[:id]) if @book.nil? @books = Book.all - render "index", alert: "Your book was not found!" + flash[:alert] = "Your book was not found" + render "index" end end ``` @@ -577,7 +639,7 @@ This would detect that there are no books with the specified ID, populate the `@ ### Using `head` To Build Header-Only Responses -The `head` method can be used to send responses with only headers to the browser. It provides a more obvious alternative to calling `render :nothing`. The `head` method takes one parameter, which is interpreted as a hash of header names and values. For example, you can return only an error header: +The `head` method can be used to send responses with only headers to the browser. It provides a more obvious alternative to calling `render :nothing`. The `head` method accepts a number or symbol (see [reference table](#the-status-option)) representing a HTTP status code. The options argument is interpreted as a hash of header names and values. For example, you can return only an error header: ```ruby head :bad_request @@ -653,7 +715,7 @@ There are three tag options available for the `auto_discovery_link_tag`: * `:rel` specifies the `rel` value in the link. The default value is "alternate". * `:type` specifies an explicit MIME type. Rails will generate an appropriate MIME type automatically. -* `:title` specifies the title of the link. The default value is the uppercased `:type` value, for example, "ATOM" or "RSS". +* `:title` specifies the title of the link. The default value is the uppercase `:type` value, for example, "ATOM" or "RSS". #### Linking to JavaScript Files with the `javascript_include_tag` diff --git a/guides/source/migrations.md b/guides/source/migrations.md index cefbc3b829..eb0cfd9451 100644 --- a/guides/source/migrations.md +++ b/guides/source/migrations.md @@ -61,6 +61,10 @@ migrations are wrapped in a transaction. If the database does not support this then when a migration fails the parts of it that succeeded will not be rolled back. You will have to rollback the changes that were made by hand. +NOTE: There are certain queries that can't run inside a transaction. If your +adapter supports DDL transactions you can use `disable_ddl_transaction!` to +disable them for a single migration. + If you wish for a migration to do something that Active Record doesn't know how to reverse, you can use `reversible`: @@ -146,7 +150,25 @@ class AddPartNumberToProducts < ActiveRecord::Migration end ``` -Similarly, +If you'd like to add an index on the new column, you can do that as well: + +```bash +$ rails generate migration AddPartNumberToProducts part_number:string:index +``` + +will generate + +```ruby +class AddPartNumberToProducts < ActiveRecord::Migration + def change + add_column :products, :part_number, :string + add_index :products, :part_number + end +end +``` + + +Similarly, you can generate a migration to remove a column from the command line: ```bash $ rails generate migration RemovePartNumberFromProducts part_number:string @@ -179,6 +201,27 @@ class AddDetailsToProducts < ActiveRecord::Migration end ``` +If the migration name is of the form "CreateXXX" and is +followed by a list of column names and types then a migration creating the table +XXX with the columns listed will be generated. For example: + +```bash +$ rails generate migration CreateProducts name:string part_number:string +``` + +generates + +```ruby +class CreateProducts < ActiveRecord::Migration + def change + create_table :products do |t| + t.string :name + t.string :part_number + end + end +end +``` + As always, what has been generated for you is just a starting point. You can add or remove from it as you see fit by editing the `db/migrate/YYYYMMDDHHMMSS_add_details_to_products.rb` file. @@ -323,7 +366,15 @@ create_join_table :products, :categories which creates a `categories_products` table with two columns called `category_id` and `product_id`. These columns have the option `:null` set to -`false` by default. +`false` by default. This can be overridden by specifying the `:column_options` +option. + +```ruby +create_join_table :products, :categories, column_options: {null: true} +``` + +will create the `product_id` and `category_id` with the `:null` option as +`true`. You can pass the option `:table_name` with you want to customize the table name. For example, @@ -334,16 +385,16 @@ create_join_table :products, :categories, table_name: :categorization will create a `categorization` table. -By default, `create_join_table` will create two columns with no options, but -you can specify these options using the `:column_options` option. For example, +`create_join_table` also accepts a block, which you can use to add indices +(which are not created by default) or additional columns: ```ruby -create_join_table :products, :categories, column_options: {null: true} +create_join_table :products, :categories do |t| + t.index :products + t.index :categories +end ``` -will create the `product_id` and `category_id` with the `:null` option as -`true`. - ### Changing Tables A close cousin of `create_table` is `change_table`, used for changing existing @@ -395,7 +446,7 @@ definitions: * `create_table` * `create_join_table` * `drop_table` (must supply a block) -* `drop_join_table` (must supply a block) +* `drop_join_table` (must supply a block) * `remove_timestamps` * `rename_column` * `rename_index` @@ -444,7 +495,7 @@ class ExampleMigration < ActiveRecord::Migration end ``` -Using `reversible` will insure that the instructions are executed in the +Using `reversible` will ensure that the instructions are executed in the right order too. If the previous example migration is reverted, the `down` block will be run after the `home_page_url` column is removed and right before the table `products` is dropped. @@ -796,10 +847,10 @@ end ``` ```ruby -# app/model/product.rb +# app/models/product.rb class Product < ActiveRecord::Base - validates :flag, presence: true + validates :flag, inclusion: { in: [true, false] } end ``` @@ -821,10 +872,11 @@ end ``` ```ruby -# app/model/product.rb +# app/models/product.rb class Product < ActiveRecord::Base - validates :flag, :fuzz, presence: true + validates :flag, inclusion: { in: [true, false] } + validates :fuzz, presence: true end ``` @@ -1011,8 +1063,8 @@ with foreign key constraints in the database. Although Active Record does not provide any tools for working directly with such features, the `execute` method can be used to execute arbitrary SQL. You -could also use some gem like -[foreigner](https://github.com/matthuhiggins/foreigner) which add foreign key +can also use a gem like +[foreigner](https://github.com/matthuhiggins/foreigner) which adds foreign key support to Active Record (including support for dumping foreign keys in `db/schema.rb`). diff --git a/guides/source/rails_application_templates.md b/guides/source/rails_application_templates.md index 77138d8871..f8062a1f7c 100644 --- a/guides/source/rails_application_templates.md +++ b/guides/source/rails_application_templates.md @@ -13,7 +13,7 @@ After reading this guide, you will know: Usage ----- -To apply a template, you need to provide the Rails generator with the location of the template you wish to apply, using -m option. This can either be path to a file or a URL. +To apply a template, you need to provide the Rails generator with the location of the template you wish to apply using the -m option. This can either be a path to a file or a URL. ```bash $ rails new blog -m ~/template.rb @@ -30,7 +30,7 @@ $ rake rails:template LOCATION=http://example.com/template.rb Template API ------------ -Rails templates API is very self explanatory and easy to understand. Here's an example of a typical Rails template: +The Rails templates API is easy to understand. Here's an example of a typical Rails template: ```ruby # template.rb @@ -43,7 +43,7 @@ git add: "." git commit: %Q{ -m 'Initial commit' } ``` -The following sections outlines the primary methods provided by the API: +The following sections outline the primary methods provided by the API: ### gem(*args) @@ -66,7 +66,7 @@ bundle install Wraps gem entries inside a group. -For example, if you want to load `rspec-rails` only in `development` and `test` group: +For example, if you want to load `rspec-rails` only in the `development` and `test` groups: ```ruby gem_group :development, :test do @@ -91,7 +91,7 @@ Adds a line inside the `Application` class for `config/application.rb`. If `options[:env]` is specified, the line is appended to the corresponding file in `config/environments`. ```ruby -environment 'config.action_mailer.default_url_options = {host: 'http://yourwebsite.example.com'}, env: 'production' +environment 'config.action_mailer.default_url_options = {host: "http://yourwebsite.example.com"}', env: 'production' ``` A block can be used in place of the `data` argument. @@ -100,7 +100,7 @@ A block can be used in place of the `data` argument. Adds an initializer to the generated application’s `config/initializers` directory. -Lets say you like using `Object#not_nil?` and `Object#not_blank?`: +Let's say you like using `Object#not_nil?` and `Object#not_blank?`: ```ruby initializer 'bloatlol.rb', <<-CODE @@ -116,9 +116,9 @@ initializer 'bloatlol.rb', <<-CODE CODE ``` -Similarly `lib()` creates a file in the `lib/` directory and `vendor()` creates a file in the `vendor/` directory. +Similarly, `lib()` creates a file in the `lib/` directory and `vendor()` creates a file in the `vendor/` directory. -There is even `file()`, which accepts a relative path from `Rails.root` and creates all the directories/file needed: +There is even `file()`, which accepts a relative path from `Rails.root` and creates all the directories/files needed: ```ruby file 'app/components/foo.rb', <<-CODE @@ -127,7 +127,7 @@ file 'app/components/foo.rb', <<-CODE CODE ``` -That’ll create `app/components` directory and put `foo.rb` in there. +That’ll create the `app/components` directory and put `foo.rb` in there. ### rakefile(filename, data = nil, &block) @@ -179,7 +179,7 @@ rake "db:migrate", env: 'production' ### route(routing_code) -Adds a routing entry to the `config/routes.rb` file. In above steps, we generated a person scaffold and also removed `README.rdoc`. Now to make `PeopleController#index` as the default page for the application: +Adds a routing entry to the `config/routes.rb` file. In the steps above, we generated a person scaffold and also removed `README.rdoc`. Now, to make `PeopleController#index` the default page for the application: ```ruby route "root to: 'person#index'" @@ -197,7 +197,7 @@ end ### ask(question) -`ask()` gives you a chance to get some feedback from the user and use it in your templates. Lets say you want your user to name the new shiny library you’re adding: +`ask()` gives you a chance to get some feedback from the user and use it in your templates. Let's say you want your user to name the new shiny library you’re adding: ```ruby lib_name = ask("What do you want to call the shiny library ?") @@ -211,7 +211,7 @@ CODE ### yes?(question) or no?(question) -These methods let you ask questions from templates and decide the flow based on the user’s answer. Lets say you want to freeze rails only if the user want to: +These methods let you ask questions from templates and decide the flow based on the user’s answer. Let's say you want to freeze rails only if the user wants to: ```ruby rake("rails:freeze:gems") if yes?("Freeze rails gems?") diff --git a/guides/source/rails_on_rack.md b/guides/source/rails_on_rack.md index d8477d89e3..d144fba762 100644 --- a/guides/source/rails_on_rack.md +++ b/guides/source/rails_on_rack.md @@ -28,7 +28,10 @@ Rails on Rack ### Rails Application's Rack Object -`ApplicationName::Application` is the primary Rack application object of a Rails application. Any Rack compliant web server should be using `ApplicationName::Application` object to serve a Rails application. +`ApplicationName::Application` is the primary Rack application object of a Rails +application. Any Rack compliant web server should be using +`ApplicationName::Application` object to serve a Rails +application. `Rails.application` refers to the same application object. ### `rails server` @@ -79,11 +82,11 @@ To use `rackup` instead of Rails' `rails server`, you can put the following insi ```ruby # Rails.root/config.ru -require "config/environment" +require ::File.expand_path('../config/environment', __FILE__) use Rack::Debugger use Rack::ContentLength -run ApplicationName::Application +run Rails.application ``` And start the server: @@ -101,7 +104,7 @@ $ rackup --help Action Dispatcher Middleware Stack ---------------------------------- -Many of Action Dispatchers's internal components are implemented as Rack middlewares. `Rails::Application` uses `ActionDispatch::MiddlewareStack` to combine various internal and external middlewares to form a complete Rails Rack application. +Many of Action Dispatcher's internal components are implemented as Rack middlewares. `Rails::Application` uses `ActionDispatch::MiddlewareStack` to combine various internal and external middlewares to form a complete Rails Rack application. NOTE: `ActionDispatch::MiddlewareStack` is Rails equivalent of `Rack::Builder`, but built for better flexibility and more features to meet Rails' requirements. @@ -128,6 +131,7 @@ use ActionDispatch::DebugExceptions use ActionDispatch::RemoteIp use ActionDispatch::Reloader use ActionDispatch::Callbacks +use ActiveRecord::Migration::CheckPending use ActiveRecord::ConnectionAdapters::ConnectionManagement use ActiveRecord::QueryCache use ActionDispatch::Cookies @@ -269,6 +273,10 @@ Much of Action Controller's functionality is implemented as Middlewares. The fol * Runs the prepare callbacks before serving the request. + **`ActiveRecord::Migration::CheckPending`** + +* Checks pending migrations and raises `ActiveRecord::PendingMigrationError` if any migrations are pending. + **`ActiveRecord::ConnectionAdapters::ConnectionManagement`** * Cleans active connections after each request, unless the `rack.test` key in the request environment is set to `true`. @@ -324,7 +332,7 @@ config.middleware.clear ```ruby # config.ru use MyOwnStackFromScratch -run ApplicationName::Application +run Rails.application ``` Resources @@ -332,7 +340,7 @@ Resources ### Learning Rack -* [Official Rack Website](http://rack.github.com) +* [Official Rack Website](http://rack.github.io) * [Introducing Rack](http://chneukirchen.org/blog/archive/2007/02/introducing-rack.html) * [Ruby on Rack #1 - Hello Rack!](http://m.onkey.org/ruby-on-rack-1-hello-rack) * [Ruby on Rack #2 - The Builder](http://m.onkey.org/ruby-on-rack-2-the-builder) diff --git a/guides/source/routing.md b/guides/source/routing.md index 4614169653..076b9dd176 100644 --- a/guides/source/routing.md +++ b/guides/source/routing.md @@ -36,7 +36,7 @@ the request is dispatched to the `patients` controller's `show` action with `{ i ### Generating Paths and URLs from Code -You can also generate paths and URLs. If the route above is modified to be: +You can also generate paths and URLs. If the route above is modified to be: ```ruby get '/patients/:id', to: 'patients#show', as: 'patient' @@ -138,6 +138,12 @@ Sometimes, you have a resource that clients always look up without referencing a get 'profile', to: 'users#show' ``` +Passing a `String` to `match` will expect a `controller#action` format, while passing a `Symbol` will map directly to an action: + +```ruby +get 'profile', to: :show +``` + This resourceful route: ```ruby @@ -155,7 +161,7 @@ creates six different routes in your application, all mapping to the `Geocoders` | PATCH/PUT | /geocoder | update | update the one and only geocoder resource | | DELETE | /geocoder | destroy | delete the geocoder resource | -NOTE: Because you might want to use the same controller for a singular route (`/account`) and a plural route (`/accounts/45`), singular resources map to plural controllers. +NOTE: Because you might want to use the same controller for a singular route (`/account`) and a plural route (`/accounts/45`), singular resources map to plural controllers. So that, for example, `resource :photo` and `resources :photos` creates both singular and plural routes that map to the same controller (`PhotosController`). A singular resourceful route generates these helpers: @@ -530,7 +536,7 @@ In particular, simple routing makes it very easy to map legacy URLs to new Rails ### Bound Parameters -When you set up a regular route, you supply a series of symbols that Rails maps to parts of an incoming HTTP request. Two of these symbols are special: `:controller` maps to the name of a controller in your application, and `:action` maps to the name of an action within that controller. For example, consider one of the default Rails routes: +When you set up a regular route, you supply a series of symbols that Rails maps to parts of an incoming HTTP request. Two of these symbols are special: `:controller` maps to the name of a controller in your application, and `:action` maps to the name of an action within that controller. For example, consider this route: ```ruby get ':controller(/:action(/:id))' @@ -797,6 +803,16 @@ You should put the `root` route at the top of the file, because it is the most p NOTE: The `root` route only routes `GET` requests to the action. +You can also use root inside namespaces and scopes as well. For example: + +```ruby +namespace :admin do + root to: "admin#index" +end + +root to: "home#index" +``` + ### Unicode character routes You can specify unicode character routes directly. For example: @@ -840,8 +856,8 @@ resources :user_permissions, controller: 'admin/user_permissions' This will route to the `Admin::UserPermissions` controller. -NOTE: Only the directory notation is supported. specifying the -controller with ruby constant notation (eg. `:controller => +NOTE: Only the directory notation is supported. Specifying the +controller with Ruby constant notation (eg. `:controller => 'Admin::UserPermissions'`) can lead to routing problems and results in a warning. diff --git a/guides/source/ruby_on_rails_guides_guidelines.md b/guides/source/ruby_on_rails_guides_guidelines.md index a78711f4b2..5564b0648b 100644 --- a/guides/source/ruby_on_rails_guides_guidelines.md +++ b/guides/source/ruby_on_rails_guides_guidelines.md @@ -63,9 +63,13 @@ Those guidelines apply also to guides. HTML Guides ----------- +Before generating the guides, make sure that you have the latest version of Bundler installed on your system. As of this writing, you must install Bundler 1.3.5 on your device. + +To install the latest version of Bundler, simply run the `gem install bundler` command + ### Generation -To generate all the guides, just `cd` into the **`guides`** directory, run `bundle install` and execute: +To generate all the guides, just `cd` into the `guides` directory, run `bundle install` and execute: ``` bundle exec rake guides:generate diff --git a/guides/source/security.md b/guides/source/security.md index 769bd130be..ad0546810d 100644 --- a/guides/source/security.md +++ b/guides/source/security.md @@ -1,4 +1,4 @@ -Ruby On Rails Security Guide +Ruby on Rails Security Guide ============================ This manual describes common security problems in web applications and how to avoid them with Rails. @@ -58,7 +58,7 @@ WARNING: _Stealing a user's session id lets an attacker use the web application Many web applications have an authentication system: a user provides a user name and password, the web application checks them and stores the corresponding user id in the session hash. From now on, the session is valid. On every request the application will load the user, identified by the user id in the session, without the need for new authentication. The session id in the cookie identifies the session. -Hence, the cookie serves as temporary authentication for the web application. Everyone who seizes a cookie from someone else, may use the web application as this user – with possibly severe consequences. Here are some ways to hijack a session, and their countermeasures: +Hence, the cookie serves as temporary authentication for the web application. Anyone who seizes a cookie from someone else, may use the web application as this user – with possibly severe consequences. Here are some ways to hijack a session, and their countermeasures: * Sniff the cookie in an insecure network. A wireless LAN can be an example of such a network. In an unencrypted wireless LAN it is especially easy to listen to the traffic of all connected clients. This is one more reason not to work from a coffee shop. For the web application builder this means to _provide a secure connection over SSL_. In Rails 3.1 and later, this could be accomplished by always forcing SSL connection in your application config file: @@ -268,7 +268,7 @@ def legacy 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: +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 be exploited by an attacker if he includes a host key in the URL: ``` http://www.example.com/site/legacy?param1=xy¶m2=23&host=www.attacker.com @@ -346,13 +346,13 @@ Intranet and administration interfaces are popular attack targets, because they In 2007 there was the first tailor-made trojan which stole information from an Intranet, namely the "Monster for employers" web site of Monster.com, an online recruitment web application. Tailor-made Trojans are very rare, so far, and the risk is quite low, but it is certainly a possibility and an example of how the security of the client host is important, too. However, the highest threat to Intranet and Admin applications are XSS and CSRF.
-**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. +**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. Refer to the Injection section for countermeasures against XSS. It is _recommended to use the SafeErb plugin_ also in an Intranet or administration 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. +**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.h-online.com/security/Symantec-reports-first-active-attack-on-a-DSL-router--/news/102352). 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. @@ -432,7 +432,7 @@ Depending on your web application, there may be more ways to hijack the user's a INFO: _A CAPTCHA is a challenge-response test to determine that the response is not generated by a computer. It is often used to protect comment forms from automatic spam bots by asking the user to type the letters of a distorted image. The idea of a negative CAPTCHA is not for a user to prove that he is human, but reveal that a robot is a robot._ -But not only spam robots (bots) are a problem, but also automatic login bots. A popular CAPTCHA API is [reCAPTCHA](http://recaptcha.net/) which displays two distorted images of words from old books. It also adds an angled line, rather than a distorted background and high levels of warping on the text as earlier CAPTCHAs did, because the latter were broken. As a bonus, using reCAPTCHA helps to digitize old books. [ReCAPTCHA](http://ambethia.com/recaptcha/) is also a Rails plug-in with the same name as the API. +But not only spam robots (bots) are a problem, but also automatic login bots. A popular CAPTCHA API is [reCAPTCHA](http://recaptcha.net/) which displays two distorted images of words from old books. It also adds an angled line, rather than a distorted background and high levels of warping on the text as earlier CAPTCHAs did, because the latter were broken. As a bonus, using reCAPTCHA helps to digitize old books. [ReCAPTCHA](https://github.com/ambethia/recaptcha/) is also a Rails plug-in with the same name as the API. You will get two keys from the API, a public and a private key, which you have to put into your Rails environment. After that you can use the recaptcha_tags method in the view, and the verify_recaptcha method in the controller. Verify_recaptcha will return false if the validation fails. The problem with CAPTCHAs is, they are annoying. Additionally, some visually impaired users have found certain kinds of distorted CAPTCHAs difficult to read. The idea of negative CAPTCHAs is not to ask a user to proof that he is human, but reveal that a spam robot is a bot. @@ -942,7 +942,7 @@ Or you can remove them. config.action_dispatch.default_headers.clear ``` -Here is the list of common headers: +Here is a list of common headers: * X-Frame-Options _'SAMEORIGIN' in Rails by default_ - allow framing on same domain. Set it to 'DENY' to deny framing at all or 'ALLOWALL' if you want to allow framing for all website. diff --git a/guides/source/testing.md b/guides/source/testing.md index 540197e6e7..416a8b592f 100644 --- a/guides/source/testing.md +++ b/guides/source/testing.md @@ -1,8 +1,7 @@ A Guide to Testing Rails Applications ===================================== -This guide covers built-in mechanisms offered by Rails to test your -application. +This guide covers built-in mechanisms in Rails for testing your application. After reading this guide, you will know: @@ -38,11 +37,11 @@ Rails creates a `test` folder for you as soon as you create a Rails project usin ```bash $ ls -F test - -fixtures/ functional/ integration/ test_helper.rb unit/ +controllers/ helpers/ mailers/ test_helper.rb +fixtures/ integration/ models/ ``` -The `unit` directory is meant to hold tests for your models, the `functional` directory is meant to hold tests for your controllers and the `integration` directory is meant to hold tests that involve any number of controllers interacting. +The `models` directory is meant to hold tests for your models, the `controllers` directory is meant to hold tests for your controllers and the `integration` directory is meant to hold tests that involve any number of controllers interacting. Fixtures are a way of organizing test data; they reside in the `fixtures` folder. @@ -65,7 +64,7 @@ YAML-formatted fixtures are a very human-friendly way to describe your sample da Here's a sample YAML fixture file: ```yaml -# lo & behold! I am a YAML comment! +# lo & behold! I am a YAML comment! david: name: David Heinemeier Hansson birthday: 1979-10-15 @@ -86,8 +85,8 @@ ERB allows you to embed Ruby code within templates. The YAML fixture format is p ```erb <% 1000.times do |n| %> user_<%= n %>: - username: <%= "user%03d" % n %> - email: <%= "user%03d@example.com" % n %> + username: <%= "user#{n}" %> + email: <%= "user#{n}@example.com" %> <% end %> ``` @@ -140,10 +139,9 @@ The default test stub in `test/models/post_test.rb` looks like this: require 'test_helper' class PostTest < ActiveSupport::TestCase - # Replace this with your real tests. - test "the truth" do - assert true - end + # test "the truth" do + # assert true + # end end ``` @@ -161,9 +159,10 @@ class PostTest < ActiveSupport::TestCase The `PostTest` class defines a _test case_ because it inherits from `ActiveSupport::TestCase`. `PostTest` thus has all the methods available from `ActiveSupport::TestCase`. You'll see those methods a little later in this guide. -Any method defined within a `Test::Unit` test case that begins with `test` (case sensitive) is simply called a test. So, `test_password`, `test_valid_password` and `testValidPassword` all are legal test names and are run automatically when the test case is run. +Any method defined within a class inherited from `MiniTest::Unit::TestCase` +(which is the superclass of `ActiveSupport::TestCase`) that begins with `test` (case sensitive) is simply called a test. So, `test_password`, `test_valid_password` and `testValidPassword` all are legal test names and are run automatically when the test case is run. -Rails adds a `test` method that takes a test name and a block. It generates a normal `Test::Unit` test with method names prefixed with `test_`. So, +Rails adds a `test` method that takes a test name and a block. It generates a normal `MiniTest::Unit` test with method names prefixed with `test_`. So, ```ruby test "the truth" do @@ -224,34 +223,30 @@ TIP: You can see all these rake tasks and their descriptions by running `rake -- ### Running Tests -Running a test is as simple as invoking the file containing the test cases through Ruby: +Running a test is as simple as invoking the file containing the test cases through `rake test` command. ```bash -$ ruby -Itest test/models/post_test.rb - -Loaded suite models/post_test -Started +$ rake test test/models/post_test.rb . -Finished in 0.023513 seconds. -1 tests, 1 assertions, 0 failures, 0 errors -``` +Finished tests in 0.009262s, 107.9680 tests/s, 107.9680 assertions/s. -This will run all the test methods from the test case. Note that `test_helper.rb` is in the `test` directory, hence this directory needs to be added to the load path using the `-I` switch. +1 tests, 1 assertions, 0 failures, 0 errors, 0 skips +``` -You can also run a particular test method from the test case by using the `-n` switch with the `test method name`. +You can also run a particular test method from the test case by running the test and providing the `test method name`. ```bash -$ ruby -Itest test/models/post_test.rb -n test_the_truth - -Loaded suite models/post_test -Started +$ rake test test/models/post_test.rb test_the_truth . -Finished in 0.023513 seconds. -1 tests, 1 assertions, 0 failures, 0 errors +Finished tests in 0.009064s, 110.3266 tests/s, 110.3266 assertions/s. + +1 tests, 1 assertions, 0 failures, 0 errors, 0 skips ``` +This will run all test methods from the test case. Note that `test_helper.rb` is in the `test` directory, hence this directory needs to be added to the load path using the `-I` switch. + The `.` (dot) above indicates a passing test. When a test fails you see an `F`; when a test throws an error you see an `E` in its place. The last line of the output is the summary. To see how a test failure is reported, you can add a failing test to the `post_test.rb` test case. @@ -266,17 +261,16 @@ end Let us run this newly added test. ```bash -$ ruby unit/post_test.rb -n test_should_not_save_post_without_title -Loaded suite -e -Started +$ rake test test/models/post_test.rb test_should_not_save_post_without_title F -Finished in 0.102072 seconds. + +Finished tests in 0.044632s, 22.4054 tests/s, 22.4054 assertions/s. 1) Failure: -test_should_not_save_post_without_title(PostTest) [/test/models/post_test.rb:6]: -<false> is not true. +test_should_not_save_post_without_title(PostTest) [test/models/post_test.rb:6]: +Failed assertion, no message given. -1 tests, 1 assertions, 1 failures, 0 errors +1 tests, 1 assertions, 1 failures, 0 errors, 0 skips ``` In the output, `F` denotes a failure. You can see the corresponding trace shown under `1)` along with the name of the failing test. The next few lines contain the stack trace followed by a message which mentions the actual value and the expected value by the assertion. The default assertion messages provide just enough information to help pinpoint the error. To make the assertion failure message more readable, every assertion provides an optional message parameter, as shown here: @@ -292,9 +286,8 @@ Running this test shows the friendlier assertion message: ```bash 1) Failure: -test_should_not_save_post_without_title(PostTest) [/test/models/post_test.rb:6]: -Saved the post without a title. -<false> is not true. +test_should_not_save_post_without_title(PostTest) [test/models/post_test.rb:6]: +Saved the post without a title ``` Now to get this test to pass we can add a model level validation for the _title_ field. @@ -308,13 +301,12 @@ end Now the test should pass. Let us verify by running the test again: ```bash -$ ruby unit/post_test.rb -n test_should_not_save_post_without_title -Loaded suite unit/post_test -Started +$ rake test test/models/post_test.rb test_should_not_save_post_without_title . -Finished in 0.193608 seconds. -1 tests, 1 assertions, 0 failures, 0 errors +Finished tests in 0.047721s, 20.9551 tests/s, 20.9551 assertions/s. + +1 tests, 1 assertions, 0 failures, 0 errors, 0 skips ``` Now, if you noticed, we first wrote a test which fails for a desired functionality, then we wrote some code which adds the functionality and finally we ensured that our test passes. This approach to software development is referred to as _Test-Driven Development_ (TDD). @@ -334,18 +326,17 @@ end Now you can see even more output in the console from running the tests: ```bash -$ ruby unit/post_test.rb -n test_should_report_error -Loaded suite -e -Started +$ rake test test/models/post_test.rb test_should_report_error E -Finished in 0.082603 seconds. + +Finished tests in 0.030974s, 32.2851 tests/s, 0.0000 assertions/s. 1) Error: test_should_report_error(PostTest): -NameError: undefined local variable or method `some_undefined_variable' for #<PostTest:0x249d354> - /test/models/post_test.rb:6:in `test_should_report_error' +NameError: undefined local variable or method `some_undefined_variable' for #<PostTest:0x007fe32e24afe0> + test/models/post_test.rb:10:in `block in <class:PostTest>' -1 tests, 0 assertions, 0 failures, 1 errors +1 tests, 0 assertions, 0 failures, 1 errors, 0 skips ``` Notice the 'E' in the output. It denotes a test with error. @@ -356,31 +347,38 @@ NOTE: The execution of each test method stops as soon as any error or an asserti Ideally, you would like to include a test for everything which could possibly break. It's a good practice to have at least one test for each of your validations and at least one test for every method in your model. -### Assertions Available +### Available Assertions By now you've caught a glimpse of some of the assertions that are available. Assertions are the worker bees of testing. They are the ones that actually perform the checks to ensure that things are going as planned. -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 default 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. +There are a bunch of different types of assertions you can use. +Here's an extract of the assertions you can use with `minitest`, the default 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 | | ---------------------------------------------------------------- | ------- | -| `assert( boolean, [msg] )` | Ensures that the object/expression is true.| +| `assert( test, [msg] )` | Ensures that `test` is true.| +| `refute( test, [msg] )` | Ensures that `test` is false.| | `assert_equal( expected, actual, [msg] )` | Ensures that `expected == actual` is true.| -| `assert_not_equal( expected, actual, [msg] )` | Ensures that `expected != actual` is true.| +| `refute_equal( expected, actual, [msg] )` | Ensures that `expected != actual` is true.| | `assert_same( expected, actual, [msg] )` | Ensures that `expected.equal?(actual)` is true.| -| `assert_not_same( expected, actual, [msg] )` | Ensures that `!expected.equal?(actual)` is true.| +| `refute_same( expected, actual, [msg] )` | Ensures that `expected.equal?(actual)` is false.| | `assert_nil( obj, [msg] )` | Ensures that `obj.nil?` is true.| -| `assert_not_nil( obj, [msg] )` | Ensures that `!obj.nil?` is true.| +| `refute_nil( obj, [msg] )` | Ensures that `obj.nil?` is false.| | `assert_match( regexp, string, [msg] )` | Ensures that a string matches the regular expression.| -| `assert_no_match( regexp, string, [msg] )` | Ensures that a string doesn't match the regular expression.| -| `assert_in_delta( expecting, actual, delta, [msg] )` | Ensures that the numbers `expecting` and `actual` are within `delta` of each other.| +| `refute_match( regexp, string, [msg] )` | Ensures that a string doesn't match the regular expression.| +| `assert_in_delta( expecting, actual, [delta], [msg] )` | Ensures that the numbers `expected` and `actual` are within `delta` of each other.| +| `refute_in_delta( expecting, actual, [delta], [msg] )` | Ensures that the numbers `expected` and `actual` are not within `delta` of each other.| | `assert_throws( symbol, [msg] ) { block }` | Ensures that the given block throws the symbol.| -| `assert_raise( exception1, exception2, ... ) { block }` | Ensures that the given block raises one of the given exceptions.| +| `assert_raises( exception1, exception2, ... ) { block }` | Ensures that the given block raises one of the given exceptions.| | `assert_nothing_raised( exception1, exception2, ... ) { block }` | Ensures that the given block doesn't raise one of the given exceptions.| -| `assert_instance_of( class, obj, [msg] )` | Ensures that `obj` is of the `class` type.| +| `assert_instance_of( class, obj, [msg] )` | Ensures that `obj` is an instance of `class`.| +| `refute_instance_of( class, obj, [msg] )` | Ensures that `obj` is not an instance of `class`.| | `assert_kind_of( class, obj, [msg] )` | Ensures that `obj` is or descends from `class`.| -| `assert_respond_to( obj, symbol, [msg] )` | Ensures that `obj` has a method called `symbol`.| -| `assert_operator( obj1, operator, obj2, [msg] )` | Ensures that `obj1.operator(obj2)` is true.| +| `refute_kind_of( class, obj, [msg] )` | Ensures that `obj` is not an instance of `class` and is not descending from it.| +| `assert_respond_to( obj, symbol, [msg] )` | Ensures that `obj` responds to `symbol`.| +| `refute_respond_to( obj, symbol, [msg] )` | Ensures that `obj` does not respond to `symbol`.| +| `assert_operator( obj1, operator, [obj2], [msg] )` | Ensures that `obj1.operator(obj2)` is true.| +| `refute_operator( obj1, operator, [obj2], [msg] )` | Ensures that `obj1.operator(obj2)` is false.| | `assert_send( array, [msg] )` | Ensures that executing the method listed in `array[1]` on the object in `array[0]` with the parameters of `array[2 and up]` is true. This one is weird eh?| | `flunk( [msg] )` | Ensures failure. This is useful to explicitly mark a test that isn't finished yet.| @@ -398,7 +396,7 @@ Rails adds some custom assertions of its own to the `test/unit` framework: | `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.| | `assert_recognizes(expected_options, path, extras={}, message=nil)` | Asserts that the routing of the given path was handled correctly and that the parsed options (given in the expected_options hash) match path. Basically, it asserts that Rails recognizes the route given by expected_options.| | `assert_generates(expected_path, options, defaults={}, extras = {}, message=nil)` | Asserts that the provided options can be used to generate the provided path. This is the inverse of assert_recognizes. The extras parameter is used to tell the request the names and values of additional request parameters that would be in a query string. The message parameter allows you to specify a custom error message for assertion failures.| -| `assert_response(type, message = nil)` | Asserts that the response comes with a specific status code. You can specify `:success` to indicate 200-299, `:redirect` to indicate 300-399, `:missing` to indicate 404, or `:error` to match the 500-599 range| +| `assert_response(type, message = nil)` | Asserts that the response comes with a specific status code. You can specify `:success` to indicate 200-299, `:redirect` to indicate 300-399, `:missing` to indicate 404, or `:error` to match the 500-599 range| | `assert_redirected_to(options = {}, message=nil)` | Assert that the redirection options passed in match those of the redirect called in the latest action. This match can be partial, such that `assert_redirected_to(controller: "weblog")` will also match the redirection of `redirect_to(controller: "weblog", action: "show")` and so on.| | `assert_template(expected = nil, message=nil)` | Asserts that the request was rendered with the appropriate template file.| @@ -485,7 +483,7 @@ NOTE: Functional tests do not verify whether the specified request type should b ### The Four Hashes of the Apocalypse -After a request has been made by using one of the 5 methods (`get`, `post`, etc.) and processed, you will have 4 Hash objects ready for use: +After a request has been made using one of the 6 methods (`get`, `post`, etc.) and processed, you will have 4 Hash objects ready for use: * `assigns` - Any objects that are stored as instance variables in actions for use in views. * `cookies` - Any cookies that are set. @@ -511,6 +509,21 @@ You also have access to three instance variables in your functional tests: * `@request` - The request * `@response` - The response +### Setting Headers and CGI variables + +Headers and cgi variables can be set directly on the `@request` +instance variable: + +```ruby +# setting a HTTP Header +@request.headers["Accepts"] = "text/plain, text/html" +get :index # simulate the request with custom header + +# setting a CGI variable +@request.headers["HTTP_REFERER"] = "http://example.com/home" +post :create # simulate the request with custom env variable +``` + ### Testing Templates and Layouts If you want to make sure that the response rendered the correct template and layout, you can use the `assert_template` @@ -609,11 +622,11 @@ The `assert_select` assertion is quite powerful. For more advanced usage, refer There are more assertions that are primarily used in testing views: -| Assertion | Purpose | -| ---------------------------------------------------------- | ------- | -| `assert_select_email` | Allows you to make assertions on the body of an e-mail. | -| `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.| -| `css_select(selector)` or `css_select(element, selector)` | Returns an array of all the elements selected by the _selector_. In the second variant it first matches the base _element_ and tries to match the _selector_ expression on any of its children. If there are no matches both variants return an empty array.| +| Assertion | Purpose | +| --------------------------------------------------------- | ------- | +| `assert_select_email` | Allows you to make assertions on the body of an e-mail. | +| `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.| +| `css_select(selector)` or `css_select(element, selector)` | Returns an array of all the elements selected by the _selector_. In the second variant it first matches the base _element_ and tries to match the _selector_ expression on any of its children. If there are no matches both variants return an empty array.| Here's an example of using `assert_select_email`: @@ -642,12 +655,9 @@ Here's what a freshly-generated integration test looks like: require 'test_helper' class UserFlowsTest < ActionDispatch::IntegrationTest - fixtures :all - - # Replace this with your real tests. - test "the truth" do - assert true - end + # test "the truth" do + # assert true + # end end ``` @@ -688,9 +698,9 @@ class UserFlowsTest < ActionDispatch::IntegrationTest get "/login" assert_response :success - post_via_redirect "/login", username: users(:avs).username, password: users(:avs).password + post_via_redirect "/login", username: users(:david).username, password: users(:david).password assert_equal '/welcome', path - assert_equal 'Welcome avs!', flash[:notice] + assert_equal 'Welcome david!', flash[:notice] https!(false) get "/posts/all" @@ -712,17 +722,17 @@ class UserFlowsTest < ActionDispatch::IntegrationTest test "login and browse site" do - # User avs logs in - avs = login(:avs) + # User david logs in + david = login(:david) # User guest logs in guest = login(:guest) # Both are now available in different sessions - assert_equal 'Welcome avs!', avs.flash[:notice] + assert_equal 'Welcome david!', david.flash[:notice] assert_equal 'Welcome guest!', guest.flash[:notice] - # User avs can browse site - avs.browses_site + # User david can browse site + david.browses_site # User guest can browse site as well guest.browses_site @@ -755,23 +765,21 @@ end 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 Rails project. +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 commands to help in testing. The table below lists all commands that come along in the default Rakefile when you initiate a Rails project. -| 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:controllers` | Runs all the controller tests from `test/controllers`| -| `rake test:functionals` | Runs all the functional tests from `test/controllers`, `test/mailers`, and `test/functional`| -| `rake test:helpers` | Runs all the helper tests from `test/helpers`| -| `rake test:integration` | Runs all the integration tests from `test/integration`| -| `rake test:mailers` | Runs all the mailer tests from `test/mailers`| -| `rake test:models` | Runs all the model tests from `test/models`| -| `rake test:recent` | Tests recent changes| -| `rake test:uncommitted` | Runs all the tests which are uncommitted. Supports Subversion and Git| -| `rake test:units` | Runs all the unit tests from `test/models`, `test/helpers`, and `test/unit`| +| Tasks | Description | +| ----------------------- | ----------- | +| `rake test` | Runs all unit, functional and integration tests. You can also simply run `rake test` as Rails will run all the tests by default| +| `rake test:controllers` | Runs all the controller tests from `test/controllers`| +| `rake test:functionals` | Runs all the functional tests from `test/controllers`, `test/mailers`, and `test/functional`| +| `rake test:helpers` | Runs all the helper tests from `test/helpers`| +| `rake test:integration` | Runs all the integration tests from `test/integration`| +| `rake test:mailers` | Runs all the mailer tests from `test/mailers`| +| `rake test:models` | Runs all the model tests from `test/models`| +| `rake test:units` | Runs all the unit tests from `test/models`, `test/helpers`, and `test/unit`| -Brief Note About `Test::Unit` +Brief Note About `MiniTest` ----------------------------- Ruby ships with a boat load of libraries. Ruby 1.8 provides `Test::Unit`, a framework for unit testing in Ruby. All the basic assertions discussed above are actually defined in `Test::Unit::Assertions`. The class `ActiveSupport::TestCase` which we have been using in our unit and functional tests extends `Test::Unit::TestCase`, allowing @@ -920,19 +928,24 @@ require 'test_helper' class UserMailerTest < ActionMailer::TestCase tests UserMailer test "invite" do - @expected.from = 'me@example.com' - @expected.to = 'friend@example.com' - @expected.subject = "You have been invited by #{@expected.from}" - @expected.body = read_fixture('invite') - @expected.date = Time.now - - assert_equal @expected.encoded, UserMailer.create_invite('me@example.com', 'friend@example.com', @expected.date).encoded + # Send the email, then test that it got queued + email = UserMailer.create_invite('me@example.com', + 'friend@example.com', Time.now).deliver + assert !ActionMailer::Base.deliveries.empty? + + # Test the body of the sent email contains what we expect it to + assert_equal ['me@example.com'], email.from + assert_equal ['friend@example.com'], email.to + assert_equal 'You have been invited by me@example.com', email.subject + assert_equal read_fixture('invite').join, email.body.to_s end - end ``` -In this test, `@expected` is an instance of `TMail::Mail` that you can use in your tests. It is defined in `ActionMailer::TestCase`. The test above uses `@expected` to construct an email, which it then asserts with email created by the custom mailer. The `invite` fixture is the body of the email and is used as the sample content to assert against. The helper `read_fixture` is used to read in the content from this file. +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 what we +expect. The helper `read_fixture` is used to read in the content from this file. Here's the content of the `invite` fixture: @@ -944,9 +957,17 @@ You have been invited. Cheers! ``` -This is the right time to understand a little more about writing tests for your mailers. The line `ActionMailer::Base.delivery_method = :test` in `config/environments/test.rb` sets the delivery method to test mode so that email will not actually be delivered (useful to avoid spamming your users while testing) but instead it will be appended to an array (`ActionMailer::Base.deliveries`). +This is the right time to understand a little more about writing tests for your +mailers. The line `ActionMailer::Base.delivery_method = :test` in +`config/environments/test.rb` sets the delivery method to test mode so that +email will not actually be delivered (useful to avoid spamming your users while +testing) but instead it will be appended to an array +(`ActionMailer::Base.deliveries`). -This way, emails are not actually sent, simply constructed. The precise content of the email can then be checked against what is expected, as in the example above. +NOTE: The `ActionMailer::Base.deliveries` array is only reset automatically in +`ActionMailer::TestCase` tests. If you want to have a clean slate outside Action +Mailer tests, you can reset it manually with: +`ActionMailer::Base.deliveries.clear` ### Functional Testing @@ -977,5 +998,6 @@ The built-in `test/unit` based testing is not the only way to test Rails applica * [NullDB](http://avdi.org/projects/nulldb/), a way to speed up testing by avoiding database use. * [Factory Girl](https://github.com/thoughtbot/factory_girl/tree/master), a replacement for fixtures. * [Machinist](https://github.com/notahat/machinist/tree/master), another replacement for fixtures. +* [MiniTest::Spec Rails](https://github.com/metaskills/minitest-spec-rails), use the MiniTest::Spec DSL within your rails tests. * [Shoulda](http://www.thoughtbot.com/projects/shoulda), an extension to `test/unit` with additional helpers, macros, and assertions. * [RSpec](http://relishapp.com/rspec), a behavior-driven development framework diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md index 568767d9de..35a9617b80 100644 --- a/guides/source/upgrading_ruby_on_rails.md +++ b/guides/source/upgrading_ruby_on_rails.md @@ -22,6 +22,104 @@ Rails generally stays close to the latest released Ruby version when it's releas TIP: Ruby 1.8.7 p248 and p249 have marshaling bugs that crash Rails. Ruby Enterprise Edition has these fixed since the release of 1.8.7-2010.02. On the 1.9 front, Ruby 1.9.1 is not usable because it outright segfaults, so if you want to use 1.9.x, jump straight to 1.9.3 for smooth sailing. +### HTTP PATCH + +Rails 4 now uses `PATCH` as the primary HTTP verb for updates when a RESTful +resource is declared in `config/routes.rb`. The `update` action is still used, +and `PUT` requests will continue to be routed to the `update` action as well. +So, if you're using only the standard RESTful routes, no changes need to be made: + +```ruby +resources :users +``` + +```erb +<%= form_for @user do |f| %> +``` + +```ruby +class UsersController < ApplicationController + def update + # No change needed; PATCH will be preferred, and PUT will still work. + end +end +``` + +However, you will need to make a change if you are using `form_for` to update +a resource in conjunction with a custom route using the `PUT` HTTP method: + +```ruby +resources :users, do + put :update_name, on: :member +end +``` + +```erb +<%= form_for [ :update_name, @user ] do |f| %> +``` + +```ruby +class UsersController < ApplicationController + def update_name + # Change needed; form_for will try to use a non-existant PATCH route. + end +end +``` + +If the action is not being used in a public API and you are free to change the +HTTP method, you can update your route to use `patch` instead of `put`: + +`PUT` requests to `/users/:id` in Rails 4 get routed to `update` as they are +today. So, if you have an API that gets real PUT requests it is going to work. +The router also routes `PATCH` requests to `/users/:id` to the `update` action. + +```ruby +resources :users do + patch :update_name, on: :member +end +``` + +If the action is being used in a public API and you can't change to HTTP method +being used, you can update your form to use the `PUT` method instead: + +```erb +<%= form_for [ :update_name, @user ], method: :put do |f| %> +``` + +For more on PATCH and why this change was made, see [this post](http://weblog.rubyonrails.org/2012/2/25/edge-rails-patch-is-the-new-primary-http-method-for-updates/) +on the Rails blog. + +#### A note about media types + +The errata for the `PATCH` verb [specifies that a 'diff' media type should be +used with `PATCH`](http://www.rfc-editor.org/errata_search.php?rfc=5789). One +such format is [JSON Patch](http://tools.ietf.org/html/rfc6902). While Rails +does not support JSON Patch natively, it's easy enough to add support: + +``` +# in your controller +def update + respond_to do |format| + format.json do + # perform a partial update + @post.update params[:post] + end + + format.json_patch do + # perform sophisticated change + end + end +end + +# In config/initializers/json_patch.rb: +Mime::Type.register 'application/json-patch+json', :json_patch +``` + +As JSON Patch was only recently made into an RFC, there aren't a lot of great +Ruby libraries yet. Aaron Patterson's +[hana](https://github.com/tenderlove/hana) is one such gem, but doesn't have +full support for the last few changes in the specification. + Upgrading from Rails 3.2 to Rails 4.0 ------------------------------------- @@ -31,6 +129,10 @@ If your application is currently on any version of Rails older than 3.2.x, you s The following changes are meant for upgrading your application to Rails 4.0. +### Gemfile + +Rails 4.0 removed the `assets` group from Gemfile. You'd need to remove that line from your Gemfile when upgrading. + ### vendor/plugins Rails 4.0 no longer supports loading plugins from `vendor/plugins`. You must replace any plugins by extracting them to gems and adding them to your Gemfile. If you choose not to make them gems, you can move them into, say, `lib/my_plugin/*` and add an appropriate initializer in `config/initializers/my_plugin.rb`. @@ -43,11 +145,29 @@ Rails 4.0 no longer supports loading plugins from `vendor/plugins`. You must rep * Rails 4.0 has changed how orders get stacked in `ActiveRecord::Relation`. In previous versions of Rails, the new order was applied after the previously defined order. But this is no longer true. Check [Active Record Query guide](active_record_querying.html#ordering) for more information. -* Rails 4.0 has changed `serialized_attributes` and `attr_readonly` to class methods only. Now you shouldn't use instance methods, it's deprecated. You must change them, e.g. `self.serialized_attributes` to `self.class.serialized_attributes`. +* Rails 4.0 has changed `serialized_attributes` and `attr_readonly` to class methods only. You shouldn't use instance methods since it's now deprecated. You should change them to use class methods, e.g. `self.serialized_attributes` to `self.class.serialized_attributes`. + +* Rails 4.0 has removed `attr_accessible` and `attr_protected` feature in favor of Strong Parameters. You can use the [Protected Attributes gem](https://github.com/rails/protected_attributes) to a smoothly upgrade path. + +* Rails 4.0 requires that scopes use a callable object such as a Proc or lambda: + +```ruby + scope :active, where(active: true) + + # becomes + scope :active, -> { where active: true } +``` + +* Rails 4.0 has deprecated `ActiveRecord::Fixtures` in favor of `ActiveRecord::FixtureSet`. +* Rails 4.0 has deprecated `ActiveRecord::TestCase` in favor of `ActiveSupport::TestCase`. + +### Active Resource + +Rails 4.0 extracted Active Resource to its own gem. If you still need the feature you can add the [Active Resource gem](https://github.com/rails/activeresource) in your Gemfile. ### Active Model -* Rails 4.0 has changed how errors attach with the `ActiveModel::Validations::ConfirmationValidator`. Now when confirmation validations fail the error will be attached to `:#{attribute}_confirmation` instead of `attribute`. +* Rails 4.0 has changed how errors attach with the `ActiveModel::Validations::ConfirmationValidator`. Now when confirmation validations fail, the error will be attached to `:#{attribute}_confirmation` instead of `attribute`. * Rails 4.0 has changed `ActiveModel::Serializers::JSON.include_root_in_json` default value to `false`. Now, Active Model Serializers and Active Record objects have the same default behaviour. This means that you can comment or remove the following option in the `config/initializers/wrap_parameters.rb` file: @@ -60,7 +180,31 @@ Rails 4.0 no longer supports loading plugins from `vendor/plugins`. You must rep ### Action Pack -* There is an upgrading cookie store `UpgradeSignatureToEncryptionCookieStore` which helps you upgrading apps that use `CookieStore` to the new default `EncryptedCookieStore`. To use this `CookieStore` set `Myapp::Application.config.session_store :upgrade_signature_to_encryption_cookie_store, key: '_myapp_session'` in `config/initializers/session_store.rb`. Additionally, add `Myapp::Application.config.secret_key_base = 'some secret'` in `config/initializers/secret_token.rb`. Do not remove `Myapp::Application.config.secret_token = 'some secret'`. +* Rails 4.0 introduces `ActiveSupport::KeyGenerator` and uses this as a base from which to generate and verify signed cookies (among other things). Existing signed cookies generated with Rails 3.x will be transparently upgraded if you leave your existing `secret_token` in place and add the new `secret_key_base`. + +```ruby + # config/initializers/secret_token.rb + Myapp::Application.config.secret_token = 'existing secret token' + Myapp::Application.config.secret_key_base = 'new secret key base' +``` + +Please note that you should wait to set `secret_key_base` until you have 100% of your userbase on Rails 4.x and are reasonably sure you will not need to rollback to Rails 3.x. This is because cookies signed based on the new `secret_key_base` in Rails 4.x are not backwards compatible with Rails 3.x. You are free to leave your existing `secret_token` in place, not set the new `secret_key_base`, and ignore the deprecation warnings until you are reasonably sure that your upgrade is otherwise complete. + +If you are relying on the ability for external applications or Javascript to be able to read your Rails app's signed session cookies (or signed cookies in general) you should not set `secret_key_base` until you have decoupled these concerns. + +* Rails 4.0 encrypts the contents of cookie-based sessions if `secret_key_base` has been set. Rails 3.x signed, but did not encrypt, the contents of cookie-based session. Signed cookies are "secure" in that they are verified to have been generated by your app and are tamper-proof. However, the contents can be viewed by end users, and encrypting the contents eliminates this caveat/concern without a significant performance penalty. + +As described above, existing signed cookies generated with Rails 3.x will be transparently upgraded if you leave your existing `secret_token` in place and add the new `secret_key_base`. + +```ruby + # config/initializers/secret_token.rb + Myapp::Application.config.secret_token = 'existing secret token' + Myapp::Application.config.secret_key_base = 'new secret key base' +``` + +The same caveats apply here, too. You should wait to set `secret_key_base` until you have 100% of your userbase on Rails 4.x and are reasonably sure you will not need to rollback to Rails 3.x. You should also take care to make sure you are not relying on the ability to decode signed cookies generated by your app in external applications or Javascript before upgrading. + +Please read [Pull Request #9978](https://github.com/rails/rails/pull/9978) for details on the move to encrypted session cookies. * Rails 4.0 removed the `ActionController::Base.asset_path` option. Use the assets pipeline feature. @@ -68,8 +212,31 @@ Rails 4.0 no longer supports loading plugins from `vendor/plugins`. You must rep * Rails 4.0 has removed Action and Page caching from Action Pack. You will need to add the `actionpack-action_caching` gem in order to use `caches_action` and the `actionpack-page_caching` to use `caches_pages` in your controllers. +* Rails 4.0 has removed the XML parameters parser. You will need to add the `actionpack-xml_parser` gem if you require this feature. + +* Rails 4.0 changes the default memcached client from `memcache-client` to `dalli`. To upgrade, simply add `gem 'dalli'` to your `Gemfile`. + +* Rails 4.0 deprecates the `dom_id` and `dom_class` methods in controllers (they are fine in views). You will need to include the `ActionView::RecordIdentifier` module in controllers requiring this feature. + * Rails 4.0 changed how `assert_generates`, `assert_recognizes`, and `assert_routing` work. Now all these assertions raise `Assertion` instead of `ActionController::RoutingError`. +* Rails 4.0 raises an `ArgumentError` if clashing named routes are defined. This can be triggered by explicitly defined named routes or by the `resources` method. Here are two examples that clash with routes named `example_path`: + +```ruby + get 'one' => 'test#example', as: :example + get 'two' => 'test#example', as: :example +``` + +```ruby + resources :examples + get 'clashing/:id' => 'test#example', as: :example +``` + +In the first case, you can simply avoid using the same name for multiple +routes. In the second, you can use the `only` or `except` options provided by +the `resources` method to restrict the routes created as detailed in the +[Routing Guide](routing.html#restricting-the-routes-created). + * Rails 4.0 also changed the way unicode character routes are drawn. Now you can draw unicode character routes directly. If you already draw such routes, you must change them, for example: ```ruby @@ -82,7 +249,46 @@ becomes get 'こんにちは', controller: 'welcome', action: 'index' ``` -* Rails 4.0 has removed ActionDispatch::BestStandardsSupport middleware, !DOCTYPE html already triggers standards mode per http://msdn.microsoft.com/en-us/library/jj676915(v=vs.85).aspx and ChromeFrame header has been moved to `config.action_dispatch.default_headers` +* Rails 4.0 requires that routes using `match` must specify the request method. For example: + +```ruby + # Rails 3.x + match "/" => "root#index" + + # becomes + match "/" => "root#index", via: :get + + # or + get "/" => "root#index" +``` + +* Rails 4.0 has removed `ActionDispatch::BestStandardsSupport` middleware, `<!DOCTYPE html>` already triggers standards mode per http://msdn.microsoft.com/en-us/library/jj676915(v=vs.85).aspx and ChromeFrame header has been moved to `config.action_dispatch.default_headers`. + +Remember you must also remove any references to the middleware from your application code, for example: + +```ruby +# Raise exception +config.middleware.insert_before(Rack::Lock, ActionDispatch::BestStandardsSupport) +``` + +Also check your environment settings for `config.action_dispatch.best_standards_support` and remove it if present. + +* In Rails 4.0, precompiling assets no longer automatically copies non-JS/CSS assets from `vendor/assets` and `lib/assets`. Rails application and engine developers should put these assets in `app/assets` or configure `config.assets.precompile`. + +* In Rails 4.0, `ActionController::UnknownFormat` is raised when the action doesn't handle the request format. By default, the exception is handled by responding with 406 Not Acceptable, but you can override that now. In Rails 3, 406 Not Acceptable was always returned. No overrides. + +* In Rails 4.0, a generic `ActionDispatch::ParamsParser::ParseError` exception is raised when `ParamsParser` fails to parse request params. You will want to rescue this exception instead of the low-level `MultiJson::DecodeError`, for example. + +* In Rails 4.0, `SCRIPT_NAME` is properly nested when engines are mounted on an app that's served from a URL prefix. You no longer have to set `default_url_options[:script_name]` to work around overwritten URL prefixes. + +* Rails 4.0 deprecated `ActionController::Integration` in favor of `ActionDispatch::Integration`. +* Rails 4.0 deprecated `ActionController::IntegrationTest` in favor of `ActionDispatch::IntegrationTest`. +* Rails 4.0 deprecated `ActionController::PerformanceTest` in favor of `ActionDispatch::PerformanceTest`. +* Rails 4.0 deprecated `ActionController::AbstractRequest` in favor of `ActionDispatch::Request`. +* Rails 4.0 deprecated `ActionController::Request` in favor of `ActionDispatch::Request`. +* Rails 4.0 deprecated `ActionController::AbstractResponse` in favor of `ActionDispatch::Response`. +* Rails 4.0 deprecated `ActionController::Response` in favor of `ActionDispatch::Response`. +* Rails 4.0 deprecated `ActionController::Routing` in favor of `ActionDispatch::Routing`. ### Active Support @@ -92,19 +298,31 @@ Rails 4.0 removes the `j` alias for `ERB::Util#json_escape` since `j` is already The order in which helpers from more than one directory are loaded has changed in Rails 4.0. Previously, they were gathered and then sorted alphabetically. After upgrading to Rails 4.0, helpers will preserve the order of loaded directories and will be sorted alphabetically only within each directory. Unless you explicitly use the `helpers_path` parameter, this change will only impact the way of loading helpers from engines. If you rely on the ordering, you should check if correct methods are available after upgrade. If you would like to change the order in which engines are loaded, you can use `config.railties_order=` method. +### Active Record Observer and Action Controller Sweeper + +Active Record Observer and Action Controller Sweeper have been extracted to the `rails-observers` gem. You will need to add the `rails-observers` gem if you require these features. + +### sprockets-rails + +* `assets:precompile:primary` has been removed. Use `assets:precompile` instead. + +### sass-rails + +* `asset_url` with two arguments is deprecated. For example: `asset-url("rails.png", image)` becomes `asset-url("rails.png")` + Upgrading from Rails 3.1 to Rails 3.2 ------------------------------------- If your application is currently on any version of Rails older than 3.1.x, you should upgrade to Rails 3.1 before attempting an update to Rails 3.2. -The following changes are meant for upgrading your application to Rails 3.2.2, the latest 3.2.x version of Rails. +The following changes are meant for upgrading your application to Rails 3.2.12, the latest 3.2.x version of Rails. ### Gemfile Make the following changes to your `Gemfile`. ```ruby -gem 'rails', '= 3.2.2' +gem 'rails', '= 3.2.12' group :assets do gem 'sass-rails', '~> 3.2.3' @@ -144,14 +362,14 @@ Upgrading from Rails 3.0 to Rails 3.1 If your application is currently on any version of Rails older than 3.0.x, you should upgrade to Rails 3.0 before attempting an update to Rails 3.1. -The following changes are meant for upgrading your application to Rails 3.1.3, the latest 3.1.x version of Rails. +The following changes are meant for upgrading your application to Rails 3.1.11, the latest 3.1.x version of Rails. ### Gemfile Make the following changes to your `Gemfile`. ```ruby -gem 'rails', '= 3.1.3' +gem 'rails', '= 3.1.11' gem 'mysql2' # Needed for the new asset pipeline diff --git a/guides/source/working_with_javascript_in_rails.md b/guides/source/working_with_javascript_in_rails.md index 7c4192ee26..22a59cdfec 100644 --- a/guides/source/working_with_javascript_in_rails.md +++ b/guides/source/working_with_javascript_in_rails.md @@ -394,3 +394,4 @@ Here are some helpful links to help you learn even more: * [jquery-ujs list of external articles](https://github.com/rails/jquery-ujs/wiki/External-articles) * [Rails 3 Remote Links and Forms: A Definitive Guide](http://www.alfajango.com/blog/rails-3-remote-links-and-forms/) * [Railscasts: Unobtrusive JavaScript](http://railscasts.com/episodes/205-unobtrusive-javascript) +* [Railscasts: Turbolinks](http://railscasts.com/episodes/390-turbolinks) |