diff options
Diffstat (limited to 'guides')
55 files changed, 3239 insertions, 3513 deletions
diff --git a/guides/CHANGELOG.md b/guides/CHANGELOG.md index 11fe1c5efa..e9f7ff9d68 100644 --- a/guides/CHANGELOG.md +++ b/guides/CHANGELOG.md @@ -1,5 +1,7 @@ ## Rails 4.0.0 (unreleased) ## +* 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* diff --git a/guides/Rakefile b/guides/Rakefile index 7881a3d9b3..d6dd950d01 100644 --- a/guides/Rakefile +++ b/guides/Rakefile @@ -13,6 +13,12 @@ namespace :guides do desc "Generate .mobi file. The kindlegen executable must be in your PATH. You can get it for free from http://www.amazon.com/kindlepublishing" task :kindle do + unless `kindlerb -v 2> /dev/null` =~ /kindlerb 0.1.1/ + abort "Please `gem install kindlerb`" + end + unless `convert` =~ /convert/ + abort "Please install ImageMagick`" + end ENV['KINDLE'] = '1' Rake::Task['guides:generate:html'].invoke end diff --git a/guides/assets/images/customized_error_messages.png b/guides/assets/images/customized_error_messages.png Binary files differdeleted file mode 100644 index fcf47b4be0..0000000000 --- a/guides/assets/images/customized_error_messages.png +++ /dev/null diff --git a/guides/assets/images/error_messages.png b/guides/assets/images/error_messages.png Binary files differdeleted file mode 100644 index 1189e486d4..0000000000 --- a/guides/assets/images/error_messages.png +++ /dev/null diff --git a/guides/assets/images/rails4_features.png b/guides/assets/images/rails4_features.png Binary files differnew file mode 100644 index 0000000000..a979f02207 --- /dev/null +++ b/guides/assets/images/rails4_features.png diff --git a/guides/assets/images/validation_error_messages.png b/guides/assets/images/validation_error_messages.png Binary files differdeleted file mode 100644 index 30e4ca4a3d..0000000000 --- a/guides/assets/images/validation_error_messages.png +++ /dev/null diff --git a/guides/assets/stylesheets/main.css b/guides/assets/stylesheets/main.css index 589c96e0e9..dd029e6314 100644 --- a/guides/assets/stylesheets/main.css +++ b/guides/assets/stylesheets/main.css @@ -83,6 +83,10 @@ table th { padding: 0.5em 1em; } +img { + max-width: 100%; +} + /* Structure and Layout --------------------------------------- */ @@ -573,7 +577,7 @@ h6 { #mainCol div.warning, #subCol dd.warning { background: #f9d9d8 url(../images/tab_red.gif) no-repeat left top; border: none; - padding: 1.25em 1.25em 1.25em 48px; + padding: 1.25em 1.25em 0.25em 48px; margin-left: 0; margin-top: 0.25em; } diff --git a/guides/code/getting_started/app/controllers/posts_controller.rb b/guides/code/getting_started/app/controllers/posts_controller.rb index a8ac9aba5a..b74c66ef13 100644 --- a/guides/code/getting_started/app/controllers/posts_controller.rb +++ b/guides/code/getting_started/app/controllers/posts_controller.rb @@ -31,7 +31,7 @@ class PostsController < ApplicationController def update @post = Post.find(params[:id]) - if @post.update_attributes(params[:post]) + if @post.update(params[:post]) redirect_to :action => :show, :id => @post.id else render 'edit' diff --git a/guides/code/getting_started/config/application.rb b/guides/code/getting_started/config/application.rb index d2cd5c028b..d53c9fd8bc 100644 --- a/guides/code/getting_started/config/application.rb +++ b/guides/code/getting_started/config/application.rb @@ -18,9 +18,6 @@ module Blog # Custom directories with classes and modules you want to be autoloadable. # config.autoload_paths += %W(#{config.root}/extras) - # Activate observers that should always be running. - # config.active_record.observers = :cacher, :garbage_collector, :forum_observer - # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. # config.time_zone = 'Central Time (US & Canada)' diff --git a/guides/rails_guides/generator.rb b/guides/rails_guides/generator.rb index 3b124ef236..af9c5b8372 100644 --- a/guides/rails_guides/generator.rb +++ b/guides/rails_guides/generator.rb @@ -84,7 +84,7 @@ module RailsGuides @warnings = ENV['WARNINGS'] == '1' @all = ENV['ALL'] == '1' @kindle = ENV['KINDLE'] == '1' - @version = ENV['RAILS_VERSION'] || `git rev-parse --short HEAD`.chomp + @version = ENV['RAILS_VERSION'] || 'local' @lang = ENV['GUIDES_LANGUAGE'] end @@ -112,11 +112,9 @@ module RailsGuides end def generate_mobi - opf = "#{output_dir}/rails_guides.opf" + require 'rails_guides/kindle' out = "#{output_dir}/kindlegen.out" - - system "kindlegen #{opf} -o #{mobi} > #{out} 2>&1" - puts "Guides compiled as Kindle book to #{mobi}" + Kindle.generate(output_dir, mobi, out) puts "(kindlegen log at #{out})." end diff --git a/guides/rails_guides/kindle.rb b/guides/rails_guides/kindle.rb new file mode 100644 index 0000000000..09eecd5634 --- /dev/null +++ b/guides/rails_guides/kindle.rb @@ -0,0 +1,119 @@ +#!/usr/bin/env ruby + +unless `which kindlerb` + abort "Please gem install kindlerb" +end + +require 'nokogiri' +require 'fileutils' +require 'yaml' +require 'date' + +module Kindle + extend self + + def generate(output_dir, mobi_outfile, logfile) + output_dir = File.absolute_path(output_dir) + Dir.chdir output_dir do + puts "=> Using output dir: #{output_dir}" + puts "=> Arranging html pages in document order" + toc = File.read("toc.ncx") + doc = Nokogiri::XML(toc).xpath("//ncx:content", 'ncx' => "http://www.daisy.org/z3986/2005/ncx/") + html_pages = doc.select {|c| c[:src]}.map {|c| c[:src]}.uniq + + generate_front_matter(html_pages) + + generate_sections(html_pages) + + generate_document_metadata(mobi_outfile) + + puts "Creating MOBI document with kindlegen. This make take a while." + cmd = "kindlerb . > #{File.absolute_path logfile} 2>&1" + puts cmd + system(cmd) + puts "MOBI document generated at #{File.expand_path(mobi_outfile, output_dir)}" + end + end + + def generate_front_matter(html_pages) + frontmatter = [] + html_pages.delete_if {|x| + if x =~ /(toc|welcome|credits|copyright).html/ + frontmatter << x unless x =~ /toc/ + true + end + } + html = frontmatter.map {|x| + Nokogiri::HTML(File.open(x)).at("body").inner_html + }.join("\n") + + fdoc = Nokogiri::HTML(html) + fdoc.search("h3").each do |h3| + h3.name = 'h4' + end + fdoc.search("h2").each do |h2| + h2.name = 'h3' + h2['id'] = h2.inner_text.gsub(/\s/, '-') + end + add_head_section fdoc, "Front Matter" + File.open("frontmatter.html",'w') {|f| f.puts fdoc.to_html} + html_pages.unshift "frontmatter.html" + end + + def generate_sections(html_pages) + FileUtils::rm_rf("sections/") + html_pages.each_with_index do |page, section_idx| + FileUtils::mkdir_p("sections/%03d" % section_idx) + doc = Nokogiri::HTML(File.open(page)) + title = doc.at("title").inner_text.gsub("Ruby on Rails Guides: ", '') + title = page.capitalize.gsub('.html', '') if title.strip == '' + File.open("sections/%03d/_section.txt" % section_idx, 'w') {|f| f.puts title} + doc.xpath("//h3[@id]").each_with_index do |h3,item_idx| + subsection = h3.inner_text + content = h3.xpath("./following-sibling::*").take_while {|x| x.name != "h3"}.map {|x| x.to_html} + item = Nokogiri::HTML(h3.to_html + content.join("\n")) + item_path = "sections/%03d/%03d.html" % [section_idx, item_idx] + add_head_section(item, subsection) + item.search("img").each do |img| + img['src'] = "#{Dir.pwd}/#{img['src']}" + end + item.xpath("//li/p").each {|p| p.swap(p.children); p.remove} + File.open(item_path, 'w') {|f| f.puts item.to_html} + end + end + end + + def generate_document_metadata(mobi_outfile) + puts "=> Generating _document.yml" + x = Nokogiri::XML(File.open("rails_guides.opf")).remove_namespaces! + cover_jpg = "#{Dir.pwd}/images/rails_guides_kindle_cover.jpg" + cover_gif = cover_jpg.sub(/jpg$/, 'gif') + puts `convert #{cover_jpg} #{cover_gif}` + document = { + 'doc_uuid' => x.at("package")['unique-identifier'], + 'title' => x.at("title").inner_text.gsub(/\(.*$/, " v2"), + 'publisher' => x.at("publisher").inner_text, + 'author' => x.at("creator").inner_text, + 'subject' => x.at("subject").inner_text, + 'date' => x.at("date").inner_text, + 'cover' => cover_gif, + 'masthead' => nil, + 'mobi_outfile' => mobi_outfile + } + puts document.to_yaml + File.open("_document.yml", 'w'){|f| f.puts document.to_yaml} + end + + def add_head_section(doc, title) + head = Nokogiri::XML::Node.new "head", doc + title_node = Nokogiri::XML::Node.new "title", doc + title_node.content = title + title_node.parent = head + css = Nokogiri::XML::Node.new "link", doc + css['rel'] = 'stylesheet' + css['type'] = 'text/css' + css['href'] = "#{Dir.pwd}/stylesheets/kindle.css" + css.parent = head + doc.at("body").before head + end +end diff --git a/guides/source/4_0_release_notes.md b/guides/source/4_0_release_notes.md index ecb8dd04f5..80af0c1225 100644 --- a/guides/source/4_0_release_notes.md +++ b/guides/source/4_0_release_notes.md @@ -1,41 +1,23 @@ Ruby on Rails 4.0 Release Notes =============================== -Highlights in Rails 4.0: (WIP) +Highlights in Rails 4.0: * Ruby 1.9.3 only * Strong Parameters -* Queue API -* Caching Improvements +* Turbolinks +* Russian Doll Caching +* Asynchronous Mailers -These release notes cover the major changes, but do not include each bug-fix and changes. If you want to see everything, check out the [list of commits](https://github.com/rails/rails/commits/master) in the main Rails repository on GitHub. +These release notes cover only the major changes. To know about various bug fixes and changes, please refer to the change logs or check out the [list of commits](https://github.com/rails/rails/commits/master) in the main Rails repository on GitHub. -------------------------------------------------------------------------------- Upgrading to Rails 4.0 ---------------------- -TODO. This is a WIP guide. +If you're upgrading an existing application, it's a great idea to have good test coverage before going in. You should also first upgrade to Rails 3.2 in case you haven't and make sure your application still runs as expected before attempting an update to Rails 4.0. A list of things to watch out for when upgrading is available in the [Upgrading to Rails](upgrading_ruby_on_rails.html#upgrading-from-rails-3-2-to-rails-4-0) guide. -If you're upgrading an existing application, it's a great idea to have good test coverage before going in. You should also first upgrade to Rails 3.2 in case you haven't and make sure your application still runs as expected before attempting an update to Rails 4.0. Then take heed of the following changes: - -### Rails 4.0 requires at least Ruby 1.9.3 - -Rails 4.0 requires Ruby 1.9.3 or higher. Support for all of the previous Ruby versions has been dropped officially and you should upgrade as early as possible. - -### What to update in your apps - -* Update your Gemfile to depend on - * `rails = 4.0.0` - * `sass-rails ~> 3.2.3` - * `coffee-rails ~> 3.2.1` - * `uglifier >= 1.0.3` - -TODO: Update the versions above. - -* Rails 4.0 removes `vendor/plugins` completely. You have to replace these plugins by extracting them as gems and adding them in 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`. - -TODO: Configuration changes in environment files Creating a Rails 4.0 application -------------------------------- @@ -69,6 +51,24 @@ $ ruby /path/to/rails/railties/bin/rails new myapp --dev Major Features -------------- +TODO. Give a list and then talk about each of them briefly. We can point to relevant code commits or documentation from these sections. + + + +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)) + Documentation ------------- @@ -79,841 +79,144 @@ Documentation Railties -------- -* Ensure that RAILS_ENV is set when accessing Rails.env. - -* Don't eager-load app/assets and app/views. - -* Add `.rake` to list of file extensions included by `rake notes` and `rake notes:custom`. - -* New test locations `test/models`, `test/helpers`, `test/controllers`, and `test/mailers`. Corresponding rake tasks added as well. - -* Set a different cache per environment for assets pipeline through `config.assets.cache`. - -* `Rails.public_path` now returns a Pathname object. - -* Remove highly uncommon `config.assets.manifest` option for moving the manifest path. This option is now unsupported in sprockets-rails. +Please refer to the [Changelog](https://github.com/rails/rails/blob/master/railties/CHANGELOG.md) for detailed changes. -* Add `config.action_controller.permit_all_parameters` to disable StrongParameters protection, it's false by default. +### Notable changes -* Remove `config.active_record.whitelist_attributes` and `config.active_record.mass_assignment_sanitizer` from new applications since MassAssignmentSecurity has been extracted from Rails. +* 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)) -* Change `rails new` and `rails plugin new` generators to name the `.gitkeep` files as `.keep` in a more SCM-agnostic way. Change `--skip-git` option to only skip the `.gitignore` file and still generate the `.keep` files. Add `--skip-keeps` option to skip the `.keep` files. +* Threadsafe on by default -* Fixed support for DATABASE_URL environment variable for rake db tasks. - -* rails dbconsole now can use SSL for MySQL. The database.yml options sslca, sslcert, sslcapath, sslcipher and sslkey now affect rails dbconsole. - -* Correctly handle SCRIPT_NAME when generating routes to engine in application that's mounted at a sub-uri. With this behavior, you *should not* use default_url_options[:script_name] to set proper application's mount point by yourself. - -* `config.threadsafe!` is deprecated in favor of `config.eager_load` which provides a more fine grained control on what is eager loaded. - -* The migration generator will now produce AddXXXToYYY/RemoveXXXFromYYY migrations with references statements, for instance +### Deprecations - rails g migration AddReferencesToProducts user:references supplier:references{polymorphic} +* `config.threadsafe!` is deprecated in favor of `config.eager_load` which provides a more fine grained control on what is eager loaded. - will generate the migration with: +* `Rails::Plugin` has gone. Instead of adding plugins to `vendor/plugins` use gems or bundler with path or git dependencies. - add_reference :products, :user, index: true - add_reference :products, :supplier, polymorphic: true, index: true +Action Mailer +------------- -* Allow scaffold/model/migration generators to accept a `polymorphic` modifier for `references`/`belongs_to`, for instance +Please refer to the [Changelog](https://github.com/rails/rails/blob/master/actionmailer/CHANGELOG.md) for detailed changes. - ``` - rails g model Product supplier:references{polymorphic} - ``` +### Notable changes - will generate the model with `belongs_to :supplier, polymorphic: true` association and appropriate migration. +### Deprecations -* Set `config.active_record.migration_error` to `:page_load` for development. +Active Model +------------ -* Add runner to `Rails::Railtie` as a hook called just after runner starts. +Please refer to the [Changelog](https://github.com/rails/rails/blob/master/activemodel/CHANGELOG.md) for detailed changes. -* Add `/rails/info/routes` path which displays the same information as `rake routes`. +### Notable changes -* Improved `rake routes` output for redirects. +* Add `ActiveModel::ForbiddenAttributesProtection`, a simple module to protect attributes from mass assignment when non-permitted attributes are passed. -* Load all environments available in `config.paths["config/environments"]`. +* Added `ActiveModel::Model`, a mixin to make Ruby objects work with AP out of box. -* Add `config.queue_consumer` to change the job queue consumer from the default `ActiveSupport::ThreadedQueueConsumer`. +### Deprecations -* Add `Rails.queue` for processing jobs in the background. +Active Support +-------------- -* Remove `Rack::SSL` in favour of `ActionDispatch::SSL`. +Please refer to the [Changelog](https://github.com/rails/rails/blob/master/activesupport/CHANGELOG.md) for detailed changes. -* Allow to set class that will be used to run as a console, other than IRB, with `Rails.application.config.console=`. It's best to add it to console block. +### Notable changes - ```ruby - # it can be added to config/application.rb - console do - # this block is called only when running console, - # so we can safely require pry here - require "pry" - config.console = Pry - end - ``` +* Replace deprecated `memcache-client` gem with `dalli` in ActiveSupport::Cache::MemCacheStore. -* Add a convenience method `hide!` to Rails generators to hide the current generator namespace from showing when running `rails generate`. +* Optimize ActiveSupport::Cache::Entry to reduce memory and processing overhead. -* Scaffold now uses `content_tag_for` in `index.html.erb`. +* Inflections can now be defined per locale. `singularize` and `pluralize` accept locale as an extra argument. -* `Rails::Plugin` is removed. Instead of adding plugins to `vendor/plugins`, use gems or bundler with path or git dependencies. +* `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 -Action Mailer -------------- - -* Allow to set default Action Mailer options via `config.action_mailer.default_options=`. - -* Raise an `ActionView::MissingTemplate` exception when no implicit template could be found. +* Deprecate `ActiveSupport::TestCase#pending` method, use `skip` from MiniTest instead. -* Asynchronously send messages via the Rails Queue. +* ActiveSupport::Benchmarkable#silence has been deprecated due to its lack of thread safety. It will be removed without replacement in Rails 4.1. -* Delivery Options (such as SMTP Settings) can now be set dynamically per mailer action. +* `ActiveSupport::JSON::Variable` is deprecated. Define your own `#as_json` and `#encode_json` methods for custom JSON string literals. - Delivery options are set via <tt>:delivery_method_options</tt> key on mail. +* Deprecates the compatibility method Module#local_constant_names, use Module#local_constants instead (which returns symbols). - ```ruby - def welcome_mailer(user,company) - delivery_options = { user_name: company.smtp_user, password: company.smtp_password, address: company.smtp_host } - mail(to: user.email, subject: "Welcome!", delivery_method_options: delivery_options) - end - ``` +* BufferedLogger is deprecated. Use ActiveSupport::Logger, or the logger from Ruby stdlib. -* Allow for callbacks in mailers similar to ActionController::Base. You can now set up headers/attachments using `before_filter` or `after_filter`. You could also change delivery settings or prevent delivery in an after filter based on instance variables set in your mailer action. You have access to `ActionMailer::Base` instance methods like `message`, `attachments`, `headers`. +* Deprecate `assert_present` and `assert_blank` in favor of `assert object.blank?` and `assert object.present?` Action Pack ----------- -### Action Controller - -* Add `ActionController::Flash.add_flash_types` method to allow people to register their own flash types. e.g.: - - ```ruby - class ApplicationController - add_flash_types :error, :warning - end - ``` - - If you add the above code, you can use `<%= error %>` in an erb, and `redirect_to /foo, :error => 'message'` in a controller. - -* Remove Active Model dependency from Action Pack. - -* Support unicode characters in routes. Route will be automatically escaped, so instead of manually escaping: - - ```ruby - get Rack::Utils.escape('こんにちは') => 'home#index' - ``` - - You just have to write the unicode route: - - ```ruby - get 'こんにちは' => 'home#index' - ``` - -* Return proper format on exceptions. - -* Extracted redirect logic from `ActionController::ForceSSL::ClassMethods.force_ssl` into `ActionController::ForceSSL#force_ssl_redirect`. - -* URL path parameters with invalid encoding now raise `ActionController::BadRequest`. - -* Malformed query and request parameter hashes now raise `ActionController::BadRequest`. - -* `respond_to` and `respond_with` now raise `ActionController::UnknownFormat` instead of directly returning head 406. The exception is rescued and converted to 406 in the exception handling middleware. - -* JSONP now uses `application/javascript` instead of `application/json` as the MIME type. - -* Session arguments passed to process calls in functional tests are now merged into the existing session, whereas previously they would replace the existing session. This change may break some existing tests if they are asserting the exact contents of the session but should not break existing tests that only assert individual keys. - -* Forms of persisted records use always PATCH (via the `_method` hack). - -* For resources, both PATCH and PUT are routed to the `update` action. - -* Don't ignore `force_ssl` in development. This is a change of behavior - use an `:if` condition to recreate the old behavior. - - ```ruby - class AccountsController < ApplicationController - force_ssl :if => :ssl_configured? - - def ssl_configured? - !Rails.env.development? - end - end - ``` - -#### Deprecations - -* Deprecated `ActionController::Integration` in favour of `ActionDispatch::Integration`. - -* Deprecated `ActionController::IntegrationTest` in favour of `ActionDispatch::IntegrationTest`. - -* Deprecated `ActionController::PerformanceTest` in favour of `ActionDispatch::PerformanceTest`. - -* Deprecated `ActionController::AbstractRequest` in favour of `ActionDispatch::Request`. - -* Deprecated `ActionController::Request` in favour of `ActionDispatch::Request`. - -* Deprecated `ActionController::AbstractResponse` in favour of `ActionDispatch::Response`. - -* Deprecated `ActionController::Response` in favour of `ActionDispatch::Response`. - -* Deprecated `ActionController::Routing` in favour of `ActionDispatch::Routing`. - -### Action Dispatch - -* Add Routing Concerns to declare common routes that can be reused inside others resources and routes. - - Code before: - - ```ruby - resources :messages do - resources :comments - end - - resources :posts do - resources :comments - resources :images, only: :index - end - ``` +Please refer to the [Changelog](https://github.com/rails/rails/blob/master/railties/CHANGELOG.md) for detailed changes. - Code after: +### Notable changes - ```ruby - concern :commentable do - resources :comments - end +* Change the stylesheet of exception pages for development mode. Additionally display also the line of code and fragment that raised the exception in all exceptions pages. - concern :image_attachable do - resources :images, only: :index - end - - resources :messages, concerns: :commentable - - resources :posts, concerns: [:commentable, :image_attachable] - ``` - -* Show routes in exception page while debugging a `RoutingError` in development. - -* Include `mounted_helpers` (helpers for accessing mounted engines) in `ActionDispatch::IntegrationTest` by default. - -* Added `ActionDispatch::SSL` middleware that when included force all the requests to be under HTTPS protocol. - -* Copy literal route constraints to defaults so that url generation know about them. The copied constraints are `:protocol`, `:subdomain`, `:domain`, `:host` and `:port`. - -* Allows `assert_redirected_to` to match against a regular expression. - -* Adds a backtrace to the routing error page in development. - -* `assert_generates`, `assert_recognizes`, and `assert_routing` all raise `Assertion` instead of `RoutingError`. - -* Allows the route helper root to take a string argument. For example, `root 'pages#main'` as a shortcut for `root to: 'pages#main'`. - -* Adds support for the PATCH verb: Request objects respond to `patch?`. Routes now have a new `patch` method, and understand `:patch` in the existing places where a verb is configured, like `:via`. Functional tests have a new method `patch` and integration tests have a new method `patch_via_redirect`. -If `:patch` is the default verb for updates, edits are tunneled as `PATCH` rather than as `PUT` and routing acts accordingly. - -* Integration tests support the OPTIONS method. - -* `expires_in` accepts a `must_revalidate` flag. If true, "must-revalidate" is added to the `Cache-Control` header. - -* Default responder will now always use your overridden block in `respond_with` to render your response. - -* Turn off verbose mode of `rack-cache`, we still have `X-Rack-Cache` to check that info. - -#### Deprecations - -### Action View - -* Remove Active Model dependency from Action Pack. - -* Allow to use `mounted_helpers` (helpers for accessing mounted engines) in `ActionView::TestCase`. - -* Make current object and counter (when it applies) variables accessible when rendering templates with `:object` or `:collection`. - -* Allow to lazy load `default_form_builder` by passing a string instead of a constant. - -* Add index method to `FormBuilder` class. - -* Adds support for layouts when rendering a partial with a given collection. - -* Remove `:disable_with` in favor of `data-disable-with` option from `submit_tag`, `button_tag` and `button_to` helpers. - -* Remove `:mouseover` option from `image_tag` helper. - -* Templates without a handler extension now raises a deprecation warning but still defaults to `ERb`. In future releases, it will simply return the template content. - -* Add a `divider` option to `grouped_options_for_select` to generate a separator optgroup automatically, and deprecate prompt as third argument, in favor of using an options hash. - -* Add `time_field` and `time_field_tag` helpers which render an `input[type="time"]` tag. - -* Removed old `text_helper` apis for `highlight`, `excerpt` and `word_wrap`. - -* Remove the leading \n added by textarea on `assert_select`. - -* Changed default value for `config.action_view.embed_authenticity_token_in_remote_forms` to false. This change breaks remote forms that need to work also without JavaScript, so if you need such behavior, you can either set it to true or explicitly pass `:authenticity_token => true` in form options. - -* Make possible to use a block in `button_to` helper if button text is hard to fit into the name parameter: - - ```ruby - <%= button_to [:make_happy, @user] do %> - Make happy <strong><%= @user.name %></strong> - <% end %> - # => "<form method="post" action="/users/1/make_happy" class="button_to"> - # <div> - # <button type="submit"> - # Make happy <strong>Name</strong> - # </button> - # </div> - # </form>" - ``` - -* Replace `include_seconds` boolean argument with `:include_seconds => true` option in `distance_of_time_in_words` and `time_ago_in_words` signature. - -* Remove `button_to_function` and `link_to_function` helpers. - -* `truncate` now always returns an escaped HTML-safe string. The option `:escape` can be used as `false` to not escape the result. - -* `truncate` now accepts a block to show extra content when the text is truncated. - -* Add `week_field`, `week_field_tag`, `month_field`, `month_field_tag`, `datetime_local_field`, `datetime_local_field_tag`, `datetime_field` and `datetime_field_tag` helpers. - -* Add `color_field` and `color_field_tag` helpers. - -* Add `include_hidden` option to select tag. With `:include_hidden => false` select with multiple attribute doesn't generate hidden input with blank value. - -* Removed default size option from the `text_field`, `search_field`, `telephone_field`, `url_field`, `email_field` helpers. - -* Removed default cols and rows options from the `text_area` helper. - -* Adds `image_url`, `javascript_url`, `stylesheet_url`, `audio_url`, `video_url`, and `font_url` to assets tag helper. These URL helpers will return the full path to your assets. This is useful when you are going to reference this asset from external host. - -* Allow `value_method` and `text_method` arguments from `collection_select` and `options_from_collection_for_select` to receive an object that responds to `:call` such as a proc, to evaluate the option in the current element context. This works the same way with `collection_radio_buttons` and `collection_check_boxes`. - -* Add `date_field` and `date_field_tag` helpers which render an `input[type="date"]` tag. - -* Add `collection_check_boxes` form helper, similar to `collection_select`: - - ```ruby - collection_check_boxes :post, :author_ids, Author.all, :id, :name - # Outputs something like: - <input id="post_author_ids_1" name="post[author_ids][]" type="checkbox" value="1" /> - <label for="post_author_ids_1">D. Heinemeier Hansson</label> - <input id="post_author_ids_2" name="post[author_ids][]" type="checkbox" value="2" /> - <label for="post_author_ids_2">D. Thomas</label> - <input name="post[author_ids][]" type="hidden" value="" /> - ``` - - The label/check_box pairs can be customized with a block. - -* Add `collection_radio_buttons` form helper, similar to `collection_select`: - - ```ruby - collection_radio_buttons :post, :author_id, Author.all, :id, :name - # Outputs something like: - <input id="post_author_id_1" name="post[author_id]" type="radio" value="1" /> - <label for="post_author_id_1">D. Heinemeier Hansson</label> - <input id="post_author_id_2" name="post[author_id]" type="radio" value="2" /> - <label for="post_author_id_2">D. Thomas</label> - ``` - - The label/radio_button pairs can be customized with a block. - -* `check_box` with an HTML5 attribute `:form` will now replicate the `:form` attribute to the hidden field as well. - -* label form helper accepts `:for => nil` to not generate the attribute. - -* Add `:format` option to `number_to_percentage`. - -* Add `config.action_view.logger` to configure logger for `Action View`. - -* `check_box` helper with `:disabled => true` will generate a `disabled` hidden field to conform with the HTML convention where disabled fields are not submitted with the form. This is a behavior change, previously the hidden tag had a value of the disabled checkbox. - -* `favicon_link_tag` helper will now use the favicon in `app/assets` by default. - -* `ActionView::Helpers::TextHelper#highlight` now defaults to the HTML5 `mark` element. - -#### Deprecations - -### Sprockets +### Deprecations -Moved into a separate gem `sprockets-rails`. Active Record ------------- -* Add `add_reference` and `remove_reference` schema statements. Aliases, `add_belongs_to` and `remove_belongs_to` are acceptable. References are reversible. - - ```ruby - # Create a user_id column - add_reference(:products, :user) - - # Create a supplier_id, supplier_type columns and appropriate index - add_reference(:products, :supplier, polymorphic: true, index: true) - - # Remove polymorphic reference - remove_reference(:products, :supplier, polymorphic: true) - ``` - -* Add `:default` and `:null` options to `column_exists?`. - - ```ruby - column_exists?(:testings, :taggable_id, :integer, null: false) - column_exists?(:testings, :taggable_type, :string, default: 'Photo') - ``` - -* `ActiveRecord::Relation#inspect` now makes it clear that you are dealing with a `Relation` object rather than an array: - - ```ruby - User.where(:age => 30).inspect - # => <ActiveRecord::Relation [#<User ...>, #<User ...>]> - - User.where(:age => 30).to_a.inspect - # => [#<User ...>, #<User ...>] - ``` +Please refer to the [Changelog](https://github.com/rails/rails/blob/master/railties/CHANGELOG.md) for detailed changes. - if more than 10 items are returned by the relation, inspect will only show the first 10 followed by ellipsis. +### Notable changes -* Add `:collation` and `:ctype` support to PostgreSQL. These are available for PostgreSQL 8.4 or later. +* Improve ways to write `change` migrations, making the old `up` & `down` methods no longer necessary. - ```yaml - development: - adapter: postgresql - host: localhost - database: rails_development - username: foo - password: bar - encoding: UTF8 - collation: ja_JP.UTF8 - ctype: ja_JP.UTF8 - ``` + * 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). + The method `change_table` is also reversible, as long as its block doesn't call `remove`, `change` or `change_default` -* `FinderMethods#exists?` now returns `false` with the `false` argument. + * New method `reversible` makes it possible to specify code to be run when migrating up or down. + See the [Guide on Migration](https://github.com/rails/rails/blob/master/guides/source/migrations.md#using-the-reversible-method) -* Added support for specifying the precision of a timestamp in the postgresql adapter. So, instead of having to incorrectly specify the precision using the `:limit` option, you may use `:precision`, as intended. For example, in a migration: + * New method `revert` will revert a whole migration or the given block. + 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) - ```ruby - def change - create_table :foobars do |t| - t.timestamps :precision => 0 - end - end - ``` +* Adds some metadata columns to `schema_migrations` table. -* Allow `ActiveRecord::Relation#pluck` to accept multiple columns. Returns an array of arrays containing the typecasted values: + * `migrated_at` + * `fingerprint` - an md5 hash of the migration. + * `name` - the filename minus version and extension. - ```ruby - Person.pluck(:id, :name) - # SELECT people.id, people.name FROM people - # => [[1, 'David'], [2, 'Jeremy'], [3, 'Jose']] - ``` +* Adds PostgreSQL array type support. Any datatype can be used to create an array column, with full migration and schema dumper support. -* Improve the derivation of HABTM join table name to take account of nesting. It now takes the table names of the two models, sorts them lexically and then joins them, stripping any common prefix from the second table name. Some examples: +* Add `Relation#load` to explicitly load the record and return `self`. - ``` - Top level models (Category <=> Product) - Old: categories_products - New: categories_products - - Top level models with a global table_name_prefix (Category <=> Product) - Old: site_categories_products - New: site_categories_products - - Nested models in a module without a table_name_prefix method (Admin::Category <=> Admin::Product) - Old: categories_products - New: categories_products - - Nested models in a module with a table_name_prefix method (Admin::Category <=> Admin::Product) - Old: categories_products - New: admin_categories_products - - Nested models in a parent model (Catalog::Category <=> Catalog::Product) - Old: categories_products - New: catalog_categories_products - - Nested models in different parent models (Catalog::Category <=> Content::Page) - Old: categories_pages - New: catalog_categories_content_pages - ``` - -* Move HABTM validity checks to `ActiveRecord::Reflection`. One side effect of this is to move when the exceptions are raised from the point of declaration to when the association is built. This is consistant with other association validity checks. - -* Added `stored_attributes` hash which contains the attributes stored using `ActiveRecord::Store`. This allows you to retrieve the list of attributes you've defined. - - ```ruby - class User < ActiveRecord::Base - store :settings, accessors: [:color, :homepage] - end - - User.stored_attributes[:settings] # [:color, :homepage] - ``` - -* PostgreSQL default log level is now 'warning', to bypass the noisy notice messages. You can change the log level using the `min_messages` option available in your `config/database.yml`. - -* Add uuid datatype support to PostgreSQL adapter. +* `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 `#destroy!` which acts like `#destroy` but will raise an `ActiveRecord::RecordNotDestroyed` exception instead of returning `false`. - -* Allow blocks for count with `ActiveRecord::Relation`, to work similar as `Array#count`: `Person.where("age > 26").count { |person| person.gender == 'female' }` - -* Added support to `CollectionAssociation#delete` for passing fixnum or string values as record ids. This finds the records responding to the ids and deletes them. - - ```ruby - class Person < ActiveRecord::Base - has_many :pets - end - - person.pets.delete("1") # => [#<Pet id: 1>] - person.pets.delete(2, 3) # => [#<Pet id: 2>, #<Pet id: 3>] - ``` - -* It's not possible anymore to destroy a model marked as read only. - -* Added ability to `ActiveRecord::Relation#from` to accept other `ActiveRecord::Relation` objects. - * Added custom coders support for `ActiveRecord::Store`. Now you can set your custom coder like this: - ```ruby - 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 `config/database.yml`. - -* Added default order to `ActiveRecord::Base#first` to assure consistent results among different database engines. Introduced `ActiveRecord::Base#take` as a replacement to the old behavior. - -* Added an `:index` option to automatically create indexes for `references` and `belongs_to` statements in migrations. This can be either a boolean or a hash that is identical to options available to the `add_index` method: - - ```ruby - create_table :messages do |t| - t.references :person, :index => true - end - ``` - - Is the same as: - - ```ruby - create_table :messages do |t| - t.references :person - end - add_index :messages, :person_id - ``` - - Generators have also been updated to use the new syntax. - -* Added bang methods for mutating `ActiveRecord::Relation` objects. For example, while `foo.where(:bar)` will return a new object leaving foo unchanged, `foo.where!(:bar)` will mutate the foo object. - -* Added `#find_by` and `#find_by!` to mirror the functionality provided by dynamic finders in a way that allows dynamic input more easily: - - ```ruby - Post.find_by name: 'Spartacus', rating: 4 - Post.find_by "published_at < ?", 2.weeks.ago - Post.find_by! name: 'Spartacus' - ``` - -* Added `ActiveRecord::Base#slice` to return a hash of the given methods with their names as keys and returned values as values. - -* Remove IdentityMap - IdentityMap has never graduated to be an "enabled-by-default" feature, due to some inconsistencies with associations, as described in this [commit](https://github.com/rails/rails/commit/302c912bf6bcd0fa200d964ec2dc4a44abe328a6). Hence the removal from the codebase, until such issues are fixed. - -* Added a feature to dump/load internal state of `SchemaCache` instance because we want to boot more quickly when we have many models. - - ```ruby - # execute rake task. - RAILS_ENV=production bundle exec rake db:schema:cache:dump - => generate db/schema_cache.dump - - # add config.use_schema_cache_dump = true in config/production.rb. BTW, true is default. - - # boot rails. - RAILS_ENV=production bundle exec rails server - => use db/schema_cache.dump - - # If you remove clear dumped cache, execute rake task. - RAILS_ENV=production bundle exec rake db:schema:cache:clear - => remove db/schema_cache.dump - ``` + store :settings, accessors: [ :color, :homepage ], coder: JSON -* Added support for partial indices to `PostgreSQL` adapter. +* `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`. -* The `add_index` method now supports a `where` option that receives a string with the partial index criteria. +* Remove IdentityMap. -* Added the `ActiveRecord::NullRelation` class implementing the null object pattern for the Relation class. - -* Implemented `ActiveRecord::Relation#none` method which returns a chainable relation with zero records (an instance of the `NullRelation` class). Any subsequent condition chained to the returned relation will continue generating an empty relation and will not fire any query to the database. +* 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. - ```ruby - create_join_table :products, :categories - # => - # create_table :categories_products, :id => false do |td| - # td.integer :product_id, :null => false - # td.integer :category_id, :null => false - # end - ``` - -* The primary key is always initialized in the `@attributes` hash to nil (unless another value has been specified). - -* In previous releases, the following would generate a single query with an OUTER JOIN comments, rather than two separate queries: - - ```ruby - Post.includes(:comments).where("comments.name = 'foo'") - ``` - - This behaviour relies on matching SQL string, which is an inherently flawed idea unless we write an SQL parser, which we do not wish to do. Therefore, it is now deprecated. - - To avoid deprecation warnings and for future compatibility, you must explicitly state which tables you reference, when using SQL snippets: - - ```ruby - Post.includes(:comments).where("comments.name = 'foo'").references(:comments) - ``` - - Note that you do not need to explicitly specify references in the following cases, as they can be automatically inferred: - - ```ruby - Post.where(comments: { name: 'foo' }) - Post.where('comments.name' => 'foo') - Post.order('comments.name') - ``` - - You also do not need to worry about this unless you are doing eager loading. Basically, don't worry unless you see a deprecation warning or (in future releases) an SQL error due to a missing JOIN. - -* Support for the `schema_info` table has been dropped. Please switch to `schema_migrations`. - -* Connections *must* be closed at the end of a thread. If not, your connection pool can fill and an exception will be raised. - -* PostgreSQL hstore records can be created. - -* PostgreSQL hstore types are automatically deserialized from the database. - -* Added `#update_columns` method which updates the attributes from the passed-in hash without calling save, hence skipping validations and callbacks. `ActiveRecordError` will be raised when called on new objects or when at least one of the attributes is marked as read only. - - ```ruby - post.attributes # => {"id"=>2, "title"=>"My title", "body"=>"My content", "author"=>"Peter"} - post.update_columns({title: 'New title', author: 'Sebastian'}) # => true - post.attributes # => {"id"=>2, "title"=>"New title", "body"=>"My content", "author"=>"Sebastian"} - ``` - -### Deprecations - -* Deprecated most of the 'dynamic finder' methods. All dynamic methods except for `find_by_...` and `find_by_...!` are deprecated. Here's how you can rewrite the code: - - ```ruby - find_all_by_... can be rewritten using where(...) - find_last_by_... can be rewritten using where(...).last - scoped_by_... can be rewritten using where(...) - find_or_initialize_by_... can be rewritten using where(...).first_or_initialize - find_or_create_by_... can be rewritten using where(...).first_or_create - find_or_create_by_...! can be rewritten using where(...).first_or_create! - ``` - - The implementation of the deprecated dynamic finders has been moved to the `active_record_deprecated_finders` gem. - -* Deprecated the old-style hash based finder API. This means that methods which previously accepted "finder options" no longer do. For example this: - - ```ruby - Post.find(:all, :conditions => { :comments_count => 10 }, :limit => 5) - ``` - - should be rewritten in the new style which has existed since Rails 3: - - ```ruby - Post.where(comments_count: 10).limit(5) - ``` - - Note that as an interim step, it is possible to rewrite the above as: - - ```ruby - Post.scoped(:where => { :comments_count => 10 }, :limit => 5) - ``` - - This could save you a lot of work if there is a lot of old-style finder usage in your application. - - Calling `Post.scoped(options)` is a shortcut for `Post.scoped.merge(options)`. `Relation#merge` now accepts a hash of options, but they must be identical to the names of the equivalent finder method. These are mostly identical to the old-style finder option names, except in the following cases: - - ``` - :conditions becomes :where - :include becomes :includes - :extend becomes :extending - ``` - - The code to implement the deprecated features has been moved out to the `active_record_deprecated_finders` gem. This gem is a dependency of Active Record in Rails 4.0. It will no longer be a dependency from Rails 4.1, but if your app relies on the deprecated features then you can add it to your own Gemfile. It will be maintained by the Rails core team until Rails 5.0 is released. - -* Deprecate eager-evaluated scopes. - - Don't use this: - - ```ruby - scope :red, where(color: 'red') - default_scope where(color: 'red') - ``` - - Use this: - - ```ruby - scope :red, -> { where(color: 'red') } - default_scope { where(color: 'red') } - ``` - - The former has numerous issues. It is a common newbie gotcha to do the following: - - ```ruby - scope :recent, where(published_at: Time.now - 2.weeks) - ``` - - Or a more subtle variant: - - ```ruby - scope :recent, -> { where(published_at: Time.now - 2.weeks) } - scope :recent_red, recent.where(color: 'red') - ``` - - Eager scopes are also very complex to implement within Active Record, and there are still bugs. For example, the following does not do what you expect: - - ```ruby - scope :remove_conditions, except(:where) - where(...).remove_conditions # => still has conditions - ``` - -* Added deprecation for the `:dependent => :restrict` association option. - -* Up until now `has_many` and `has_one, :dependent => :restrict` option raised a `DeleteRestrictionError` at the time of destroying the object. Instead, it will add an error on the model. - -* To fix this warning, make sure your code isn't relying on a `DeleteRestrictionError` and then add `config.active_record.dependent_restrict_raises = false` to your application config. - -* New rails application would be generated with the `config.active_record.dependent_restrict_raises = false` in the application config. - -* The migration generator now creates a join table with (commented) indexes every time the migration name contains the word "join_table". - -* `ActiveRecord::SessionStore` is removed from Rails 4.0 and is now a separate [gem](https://github.com/rails/activerecord-session_store). - -Active Model ------------- - -* Changed `AM::Serializers::JSON.include_root_in_json` default value to false. Now, AM Serializers and AR objects have the same default behaviour. - - ```ruby - class User < ActiveRecord::Base; end - - class Person - include ActiveModel::Model - include ActiveModel::AttributeMethods - include ActiveModel::Serializers::JSON - - attr_accessor :name, :age - - def attributes - instance_values - end - end - - user.as_json - => {"id"=>1, "name"=>"Konata Izumi", "age"=>16, "awesome"=>true} - # root is not included - - person.as_json - => {"name"=>"Francesco", "age"=>22} - # root is not included - ``` - -* Passing false hash values to `validates` will no longer enable the corresponding validators. - -* `ConfirmationValidator` error messages will attach to `:#{attribute}_confirmation` instead of `attribute`. - -* Added `ActiveModel::Model`, a mixin to make Ruby objects work with Action Pack out of the box. - -* `ActiveModel::Errors#to_json` supports a new parameter `:full_messages`. - -* Trims down the API by removing `valid?` and `errors.full_messages`. - -### Deprecations - -Active Resource ---------------- - -* Active Resource is removed from Rails 4.0 and is now a separate [gem](https://github.com/rails/activeresource). - -Active Support --------------- - -* Add default values to all `ActiveSupport::NumberHelper` methods, to avoid errors with empty locales or missing values. - -* `Time#change` now works with time values with offsets other than UTC or the local time zone. - -* Add `Time#prev_quarter` and `Time#next_quarter` short-hands for `months_ago(3)` and `months_since(3)`. - -* Add `Time#last_week`, `Time#last_month`, `Time#last_year` as aliases for `Time#prev_week`, `Time#prev_month`, and `Time#prev_year`. - -* Add `Date#last_week`, `Date#last_month`, `Date#last_year` as aliases for `Date#prev_week`, `Date#prev_month`, and `Date#prev_year`. - -* Remove obsolete and unused `require_association` method from dependencies. - -* Add `:instance_accessor` option for `config_accessor`. - - ```ruby - class User - include ActiveSupport::Configurable - config_accessor :allowed_access, instance_accessor: false - end - - User.new.allowed_access = true # => NoMethodError - User.new.allowed_access # => NoMethodError - ``` - -* `ActionView::Helpers::NumberHelper` methods have been moved to `ActiveSupport::NumberHelper` and are now available via `Numeric#to_s`. - -* `Numeric#to_s` now accepts the formatting options :phone, :currency, :percentage, :delimited, :rounded, :human, and :human_size. - -* Add `Hash#transform_keys`, `Hash#transform_keys!`, `Hash#deep_transform_keys` and `Hash#deep_transform_keys!`. - -* Changed xml type datetime to dateTime (with upper case letter T). - -* Add `:instance_accessor` option for `class_attribute`. - -* `constantize` now looks in the ancestor chain. - -* Add `Hash#deep_stringify_keys` and `Hash#deep_stringify_keys!` to convert all keys from a `Hash` instance into strings. - -* Add `Hash#deep_symbolize_keys` and `Hash#deep_symbolize_keys!` to convert all keys from a `Hash` instance into symbols. - -* `Object#try` can't call private methods. - -* AS::Callbacks#run_callbacks remove key argument. - -* `deep_dup` works more expectedly now and duplicates also values in `Hash` instances and elements in `Array` instances. - -* Inflector no longer applies ice -> ouse to words like slice, police. - -* Add `ActiveSupport::Deprecations.behavior = :silence` to completely ignore Rails runtime deprecations. - -* Make `Module#delegate` stop using send - can no longer delegate to private methods. - -* AS::Callbacks deprecate :rescuable option. - -* Adds `Integer#ordinal` to get the ordinal suffix string of an integer. - -* AS::Callbacks :per_key option is no longer supported. - -* AS::Callbacks#define_callbacks add :skip_after_callbacks_if_terminated option. - -* Add html_escape_once to ERB::Util, and delegate escape_once tag helper to it. - -* Remove `ActiveSupport::TestCase#pending` method, use `skip` instead. - -* Deletes the compatibility method `Module#method_names`, use `Module#methods` from now on (which returns symbols). - -* Deletes the compatibility method `Module#instance_method_names`, use `Module#instance_methods` from now on (which returns symbols). - -* Unicode database updated to 6.1.0. - -* Adds `encode_big_decimal_as_string` option to force JSON serialization of BigDecimals as numeric instead of wrapping them in strings for safety. +* Allows PostgreSQL hstore records to be created. ### Deprecations -* `ActiveSupport::Callbacks`: deprecate usage of filter object with `#before` and `#after` methods as `around` callback. +* Deprecated the old-style hash based finder API. This means that methods which previously accepted "finder options" no longer do. -* `BufferedLogger` is deprecated. Use `ActiveSupport::Logger` or the `logger` from Ruby stdlib. +* All dynamic methods except for `find_by_...` and `find_by_...!` are deprecated. Here's + how you can rewrite the code: -* Deprecates the compatibility method `Module#local_constant_names` and use `Module#local_constants` instead (which returns symbols). + * `find_all_by_...` can be rewritten using `where(...)`. + * `find_last_by_...` can be rewritten using `where(...).last`. + * `scoped_by_...` can be rewritten using `where(...)`. + * `find_or_initialize_by_...` can be rewritten using `where(...).first_or_initialize`. + * `find_or_create_by_...` can be rewritten using `find_or_create_by(...)` or `where(...).first_or_create`. + * `find_or_create_by_...!` can be rewritten using `find_or_create_by!(...)` or `where(...).first_or_create!`. Credits ------- diff --git a/guides/source/_welcome.html.erb b/guides/source/_welcome.html.erb index 9d2e9c1d68..a50961a0c7 100644 --- a/guides/source/_welcome.html.erb +++ b/guides/source/_welcome.html.erb @@ -1,4 +1,4 @@ -<h2>Ruby on Rails Guides (<%= @version %>)</h2> +<h2>Ruby on Rails Guides (<%= @edge ? @version[0, 7] : @version %>)</h2> <% if @edge %> <p> diff --git a/guides/source/action_controller_overview.md b/guides/source/action_controller_overview.md index 6f161e83ea..46ff9027fd 100644 --- a/guides/source/action_controller_overview.md +++ b/guides/source/action_controller_overview.md @@ -1,15 +1,17 @@ Action Controller Overview ========================== -In this guide you will learn how controllers work and how they fit into the request cycle in your application. After reading this guide, you will be able to: +In this guide you will learn how controllers work and how they fit into the request cycle in your application. -* Follow the flow of a request through a controller -* Understand why and how to store data in the session or cookies -* Work with filters to execute code during request processing -* Use Action Controller's built-in HTTP authentication -* Stream data directly to the user's browser -* Filter sensitive parameters so they do not appear in the application's log -* Deal with exceptions that may be raised during request processing +After reading this guide, you will know: + +* How to follow the flow of a request through a 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. +* How to stream data directly to the user's browser. +* How to filter sensitive parameters so they do not appear in the application's log. +* How to deal with exceptions that may be raised during request processing. -------------------------------------------------------------------------------- @@ -432,7 +434,7 @@ Filters are inherited, so if you set a filter on `ApplicationController`, it wil ```ruby class ApplicationController < ActionController::Base - before_filter :require_login + before_action :require_login private @@ -456,11 +458,11 @@ end The method simply stores an error message in the flash and redirects to the login form if the user is not logged in. If a "before" filter renders or redirects, the action will not run. If there are additional filters scheduled to run after that filter, they are also cancelled. -In this example the filter is added to `ApplicationController` and thus all controllers in the application inherit it. This will make everything in the application require the user to be logged in in order to use it. For obvious reasons (the user wouldn't be able to log in in the first place!), not all controllers or actions should require this. You can prevent this filter from running before particular actions with `skip_before_filter`: +In this example the filter is added to `ApplicationController` and thus all controllers in the application inherit it. This will make everything in the application require the user to be logged in in order to use it. For obvious reasons (the user wouldn't be able to log in in the first place!), not all controllers or actions should require this. You can prevent this filter from running before particular actions with `skip_before_action`: ```ruby class LoginsController < ApplicationController - skip_before_filter :require_login, only: [:new, :create] + skip_before_action :require_login, only: [:new, :create] end ``` @@ -478,7 +480,7 @@ For example, in a website where changes have an approval workflow an administrat ```ruby class ChangesController < ActionController::Base - around_filter :wrap_in_transaction, only: :show + around_action :wrap_in_transaction, only: :show private @@ -500,13 +502,13 @@ You can choose not to yield and build the response yourself, in which case the a ### Other Ways to Use Filters -While the most common way to use filters is by creating private methods and using *_filter to add them, there are two other ways to do the same thing. +While the most common way to use filters is by creating private methods and using *_action to add them, there are two other ways to do the same thing. -The first is to use a block directly with the *_filter methods. The block receives the controller as an argument, and the `require_login` filter from above could be rewritten to use a block: +The first is to use a block directly with the *_action methods. The block receives the controller as an argument, and the `require_login` filter from above could be rewritten to use a block: ```ruby class ApplicationController < ActionController::Base - before_filter do |controller| + before_action do |controller| redirect_to new_login_url unless controller.send(:logged_in?) end end @@ -518,7 +520,7 @@ The second way is to use a class (actually, any object that responds to the righ ```ruby class ApplicationController < ActionController::Base - before_filter LoginFilter + before_action LoginFilter end class LoginFilter @@ -646,7 +648,7 @@ HTTP digest authentication is superior to the basic authentication as it does no class AdminController < ApplicationController USERS = { "lifo" => "world" } - before_filter :authenticate + before_action :authenticate private @@ -749,15 +751,36 @@ Now the user can request to get a PDF version of a client just by adding ".pdf" GET /clients/1.pdf ``` -Parameter Filtering -------------------- +Log Filtering +------------- + +Rails keeps a log file for each environment in the `log` folder. These are extremely useful when debugging what's actually going on in your application, but in a live application you may not want every bit of information to be stored in the log file. + +### Parameters Filtering -Rails keeps a log file for each environment in the `log` folder. These are extremely useful when debugging what's actually going on in your application, but in a live application you may not want every bit of information to be stored in the log file. You can filter certain request parameters from your log files by appending them to `config.filter_parameters` in the application configuration. These parameters will be marked [FILTERED] in the log. +You can filter certain request parameters from your log files by appending them to `config.filter_parameters` in the application configuration. These parameters will be marked [FILTERED] in the log. ```ruby config.filter_parameters << :password ``` +### Redirects Filtering + +Sometimes it's desirable to filter out from log files some sensible locations your application is redirecting to. +You can do that by using the `config.filter_redirect` configuration option: + +```ruby +config.filter_redirect << 's3.amazonaws.com' +``` + +You can set it to a String, a Regexp, or an array of both. + +```ruby +config.filter_redirect.concat ['s3.amazonaws.com', /private_path/] +``` + +Matching URLs will be marked as '[FILTERED]'. + Rescue ------ @@ -805,7 +828,7 @@ end class ClientsController < ApplicationController # Check that the user has the right authorization to access clients. - before_filter :check_authorization + before_action :check_authorization # Note how the actions don't have to worry about all the auth stuff. def edit @@ -826,7 +849,7 @@ NOTE: Certain exceptions are only rescuable from the `ApplicationController` cla Force HTTPS protocol -------------------- -Sometime you might want to force a particular controller to only be accessible via an HTTPS protocol for security reasons. Since Rails 3.1 you can now use the `force_ssl` method in your controller to enforce that: +Sometime you might want to force a particular controller to only be accessible via an HTTPS protocol for security reasons. You can use the `force_ssl` method in your controller to enforce that: ```ruby class DinnerController @@ -834,7 +857,7 @@ class DinnerController end ``` -Just like the filter, you could also passing `:only` and `:except` to enforce the secure connection only to specific actions: +Just like the filter, you could also pass `:only` and `:except` to enforce the secure connection only to specific actions: ```ruby class DinnerController diff --git a/guides/source/action_mailer_basics.md b/guides/source/action_mailer_basics.md index fb26a3a6a3..795afd0150 100644 --- a/guides/source/action_mailer_basics.md +++ b/guides/source/action_mailer_basics.md @@ -3,9 +3,13 @@ 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. --------------------------------------------------------------------------------- +After reading this guide, you will know: -WARNING. This guide is based on Rails 3.2. Some of the code shown here will not work in earlier versions of Rails. +* How to send and receive email within a Rails application. +* 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 ------------ @@ -105,7 +109,7 @@ When you call the `mail` method now, Action Mailer will detect the two templates #### Wire It Up So That the System Sends the Email When a User Signs Up -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, in Rails 3, 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. +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. Setting this up is painfully simple. @@ -145,10 +149,6 @@ This provides a much simpler implementation that does not require the registerin The method `welcome_email` returns a `Mail::Message` object which can then just be told `deliver` to send itself out. -NOTE: In previous versions of Rails, you would call `deliver_welcome_email` or `create_welcome_email`. This has been deprecated in Rails 3.0 in favour of just calling the method name itself. - -WARNING: Sending out an email should only take a fraction of a second. If you are planning on sending out many emails, or you have a slow domain resolution service, you might want to investigate using a background process like Delayed Job. - ### Auto encoding header values Action Mailer now handles the auto encoding of multibyte characters inside of headers and bodies. @@ -377,23 +377,7 @@ If you use this setting, you should pass the `only_path: false` option when usin 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. If you want to explicitly alter the order, you can either change the `:parts_order` or explicitly render the parts in a different order: - -```ruby -class UserMailer < ActionMailer::Base - def welcome_email(user) - @user = user - @url = user_url(@user) - mail(to: user.email, - subject: 'Welcome to My Awesome Site') do |format| - format.html - format.text - end - end -end -``` - -Will put the HTML part first, and the plain text part second. +The order of the parts getting inserted is determined by the `:parts_order` inside of the `ActionMailer::Base.default` method. ### Sending Emails with Attachments @@ -413,7 +397,7 @@ 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. -#### Sending Emails with Dynamic Delivery Options +### 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. @@ -460,6 +444,57 @@ class UserMailer < ActionMailer::Base end ``` +Action Mailer Callbacks +--------------------------- + +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. + +* 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 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 + + def feedback_message(business, user) + @business = business + @user = user + mail + end + + def campaign_message(business, user) + @business = business + @user = user + end + + private + + def set_delivery_options + # You have access to the mail instance and @business and @user instance variables here + if @business && @business.has_smtp_settings? + mail.delivery_method.settings.merge!(@business.smtp_settings) + end + end + + def prevent_delivery_to_guests + if @user && @user.guest? + mail.perform_deliveries = false + end + end + + def set_business_headers + if @business + headers["X-SMTPAPI-CATEGORY"] = @business.code + end + end +end +``` + +* Mailer Filters abort further processing if body is set to a non-nil value. + Using Action Mailer Helpers --------------------------- @@ -481,7 +516,6 @@ The following configuration options are best made in one of the environment file |`perform_deliveries`|Determines whether deliveries are actually carried out when the `deliver` method is invoked on the Mail message. By default they are, but this can be turned off to help functional testing.| |`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.).| -|`async`|Setting this flag will turn on asynchronous message sending, message rendering and delivery will be pushed to `Rails.queue` for processing.| ### Example Action Mailer Configuration @@ -541,26 +575,3 @@ 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. - -Asynchronous ------------- - -Rails provides a Synchronous Queue by default. If you want to use an Asynchronous one you will need to configure an async Queue provider like Resque. Queue providers are supposed to have a Railtie where they configure it's own async queue. - -### Custom Queues - -If you need a different queue than `Rails.queue` for your mailer you can use `ActionMailer::Base.queue=`: - -```ruby -class WelcomeMailer < ActionMailer::Base - self.queue = MyQueue.new -end -``` - -or adding to your `config/environments/$RAILS_ENV.rb`: - -```ruby -config.action_mailer.queue = MyQueue.new -``` - -Your custom queue should expect a job that responds to `#run`. diff --git a/guides/source/action_view_overview.md b/guides/source/action_view_overview.md index 2625e237bf..4cdac43a7e 100644 --- a/guides/source/action_view_overview.md +++ b/guides/source/action_view_overview.md @@ -1,13 +1,13 @@ Action View Overview ==================== -In this guide you will learn: +After reading this guide, you will know: -* What Action View is and how to use it with Rails -* How best to use templates, partials, and layouts -* What helpers are provided by Action View and how to make your own -* How to use localized views -* How to use Action View outside of Rails +* What Action View is and how to use it with Rails. +* How best to use templates, partials, and layouts. +* What helpers are provided by Action View and how to make your own. +* How to use localized views. +* How to use Action View outside of Rails. -------------------------------------------------------------------------------- @@ -1263,8 +1263,6 @@ Creates a field set for grouping HTML form elements. Creates a file upload field. -Prior to Rails 3.1, if you are using file uploads, then you will need to set the multipart option for the form tag. Rails 3.1+ does this automatically. - ```html+erb <%= form_tag {action: "post"}, {multipart: true} do %> <label for="file">File to Upload</label> <%= file_field_tag "file" %> @@ -1486,7 +1484,7 @@ You can use the same technique to localize the rescue files in your public direc Since Rails doesn't restrict the symbols that you use to set I18n.locale, you can leverage this system to display different content depending on anything you like. For example, suppose you have some "expert" users that should see different pages from "normal" users. You could add the following to `app/controllers/application.rb`: ```ruby -before_filter :set_expert_locale +before_action :set_expert_locale def set_expert_locale I18n.locale = :expert if current_user.expert? diff --git a/guides/source/active_model_basics.md b/guides/source/active_model_basics.md index 92b51334a3..68ac26c681 100644 --- a/guides/source/active_model_basics.md +++ b/guides/source/active_model_basics.md @@ -1,16 +1,16 @@ Active Model Basics =================== -This guide should provide you with all you need to get started using model classes. Active Model allows for Action Pack helpers to interact with non-ActiveRecord models. Active Model also helps building custom ORMs for use outside of the Rails framework. +This guide should provide you with all you need to get started using model classes. Active Model allows for Action Pack helpers to interact with non-Active Record models. Active Model also helps building custom ORMs for use outside of the Rails framework. --------------------------------------------------------------------------------- +After reading this guide, you will know: -WARNING. This guide is based on Rails 3.0. Some of the code shown here will not work in earlier versions of Rails. +-------------------------------------------------------------------------------- Introduction ------------ -Active Model is a library containing various modules used in developing frameworks that need to interact with the Rails Action Pack library. Active Model provides a known set of interfaces for usage in classes. Some of modules are explained below. +Active Model is a library containing various modules used in developing frameworks that need to interact with the Rails Action Pack library. Active Model provides a known set of interfaces for usage in classes. Some of modules are explained below. ### AttributeMethods @@ -26,23 +26,21 @@ class Person attr_accessor :age -private - def reset_attribute(attribute) - send("#{attribute}=", 0) - end + private + def reset_attribute(attribute) + send("#{attribute}=", 0) + end - def attribute_highest?(attribute) - send(attribute) > 100 ? true : false - end - + def attribute_highest?(attribute) + send(attribute) > 100 + end end person = Person.new person.age = 110 person.age_highest? # true person.reset_age # 0 -person.age_highest? # false - +person.age_highest? # false ``` ### Callbacks @@ -87,14 +85,14 @@ class Person end person = Person.new -person.to_model == person #=> true -person.to_key #=> nil -person.to_param #=> nil +person.to_model == person # => true +person.to_key # => nil +person.to_param # => nil ``` ### Dirty -An object becomes dirty when it has gone through one or more changes to its attributes and has not been saved. This gives the ability to check whether an object has been changed or not. It also has attribute based accessor methods. Let's consider a Person class with attributes first_name and last_name +An object becomes dirty when it has gone through one or more changes to its attributes and has not been saved. This gives the ability to check whether an object has been changed or not. It also has attribute based accessor methods. Let's consider a Person class with attributes `first_name` and `last_name`: ```ruby require 'active_model' @@ -123,8 +121,8 @@ class Person def save @previously_changed = changes + # do save work... end - end ``` @@ -132,21 +130,22 @@ end ```ruby person = Person.new -person.first_name = "First Name" +person.changed? # => false -person.first_name #=> "First Name" -person.first_name = "First Name Changed" +person.first_name = "First Name" +person.first_name # => "First Name" -person.changed? #=> true +# returns if any attribute has changed. +person.changed? # => true -#returns an list of fields arry which all has been changed before saved. -person.changed #=> ["first_name"] +# returns a list of attributes that have changed before saving. +person.changed # => ["first_name"] -#returns a hash of the fields that have changed with their original values. -person.changed_attributes #=> {"first_name" => "First Name Changed"} +# returns a hash of the attributes that have changed with their original values. +person.changed_attributes # => {"first_name"=>nil} -#returns a hash of changes, with the attribute names as the keys, and the values will be an array of the old and new value for that field. -person.changes #=> {"first_name" => ["First Name","First Name Changed"]} +# returns a hash of changes, with the attribute names as the keys, and the values will be an array of the old and new value for that field. +person.changes # => {"first_name"=>[nil, "First Name"]} ``` #### Attribute based accessor methods @@ -154,28 +153,24 @@ person.changes #=> {"first_name" => ["First Name","First Name Changed"]} Track whether the particular attribute has been changed or not. ```ruby -#attr_name_changed? -person.first_name #=> "First Name" - -#assign some other value to first_name attribute -person.first_name = "First Name 1" - -person.first_name_changed? #=> true +# attr_name_changed? +person.first_name # => "First Name" +person.first_name_changed? # => true ``` Track what was the previous value of the attribute. ```ruby -#attr_name_was accessor -person.first_name_was #=> "First Name" +# attr_name_was accessor +person.first_name_was # => "First Name" ``` Track both previous and current value of the changed attribute. Returns an array if changed, else returns nil. ```ruby -#attr_name_change -person.first_name_change #=> ["First Name", "First Name 1"] -person.last_name_change #=> nil +# attr_name_change +person.first_name_change # => [nil, "First Name"] +person.last_name_change # => nil ``` ### Validations @@ -187,20 +182,19 @@ class Person include ActiveModel::Validations attr_accessor :name, :email, :token - + validates :name, presence: true - validates_format_of :email, with: /\A([^\s]+)((?:[-a-z0-9]\.)[a-z]{2,})\z/i + validates_format_of :email, with: /\A([^\s]+)((?:[-a-z0-9]\.)[a-z]{2,})\z/i validates! :token, presence: true - end person = Person.new(token: "2b1f325") -person.valid? #=> false -person.name = 'vishnu' -person.email = 'me' -person.valid? #=> false +person.valid? # => false +person.name = 'vishnu' +person.email = 'me' +person.valid? # => false person.email = 'me@vishnuatrai.com' -person.valid? #=> true +person.valid? # => true person.token = nil -person.valid? #=> raises ActiveModel::StrictValidationFailed +person.valid? # => raises ActiveModel::StrictValidationFailed ``` diff --git a/guides/source/active_record_basics.md b/guides/source/active_record_basics.md index 810a0263c0..c90f42c492 100644 --- a/guides/source/active_record_basics.md +++ b/guides/source/active_record_basics.md @@ -1,32 +1,51 @@ Active Record Basics ==================== -This guide is an introduction to Active Record. After reading this guide we hope that you'll learn: +This guide is an introduction to Active Record. -* 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 database -* Active Record schema naming conventions -* The concepts of database migrations, validations and callbacks +After reading this guide, you will know: + +* 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 + database. +* Active Record schema naming conventions. +* The concepts of database migrations, validations and callbacks. -------------------------------------------------------------------------------- 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 Object Relational Mapping system. +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 object on how to write to and read from the database. +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 +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 overall database access code. +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 to: +Active Record gives us several mechanisms, the most important being the ability +to: * Represent models and their data * Represent associations between these models @@ -37,14 +56,30 @@ Active Record gives us several mechanisms, the most important being the ability 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 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. ### 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 underscores. Examples: +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., `BookClub`) +* Model Class - Singular with the first letter of each word capitalized (e.g., +`BookClub`) | Model / Class | Table / Schema | | ------------- | -------------- | @@ -57,34 +92,52 @@ By default, Active Record uses some naming conventions to find out how the mappi ### Schema Conventions -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 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 automatically created. - -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 record is first created. -* `created_on` - Automatically gets set to the current date when the record is first created. -* `updated_at` - Automatically gets set to the current date and time whenever the record is updated. -* `updated_on` - Automatically gets set to the current date whenever the record is updated. -* `lock_version` - Adds [optimistic locking](http://api.rubyonrails.org/classes/ActiveRecord/Locking.html) to a model. -* `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 for each post. +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 + 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 + automatically created. + +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 + record is first created. +* `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 + a model. +* `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 + 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. Creating Active Record Models ----------------------------- -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: +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 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 that the `products` table was created using an SQL sentence like: +This will create a `Product` model, mapped to a `products` table at the +database. By doing this you'll also have the ability to map the columns of each +row in that table with the attributes of the instances of your model. Suppose +that the `products` table was created using an SQL sentence like: ```sql CREATE TABLE products ( @@ -94,7 +147,8 @@ CREATE TABLE products ( ); ``` -Following the table schema above, you would be able to write code like the following: +Following the table schema above, you would be able to write code like the +following: ```ruby p = Product.new @@ -105,9 +159,12 @@ 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 the default 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 +the default conventions. -You can use the `ActiveRecord::Base.table_name=` method to specify the table name that should be used: +You can use the `ActiveRecord::Base.table_name=` method to specify the table +name that should be used: ```ruby class Product < ActiveRecord::Base @@ -115,7 +172,9 @@ 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 definition: +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 class FunnyJoke < ActiveSupport::TestCase @@ -125,7 +184,8 @@ class FunnyJoke < ActiveSupport::TestCase end ``` -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: +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 class Product < ActiveRecord::Base @@ -136,93 +196,175 @@ 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 to allow an application to read and manipulate data stored within its tables. +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 object while `create` will return the object and save it to the database. +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`, the `create` method call will create and save a new record into the database: +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 - user = User.create(name: "David", occupation: "Code Artist") +user = User.create(name: "David", occupation: "Code Artist") ``` -Using the `new` method, an object can be created without being saved: +Using the `new` method, an object can be instantiated without being saved: ```ruby - user = User.new - user.name = "David" - user.occupation = "Code Artist" +user = User.new +user.name = "David" +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 object to that block for initialization: +Finally, if a block is provided, both `create` and `new` will yield the new +object to that block for initialization: ```ruby - user = User.new do |u| - u.name = "David" - u.occupation = "Code Artist" - end +user = User.new do |u| + u.name = "David" + u.occupation = "Code Artist" +end ``` ### Read -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. +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 - # return array with all records - users = User.all +# return array with all records +users = User.all ``` ```ruby - # return the first record - user = User.first +# return the first record +user = User.first ``` ```ruby - # return the first user named David - david = User.find_by_name('David') +# return the first user named David +david = User.find_by_name('David') ``` ```ruby - # find all users named David who are Code Artists and sort by created_at in reverse chronological order - users = User.where(name: 'David', occupation: 'Code Artist').order('created_at DESC') +# find all users named David who are Code Artists and sort by created_at in reverse chronological order +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 Query Interface](active_record_querying.html) guide. +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 and it can be saved to the database. +Once an Active Record object has been retrieved, its attributes can be modified +and it can be saved to the database. + +```ruby +user = User.find_by_name('David') +user.name = 'Dave' +user.save +``` + +A shorthand for this is to use a hash mapping attribute names to the desired +value, like so: + +```ruby +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 +`update_all` class method useful: ```ruby - user = User.find_by_name('David') - user.name = 'Dave' - user.save +User.update_all "max_login_attempts = 3, must_change_password = 'true'" ``` ### Delete -Likewise, once retrieved an Active Record object can be destroyed which removes it from the database. +Likewise, once retrieved an Active Record object can be destroyed which removes +it from the database. ```ruby - user = User.find_by_name('David') - user.destroy +user = User.find_by_name('David') +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 already in the database, follows a specific format and many more. You can learn more about validations in the [Active Record Validations and Callbacks guide](active_record_validations_callbacks.html#validations-overview). +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. +A quick example to illustrate: + +```ruby +class User < ActiveRecord::Base + validates_presence_of :name +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 +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 the [Active Record Validations and Callbacks guide](active_record_validations_callbacks.html#callbacks-overview). +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. Rails keeps track of which files have been committed to the database and provides rollback features. You can learn more about migrations in the [Active Record Migrations guide](migrations.html) +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 +class CreatePublications < ActiveRecord::Migration + def change + create_table :publications do |t| + t.string :title + t.text :description + t.references :publication_type + t.integer :publisher_id + t.string :publisher_type + t.boolean :single_issue + + t.timestamps + end + add_index :publications, :publication_type_id + end +end +``` + +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 +Migrations guide](migrations.html) diff --git a/guides/source/active_record_callbacks.md b/guides/source/active_record_callbacks.md new file mode 100644 index 0000000000..20959a1a35 --- /dev/null +++ b/guides/source/active_record_callbacks.md @@ -0,0 +1,362 @@ +Active Record Callbacks +======================= + +This guide teaches you how to hook into the life cycle of your Active Record +objects. + +After reading this guide, you will know: + +* The life cycle of Active Record objects. +* How to create callback methods that respond to events in the object life cycle. +* How to create special classes that encapsulate common behavior for your callbacks. + +-------------------------------------------------------------------------------- + +The Object Life Cycle +--------------------- + +During the normal operation of a Rails application, objects may be created, updated, and destroyed. Active Record provides hooks into this <em>object life cycle</em> so that you can control your application and its data. + +Callbacks allow you to trigger logic before or after an alteration of an object's state. + +Callbacks Overview +------------------ + +Callbacks are methods that get called at certain moments of an object's life cycle. With callbacks it is possible to write code that will run whenever an Active Record object is created, saved, updated, deleted, validated, or loaded from the database. + +### Callback Registration + +In order to use the available callbacks, you need to register them. You can implement the callbacks as ordinary methods and use a macro-style class method to register them as callbacks: + +```ruby +class User < ActiveRecord::Base + validates :login, :email, presence: true + + before_validation :ensure_login_has_a_value + + protected + def ensure_login_has_a_value + if login.nil? + self.login = email unless email.blank? + end + end +end +``` + +The macro-style class methods can also receive a block. Consider using this style if the code inside your block is so short that it fits in a single line: + +```ruby +class User < ActiveRecord::Base + validates :login, :email, presence: true + + before_create do |user| + user.name = user.login.capitalize if user.name.blank? + end +end +``` + +Callbacks can also be registered to only fire on certain lifecycle events: + +```ruby +class User < ActiveRecord::Base + before_validation :normalize_name, on: :create + + # :on takes an array as well + after_validation :set_location, on: [ :create, :update ] + + protected + def normalize_name + self.name = self.name.downcase.titleize + end + + def set_location + self.location = LocationService.query(self) + end +end +``` + +It is considered good practice to declare callback methods as protected or private. If left public, they can be called from outside of the model and violate the principle of object encapsulation. + +Available Callbacks +------------------- + +Here is a list with all the available Active Record callbacks, listed in the same order in which they will get called during the respective operations: + +### Creating an Object + +* `before_validation` +* `after_validation` +* `before_save` +* `around_save` +* `before_create` +* `around_create` +* `after_create` +* `after_save` + +### Updating an Object + +* `before_validation` +* `after_validation` +* `before_save` +* `around_save` +* `before_update` +* `around_update` +* `after_update` +* `after_save` + +### Destroying an Object + +* `before_destroy` +* `around_destroy` +* `after_destroy` + +WARNING. `after_save` runs both on create and update, but always _after_ the more specific callbacks `after_create` and `after_update`, no matter the order in which the macro calls were executed. + +### `after_initialize` and `after_find` + +The `after_initialize` callback will be called whenever an Active Record object is instantiated, either by directly using `new` or when a record is loaded from the database. It can be useful to avoid the need to directly override your Active Record `initialize` method. + +The `after_find` callback will be called whenever Active Record loads a record from the database. `after_find` is called before `after_initialize` if both are defined. + +The `after_initialize` and `after_find` callbacks have no `before_*` counterparts, but they can be registered just like the other Active Record callbacks. + +```ruby +class User < ActiveRecord::Base + after_initialize do |user| + puts "You have initialized an object!" + end + + after_find do |user| + puts "You have found an object!" + end +end + +>> User.new +You have initialized an object! +=> #<User id: nil> + +>> User.first +You have found an object! +You have initialized an object! +=> #<User id: 1> +``` + +Running Callbacks +----------------- + +The following methods trigger callbacks: + +* `create` +* `create!` +* `decrement!` +* `destroy` +* `destroy!` +* `destroy_all` +* `increment!` +* `save` +* `save!` +* `save(validate: false)` +* `toggle!` +* `update` +* `update_attribute` +* `update` +* `update!` +* `valid?` + +Additionally, the `after_find` callback is triggered by the following finder methods: + +* `all` +* `first` +* `find` +* `find_all_by_*` +* `find_by_*` +* `find_by_*!` +* `find_by_sql` +* `last` + +The `after_initialize` callback is triggered every time a new object of the class is initialized. + +NOTE: The `find_all_by_*`, `find_by_*` and `find_by_*!` methods are dynamic finders generated automatically for every attribute. Learn more about them at the [Dynamic finders section](active_record_querying.html#dynamic-finders) + +Skipping Callbacks +------------------ + +Just as with validations, it is also possible to skip callbacks. These methods should be used with caution, however, because important business rules and application logic may be kept in callbacks. Bypassing them without understanding the potential implications may lead to invalid data. + +* `decrement` +* `decrement_counter` +* `delete` +* `delete_all` +* `increment` +* `increment_counter` +* `toggle` +* `touch` +* `update_column` +* `update_columns` +* `update_all` +* `update_counters` + +Halting Execution +----------------- + +As you start registering new callbacks for your models, they will be queued for execution. This queue will include all your model's validations, the registered callbacks, and the database operation to be executed. + +The whole callback chain is wrapped in a transaction. If any _before_ callback method returns exactly `false` or raises an exception, the execution chain gets halted and a ROLLBACK is issued; _after_ callbacks can only accomplish that by raising an exception. + +WARNING. Raising an arbitrary exception may break code that expects `save` and its friends not to fail like that. The `ActiveRecord::Rollback` exception is thought precisely to tell Active Record a rollback is going on. That one is internally captured but not reraised. + +Relational Callbacks +-------------------- + +Callbacks work through model relationships, and can even be defined by them. Suppose an example where a user has many posts. A user's posts should be destroyed if the user is destroyed. Let's add an `after_destroy` callback to the `User` model by way of its relationship to the `Post` model: + +```ruby +class User < ActiveRecord::Base + has_many :posts, dependent: :destroy +end + +class Post < ActiveRecord::Base + after_destroy :log_destroy_action + + def log_destroy_action + puts 'Post destroyed' + end +end + +>> user = User.first +=> #<User id: 1> +>> user.posts.create! +=> #<Post id: 1, user_id: 1> +>> user.destroy +Post destroyed +=> #<User id: 1> +``` + +Conditional Callbacks +--------------------- + +As with validations, we can also make the calling of a callback method conditional on the satisfaction of a given predicate. We can do this using the `:if` and `:unless` options, which can take a symbol, a string, a `Proc` or an `Array`. You may use the `:if` option when you want to specify under which conditions the callback **should** be called. If you want to specify the conditions under which the callback **should not** be called, then you may use the `:unless` option. + +### Using `:if` and `:unless` with a `Symbol` + +You can associate the `:if` and `:unless` options with a symbol corresponding to the name of a predicate method that will get called right before the callback. When using the `:if` option, the callback won't be executed if the predicate method returns false; when using the `:unless` option, the callback won't be executed if the predicate method returns true. This is the most common option. Using this form of registration it is also possible to register several different predicates that should be called to check if the callback should be executed. + +```ruby +class Order < ActiveRecord::Base + before_save :normalize_card_number, if: :paid_with_card? +end +``` + +### Using `:if` and `:unless` with a String + +You can also use a string that will be evaluated using `eval` and hence needs to contain valid Ruby code. You should use this option only when the string represents a really short condition: + +```ruby +class Order < ActiveRecord::Base + before_save :normalize_card_number, if: "paid_with_card?" +end +``` + +### Using `:if` and `:unless` with a `Proc` + +Finally, it is possible to associate `:if` and `:unless` with a `Proc` object. This option is best suited when writing short validation methods, usually one-liners: + +```ruby +class Order < ActiveRecord::Base + before_save :normalize_card_number, + if: Proc.new { |order| order.paid_with_card? } +end +``` + +### Multiple Conditions for Callbacks + +When writing conditional callbacks, it is possible to mix both `:if` and `:unless` in the same callback declaration: + +```ruby +class Comment < ActiveRecord::Base + after_create :send_email_to_author, if: :author_wants_emails?, + unless: Proc.new { |comment| comment.post.ignore_comments? } +end +``` + +Callback Classes +---------------- + +Sometimes the callback methods that you'll write will be useful enough to be reused by other models. Active Record makes it possible to create classes that encapsulate the callback methods, so it becomes very easy to reuse them. + +Here's an example where we create a class with an `after_destroy` callback for a `PictureFile` model: + +```ruby +class PictureFileCallbacks + def after_destroy(picture_file) + if File.exists?(picture_file.filepath) + File.delete(picture_file.filepath) + end + end +end +``` + +When declared inside a class, as above, the callback methods will receive the model object as a parameter. We can now use the callback class in the model: + +```ruby +class PictureFile < ActiveRecord::Base + after_destroy PictureFileCallbacks.new +end +``` + +Note that we needed to instantiate a new `PictureFileCallbacks` object, since we declared our callback as an instance method. This is particularly useful if the callbacks make use of the state of the instantiated object. Often, however, it will make more sense to declare the callbacks as class methods: + +```ruby +class PictureFileCallbacks + def self.after_destroy(picture_file) + if File.exists?(picture_file.filepath) + File.delete(picture_file.filepath) + end + end +end +``` + +If the callback method is declared this way, it won't be necessary to instantiate a `PictureFileCallbacks` object. + +```ruby +class PictureFile < ActiveRecord::Base + after_destroy PictureFileCallbacks +end +``` + +You can declare as many callbacks as you want inside your callback classes. + +Transaction Callbacks +--------------------- + +There are two additional callbacks that are triggered by the completion of a database transaction: `after_commit` and `after_rollback`. These callbacks are very similar to the `after_save` callback except that they don't execute until after database changes have either been committed or rolled back. They are most useful when your active record models need to interact with external systems which are not part of the database transaction. + +Consider, for example, the previous example where the `PictureFile` model needs to delete a file after the corresponding record is destroyed. If anything raises an exception after the `after_destroy` callback is called and the transaction rolls back, the file will have been deleted and the model will be left in an inconsistent state. For example, suppose that `picture_file_2` in the code below is not valid and the `save!` method raises an error. + +```ruby +PictureFile.transaction do + picture_file_1.destroy + picture_file_2.save! +end +``` + +By using the `after_commit` callback we can account for this case. + +```ruby +class PictureFile < ActiveRecord::Base + attr_accessor :delete_file + + 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 + end + end +end +``` + +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 79d00ded0a..24f98f68ca 100644 --- a/guides/source/active_record_querying.md +++ b/guides/source/active_record_querying.md @@ -1,15 +1,17 @@ Active Record Query Interface ============================= -This guide covers different ways to retrieve data from the database using Active Record. By referring to this guide, you will be able to: +This guide covers different ways to retrieve data from the database using Active Record. -* Find records using a variety of methods and conditions -* Specify the order, retrieved attributes, grouping, and other properties of the found records -* Use eager loading to reduce the number of database queries needed for data retrieval -* Use dynamic finders methods -* Check for the existence of particular records -* Perform various calculations on Active Record models -* Run EXPLAIN on relations +After reading this guide, you will know: + +* How to find records using a variety of methods and conditions. +* How to specify the order, retrieved attributes, grouping, and other properties of the found records. +* How to use eager loading to reduce the number of database queries needed for data retrieval. +* How to use dynamic finders methods. +* How to check for the existence of particular records. +* How to perform various calculations on Active Record models. +* How to run EXPLAIN on relations. -------------------------------------------------------------------------------- @@ -466,7 +468,7 @@ The field name can also be a string: Client.where('locked' => true) ``` -In the case of a belongs_to relationship, an association key can be used to specify the model if an ActiveRecord object is used as the value. This method works with polymorphic relationships as well. +In the case of a belongs_to relationship, an association key can be used to specify the model if an Active Record object is used as the value. This method works with polymorphic relationships as well. ```ruby Post.where(author: author) @@ -503,6 +505,20 @@ 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`, `LIKE`, and `NOT LIKE` SQL queries can be built by `where.not`, `where.like`, and `where.not_like` respectively. + +```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. + Ordering -------- @@ -985,7 +1001,7 @@ SELECT categories.* FROM categories ### Specifying Conditions on the Joined Tables -You can specify conditions on the joined tables using the regular [Array](array-conditions) and [String](#pure-string-conditions) conditions. [Hash conditions](#hash-conditions) provides a special syntax for specifying conditions for the joined tables: +You can specify conditions on the joined tables using the regular [Array](#array-conditions) and [String](#pure-string-conditions) conditions. [Hash conditions](#hash-conditions) provides a special syntax for specifying conditions for the joined tables: ```ruby time_range = (Time.now.midnight - 1.day)..Time.now.midnight @@ -1188,7 +1204,7 @@ class Client < ActiveRecord::Base end ``` -### Removing all scoping +### Removing All Scoping If we wish to remove scoping for any reason we can use the `unscoped` method. This is especially useful if a `default_scope` is specified in the model and should not be @@ -1220,9 +1236,7 @@ You can specify an exclamation point (`!`) on the end of the dynamic finders to If you want to find both by name and locked, you can chain these finders together by simply typing "`and`" between the fields. For example, `Client.find_by_first_name_and_locked("Ryan", true)`. -WARNING: Up to and including Rails 3.1, when the number of arguments passed to a dynamic finder method is lesser than the number of fields, say `Client.find_by_name_and_locked("Ryan")`, the behavior is to pass `nil` as the missing argument. This is **unintentional** and this behavior will be changed in Rails 3.2 to throw an `ArgumentError`. - -Find or build a new object +Find or Build a New Object -------------------------- It's common that you need to find a record or create it if it doesn't exist. You can do that with the `find_or_create_by` and `find_or_create_by!` methods. diff --git a/guides/source/active_record_validations.md b/guides/source/active_record_validations.md new file mode 100644 index 0000000000..a911d6b941 --- /dev/null +++ b/guides/source/active_record_validations.md @@ -0,0 +1,1100 @@ +Active Record Validations +========================= + +This guide teaches you how to validate the state of objects before they go into +the database using Active Record's validations feature. + +After reading this guide, you will know: + +* How to use the built-in Active Record validation helpers. +* How to create your own custom validation methods. +* How to work with the error messages generated by the validation process. + +-------------------------------------------------------------------------------- + +Validations Overview +-------------------- + +Here's an example of a very simple validation: + +```ruby +class Person < ActiveRecord::Base + validates :name, presence: true +end + +Person.create(name: "John Doe").valid? # => true +Person.create(name: nil).valid? # => false +``` + +As you can see, our validation lets us know that our `Person` is not valid +without a `name` attribute. The second `Person` will not be persisted to the +database. + +Before we dig into more details, let's talk about how validations fit into the +big picture of your application. + +### Why Use Validations? + +Validations are used to ensure that only valid data is saved into your +database. For example, it may be important to your application to ensure that +every user provides a valid email address and mailing address. Model-level +validations are the best way to ensure that only valid data is saved into your +database. They are database agnostic, cannot be bypassed by end users, and are +convenient to test and maintain. Rails makes them easy to use, provides +built-in helpers for common needs, and allows you to create your own validation +methods as well. + +There are several other ways to validate data before it is saved into your +database, including native database constraints, client-side validations, +controller-level validations. Here's a summary of the pros and cons: + +* Database constraints and/or stored procedures make the validation mechanisms + database-dependent and can make testing and maintenance more difficult. + However, if your database is used by other applications, it may be a good + idea to use some constraints at the database level. Additionally, + database-level validations can safely handle some things (such as uniqueness + in heavily-used tables) that can be difficult to implement otherwise. +* Client-side validations can be useful, but are generally unreliable if used + alone. If they are implemented using JavaScript, they may be bypassed if + JavaScript is turned off in the user's browser. However, if combined with + other techniques, client-side validation can be a convenient way to provide + users with immediate feedback as they use your site. +* Controller-level validations can be tempting to use, but often become + unwieldy and difficult to test and maintain. Whenever possible, it's a good + idea to keep your controllers skinny, as it will make your application a + pleasure to work with in the long run. + +Choose these in certain, specific cases. It's the opinion of the Rails team +that model-level validations are the most appropriate in most circumstances. + +### When Does Validation Happen? + +There are two kinds of Active Record objects: those that correspond to a row +inside your database and those that do not. When you create a fresh object, for +example using the `new` method, that object does not belong to the database +yet. Once you call `save` upon that object it will be saved into the +appropriate database table. Active Record uses the `new_record?` instance +method to determine whether an object is already in the database or not. +Consider the following simple Active Record class: + +```ruby +class Person < ActiveRecord::Base +end +``` + +We can see how it works by looking at some `rails console` output: + +```ruby +$ rails console +>> p = Person.new(name: "John Doe") +=> #<Person id: nil, name: "John Doe", created_at: nil, updated_at: nil> +>> p.new_record? +=> true +>> p.save +=> true +>> p.new_record? +=> false +``` + +Creating and saving a new record will send an SQL `INSERT` operation to the +database. Updating an existing record will send an SQL `UPDATE` operation +instead. Validations are typically run before these commands are sent to the +database. If any validations fail, the object will be marked as invalid and +Active Record will not perform the `INSERT` or `UPDATE` operation. This avoids +storing an invalid object in the database. You can choose to have specific +validations run when an object is created, saved, or updated. + +CAUTION: There are many ways to change the state of an object in the database. +Some methods will trigger validations, but some will not. This means that it's +possible to save an object in the database in an invalid state if you aren't +careful. + +The following methods trigger validations, and will save the object to the +database only if the object is valid: + +* `create` +* `create!` +* `save` +* `save!` +* `update` +* `update` +* `update!` + +The bang versions (e.g. `save!`) raise an exception if the record is invalid. +The non-bang versions don't: `save` and `update` return `false`, +`create` and `update` just return the objects. + +### Skipping Validations + +The following methods skip validations, and will save the object to the +database regardless of its validity. They should be used with caution. + +* `decrement!` +* `decrement_counter` +* `increment!` +* `increment_counter` +* `toggle!` +* `touch` +* `update_all` +* `update_attribute` +* `update_column` +* `update_columns` +* `update_counters` + +Note that `save` also has the ability to skip validations if passed `validate: +false` as argument. This technique should be used with caution. + +* `save(validate: false)` + +### `valid?` and `invalid?` + +To verify whether or not an object is valid, Rails uses the `valid?` method. +You can also use this method on your own. `valid?` triggers your validations +and returns true if no errors were found in the object, and false otherwise. +As you saw above: + +```ruby +class Person < ActiveRecord::Base + validates :name, presence: true +end + +Person.create(name: "John Doe").valid? # => true +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 +validations. + +Note that an object instantiated with `new` will not report errors even if it's +technically invalid, because validations are not run when using `new`. + +```ruby +class Person < ActiveRecord::Base + validates :name, presence: true +end + +>> p = Person.new +#=> #<Person id: nil, name: nil> +>> p.errors +#=> {} + +>> p.valid? +#=> false +>> p.errors +#=> {name:["can't be blank"]} + +>> p = Person.create +#=> #<Person id: nil, name: nil> +>> p.errors +#=> {name:["can't be blank"]} + +>> p.save +#=> false + +>> p.save! +#=> ActiveRecord::RecordInvalid: Validation failed: Name can't be blank + +>> Person.create! +#=> ActiveRecord::RecordInvalid: Validation failed: Name can't be blank +``` + +`invalid?` is simply the inverse of `valid?`. It triggers your validations, +returning true if any errors were found in the object, and false otherwise. + +### `errors[]` + +To verify whether or not a particular attribute of an object is valid, you can +use `errors[:attribute]`. It returns an array of all the errors for +`:attribute`. If there are no errors on the specified attribute, an empty array +is returned. + +This method is only useful _after_ validations have been run, because it only +inspects the errors collection and does not trigger validations itself. It's +different from the `ActiveRecord::Base#invalid?` method explained above because +it doesn't verify the validity of the object as a whole. It only checks to see +whether there are errors found on an individual attribute of the object. + +```ruby +class Person < ActiveRecord::Base + validates :name, presence: true +end + +>> Person.new.errors[:name].any? # => false +>> Person.create.errors[:name].any? # => true +``` + +We'll cover validation errors in greater depth in the [Working with Validation +Errors](#working-with-validation-errors) section. For now, let's turn to the +built-in validation helpers that Rails provides by default. + +Validation Helpers +------------------ + +Active Record offers many pre-defined validation helpers that you can use +directly inside your class definitions. These helpers provide common validation +rules. Every time a validation fails, an error message is added to the object's +`errors` collection, and this message is associated with the attribute being +validated. + +Each helper accepts an arbitrary number of attribute names, so with a single +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 +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. + +### `acceptance` + +This method validates that a checkbox on the user interface was checked when a +form was submitted. This is typically used when the user needs to agree to your +application's terms of service, confirm reading some text, or any similar +concept. This validation is very specific to web applications and this +'acceptance' does not need to be recorded anywhere in your database (if you +don't have a field for it, the helper will just create a virtual attribute). + +```ruby +class Person < ActiveRecord::Base + validates :terms_of_service, acceptance: true +end +``` + +The default error message for this helper is _"must be accepted"_. + +It can receive an `:accept` option, which determines the value that will be +considered acceptance. It defaults to "1" and can be easily changed. + +```ruby +class Person < ActiveRecord::Base + validates :terms_of_service, acceptance: { accept: 'yes' } +end +``` + +### `validates_associated` + +You should use this helper when your model has associations with other models +and they also need to be validated. When you try to save your object, `valid?` +will be called upon each one of the associated objects. + +```ruby +class Library < ActiveRecord::Base + has_many :books + validates_associated :books +end +``` + +This validation will work with all of the association types. + +CAUTION: Don't use `validates_associated` on both ends of your associations. +They would call each other in an infinite loop. + +The default error message for `validates_associated` is _"is invalid"_. Note +that each associated object will contain its own `errors` collection; errors do +not bubble up to the calling model. + +### `confirmation` + +You should use this helper when you have two text fields that should receive +exactly the same content. For example, you may want to confirm an email address +or a password. This validation creates a virtual attribute whose name is the +name of the field that has to be confirmed with "_confirmation" appended. + +```ruby +class Person < ActiveRecord::Base + validates :email, confirmation: true +end +``` + +In your view template you could use something like + +```erb +<%= text_field :person, :email %> +<%= text_field :person, :email_confirmation %> +``` + +This check is performed only if `email_confirmation` is not `nil`. To require +confirmation, make sure to add a presence check for the confirmation attribute +(we'll take a look at `presence` later on this guide): + +```ruby +class Person < ActiveRecord::Base + validates :email, confirmation: true + validates :email_confirmation, presence: true +end +``` + +The default error message for this helper is _"doesn't match confirmation"_. + +### `exclusion` + +This helper validates that the attributes' values are not included in a given +set. In fact, this set can be any enumerable object. + +```ruby +class Account < ActiveRecord::Base + validates :subdomain, exclusion: { in: %w(www us ca jp), + message: "Subdomain %{value} is reserved." } +end +``` + +The `exclusion` helper has an option `:in` that receives the set of values that +will not be accepted for the validated attributes. The `:in` option has an +alias called `:within` that you can use for the same purpose, if you'd like to. +This example uses the `:message` option to show how you can include the +attribute's value. + +The default error message is _"is reserved"_. + +### `format` + +This helper validates the attributes' values by testing whether they match a +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" } +end +``` + +The default error message is _"is invalid"_. + +### `inclusion` + +This helper validates that the attributes' values are included in a given set. +In fact, this set can be any enumerable object. + +```ruby +class Coffee < ActiveRecord::Base + validates :size, inclusion: { in: %w(small medium large), + message: "%{value} is not a valid size" } +end +``` + +The `inclusion` helper has an option `:in` that receives the set of values that +will be accepted. The `:in` option has an alias called `:within` that you can +use for the same purpose, if you'd like to. The previous example uses the +`:message` option to show how you can include the attribute's value. + +The default error message for this helper is _"is not included in the list"_. + +### `length` + +This helper validates the length of the attributes' values. It provides a +variety of options, so you can specify length constraints in different ways: + +```ruby +class Person < ActiveRecord::Base + validates :name, length: { minimum: 2 } + validates :bio, length: { maximum: 500 } + validates :password, length: { in: 6..20 } + validates :registration_number, length: { is: 6 } +end +``` + +The possible length constraint options are: + +* `:minimum` - The attribute cannot have less than the specified length. +* `:maximum` - The attribute cannot have more than the specified length. +* `:in` (or `:within`) - The attribute length must be included in a given + interval. The value for this option must be a range. +* `:is` - The attribute length must be equal to the given value. + +The default error messages depend on the type of length validation being +performed. You can personalize these messages using the `:wrong_length`, +`:too_long`, and `:too_short` options and `%{count}` as a placeholder for the +number corresponding to the length constraint being used. You can still use the +`:message` option to specify an error message. + +```ruby +class Person < ActiveRecord::Base + validates :bio, length: { maximum: 1000, + too_long: "%{count} characters is the maximum allowed" } +end +``` + +This helper counts characters by default, but you can split the value in a +different way using the `:tokenizer` option: + +```ruby +class Essay < ActiveRecord::Base + validates :content, length: { + minimum: 300, + maximum: 400, + tokenizer: lambda { |str| str.scan(/\w+/) }, + too_short: "must have at least %{count} words", + too_long: "must have at most %{count} words" + } +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 +`:in` or `:within` have a lower limit of 1, you should either provide a +personalized message or call `presence` prior to `length`. + +The `size` helper is an alias for `length`. + +### `numericality` + +This helper validates that your attributes have only numeric values. By +default, it will match an optional sign followed by an integral or floating +point number. To specify that only integral numbers are allowed set +`:only_integer` to true. + +If you set `:only_integer` to `true`, then it will use the + +```ruby +/\A[+-]?\d+\Z/ +``` + +regular expression to validate the attribute's value. Otherwise, it will try to +convert the value to a number using `Float`. + +WARNING. Note that the regular expression above allows a trailing newline +character. + +```ruby +class Player < ActiveRecord::Base + validates :points, numericality: true + validates :games_played, numericality: { only_integer: true } +end +``` + +Besides `:only_integer`, this helper also accepts the following options to add +constraints to acceptable values: + +* `:greater_than` - Specifies the value must be greater than the supplied + value. The default error message for this option is _"must be greater than + %{count}"_. +* `:greater_than_or_equal_to` - Specifies the value must be greater than or + equal to the supplied value. The default error message for this option is + _"must be greater than or equal to %{count}"_. +* `:equal_to` - Specifies the value must be equal to the supplied value. The + default error message for this option is _"must be equal to %{count}"_. +* `:less_than` - Specifies the value must be less than the supplied value. The + default error message for this option is _"must be less than %{count}"_. +* `:less_than_or_equal_to` - Specifies the value must be less than or equal the + supplied value. The default error message for this option is _"must be less + than or equal to %{count}"_. +* `:odd` - Specifies the value must be an odd number if set to true. The + default error message for this option is _"must be odd"_. +* `:even` - Specifies the value must be an even number if set to true. The + default error message for this option is _"must be even"_. + +The default error message is _"is not a number"_. + +### `presence` + +This helper validates that the specified attributes are not empty. It uses the +`blank?` method to check if the value is 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, presence: true +end +``` + +If you want to be sure that an association is present, you'll need to test +whether the associated object itself is present, and not the foreign key used +to map the association. + +```ruby +class LineItem < ActiveRecord::Base + belongs_to :order + validates :order, presence: true +end +``` + +In order to validate associated records whose presence 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 presence of an object associated via a `has_one` or +`has_many` relationship, it will check that the object is neither `blank?` nor +`marked_for_destruction?`. + +Since `false.blank?` is true, if you want to validate the presence of a boolean +field you should use `validates :field_name, inclusion: { in: [true, false] }`. + +The default error message is _"can't be empty"_. + +### `uniqueness` + +This helper validates that the attribute's value is unique right before the +object gets saved. It does not create a uniqueness constraint in the database, +so it may happen that two different database connections create two records +with the same value for a column that you intend to be unique. To avoid that, +you must create a unique index in your database. + +```ruby +class Account < ActiveRecord::Base + validates :email, uniqueness: true +end +``` + +The validation happens by performing an SQL query into the model's table, +searching for an existing record with the same value in that attribute. + +There is a `:scope` option that you can use to specify other attributes that +are used to limit the uniqueness check: + +```ruby +class Holiday < ActiveRecord::Base + validates :name, uniqueness: { scope: :year, + message: "should happen once per year" } +end +``` + +There is also a `:case_sensitive` option that you can use to define whether the +uniqueness constraint will be case sensitive or not. This option defaults to +true. + +```ruby +class Person < ActiveRecord::Base + validates :name, uniqueness: { case_sensitive: false } +end +``` + +WARNING. Note that some databases are configured to perform case-insensitive +searches anyway. + +The default error message is _"has already been taken"_. + +### `validates_with` + +This helper passes the record to a separate class for validation. + +```ruby +class Person < ActiveRecord::Base + validates_with GoodnessValidator +end + +class GoodnessValidator < ActiveModel::Validator + def validate(record) + if record.first_name == "Evil" + record.errors[:base] << "This person is evil" + end + end +end +``` + +NOTE: Errors added to `record.errors[:base]` relate to the state of the record +as a whole, and not to a specific attribute. + +The `validates_with` helper takes a class, or a list of classes to use for +validation. There is no default error message for `validates_with`. You must +manually add errors to the record's errors collection in the validator class. + +To implement the validate method, you must have a `record` parameter defined, +which is the record to be validated. + +Like all other validations, `validates_with` takes the `:if`, `:unless` and +`:on` options. If you pass any other options, it will send those options to the +validator class as `options`: + +```ruby +class Person < ActiveRecord::Base + validates_with GoodnessValidator, fields: [:first_name, :last_name] +end + +class GoodnessValidator < ActiveModel::Validator + def validate(record) + if options[:fields].any?{|field| record.send(field) == "Evil" } + record.errors[:base] << "This person is evil" + end + end +end +``` + +### `validates_each` + +This helper validates attributes against a block. It doesn't have a predefined +validation function. You should create one using a block, and every attribute +passed to `validates_each` will be tested against it. In the following example, +we don't want names and surnames to begin with lower case. + +```ruby +class Person < ActiveRecord::Base + validates_each :name, :surname do |record, attr, value| + record.errors.add(attr, 'must start with upper case') if value =~ /\A[a-z]/ + end +end +``` + +The block receives the record, the attribute's name and the attribute's value. +You can do anything you like to check for valid data within the block. If your +validation fails, you should add an error message to the model, therefore +making it invalid. + +Common Validation Options +------------------------- + +These are common validation options: + +### `:allow_nil` + +The `:allow_nil` option skips the validation when the value being validated is +`nil`. + +```ruby +class Coffee < ActiveRecord::Base + validates :size, inclusion: { in: %w(small medium large), + message: "%{value} is not a valid size" }, allow_nil: true +end +``` + +### `:allow_blank` + +The `:allow_blank` option is similar to the `:allow_nil` option. This option +will let validation pass if the attribute's value is `blank?`, like `nil` or an +empty string for example. + +```ruby +class Topic < ActiveRecord::Base + validates :title, length: { is: 5 }, allow_blank: true +end + +Topic.create("title" => "").valid? # => true +Topic.create("title" => nil).valid? # => true +``` + +### `:message` + +As you've already seen, the `:message` option lets you specify the message that +will be added to the `errors` collection when validation fails. When this +option is not used, Active Record will use the respective default error message +for each validation helper. + +### `:on` + +The `:on` option lets you specify when the validation should happen. The +default behavior for all the built-in validation helpers is to be run on save +(both when you're creating a new record and when you're updating it). If you +want to change it, you can use `on: :create` to run the validation only when a +new record is created or `on: :update` to run the validation only when a record +is updated. + +```ruby +class Person < ActiveRecord::Base + # it will be possible to update email with a duplicated value + validates :email, uniqueness: true, on: :create + + # it will be possible to create the record with a non-numerical age + validates :age, numericality: true, on: :update + + # the default (validates on both create and update) + validates :name, presence: true, on: :save +end +``` + +Strict Validations +------------------ + +You can also specify validations to be strict and raise +`ActiveModel::StrictValidationFailed` when the object is invalid. + +```ruby +class Person < ActiveRecord::Base + validates :name, presence: { strict: true } +end + +Person.new.valid? # => ActiveModel::StrictValidationFailed: Name can't be blank +``` + +There is also an ability to pass custom exception to `:strict` option + +```ruby +class Person < ActiveRecord::Base + validates :token, presence: true, uniqueness: true, strict: TokenGenerationException +end + +Person.new.valid? # => TokenGenerationException: Token can't be blank +``` + +Conditional Validation +---------------------- + +Sometimes it will make sense to validate an object only when a given predicate +is satisfied. You can do that by using the `:if` and `:unless` options, which +can take a symbol, a string, a `Proc` or an `Array`. You may use the `:if` +option when you want to specify when the validation **should** happen. If you +want to specify when the validation **should not** happen, then you may use the +`:unless` option. + +### Using a Symbol with `:if` and `:unless` + +You can associate the `:if` and `:unless` options with a symbol corresponding +to the name of a method that will get called right before validation happens. +This is the most commonly used option. + +```ruby +class Order < ActiveRecord::Base + validates :card_number, presence: true, if: :paid_with_card? + + def paid_with_card? + payment_type == "card" + end +end +``` + +### Using a String with `:if` and `:unless` + +You can also use a string that will be evaluated using `eval` and needs to +contain valid Ruby code. You should use this option only when the string +represents a really short condition. + +```ruby +class Person < ActiveRecord::Base + validates :surname, presence: true, if: "name.nil?" +end +``` + +### Using a Proc with `:if` and `:unless` + +Finally, it's possible to associate `:if` and `:unless` with a `Proc` object +which will be called. Using a `Proc` object gives you the ability to write an +inline condition instead of a separate method. This option is best suited for +one-liners. + +```ruby +class Account < ActiveRecord::Base + validates :password, confirmation: true, + unless: Proc.new { |a| a.password.blank? } +end +``` + +### Grouping Conditional validations + +Sometimes it is useful to have multiple validations use one condition, it can +be easily achieved using `with_options`. + +```ruby +class User < ActiveRecord::Base + with_options if: :is_admin? do |admin| + admin.validates :password, length: { minimum: 10 } + admin.validates :email, presence: true + end +end +``` + +All validations inside of `with_options` block will have automatically passed +the condition `if: :is_admin?` + +### Combining Validation Conditions + +On the other hand, when multiple conditions define whether or not a validation +should happen, an `Array` can be used. Moreover, you can apply both `:if` and +`:unless` to the same validation. + +```ruby +class Computer < ActiveRecord::Base + validates :mouse, presence: true, + if: ["market.retail?", :desktop?] + unless: Proc.new { |c| c.trackpad.present? } +end +``` + +The validation only runs when all the `:if` conditions and none of the +`:unless` conditions are evaluated to `true`. + +Performing Custom Validations +----------------------------- + +When the built-in validation helpers are not enough for your needs, you can +write your own validators or validation methods as you prefer. + +### Custom Validators + +Custom validators are classes that extend `ActiveModel::Validator`. These +classes must implement a `validate` method which takes a record as an argument +and performs the validation on it. The custom validator is called using the +`validates_with` method. + +```ruby +class MyValidator < ActiveModel::Validator + def validate(record) + unless record.name.starts_with? 'X' + record.errors[:name] << 'Need a name starting with X please!' + end + end +end + +class Person + include ActiveModel::Validations + validates_with MyValidator +end +``` + +The easiest way to add custom validators for validating individual attributes +is with the convenient `ActiveModel::EachValidator`. In this case, the custom +validator class must implement a `validate_each` method which takes three +arguments: record, attribute and value which correspond to the instance, the +attribute to be validated and the value of the attribute in the passed +instance. + +```ruby +class EmailValidator < ActiveModel::EachValidator + def validate_each(record, attribute, value) + unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i + record.errors[attribute] << (options[:message] || "is not an email") + end + end +end + +class Person < ActiveRecord::Base + validates :email, presence: true, email: true +end +``` + +As shown in the example, you can also combine standard validations with your +own custom validators. + +### Custom Methods + +You can also create methods that verify the state of your models and add +messages to the `errors` collection when they are invalid. You must then +register these methods by using the `validate` class method, passing in the +symbols for the validation methods' names. + +You can pass more than one symbol for each class method and the respective +validations will be run in the same order as they were registered. + +```ruby +class Invoice < ActiveRecord::Base + validate :expiration_date_cannot_be_in_the_past, + :discount_cannot_be_greater_than_total_value + + def expiration_date_cannot_be_in_the_past + if expiration_date.present? && expiration_date < Date.today + errors.add(:expiration_date, "can't be in the past") + end + end + + def discount_cannot_be_greater_than_total_value + if discount > total_value + errors.add(:discount, "can't be greater than total value") + end + end +end +``` + +By default such validations will run every time you call `valid?`. It is also +possible to control when to run these custom validations by giving an `:on` +option to the `validate` method, with either: `:create` or `:update`. + +```ruby +class Invoice < ActiveRecord::Base + validate :active_customer, on: :create + + def active_customer + errors.add(:customer_id, "is not active") unless customer.active? + end +end +``` + +Working with Validation Errors +------------------------------ + +In addition to the `valid?` and `invalid?` methods covered earlier, Rails provides a number of methods for working with the `errors` collection and inquiring about the validity of objects. + +The following is a list of the most commonly used methods. Please refer to the `ActiveModel::Errors` documentation for a list of all the available methods. + +### `errors` + +Returns an instance of the class `ActiveModel::Errors` containing all errors. Each key is the attribute name and the value is an array of strings with all errors. + +```ruby +class Person < ActiveRecord::Base + validates :name, presence: true, length: { minimum: 3 } +end + +person = Person.new +person.valid? # => false +person.errors + # => {:name=>["can't be blank", "is too short (minimum is 3 characters)"]} + +person = Person.new(name: "John Doe") +person.valid? # => true +person.errors # => [] +``` + +### `errors[]` + +`errors[]` is used when you want to check the error messages for a specific attribute. It returns an array of strings with all error messages for the given attribute, each string with one error message. If there are no errors related to the attribute, it returns an empty array. + +```ruby +class Person < ActiveRecord::Base + validates :name, presence: true, length: { minimum: 3 } +end + +person = Person.new(name: "John Doe") +person.valid? # => true +person.errors[:name] # => [] + +person = Person.new(name: "JD") +person.valid? # => false +person.errors[:name] # => ["is too short (minimum is 3 characters)"] + +person = Person.new +person.valid? # => false +person.errors[:name] + # => ["can't be blank", "is too short (minimum is 3 characters)"] +``` + +### `errors.add` + +The `add` method lets you manually add messages that are related to particular attributes. You can use the `errors.full_messages` or `errors.to_a` methods to view the messages in the form they might be displayed to a user. Those particular messages get the attribute name prepended (and capitalized). `add` receives the name of the attribute you want to add the message to, and the message itself. + +```ruby +class Person < ActiveRecord::Base + def a_method_used_for_validation_purposes + errors.add(:name, "cannot contain the characters !@#%*()_-+=") + end +end + +person = Person.create(name: "!@#") + +person.errors[:name] + # => ["cannot contain the characters !@#%*()_-+="] + +person.errors.full_messages + # => ["Name cannot contain the characters !@#%*()_-+="] +``` + +Another way to do this is using `[]=` setter + +```ruby + class Person < ActiveRecord::Base + def a_method_used_for_validation_purposes + errors[:name] = "cannot contain the characters !@#%*()_-+=" + end + end + + person = Person.create(name: "!@#") + + person.errors[:name] + # => ["cannot contain the characters !@#%*()_-+="] + + person.errors.to_a + # => ["Name cannot contain the characters !@#%*()_-+="] +``` + +### `errors[:base]` + +You can add error messages that are related to the object's state as a whole, instead of being related to a specific attribute. You can use this method when you want to say that the object is invalid, no matter the values of its attributes. Since `errors[:base]` is an array, you can simply add a string to it and it will be used as an error message. + +```ruby +class Person < ActiveRecord::Base + def a_method_used_for_validation_purposes + errors[:base] << "This person is invalid because ..." + end +end +``` + +### `errors.clear` + +The `clear` method is used when you intentionally want to clear all the messages in the `errors` collection. Of course, calling `errors.clear` upon an invalid object won't actually make it valid: the `errors` collection will now be empty, but the next time you call `valid?` or any method that tries to save this object to the database, the validations will run again. If any of the validations fail, the `errors` collection will be filled again. + +```ruby +class Person < ActiveRecord::Base + validates :name, presence: true, length: { minimum: 3 } +end + +person = Person.new +person.valid? # => false +person.errors[:name] + # => ["can't be blank", "is too short (minimum is 3 characters)"] + +person.errors.clear +person.errors.empty? # => true + +p.save # => false + +p.errors[:name] +# => ["can't be blank", "is too short (minimum is 3 characters)"] +``` + +### `errors.size` + +The `size` method returns the total number of error messages for the object. + +```ruby +class Person < ActiveRecord::Base + validates :name, presence: true, length: { minimum: 3 } +end + +person = Person.new +person.valid? # => false +person.errors.size # => 2 + +person = Person.new(name: "Andrea", email: "andrea@example.com") +person.valid? # => true +person.errors.size # => 0 +``` + +Displaying Validation Errors in Views +------------------------------------- + +Once you've created a model and added validations, if that model is created via +a web form, you probably want to display an error message when one of the +validations fail. + +Because every application handles this kind of thing differently, Rails does +not include any view helpers to help you generate these messages directly. +However, due to the rich number of methods Rails gives you to interact with +validations in general, it's fairly easy to build your own. In addition, when +generating a scaffold, Rails will put some ERB into the `_form.html.erb` that +it generates that displays the full list of errors on that model. + +Assuming we have a model that's been saved in an instance variable named +`@post`, it looks like this: + +```ruby +<% if @post.errors.any? %> + <div id="error_explanation"> + <h2><%= pluralize(@post.errors.count, "error") %> prohibited this post from being saved:</h2> + + <ul> + <% @post.errors.full_messages.each do |msg| %> + <li><%= msg %></li> + <% end %> + </ul> + </div> +<% end %> +``` + +Furthermore, if you use the Rails form helpers to generate your forms, when +a validation error occurs on a field, it will generate an extra `<div>` around +the entry. + +``` +<div class="field_with_errors"> + <input id="post_title" name="post[title]" size="30" type="text" value=""> +</div> +``` + +You can then style this div however you'd like. The default scaffold that +Rails generates, for example, adds this CSS rule: + +``` +.field_with_errors { + padding: 2px; + background-color: red; + display: table; +} +``` + +This means that any field with an error ends up with a 2 pixel red border. diff --git a/guides/source/active_record_validations_callbacks.md b/guides/source/active_record_validations_callbacks.md deleted file mode 100644 index 5c27ccbf9e..0000000000 --- a/guides/source/active_record_validations_callbacks.md +++ /dev/null @@ -1,1368 +0,0 @@ -Active Record Validations and Callbacks -======================================= - -This guide teaches you how to hook into the life cycle of your Active Record objects. You will learn how to validate the state of objects before they go into the database, and how to perform custom operations at certain points in the object life cycle. - -After reading this guide and trying out the presented concepts, we hope that you'll be able to: - -* Understand the life cycle of Active Record objects -* Use the built-in Active Record validation helpers -* Create your own custom validation methods -* Work with the error messages generated by the validation process -* Create callback methods that respond to events in the object life cycle -* Create special classes that encapsulate common behavior for your callbacks -* Create Observers that respond to life cycle events outside of the original class - --------------------------------------------------------------------------------- - -The Object Life Cycle ---------------------- - -During the normal operation of a Rails application, objects may be created, updated, and destroyed. Active Record provides hooks into this <em>object life cycle</em> so that you can control your application and its data. - -Validations allow you to ensure that only valid data is stored in your database. Callbacks and observers allow you to trigger logic before or after an alteration of an object's state. - -Validations Overview --------------------- - -Before you dive into the detail of validations in Rails, you should understand a bit about how validations fit into the big picture. - -### Why Use Validations? - -Validations are used to ensure that only valid data is saved into your database. For example, it may be important to your application to ensure that every user provides a valid email address and mailing address. - -There are several ways to validate data before it is saved into your database, including native database constraints, client-side validations, controller-level validations, and model-level validations: - -* Database constraints and/or stored procedures make the validation mechanisms database-dependent and can make testing and maintenance more difficult. However, if your database is used by other applications, it may be a good idea to use some constraints at the database level. Additionally, database-level validations can safely handle some things (such as uniqueness in heavily-used tables) that can be difficult to implement otherwise. -* Client-side validations can be useful, but are generally unreliable if used alone. If they are implemented using JavaScript, they may be bypassed if JavaScript is turned off in the user's browser. However, if combined with other techniques, client-side validation can be a convenient way to provide users with immediate feedback as they use your site. -* Controller-level validations can be tempting to use, but often become unwieldy and difficult to test and maintain. Whenever possible, it's a good idea to [keep your controllers skinny](http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model), as it will make your application a pleasure to work with in the long run. -* Model-level validations are the best way to ensure that only valid data is saved into your database. They are database agnostic, cannot be bypassed by end users, and are convenient to test and maintain. Rails makes them easy to use, provides built-in helpers for common needs, and allows you to create your own validation methods as well. - -### When Does Validation Happen? - -There are two kinds of Active Record objects: those that correspond to a row inside your database and those that do not. When you create a fresh object, for example using the `new` method, that object does not belong to the database yet. Once you call `save` upon that object it will be saved into the appropriate database table. Active Record uses the `new_record?` instance method to determine whether an object is already in the database or not. Consider the following simple Active Record class: - -```ruby -class Person < ActiveRecord::Base -end -``` - -We can see how it works by looking at some `rails console` output: - -```ruby -$ rails console ->> p = Person.new(name: "John Doe") -=> #<Person id: nil, name: "John Doe", created_at: nil, updated_at: nil> ->> p.new_record? -=> true ->> p.save -=> true ->> p.new_record? -=> false -``` - -TIP: All lines starting with a dollar sign `$` are intended to be run on the command line. - -Creating and saving a new record will send an SQL `INSERT` operation to the database. Updating an existing record will send an SQL `UPDATE` operation instead. Validations are typically run before these commands are sent to the database. If any validations fail, the object will be marked as invalid and Active Record will not perform the `INSERT` or `UPDATE` operation. This helps to avoid storing an invalid object in the database. You can choose to have specific validations run when an object is created, saved, or updated. - -CAUTION: There are many ways to change the state of an object in the database. Some methods will trigger validations, but some will not. This means that it's possible to save an object in the database in an invalid state if you aren't careful. - -The following methods trigger validations, and will save the object to the database only if the object is valid: - -* `create` -* `create!` -* `save` -* `save!` -* `update` -* `update_attributes` -* `update_attributes!` - -The bang versions (e.g. `save!`) raise an exception if the record is invalid. The non-bang versions don't: `save` and `update_attributes` return `false`, `create` and `update` just return the objects. - -### Skipping Validations - -The following methods skip validations, and will save the object to the database regardless of its validity. They should be used with caution. - -* `decrement!` -* `decrement_counter` -* `increment!` -* `increment_counter` -* `toggle!` -* `touch` -* `update_all` -* `update_attribute` -* `update_column` -* `update_columns` -* `update_counters` - -Note that `save` also has the ability to skip validations if passed `validate: false` as argument. This technique should be used with caution. - -* `save(validate: false)` - -### `valid?` and `invalid?` - -To verify whether or not an object is valid, Rails uses the `valid?` method. You can also use this method on your own. `valid?` triggers your validations and returns true if no errors were found in the object, and false otherwise. - -```ruby -class Person < ActiveRecord::Base - validates :name, presence: true -end - -Person.create(name: "John Doe").valid? # => true -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 validations. - -Note that an object instantiated with `new` will not report errors even if it's technically invalid, because validations are not run when using `new`. - -```ruby -class Person < ActiveRecord::Base - validates :name, presence: true -end - ->> p = Person.new -#=> #<Person id: nil, name: nil> ->> p.errors -#=> {} - ->> p.valid? -#=> false ->> p.errors -#=> {name:["can't be blank"]} - ->> p = Person.create -#=> #<Person id: nil, name: nil> ->> p.errors -#=> {name:["can't be blank"]} - ->> p.save -#=> false - ->> p.save! -#=> ActiveRecord::RecordInvalid: Validation failed: Name can't be blank - ->> Person.create! -#=> ActiveRecord::RecordInvalid: Validation failed: Name can't be blank -``` - -`invalid?` is simply the inverse of `valid?`. It triggers your validations, returning true if any errors were found in the object, and false otherwise. - -### `errors[]` - -To verify whether or not a particular attribute of an object is valid, you can use `errors[:attribute]`. It returns an array of all the errors for `:attribute`. If there are no errors on the specified attribute, an empty array is returned. - -This method is only useful _after_ validations have been run, because it only inspects the errors collection and does not trigger validations itself. It's different from the `ActiveRecord::Base#invalid?` method explained above because it doesn't verify the validity of the object as a whole. It only checks to see whether there are errors found on an individual attribute of the object. - -```ruby -class Person < ActiveRecord::Base - validates :name, presence: true -end - ->> Person.new.errors[:name].any? # => false ->> Person.create.errors[:name].any? # => true -``` - -We'll cover validation errors in greater depth in the [Working with Validation Errors](#working-with-validation-errors) section. For now, let's turn to the built-in validation helpers that Rails provides by default. - -Validation Helpers ------------------- - -Active Record offers many pre-defined validation helpers that you can use directly inside your class definitions. These helpers provide common validation rules. Every time a validation fails, an error message is added to the object's `errors` collection, and this message is associated with the attribute being validated. - -Each helper accepts an arbitrary number of attribute names, so with a single 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 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. - -### `acceptance` - -Validates that a checkbox on the user interface was checked when a form was submitted. This is typically used when the user needs to agree to your application's terms of service, confirm reading some text, or any similar concept. This validation is very specific to web applications and this 'acceptance' does not need to be recorded anywhere in your database (if you don't have a field for it, the helper will just create a virtual attribute). - -```ruby -class Person < ActiveRecord::Base - validates :terms_of_service, acceptance: true -end -``` - -The default error message for this helper is "_must be accepted_". - -It can receive an `:accept` option, which determines the value that will be considered acceptance. It defaults to "1" and can be easily changed. - -```ruby -class Person < ActiveRecord::Base - validates :terms_of_service, acceptance: { accept: 'yes' } -end -``` - -### `validates_associated` - -You should use this helper when your model has associations with other models and they also need to be validated. When you try to save your object, `valid?` will be called upon each one of the associated objects. - -```ruby -class Library < ActiveRecord::Base - has_many :books - validates_associated :books -end -``` - -This validation will work with all of the association types. - -CAUTION: Don't use `validates_associated` on both ends of your associations. They would call each other in an infinite loop. - -The default error message for `validates_associated` is "_is invalid_". Note that each associated object will contain its own `errors` collection; errors do not bubble up to the calling model. - -### `confirmation` - -You should use this helper when you have two text fields that should receive exactly the same content. For example, you may want to confirm an email address or a password. This validation creates a virtual attribute whose name is the name of the field that has to be confirmed with "_confirmation" appended. - -```ruby -class Person < ActiveRecord::Base - validates :email, confirmation: true -end -``` - -In your view template you could use something like - -```erb -<%= text_field :person, :email %> -<%= text_field :person, :email_confirmation %> -``` - -This check is performed only if `email_confirmation` is not `nil`. To require confirmation, make sure to add a presence check for the confirmation attribute (we'll take a look at `presence` later on this guide): - -```ruby -class Person < ActiveRecord::Base - validates :email, confirmation: true - validates :email_confirmation, presence: true -end -``` - -The default error message for this helper is "_doesn't match confirmation_". - -### `exclusion` - -This helper validates that the attributes' values are not included in a given set. In fact, this set can be any enumerable object. - -```ruby -class Account < ActiveRecord::Base - validates :subdomain, exclusion: { in: %w(www us ca jp), - message: "Subdomain %{value} is reserved." } -end -``` - -The `exclusion` helper has an option `:in` that receives the set of values that will not be accepted for the validated attributes. The `:in` option has an alias called `:within` that you can use for the same purpose, if you'd like to. This example uses the `:message` option to show how you can include the attribute's value. - -The default error message is "_is reserved_". - -### `format` - -This helper validates the attributes' values by testing whether they match a 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" } -end -``` - -The default error message is "_is invalid_". - -### `inclusion` - -This helper validates that the attributes' values are included in a given set. In fact, this set can be any enumerable object. - -```ruby -class Coffee < ActiveRecord::Base - validates :size, inclusion: { in: %w(small medium large), - message: "%{value} is not a valid size" } -end -``` - -The `inclusion` helper has an option `:in` that receives the set of values that will be accepted. The `:in` option has an alias called `:within` that you can use for the same purpose, if you'd like to. The previous example uses the `:message` option to show how you can include the attribute's value. - -The default error message for this helper is "_is not included in the list_". - -### `length` - -This helper validates the length of the attributes' values. It provides a variety of options, so you can specify length constraints in different ways: - -```ruby -class Person < ActiveRecord::Base - validates :name, length: { minimum: 2 } - validates :bio, length: { maximum: 500 } - validates :password, length: { in: 6..20 } - validates :registration_number, length: { is: 6 } -end -``` - -The possible length constraint options are: - -* `:minimum` - The attribute cannot have less than the specified length. -* `:maximum` - The attribute cannot have more than the specified length. -* `:in` (or `:within`) - The attribute length must be included in a given interval. The value for this option must be a range. -* `:is` - The attribute length must be equal to the given value. - -The default error messages depend on the type of length validation being performed. You can personalize these messages using the `:wrong_length`, `:too_long`, and `:too_short` options and `%{count}` as a placeholder for the number corresponding to the length constraint being used. You can still use the `:message` option to specify an error message. - -```ruby -class Person < ActiveRecord::Base - validates :bio, length: { maximum: 1000, - too_long: "%{count} characters is the maximum allowed" } -end -``` - -This helper counts characters by default, but you can split the value in a different way using the `:tokenizer` option: - -```ruby -class Essay < ActiveRecord::Base - validates :content, length: { - minimum: 300, - maximum: 400, - tokenizer: lambda { |str| str.scan(/\w+/) }, - too_short: "must have at least %{count} words", - too_long: "must have at most %{count} words" - } -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 `:in` or `:within` have a lower limit of 1, you should either provide a personalized message or call `presence` prior to `length`. - -The `size` helper is an alias for `length`. - -### `numericality` - -This helper validates that your attributes have only numeric values. By default, it will match an optional sign followed by an integral or floating point number. To specify that only integral numbers are allowed set `:only_integer` to true. - -If you set `:only_integer` to `true`, then it will use the - -```ruby -/\A[+-]?\d+\Z/ -``` - -regular expression to validate the attribute's value. Otherwise, it will try to convert the value to a number using `Float`. - -WARNING. Note that the regular expression above allows a trailing newline character. - -```ruby -class Player < ActiveRecord::Base - validates :points, numericality: true - validates :games_played, numericality: { only_integer: true } -end -``` - -Besides `:only_integer`, this helper also accepts the following options to add constraints to acceptable values: - -* `:greater_than` - Specifies the value must be greater than the supplied value. The default error message for this option is "_must be greater than %{count}_". -* `:greater_than_or_equal_to` - Specifies the value must be greater than or equal to the supplied value. The default error message for this option is "_must be greater than or equal to %{count}_". -* `:equal_to` - Specifies the value must be equal to the supplied value. The default error message for this option is "_must be equal to %{count}_". -* `:less_than` - Specifies the value must be less than the supplied value. The default error message for this option is "_must be less than %{count}_". -* `:less_than_or_equal_to` - Specifies the value must be less than or equal the supplied value. The default error message for this option is "_must be less than or equal to %{count}_". -* `:odd` - Specifies the value must be an odd number if set to true. The default error message for this option is "_must be odd_". -* `:even` - Specifies the value must be an even number if set to true. The default error message for this option is "_must be even_". - -The default error message is "_is not a number_". - -### `presence` - -This helper validates that the specified attributes are not empty. It uses the `blank?` method to check if the value is 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, presence: true -end -``` - -If you want to be sure that an association is present, you'll need to test whether the foreign key used to map the association is present, and not the associated object itself. - -```ruby -class LineItem < ActiveRecord::Base - belongs_to :order - validates :order_id, presence: true -end -``` - -If you validate the presence of an object associated via a `has_one` or `has_many` relationship, it will check that the object is neither `blank?` nor `marked_for_destruction?`. - -Since `false.blank?` is true, if you want to validate the presence of a boolean field you should use `validates :field_name, inclusion: { in: [true, false] }`. - -The default error message is "_can't be empty_". - -### `uniqueness` - -This helper validates that the attribute's value is unique right before the object gets saved. It does not create a uniqueness constraint in the database, so it may happen that two different database connections create two records with the same value for a column that you intend to be unique. To avoid that, you must create a unique index in your database. - -```ruby -class Account < ActiveRecord::Base - validates :email, uniqueness: true -end -``` - -The validation happens by performing an SQL query into the model's table, searching for an existing record with the same value in that attribute. - -There is a `:scope` option that you can use to specify other attributes that are used to limit the uniqueness check: - -```ruby -class Holiday < ActiveRecord::Base - validates :name, uniqueness: { scope: :year, - message: "should happen once per year" } -end -``` - -There is also a `:case_sensitive` option that you can use to define whether the uniqueness constraint will be case sensitive or not. This option defaults to true. - -```ruby -class Person < ActiveRecord::Base - validates :name, uniqueness: { case_sensitive: false } -end -``` - -WARNING. Note that some databases are configured to perform case-insensitive searches anyway. - -The default error message is "_has already been taken_". - -### `validates_with` - -This helper passes the record to a separate class for validation. - -```ruby -class Person < ActiveRecord::Base - validates_with GoodnessValidator -end - -class GoodnessValidator < ActiveModel::Validator - def validate(record) - if record.first_name == "Evil" - record.errors[:base] << "This person is evil" - end - end -end -``` - -NOTE: Errors added to `record.errors[:base]` relate to the state of the record as a whole, and not to a specific attribute. - -The `validates_with` helper takes a class, or a list of classes to use for validation. There is no default error message for `validates_with`. You must manually add errors to the record's errors collection in the validator class. - -To implement the validate method, you must have a `record` parameter defined, which is the record to be validated. - -Like all other validations, `validates_with` takes the `:if`, `:unless` and `:on` options. If you pass any other options, it will send those options to the validator class as `options`: - -```ruby -class Person < ActiveRecord::Base - validates_with GoodnessValidator, fields: [:first_name, :last_name] -end - -class GoodnessValidator < ActiveModel::Validator - def validate(record) - if options[:fields].any?{|field| record.send(field) == "Evil" } - record.errors[:base] << "This person is evil" - end - end -end -``` - -### `validates_each` - -This helper validates attributes against a block. It doesn't have a predefined validation function. You should create one using a block, and every attribute passed to `validates_each` will be tested against it. In the following example, we don't want names and surnames to begin with lower case. - -```ruby -class Person < ActiveRecord::Base - validates_each :name, :surname do |record, attr, value| - record.errors.add(attr, 'must start with upper case') if value =~ /\A[a-z]/ - end -end -``` - -The block receives the record, the attribute's name and the attribute's value. You can do anything you like to check for valid data within the block. If your validation fails, you should add an error message to the model, therefore making it invalid. - -Common Validation Options -------------------------- - -These are common validation options: - -### `:allow_nil` - -The `:allow_nil` option skips the validation when the value being validated is `nil`. - -```ruby -class Coffee < ActiveRecord::Base - validates :size, inclusion: { in: %w(small medium large), - message: "%{value} is not a valid size" }, allow_nil: true -end -``` - -TIP: `:allow_nil` is ignored by the presence validator. - -### `:allow_blank` - -The `:allow_blank` option is similar to the `:allow_nil` option. This option will let validation pass if the attribute's value is `blank?`, like `nil` or an empty string for example. - -```ruby -class Topic < ActiveRecord::Base - validates :title, length: { is: 5 }, allow_blank: true -end - -Topic.create("title" => "").valid? # => true -Topic.create("title" => nil).valid? # => true -``` - -TIP: `:allow_blank` is ignored by the presence validator. - -### `:message` - -As you've already seen, the `:message` option lets you specify the message that will be added to the `errors` collection when validation fails. When this option is not used, Active Record will use the respective default error message for each validation helper. - -### `:on` - -The `:on` option lets you specify when the validation should happen. The default behavior for all the built-in validation helpers is to be run on save (both when you're creating a new record and when you're updating it). If you want to change it, you can use `on: :create` to run the validation only when a new record is created or `on: :update` to run the validation only when a record is updated. - -```ruby -class Person < ActiveRecord::Base - # it will be possible to update email with a duplicated value - validates :email, uniqueness: true, on: :create - - # it will be possible to create the record with a non-numerical age - validates :age, numericality: true, on: :update - - # the default (validates on both create and update) - validates :name, presence: true, on: :save -end -``` - -Strict Validations ------------------- - -You can also specify validations to be strict and raise `ActiveModel::StrictValidationFailed` when the object is invalid. - -```ruby -class Person < ActiveRecord::Base - validates :name, presence: { strict: true } -end - -Person.new.valid? #=> ActiveModel::StrictValidationFailed: Name can't be blank -``` - -There is also an ability to pass custom exception to `:strict` option - -```ruby -class Person < ActiveRecord::Base - validates :token, presence: true, uniqueness: true, strict: TokenGenerationException -end - -Person.new.valid? #=> TokenGenerationException: Token can't be blank -``` - -Conditional Validation ----------------------- - -Sometimes it will make sense to validate an object just when a given predicate is satisfied. You can do that by using the `:if` and `:unless` options, which can take a symbol, a string, a `Proc` or an `Array`. You may use the `:if` option when you want to specify when the validation **should** happen. If you want to specify when the validation **should not** happen, then you may use the `:unless` option. - -### Using a Symbol with `:if` and `:unless` - -You can associate the `:if` and `:unless` options with a symbol corresponding to the name of a method that will get called right before validation happens. This is the most commonly used option. - -```ruby -class Order < ActiveRecord::Base - validates :card_number, presence: true, if: :paid_with_card? - - def paid_with_card? - payment_type == "card" - end -end -``` - -### Using a String with `:if` and `:unless` - -You can also use a string that will be evaluated using `eval` and needs to contain valid Ruby code. You should use this option only when the string represents a really short condition. - -```ruby -class Person < ActiveRecord::Base - validates :surname, presence: true, if: "name.nil?" -end -``` - -### Using a Proc with `:if` and `:unless` - -Finally, it's possible to associate `:if` and `:unless` with a `Proc` object which will be called. Using a `Proc` object gives you the ability to write an inline condition instead of a separate method. This option is best suited for one-liners. - -```ruby -class Account < ActiveRecord::Base - validates :password, confirmation: true, - unless: Proc.new { |a| a.password.blank? } -end -``` - -### Grouping conditional validations - -Sometimes it is useful to have multiple validations use one condition, it can be easily achieved using `with_options`. - -```ruby -class User < ActiveRecord::Base - with_options if: :is_admin? do |admin| - admin.validates :password, length: { minimum: 10 } - admin.validates :email, presence: true - end -end -``` - -All validations inside of `with_options` block will have automatically passed the condition `if: :is_admin?` - -### Combining validation conditions - -On the other hand, when multiple conditions define whether or not a validation should happen, an `Array` can be used. Moreover, you can apply both `:if` and `:unless` to the same validation. - -```ruby -class Computer < ActiveRecord::Base - validates :mouse, presence: true, - if: ["market.retail?", :desktop?] - unless: Proc.new { |c| c.trackpad.present? } -end -``` - -The validation only runs when all the `:if` conditions and none of the `:unless` conditions are evaluated to `true`. - -Performing Custom Validations ------------------------------ - -When the built-in validation helpers are not enough for your needs, you can write your own validators or validation methods as you prefer. - -### Custom Validators - -Custom validators are classes that extend `ActiveModel::Validator`. These classes must implement a `validate` method which takes a record as an argument and performs the validation on it. The custom validator is called using the `validates_with` method. - -```ruby -class MyValidator < ActiveModel::Validator - def validate(record) - unless record.name.starts_with? 'X' - record.errors[:name] << 'Need a name starting with X please!' - end - end -end - -class Person - include ActiveModel::Validations - validates_with MyValidator -end -``` - -The easiest way to add custom validators for validating individual attributes is with the convenient `ActiveModel::EachValidator`. In this case, the custom validator class must implement a `validate_each` method which takes three arguments: record, attribute and value which correspond to the instance, the attribute to be validated and the value of the attribute in the passed instance. - -```ruby -class EmailValidator < ActiveModel::EachValidator - def validate_each(record, attribute, value) - unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i - record.errors[attribute] << (options[:message] || "is not an email") - end - end -end - -class Person < ActiveRecord::Base - validates :email, presence: true, email: true -end -``` - -As shown in the example, you can also combine standard validations with your own custom validators. - -### Custom Methods - -You can also create methods that verify the state of your models and add messages to the `errors` collection when they are invalid. You must then register these methods by using the `validate` class method, passing in the symbols for the validation methods' names. - -You can pass more than one symbol for each class method and the respective validations will be run in the same order as they were registered. - -```ruby -class Invoice < ActiveRecord::Base - validate :expiration_date_cannot_be_in_the_past, - :discount_cannot_be_greater_than_total_value - - def expiration_date_cannot_be_in_the_past - if !expiration_date.blank? and expiration_date < Date.today - errors.add(:expiration_date, "can't be in the past") - end - end - - def discount_cannot_be_greater_than_total_value - if discount > total_value - errors.add(:discount, "can't be greater than total value") - end - end -end -``` - -By default such validations will run every time you call `valid?`. It is also possible to control when to run these custom validations by giving an `:on` option to the `validate` method, with either: `:create` or `:update`. - -```ruby -class Invoice < ActiveRecord::Base - validate :active_customer, on: :create - - def active_customer - errors.add(:customer_id, "is not active") unless customer.active? - end -end -``` - -You can even create your own validation helpers and reuse them in several different models. For example, an application that manages surveys may find it useful to express that a certain field corresponds to a set of choices: - -```ruby -ActiveRecord::Base.class_eval do - def self.validates_as_choice(attr_name, n, options={}) - validates attr_name, inclusion: { { in: 1..n }.merge!(options) } - end -end -``` - -Simply reopen `ActiveRecord::Base` and define a class method like that. You'd typically put this code somewhere in `config/initializers`. You can use this helper like this: - -```ruby -class Movie < ActiveRecord::Base - validates_as_choice :rating, 5 -end -``` - -Working with Validation Errors ------------------------------- - -In addition to the `valid?` and `invalid?` methods covered earlier, Rails provides a number of methods for working with the `errors` collection and inquiring about the validity of objects. - -The following is a list of the most commonly used methods. Please refer to the `ActiveModel::Errors` documentation for a list of all the available methods. - -### `errors` - -Returns an instance of the class `ActiveModel::Errors` containing all errors. Each key is the attribute name and the value is an array of strings with all errors. - -```ruby -class Person < ActiveRecord::Base - validates :name, presence: true, length: { minimum: 3 } -end - -person = Person.new -person.valid? # => false -person.errors - # => {:name=>["can't be blank", "is too short (minimum is 3 characters)"]} - -person = Person.new(name: "John Doe") -person.valid? # => true -person.errors # => [] -``` - -### `errors[]` - -`errors[]` is used when you want to check the error messages for a specific attribute. It returns an array of strings with all error messages for the given attribute, each string with one error message. If there are no errors related to the attribute, it returns an empty array. - -```ruby -class Person < ActiveRecord::Base - validates :name, presence: true, length: { minimum: 3 } -end - -person = Person.new(name: "John Doe") -person.valid? # => true -person.errors[:name] # => [] - -person = Person.new(name: "JD") -person.valid? # => false -person.errors[:name] # => ["is too short (minimum is 3 characters)"] - -person = Person.new -person.valid? # => false -person.errors[:name] - # => ["can't be blank", "is too short (minimum is 3 characters)"] -``` - -### `errors.add` - -The `add` method lets you manually add messages that are related to particular attributes. You can use the `errors.full_messages` or `errors.to_a` methods to view the messages in the form they might be displayed to a user. Those particular messages get the attribute name prepended (and capitalized). `add` receives the name of the attribute you want to add the message to, and the message itself. - -```ruby -class Person < ActiveRecord::Base - def a_method_used_for_validation_purposes - errors.add(:name, "cannot contain the characters !@#%*()_-+=") - end -end - -person = Person.create(name: "!@#") - -person.errors[:name] - # => ["cannot contain the characters !@#%*()_-+="] - -person.errors.full_messages - # => ["Name cannot contain the characters !@#%*()_-+="] -``` - -Another way to do this is using `[]=` setter - -```ruby - class Person < ActiveRecord::Base - def a_method_used_for_validation_purposes - errors[:name] = "cannot contain the characters !@#%*()_-+=" - end - end - - person = Person.create(name: "!@#") - - person.errors[:name] - # => ["cannot contain the characters !@#%*()_-+="] - - person.errors.to_a - # => ["Name cannot contain the characters !@#%*()_-+="] -``` - -### `errors[:base]` - -You can add error messages that are related to the object's state as a whole, instead of being related to a specific attribute. You can use this method when you want to say that the object is invalid, no matter the values of its attributes. Since `errors[:base]` is an array, you can simply add a string to it and it will be used as an error message. - -```ruby -class Person < ActiveRecord::Base - def a_method_used_for_validation_purposes - errors[:base] << "This person is invalid because ..." - end -end -``` - -### `errors.clear` - -The `clear` method is used when you intentionally want to clear all the messages in the `errors` collection. Of course, calling `errors.clear` upon an invalid object won't actually make it valid: the `errors` collection will now be empty, but the next time you call `valid?` or any method that tries to save this object to the database, the validations will run again. If any of the validations fail, the `errors` collection will be filled again. - -```ruby -class Person < ActiveRecord::Base - validates :name, presence: true, length: { minimum: 3 } -end - -person = Person.new -person.valid? # => false -person.errors[:name] - # => ["can't be blank", "is too short (minimum is 3 characters)"] - -person.errors.clear -person.errors.empty? # => true - -p.save # => false - -p.errors[:name] -# => ["can't be blank", "is too short (minimum is 3 characters)"] -``` - -### `errors.size` - -The `size` method returns the total number of error messages for the object. - -```ruby -class Person < ActiveRecord::Base - validates :name, presence: true, length: { minimum: 3 } -end - -person = Person.new -person.valid? # => false -person.errors.size # => 2 - -person = Person.new(name: "Andrea", email: "andrea@example.com") -person.valid? # => true -person.errors.size # => 0 -``` - -Displaying Validation Errors in the View ----------------------------------------- - -[DynamicForm](https://github.com/joelmoss/dynamic_form) provides helpers to display the error messages of your models in your view templates. - -You can install it as a gem by adding this line to your Gemfile: - -```ruby -gem "dynamic_form" -``` - -Now you will have access to the two helper methods `error_messages` and `error_messages_for` in your view templates. - -### `error_messages` and `error_messages_for` - -When creating a form with the `form_for` helper, you can use the `error_messages` method on the form builder to render all failed validation messages for the current model instance. - -```ruby -class Product < ActiveRecord::Base - validates :description, :value, presence: true - validates :value, numericality: true, allow_nil: true -end -``` - -```erb -<%= form_for(@product) do |f| %> - <%= f.error_messages %> - <p> - <%= f.label :description %><br /> - <%= f.text_field :description %> - </p> - <p> - <%= f.label :value %><br /> - <%= f.text_field :value %> - </p> - <p> - <%= f.submit "Create" %> - </p> -<% end %> -``` - -If you submit the form with empty fields, the result will be similar to the one shown below: - - - -NOTE: The appearance of the generated HTML will be different from the one shown, unless you have used scaffolding. See [Customizing the Error Messages CSS](#customizing-the-error-messages-css). - -You can also use the `error_messages_for` helper to display the error messages of a model assigned to a view template. It is very similar to the previous example and will achieve exactly the same result. - -```erb -<%= error_messages_for :product %> -``` - -The displayed text for each error message will always be formed by the capitalized name of the attribute that holds the error, followed by the error message itself. - -Both the `form.error_messages` and the `error_messages_for` helpers accept options that let you customize the `div` element that holds the messages, change the header text, change the message below the header, and specify the tag used for the header element. For example, - -```erb -<%= f.error_messages header_message: "Invalid product!", - message: "You'll need to fix the following fields:", - header_tag: :h3 %> -``` - -results in: - - - -If you pass `nil` in any of these options, the corresponding section of the `div` will be discarded. - -### Customizing the Error Messages CSS - -The selectors used to customize the style of error messages are: - -* `.field_with_errors` - Style for the form fields and labels with errors. -* `#error_explanation` - Style for the `div` element with the error messages. -* `#error_explanation h2` - Style for the header of the `div` element. -* `#error_explanation p` - Style for the paragraph holding the message that appears right below the header of the `div` element. -* `#error_explanation ul li` - Style for the list items with individual error messages. - -If scaffolding was used, file `app/assets/stylesheets/scaffolds.css.scss` will have been generated automatically. This file defines the red-based styles you saw in the examples above. - -The name of the class and the id can be changed with the `:class` and `:id` options, accepted by both helpers. - -### Customizing the Error Messages HTML - -By default, form fields with errors are displayed enclosed by a `div` element with the `field_with_errors` CSS class. However, it's possible to override that. - -The way form fields with errors are treated is defined by `ActionView::Base.field_error_proc`. This is a `Proc` that receives two parameters: - -* A string with the HTML tag -* An instance of `ActionView::Helpers::InstanceTag`. - -Below is a simple example where we change the Rails behavior to always display the error messages in front of each of the form fields in error. The error messages will be enclosed by a `span` element with a `validation-error` CSS class. There will be no `div` element enclosing the `input` element, so we get rid of that red border around the text field. You can use the `validation-error` CSS class to style it anyway you want. - -```ruby -ActionView::Base.field_error_proc = Proc.new do |html_tag, instance| - errors = Array(instance.error_message).join(',') - %(#{html_tag}<span class="validation-error"> #{errors}</span>).html_safe -end -``` - -The result looks like the following: - - - -Callbacks Overview ------------------- - -Callbacks are methods that get called at certain moments of an object's life cycle. With callbacks it is possible to write code that will run whenever an Active Record object is created, saved, updated, deleted, validated, or loaded from the database. - -### Callback Registration - -In order to use the available callbacks, you need to register them. You can implement the callbacks as ordinary methods and use a macro-style class method to register them as callbacks: - -```ruby -class User < ActiveRecord::Base - validates :login, :email, presence: true - - before_validation :ensure_login_has_a_value - - protected - def ensure_login_has_a_value - if login.nil? - self.login = email unless email.blank? - end - end -end -``` - -The macro-style class methods can also receive a block. Consider using this style if the code inside your block is so short that it fits in a single line: - -```ruby -class User < ActiveRecord::Base - validates :login, :email, presence: true - - before_create do |user| - user.name = user.login.capitalize if user.name.blank? - end -end -``` - -Callbacks can also be registered to only fire on certain lifecycle events: - -```ruby -class User < ActiveRecord::Base - before_validation :normalize_name, on: :create - - # :on takes an array as well - after_validation :set_location, on: [ :create, :update ] - - protected - def normalize_name - self.name = self.name.downcase.titleize - end - - def set_location - self.location = LocationService.query(self) - end -end -``` - -It is considered good practice to declare callback methods as protected or private. If left public, they can be called from outside of the model and violate the principle of object encapsulation. - -Available Callbacks -------------------- - -Here is a list with all the available Active Record callbacks, listed in the same order in which they will get called during the respective operations: - -### Creating an Object - -* `before_validation` -* `after_validation` -* `before_save` -* `around_save` -* `before_create` -* `around_create` -* `after_create` -* `after_save` - -### Updating an Object - -* `before_validation` -* `after_validation` -* `before_save` -* `around_save` -* `before_update` -* `around_update` -* `after_update` -* `after_save` - -### Destroying an Object - -* `before_destroy` -* `around_destroy` -* `after_destroy` - -WARNING. `after_save` runs both on create and update, but always _after_ the more specific callbacks `after_create` and `after_update`, no matter the order in which the macro calls were executed. - -### `after_initialize` and `after_find` - -The `after_initialize` callback will be called whenever an Active Record object is instantiated, either by directly using `new` or when a record is loaded from the database. It can be useful to avoid the need to directly override your Active Record `initialize` method. - -The `after_find` callback will be called whenever Active Record loads a record from the database. `after_find` is called before `after_initialize` if both are defined. - -The `after_initialize` and `after_find` callbacks have no `before_*` counterparts, but they can be registered just like the other Active Record callbacks. - -```ruby -class User < ActiveRecord::Base - after_initialize do |user| - puts "You have initialized an object!" - end - - after_find do |user| - puts "You have found an object!" - end -end - ->> User.new -You have initialized an object! -=> #<User id: nil> - ->> User.first -You have found an object! -You have initialized an object! -=> #<User id: 1> -``` - -Running Callbacks ------------------ - -The following methods trigger callbacks: - -* `create` -* `create!` -* `decrement!` -* `destroy` -* `destroy_all` -* `increment!` -* `save` -* `save!` -* `save(validate: false)` -* `toggle!` -* `update` -* `update_attribute` -* `update_attributes` -* `update_attributes!` -* `valid?` - -Additionally, the `after_find` callback is triggered by the following finder methods: - -* `all` -* `first` -* `find` -* `find_all_by_*` -* `find_by_*` -* `find_by_*!` -* `find_by_sql` -* `last` - -The `after_initialize` callback is triggered every time a new object of the class is initialized. - -NOTE: The `find_all_by_*`, `find_by_*` and `find_by_*!` methods are dynamic finders generated automatically for every attribute. Learn more about them at the [Dynamic finders section](active_record_querying.html#dynamic-finders) - -Skipping Callbacks ------------------- - -Just as with validations, it is also possible to skip callbacks. These methods should be used with caution, however, because important business rules and application logic may be kept in callbacks. Bypassing them without understanding the potential implications may lead to invalid data. - -* `decrement` -* `decrement_counter` -* `delete` -* `delete_all` -* `increment` -* `increment_counter` -* `toggle` -* `touch` -* `update_column` -* `update_columns` -* `update_all` -* `update_counters` - -Halting Execution ------------------ - -As you start registering new callbacks for your models, they will be queued for execution. This queue will include all your model's validations, the registered callbacks, and the database operation to be executed. - -The whole callback chain is wrapped in a transaction. If any <em>before</em> callback method returns exactly `false` or raises an exception, the execution chain gets halted and a ROLLBACK is issued; <em>after</em> callbacks can only accomplish that by raising an exception. - -WARNING. Raising an arbitrary exception may break code that expects `save` and its friends not to fail like that. The `ActiveRecord::Rollback` exception is thought precisely to tell Active Record a rollback is going on. That one is internally captured but not reraised. - -Relational Callbacks --------------------- - -Callbacks work through model relationships, and can even be defined by them. Suppose an example where a user has many posts. A user's posts should be destroyed if the user is destroyed. Let's add an `after_destroy` callback to the `User` model by way of its relationship to the `Post` model: - -```ruby -class User < ActiveRecord::Base - has_many :posts, dependent: :destroy -end - -class Post < ActiveRecord::Base - after_destroy :log_destroy_action - - def log_destroy_action - puts 'Post destroyed' - end -end - ->> user = User.first -=> #<User id: 1> ->> user.posts.create! -=> #<Post id: 1, user_id: 1> ->> user.destroy -Post destroyed -=> #<User id: 1> -``` - -Conditional Callbacks ---------------------- - -As with validations, we can also make the calling of a callback method conditional on the satisfaction of a given predicate. We can do this using the `:if` and `:unless` options, which can take a symbol, a string, a `Proc` or an `Array`. You may use the `:if` option when you want to specify under which conditions the callback **should** be called. If you want to specify the conditions under which the callback **should not** be called, then you may use the `:unless` option. - -### Using `:if` and `:unless` with a `Symbol` - -You can associate the `:if` and `:unless` options with a symbol corresponding to the name of a predicate method that will get called right before the callback. When using the `:if` option, the callback won't be executed if the predicate method returns false; when using the `:unless` option, the callback won't be executed if the predicate method returns true. This is the most common option. Using this form of registration it is also possible to register several different predicates that should be called to check if the callback should be executed. - -```ruby -class Order < ActiveRecord::Base - before_save :normalize_card_number, if: :paid_with_card? -end -``` - -### Using `:if` and `:unless` with a String - -You can also use a string that will be evaluated using `eval` and hence needs to contain valid Ruby code. You should use this option only when the string represents a really short condition: - -```ruby -class Order < ActiveRecord::Base - before_save :normalize_card_number, if: "paid_with_card?" -end -``` - -### Using `:if` and `:unless` with a `Proc` - -Finally, it is possible to associate `:if` and `:unless` with a `Proc` object. This option is best suited when writing short validation methods, usually one-liners: - -```ruby -class Order < ActiveRecord::Base - before_save :normalize_card_number, - if: Proc.new { |order| order.paid_with_card? } -end -``` - -### Multiple Conditions for Callbacks - -When writing conditional callbacks, it is possible to mix both `:if` and `:unless` in the same callback declaration: - -```ruby -class Comment < ActiveRecord::Base - after_create :send_email_to_author, if: :author_wants_emails?, - unless: Proc.new { |comment| comment.post.ignore_comments? } -end -``` - -Callback Classes ----------------- - -Sometimes the callback methods that you'll write will be useful enough to be reused by other models. Active Record makes it possible to create classes that encapsulate the callback methods, so it becomes very easy to reuse them. - -Here's an example where we create a class with an `after_destroy` callback for a `PictureFile` model: - -```ruby -class PictureFileCallbacks - def after_destroy(picture_file) - if File.exists?(picture_file.filepath) - File.delete(picture_file.filepath) - end - end -end -``` - -When declared inside a class, as above, the callback methods will receive the model object as a parameter. We can now use the callback class in the model: - -```ruby -class PictureFile < ActiveRecord::Base - after_destroy PictureFileCallbacks.new -end -``` - -Note that we needed to instantiate a new `PictureFileCallbacks` object, since we declared our callback as an instance method. This is particularly useful if the callbacks make use of the state of the instantiated object. Often, however, it will make more sense to declare the callbacks as class methods: - -```ruby -class PictureFileCallbacks - def self.after_destroy(picture_file) - if File.exists?(picture_file.filepath) - File.delete(picture_file.filepath) - end - end -end -``` - -If the callback method is declared this way, it won't be necessary to instantiate a `PictureFileCallbacks` object. - -```ruby -class PictureFile < ActiveRecord::Base - after_destroy PictureFileCallbacks -end -``` - -You can declare as many callbacks as you want inside your callback classes. - -Observers ---------- - -Observers are similar to callbacks, but with important differences. Whereas callbacks can pollute a model with code that isn't directly related to its purpose, observers allow you to add the same functionality without changing the code of the model. For example, it could be argued that a `User` model should not include code to send registration confirmation emails. Whenever you use callbacks with code that isn't directly related to your model, you may want to consider creating an observer instead. - -### Creating Observers - -For example, imagine a `User` model where we want to send an email every time a new user is created. Because sending emails is not directly related to our model's purpose, we should create an observer to contain the code implementing this functionality. - -```bash -$ rails generate observer User -``` - -generates `app/models/user_observer.rb` containing the observer class `UserObserver`: - -```ruby -class UserObserver < ActiveRecord::Observer -end -``` - -You may now add methods to be called at the desired occasions: - -```ruby -class UserObserver < ActiveRecord::Observer - def after_create(model) - # code to send confirmation email... - end -end -``` - -As with callback classes, the observer's methods receive the observed model as a parameter. - -### Registering Observers - -Observers are conventionally placed inside of your `app/models` directory and registered in your application's `config/application.rb` file. For example, the `UserObserver` above would be saved as `app/models/user_observer.rb` and registered in `config/application.rb` this way: - -```ruby -# Activate observers that should always be running. -config.active_record.observers = :user_observer -``` - -As usual, settings in `config/environments` take precedence over those in `config/application.rb`. So, if you prefer that an observer doesn't run in all environments, you can simply register it in a specific environment instead. - -### Sharing Observers - -By default, Rails will simply strip "Observer" from an observer's name to find the model it should observe. However, observers can also be used to add behavior to more than one model, and thus it is possible to explicitly specify the models that our observer should observe: - -```ruby -class MailerObserver < ActiveRecord::Observer - observe :registration, :user - - def after_create(model) - # code to send confirmation email... - end -end -``` - -In this example, the `after_create` method will be called whenever a `Registration` or `User` is created. Note that this new `MailerObserver` would also need to be registered in `config/application.rb` in order to take effect: - -```ruby -# Activate observers that should always be running. -config.active_record.observers = :mailer_observer -``` - -Transaction Callbacks ---------------------- - -There are two additional callbacks that are triggered by the completion of a database transaction: `after_commit` and `after_rollback`. These callbacks are very similar to the `after_save` callback except that they don't execute until after database changes have either been committed or rolled back. They are most useful when your active record models need to interact with external systems which are not part of the database transaction. - -Consider, for example, the previous example where the `PictureFile` model needs to delete a file after the corresponding record is destroyed. If anything raises an exception after the `after_destroy` callback is called and the transaction rolls back, the file will have been deleted and the model will be left in an inconsistent state. For example, suppose that `picture_file_2` in the code below is not valid and the `save!` method raises an error. - -```ruby -PictureFile.transaction do - picture_file_1.destroy - picture_file_2.save! -end -``` - -By using the `after_commit` callback we can account for this case. - -```ruby -class PictureFile < ActiveRecord::Base - attr_accessor :delete_file - - 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 - end - end -end -``` - -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_support_core_extensions.md b/guides/source/active_support_core_extensions.md index 401e6f0596..7f03363b23 100644 --- a/guides/source/active_support_core_extensions.md +++ b/guides/source/active_support_core_extensions.md @@ -5,7 +5,12 @@ Active Support is the Ruby on Rails component responsible for providing Ruby lan It offers a richer bottom-line at the language level, targeted both at the development of Rails applications, and at the development of Ruby on Rails itself. -By referring to this guide you will learn the extensions to the Ruby core classes and modules provided by Active Support. +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. -------------------------------------------------------------------------------- @@ -915,7 +920,7 @@ When interpolated into a string, the `:to` option should become an expression th delegate :logger, to: :Rails # delegates to the receiver's class -delegate :table_name, to: 'self.class' +delegate :table_name, to: :class ``` WARNING: If the `:prefix` option is `true` this is less generic, see below. @@ -1120,8 +1125,6 @@ C.subclasses # => [B, D] The order in which these classes are returned is unspecified. -WARNING: This method is redefined in some Rails core classes but should be all compatible in Rails 3.1. - NOTE: Defined in `active_support/core_ext/class/subclasses.rb`. #### `descendants` @@ -1157,7 +1160,7 @@ Inserting data into HTML templates needs extra care. For example, you can't just #### Safe Strings -Active Support has the concept of <i>(html) safe</i> strings since Rails 3. A safe string is one that is marked as being insertable into HTML as is. It is trusted, no matter whether it has been escaped or not. +Active Support has the concept of <i>(html) safe</i> strings. A safe string is one that is marked as being insertable into HTML as is. It is trusted, no matter whether it has been escaped or not. Strings are considered to be <i>unsafe</i> by default: @@ -1194,10 +1197,10 @@ Safe arguments are directly appended: "".html_safe + "<".html_safe # => "<" ``` -These methods should not be used in ordinary views. In Rails 3 unsafe values are automatically escaped: +These methods should not be used in ordinary views. Unsafe values are automatically escaped: ```erb -<%= @review.title %> <%# fine in Rails 3, escaped if needed %> +<%= @review.title %> <%# fine, escaped if needed %> ``` To insert something verbatim use the `raw` helper rather than calling `html_safe`: @@ -2067,14 +2070,6 @@ The sum of an empty receiver can be customized in this form as well: [].sum(1) {|n| n**3} # => 1 ``` -The method `ActiveRecord::Observer#observed_subclasses` for example is implemented this way: - -```ruby -def observed_subclasses - observed_classes.sum([]) { |klass| klass.send(:subclasses) } -end -``` - NOTE: Defined in `active_support/core_ext/enumerable.rb`. ### `index_by` @@ -2418,9 +2413,9 @@ or yields them in turn if a block is passed: ```html+erb <% sample.in_groups_of(3) do |a, b, c| %> <tr> - <td><%=h a %></td> - <td><%=h b %></td> - <td><%=h c %></td> + <td><%= a %></td> + <td><%= b %></td> + <td><%= c %></td> </tr> <% end %> ``` @@ -2683,13 +2678,6 @@ If the receiver responds to `convert_key`, the method is called on each of the a {a: 1}.with_indifferent_access.except("a") # => {} ``` -The method `except` may come in handy for example when you want to protect some parameter that can't be globally protected with `attr_protected`: - -```ruby -params[:account] = params[:account].except(:plan_id) unless admin? -@account.update_attributes(params[:account]) -``` - There's also the bang variant `except!` that removes keys in the very receiver. NOTE: Defined in `active_support/core_ext/hash/except.rb`. @@ -3604,7 +3592,7 @@ Time.zone_default # => #<ActiveSupport::TimeZone:0x7f73654d4f38 @utc_offset=nil, @name="Madrid", ...> # In Barcelona, 2010/03/28 02:00 +0100 becomes 2010/03/28 03:00 +0200 due to DST. -t = Time.local_time(2010, 3, 28, 1, 59, 59) +t = Time.local(2010, 3, 28, 1, 59, 59) # => Sun Mar 28 01:59:59 +0100 2010 t.advance(seconds: 1) # => Sun Mar 28 03:00:00 +0200 2010 @@ -3659,26 +3647,6 @@ Time.current Analogously to `DateTime`, the predicates `past?`, and `future?` are relative to `Time.current`. -Use the `local_time` class method to create time objects honoring the user time zone: - -```ruby -Time.zone_default -# => #<ActiveSupport::TimeZone:0x7f73654d4f38 @utc_offset=nil, @name="Madrid", ...> -Time.local_time(2010, 8, 15) -# => Sun Aug 15 00:00:00 +0200 2010 -``` - -The `utc_time` class method returns a time in UTC: - -```ruby -Time.zone_default -# => #<ActiveSupport::TimeZone:0x7f73654d4f38 @utc_offset=nil, @name="Madrid", ...> -Time.utc_time(2010, 8, 15) -# => Sun Aug 15 00:00:00 UTC 2010 -``` - -Both `local_time` and `utc_time` accept up to seven positional arguments: year, month, day, hour, min, sec, usec. Year is mandatory, month and day default to 1, and the rest default to 0. - If the time to be constructed lies beyond the range supported by `Time` in the runtime platform, usecs are discarded and a `DateTime` object is returned instead. #### Durations @@ -3697,7 +3665,7 @@ now - 1.week They translate to calls to `since` or `advance`. For example here we get the correct jump in the calendar reform: ```ruby -Time.utc_time(1582, 10, 3) + 5.days +Time.utc(1582, 10, 3) + 5.days # => Mon Oct 18 00:00:00 UTC 1582 ``` @@ -3728,6 +3696,25 @@ The auxiliary file is written in a standard directory for temporary files, but y NOTE: Defined in `active_support/core_ext/file/atomic.rb`. +Extensions to `Marshal` +----------------------- + +### `load` + +Active Support adds constant autoloading support to `load`. + +For example, the file cache store deserializes this way: + +```ruby +File.open(file_name) { |f| Marshal.load(f) } +``` + +If the cached data refers to a constant that is unknown at that point, the autoloading mechanism is triggered and if it succeeds the deserialization is retried transparently. + +WARNING. If the argument is an `IO` it needs to respond to `rewind` to be able to retry. Regular files respond to `rewind`. + +NOTE: Defined in `active_support/core_ext/marshal.rb`. + Extensions to `Logger` ---------------------- diff --git a/guides/source/active_support_instrumentation.md b/guides/source/active_support_instrumentation.md index 1163940f10..6b3be69942 100644 --- a/guides/source/active_support_instrumentation.md +++ b/guides/source/active_support_instrumentation.md @@ -3,19 +3,21 @@ Active Support Instrumentation Active Support is a part of core Rails that provides Ruby language extensions, utilities and other things. One of the things it includes is an instrumentation API that can be used inside an application to measure certain actions that occur within Ruby code, such as that inside a Rails application or the framework itself. It is not limited to Rails, however. It can be used independently in other Ruby scripts if it is so desired. -In this guide, you will learn how to use the instrumentation API inside of ActiveSupport to measure events inside of Rails and other Ruby code. We cover: +In this guide, you will learn how to use the instrumentation API inside of Active Support to measure events inside of Rails and other Ruby code. -* What instrumentation can provide -* The hooks inside the Rails framework for instrumentation -* Adding a subscriber to a hook -* Building a custom instrumentation implementation +After reading this guide, you will know: + +* What instrumentation can provide. +* The hooks inside the Rails framework for instrumentation. +* Adding a subscriber to a hook. +* Building a custom instrumentation implementation. -------------------------------------------------------------------------------- Introduction to instrumentation ------------------------------- -The instrumentation API provided by ActiveSupport allows developers to provide hooks which other developers may hook into. There are several of these within the Rails framework, as described below in <TODO: link to section detailing each hook point>. With this API, developers can choose to be notified when certain events occur inside their application or another piece of Ruby code. +The instrumentation API provided by Active Support allows developers to provide hooks which other developers may hook into. There are several of these within the Rails framework, as described below in <TODO: link to section detailing each hook point>. With this API, developers can choose to be notified when certain events occur inside their application or another piece of Ruby code. For example, there is a hook provided within Active Record that is called every time Active Record uses an SQL query on a database. This hook could be **subscribed** to, and used to track the number of queries during a certain action. There's another hook around the processing of an action of a controller. This could be used, for instance, to track how long a specific action has taken. @@ -26,8 +28,8 @@ Rails framework hooks Within the Ruby on Rails framework, there are a number of hooks provided for common events. These are detailed below. -ActionController ----------------- +Action Controller +----------------- ### write_fragment.action_controller @@ -187,8 +189,8 @@ INFO. Additional keys may be added by the caller. } ``` -ActionView ----------- +Action View +----------- ### render_template.action_view @@ -216,7 +218,7 @@ ActionView } ``` -ActiveRecord +Active Record ------------ ### sql.active_record @@ -246,8 +248,8 @@ INFO. The adapters will add their own data as well. | `:name` | Record's class | | `:connection_id` | `self.object_id` | -ActionMailer ------------- +Action Mailer +------------- ### receive.action_mailer @@ -312,8 +314,8 @@ ActiveResource | `:request_uri` | Complete URI | | `:result` | HTTP response object | -ActiveSupport -------------- +Active Support +-------------- ### cache_read.active_support @@ -430,7 +432,7 @@ from block args like this: ```ruby ActiveSupport::Notifications.subscribe "process_action.action_controller" do |*args| - event = ActiveSupport::Notification::Event.new args + event = ActiveSupport::Notifications::Event.new *args event.name # => "process_action.action_controller" event.duration # => 10 (in milliseconds) diff --git a/guides/source/api_documentation_guidelines.md b/guides/source/api_documentation_guidelines.md index 72e412e701..d0499878da 100644 --- a/guides/source/api_documentation_guidelines.md +++ b/guides/source/api_documentation_guidelines.md @@ -3,6 +3,11 @@ API Documentation Guidelines This guide documents the Ruby on Rails API documentation guidelines. +After reading this guide, you will know: + +* How to write effective prose for documentation purposes. +* Style guidelines for documenting different kinds of Ruby code. + -------------------------------------------------------------------------------- RDoc diff --git a/guides/source/asset_pipeline.md b/guides/source/asset_pipeline.md index 13df1965b6..b302ef76c6 100644 --- a/guides/source/asset_pipeline.md +++ b/guides/source/asset_pipeline.md @@ -1,14 +1,15 @@ -Asset Pipeline -============== +The Asset Pipeline +================== -This guide covers the asset pipeline introduced in Rails 3.1. -By referring to this guide you will be able to: +This guide covers the asset pipeline. -* Understand what the asset pipeline is and what it does -* Properly organize your application assets -* Understand the benefits of the asset pipeline -* Add a pre-processor to the pipeline -* Package assets with a gem +After reading this guide, you will know: + +* How to understand what the asset pipeline is and what it does. +* How to properly organize your application assets. +* How to understand the benefits of the asset pipeline. +* How to add a pre-processor to the pipeline. +* How to package assets with a gem. -------------------------------------------------------------------------------- @@ -17,11 +18,9 @@ What is the Asset Pipeline? The asset pipeline provides a framework to concatenate and minify or compress JavaScript and CSS assets. It also adds the ability to write these assets in other languages such as CoffeeScript, Sass and ERB. -Prior to Rails 3.1 these features were added through third-party Ruby libraries such as Jammit and Sprockets. Rails 3.1 is integrated with Sprockets through Action Pack which depends on the `sprockets` gem, by default. - Making the asset pipeline a core feature of Rails means that all developers can benefit from the power of having their assets pre-processed, compressed and minified by one central library, Sprockets. This is part of Rails' "fast by default" strategy as outlined by DHH in his keynote at RailsConf 2011. -In Rails 3.1, the asset pipeline is enabled by default. It can be disabled in `config/application.rb` by putting this line inside the application class definition: +The asset pipeline is enabled by default. It can be disabled in `config/application.rb` by putting this line inside the application class definition: ```ruby config.assets.enabled = false @@ -97,11 +96,25 @@ Assets can still be placed in the `public` hierarchy. Any assets under `public` In production, Rails precompiles these files to `public/assets` by default. The precompiled copies are then served as static assets by the web server. The files in `app/assets` are never served directly in production. +### Controller Specific Assets + When you generate a scaffold or a controller, Rails also generates a JavaScript file (or CoffeeScript file if the `coffee-rails` gem is in the `Gemfile`) and a Cascading Style Sheet file (or SCSS file if `sass-rails` is in the `Gemfile`) for that controller. -For example, if you generate a `ProjectsController`, Rails will also add a new file at `app/assets/javascripts/projects.js.coffee` and another at `app/assets/stylesheets/projects.css.scss`. You should put any JavaScript or CSS unique to a controller inside their respective asset files, as these files can then be loaded just for these controllers with lines such as `<%= javascript_include_tag params[:controller] %>` or `<%= stylesheet_link_tag params[:controller] %>`. +For example, if you generate a `ProjectsController`, Rails will also add a new file at `app/assets/javascripts/projects.js.coffee` and another at `app/assets/stylesheets/projects.css.scss`. By default these files will be ready to use by your application immediately using the `require_tree` directive. See [Manifest Files and Directives](#manifest-files-and-directives) for more details on require_tree. + +You can also opt to include controller specific stylesheets and JavaScript files only in their respective controllers using the following: `<%= javascript_include_tag params[:controller] %>` or `<%= stylesheet_link_tag params[:controller] %>`. Ensure that you are not using the `require_tree` directive though, as this will result in your assets being included more than once. + +WARNING: When using asset precompilation (the production default), you will need to ensure that your controller assets will be precompiled when loading them on a per page basis. By default .coffee and .scss files will not be precompiled on their own. This will result in false positives during development as these files will work just fine since assets will be compiled on the fly. When running in production however, you will see 500 errors since live compilation is turned off by default. See [Precompiling Assets](#precompiling-assets) for more information on how precompiling works. -NOTE: You must have an [ExecJS](https://github.com/sstephenson/execjs#readme) supported runtime in order to use CoffeeScript. If you are using Mac OS X or Windows you have a JavaScript runtime installed in your operating system. Check [ExecJS](https://github.com/sstephenson/execjs#readme) documentation to know all supported JavaScript runtimes. +NOTE: You must have an ExecJS supported runtime in order to use CoffeeScript. If you are using Mac OS X or Windows you have a JavaScript runtime installed in your operating system. Check [ExecJS](https://github.com/sstephenson/execjs#readme) documentation to know all supported JavaScript runtimes. + +You can also disable the generation of asset files when generating a controller by adding the following to your `config/application.rb` configuration: + +```ruby +config.generators do |g| + g.assets false +end +``` ### Asset Organization @@ -113,7 +126,7 @@ Pipeline assets can be placed inside an application in one of three locations: ` * `vendor/assets` is for assets that are owned by outside entities, such as code for JavaScript plugins and CSS frameworks. -#### Search paths +#### Search Paths When a file is referenced from a manifest or a helper, Sprockets searches the three default asset locations for it. @@ -161,7 +174,7 @@ Paths are traversed in the order that they occur in the search path. By default, It is important to note that files you want to reference outside a manifest must be added to the precompile array or they will not be available in the production environment. -#### Using index files +#### Using Index Files Sprockets uses files named `index` (with the relevant extensions) for a special purpose. @@ -256,7 +269,8 @@ $('#logo').attr src: "<%= asset_path('logo.png') %>" ### Manifest Files and Directives -Sprockets uses manifest files to determine which assets to include and serve. These manifest files contain _directives_ — instructions that tell Sprockets which files to require in order to build a single CSS or JavaScript file. With these directives, Sprockets loads the files specified, processes them if necessary, concatenates them into one single file and then compresses them (if `Rails.application.config.assets.compress` is true). By serving one file rather than many, the load time of pages can be greatly reduced because the browser makes fewer requests. +Sprockets uses manifest files to determine which assets to include and serve. These manifest files contain _directives_ — instructions that tell Sprockets which files to require in order to build a single CSS or JavaScript file. With these directives, Sprockets loads the files specified, processes them if necessary, concatenates them into one single file and then compresses them (if `Rails.application.config.assets.compress` is true). By serving one file rather than many, the load time of pages can be greatly reduced because the browser makes fewer requests. Compression also reduces the file size enabling the browser to download it faster. + For example, a new Rails application includes a default `app/assets/javascripts/application.js` file which contains the following lines: @@ -269,8 +283,6 @@ For example, a new Rails application includes a default `app/assets/javascripts/ In JavaScript files, the directives begin with `//=`. In this case, the file is using the `require` and the `require_tree` directives. The `require` directive is used to tell Sprockets the files that you wish to require. Here, you are requiring the files `jquery.js` and `jquery_ujs.js` that are available somewhere in the search path for Sprockets. You need not supply the extensions explicitly. Sprockets assumes you are requiring a `.js` file when done from within a `.js` file. -NOTE. In Rails 3.1 the `jquery-rails` gem provides the `jquery.js` and `jquery_ujs.js` files via the asset pipeline. You won't see them in the application tree. - The `require_tree` directive tells Sprockets to recursively include _all_ JavaScript files in the specified directory into the output. These paths must be specified relative to the manifest file. You can also use the `require_directory` directive which includes all JavaScript files only in the directory specified, without recursion. Directives are processed top to bottom, but the order in which files are included by `require_tree` is unspecified. You should not rely on any particular order among those. If you need to ensure some particular JavaScript ends up above some other in the concatenated file, require the prerequisite file first in the manifest. Note that the family of `require` directives prevents files from being included twice in the output. @@ -336,7 +348,7 @@ would generate this HTML: The `body` param is required by Sprockets. -### Turning Debugging off +### Turning Debugging Off You can turn off debug mode by updating `config/environments/development.rb` to include: @@ -444,6 +456,27 @@ If you have other manifests or individual stylesheets and JavaScript files to in 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.assets.precompile << Proc.new { |path| + if path =~ /\.(css|js)\z/ + full_path = Rails.application.assets.resolve(path).to_path + app_assets_path = Rails.root.join('app', 'assets').to_path + if full_path.starts_with? app_assets_path + puts "including asset: " + full_path + true + else + puts "excluding asset: " + full_path + false + end + else + false + end +} +``` + NOTE. Always specify an expected compiled filename that ends with js or css, even if you want to add Sass or CoffeeScript files to the precompile array. The rake task also generates a `manifest.yml` that contains a list with all your assets and their respective fingerprints. This is used by the Rails helper methods to avoid handing the mapping requests back to Sprockets. A typical manifest file looks like: @@ -461,7 +494,7 @@ The default location for the manifest is the root of the location specified in ` NOTE: If there are missing precompiled files in production you will get an `Sprockets::Helpers::RailsHelper::AssetPaths::AssetNotPrecompiledError` exception indicating the name of the missing file(s). -#### Far-future Expires header +#### Far-future Expires Header Precompiled assets exist on the filesystem and are served directly by your web server. They do not have far-future headers by default, so to get the benefit of fingerprinting you'll have to update your server configuration to add them. @@ -491,7 +524,7 @@ location ~ ^/assets/ { } ``` -#### GZip compression +#### GZip Compression When files are precompiled, Sprockets also creates a [gzipped](http://en.wikipedia.org/wiki/Gzip) (.gz) version of your assets. Web servers are typically configured to use a moderate compression ratio as a compromise, but since precompilation happens once, Sprockets uses the maximum compression ratio, thus reducing the size of the data transfer to the minimum. On the other hand, web servers can be configured to serve compressed content directly from disk, rather than deflating non-compressed files themselves. @@ -647,7 +680,7 @@ This can be changed to something else: config.assets.prefix = "/some_other_path" ``` -This is a handy option if you are updating an existing project (pre Rails 3.1) that already uses this path or you wish to use this path for a new resource. +This is a handy option if you are updating an older project that didn't use the asset pipeline and that already uses this path or you wish to use this path for a new resource. ### X-Sendfile Headers diff --git a/guides/source/association_basics.md b/guides/source/association_basics.md index 9bb5aa8bc2..dd59e2a8df 100644 --- a/guides/source/association_basics.md +++ b/guides/source/association_basics.md @@ -1,11 +1,13 @@ -A Guide to Active Record Associations -===================================== +Active Record Associations +========================== -This guide covers the association features of Active Record. By referring to this guide, you will be able to: +This guide covers the association features of Active Record. -* Declare associations between Active Record models -* Understand the various types of Active Record associations -* Use the methods added to your models by creating associations +After reading this guide, you will know: + +* How to declare associations between Active Record models. +* How to understand the various types of Active Record associations. +* How to use the methods added to your models by creating associations. -------------------------------------------------------------------------------- @@ -92,6 +94,25 @@ end NOTE: `belongs_to` associations _must_ use the singular term. If you used the pluralized form in the above example for the `customer` association in the `Order` model, you would be told that there was an "uninitialized constant Order::Customers". This is because Rails automatically infers the class name from the association name. If the association name is wrongly pluralized, then the inferred class will be wrongly pluralized too. +The corresponding migration might look like this: + +```ruby +class CreateOrders < ActiveRecord::Migration + def change + create_table :customers do |t| + t.string :name + t.timestamps + end + + create_table :orders do |t| + t.belongs_to :customer + t.datetime :order_date + t.timestamps + end + end +end +``` + ### The `has_one` Association A `has_one` association also sets up a one-to-one connection with another model, but with somewhat different semantics (and consequences). This association indicates that each instance of a model contains or possesses one instance of another model. For example, if each supplier in your application has only one account, you'd declare the supplier model like this: @@ -104,6 +125,25 @@ end  +The corresponding migration might look like this: + +```ruby +class CreateSuppliers < ActiveRecord::Migration + def change + create_table :suppliers do |t| + t.string :name + t.timestamps + end + + create_table :accounts do |t| + t.belongs_to :supplier + t.string :account_number + t.timestamps + end + end +end +``` + ### The `has_many` Association A `has_many` association indicates a one-to-many connection with another model. You'll often find this association on the "other side" of a `belongs_to` association. This association indicates that each instance of the model has zero or more instances of another model. For example, in an application containing customers and orders, the customer model could be declared like this: @@ -118,6 +158,25 @@ NOTE: The name of the other model is pluralized when declaring a `has_many` asso  +The corresponding migration might look like this: + +```ruby +class CreateCustomers < ActiveRecord::Migration + def change + create_table :customers do |t| + t.string :name + t.timestamps + end + + create_table :orders do |t| + t.belongs_to :customer + t.datetime :order_date + t.timestamps + end + end +end +``` + ### The `has_many :through` Association A `has_many :through` association is often used to set up a many-to-many connection with another model. This association indicates that the declaring model can be matched with zero or more instances of another model by proceeding _through_ a third model. For example, consider a medical practice where patients make appointments to see physicians. The relevant association declarations could look like this: @@ -141,6 +200,31 @@ end  +The corresponding migration might look like this: + +```ruby +class CreateAppointments < ActiveRecord::Migration + def change + create_table :physicians do |t| + t.string :name + t.timestamps + end + + create_table :patients do |t| + t.string :name + t.timestamps + end + + create_table :appointments do |t| + t.belongs_to :physician + t.belongs_to :patient + t.datetime :appointment_date + t.timestamps + end + end +end +``` + The collection of join models can be managed via the API. For example, if you assign ```ruby @@ -197,6 +281,31 @@ end  +The corresponding migration might look like this: + +```ruby +class CreateAccountHistories < ActiveRecord::Migration + def change + create_table :suppliers do |t| + t.string :name + t.timestamps + end + + create_table :accounts do |t| + t.belongs_to :supplier + t.string :account_number + t.timestamps + end + + create_table :account_histories do |t| + t.belongs_to :account + t.integer :credit_rating + t.timestamps + end + end +end +``` + ### The `has_and_belongs_to_many` Association A `has_and_belongs_to_many` association creates a direct many-to-many connection with another model, with no intervening model. For example, if your application includes assemblies and parts, with each assembly having many parts and each part appearing in many assemblies, you could declare the models this way: @@ -213,6 +322,29 @@ end  +The corresponding migration might look like this: + +```ruby +class CreateAssembliesAndParts < ActiveRecord::Migration + def change + create_table :assemblies do |t| + t.string :name + t.timestamps + end + + create_table :parts do |t| + t.string :part_number + t.timestamps + end + + create_table :assemblies_parts do |t| + t.belongs_to :assembly + t.belongs_to :part + end + end +end +``` + ### Choosing Between `belongs_to` and `has_one` If you want to set up a one-to-one relationship between two models, you'll need to add `belongs_to` to one, and `has_one` to the other. How do you know which is which? @@ -450,7 +582,7 @@ class CreateAssemblyPartJoinTable < ActiveRecord::Migration end ``` -We pass `id: false` to `create_table` because that table does not represent a model. That's required for the association to work properly. If you observe any strange behavior in a `has_and_belongs_to_many` association like mangled models IDs, or exceptions about conflicting IDs chances are you forgot that bit. +We pass `id: false` to `create_table` because that table does not represent a model. That's required for the association to work properly. If you observe any strange behavior in a `has_and_belongs_to_many` association like mangled models IDs, or exceptions about conflicting IDs, chances are you forgot that bit. ### Controlling Association Scope diff --git a/guides/source/caching_with_rails.md b/guides/source/caching_with_rails.md index 4cb76bfe5f..773102400a 100644 --- a/guides/source/caching_with_rails.md +++ b/guides/source/caching_with_rails.md @@ -3,12 +3,12 @@ Caching with Rails: An overview This guide will teach you what you need to know about avoiding that expensive round-trip to your database and returning what you need to return to the web clients in the shortest time possible. -After reading this guide, you should be able to use and configure: +After reading this guide, you will know: -* Page, action, and fragment caching -* Sweepers -* Alternative cache stores -* Conditional GET support +* Page, action, and fragment caching. +* Sweepers. +* Alternative cache stores. +* Conditional GET support. -------------------------------------------------------------------------------- @@ -67,8 +67,6 @@ class ProductsController < ActionController end ``` -If you want a more complicated expiration scheme, you can use cache sweepers to expire cached objects when things change. This is covered in the section on Sweepers. - By default, page caching automatically gzips files (for example, to `products.html.gz` if user requests `/products`) to reduce the size of data transmitted (web servers are typically configured to use a moderate compression ratio as a compromise, but since precompilation happens once, compression ratio is maximum). Nginx is able to serve compressed content directly from disk by enabling `gzip_static`: @@ -106,7 +104,7 @@ Let's say you only wanted authenticated users to call actions on `ProductsContro ```ruby class ProductsController < ActionController - before_filter :authenticate + before_action :authenticate caches_action :index def index @@ -176,102 +174,6 @@ This fragment is then available to all actions in the `ProductsController` using expire_fragment('all_available_products') ``` -### Sweepers - -Cache sweeping is a mechanism which allows you to get around having a ton of `expire_{page,action,fragment}` calls in your code. It does this by moving all the work required to expire cached content into an `ActionController::Caching::Sweeper` subclass. This class is an observer and looks for changes to an Active Record object via callbacks, and when a change occurs it expires the caches associated with that object in an around or after filter. - -TIP: Sweepers rely on the use of Active Record and Active Record Observers. The object you are observing must be an Active Record model. - -Continuing with our Product controller example, we could rewrite it with a sweeper like this: - -```ruby -class ProductSweeper < ActionController::Caching::Sweeper - observe Product # This sweeper is going to keep an eye on the Product model - - # If our sweeper detects that a Product was created call this - def after_create(product) - expire_cache_for(product) - end - - # If our sweeper detects that a Product was updated call this - def after_update(product) - expire_cache_for(product) - end - - # If our sweeper detects that a Product was deleted call this - def after_destroy(product) - expire_cache_for(product) - end - - private - def expire_cache_for(product) - # Expire the index page now that we added a new product - expire_page(controller: 'products', action: 'index') - - # Expire a fragment - expire_fragment('all_available_products') - end -end -``` - -You may notice that the actual product gets passed to the sweeper, so if we were caching the edit action for each product, we could add an expire method which specifies the page we want to expire: - -```ruby -expire_action(controller: 'products', action: 'edit', id: product.id) -``` - -Then we add it to our controller to tell it to call the sweeper when certain actions are called. So, if we wanted to expire the cached content for the list and edit actions when the create action was called, we could do the following: - -```ruby -class ProductsController < ActionController - - before_filter :authenticate - caches_action :index - cache_sweeper :product_sweeper - - def index - @products = Product.all - end - -end -``` - -Sometimes it is necessary to disambiguate the controller when you call `expire_action`, such as when there are two identically named controllers in separate namespaces: - -```ruby -class ProductsController < ActionController - caches_action :index - - def index - @products = Product.all - end -end - -module Admin - class ProductsController < ActionController - cache_sweeper :product_sweeper - - def new - @product = Product.new - end - - def create - @product = Product.create(params[:product]) - end - end -end - -class ProductSweeper < ActionController::Caching::Sweeper - observe Product - - def after_create(product) - expire_action(controller: '/products', action: 'index') - end -end -``` - -Note the use of '/products' here rather than 'products'. If you wanted to expire an action cache for the `Admin::ProductsController`, you would use 'admin/products' instead. - ### SQL Caching Query caching is a Rails feature that caches the result set returned by each query so that if Rails encounters the same query again for that request, it will use the cached result set as opposed to running the query against the database again. @@ -465,14 +367,14 @@ end Instead of a options hash, you can also simply pass in a model, Rails will use the `updated_at` and `cache_key` methods for setting `last_modified` and `etag`: -<ruby> +```ruby class ProductsController < ApplicationController def show @product = Product.find(params[:id]) respond_with(@product) if stale?(@product) end end -</ruby> +``` If you don't have any special response processing and are using the default rendering mechanism (i.e. you're not using respond_to or calling render yourself) then you’ve got an easy helper in fresh_when: diff --git a/guides/source/command_line.md b/guides/source/command_line.md index 9521212581..fb15790d90 100644 --- a/guides/source/command_line.md +++ b/guides/source/command_line.md @@ -1,20 +1,20 @@ -A Guide to The Rails Command Line -================================= +The Rails Command Line +====================== Rails comes with every command line tool you'll need to -* Create a Rails application -* Generate models, controllers, database migrations, and unit tests -* Start a development server -* Experiment with objects through an interactive shell -* Profile and benchmark your new creation +After reading this guide, you will know: + +* How to create a Rails application. +* How to generate models, controllers, database migrations, and unit tests. +* How to start a development server. +* How to experiment with objects through an interactive shell. +* How to profile and benchmark your new creation. -------------------------------------------------------------------------------- NOTE: This tutorial assumes you have basic Rails knowledge from reading the [Getting Started with Rails Guide](getting_started.html). -WARNING. This Guide is based on Rails 3.2. Some of the code shown here will not work in earlier versions of Rails. - Command Line Basics ------------------- @@ -377,7 +377,7 @@ Active Record version 4.0.0.beta Action Pack version 4.0.0.beta Action Mailer version 4.0.0.beta Active Support version 4.0.0.beta -Middleware ActionDispatch::Static, Rack::Lock, Rack::Runtime, Rack::MethodOverride, ActionDispatch::RequestId, Rails::Rack::Logger, ActionDispatch::ShowExceptions, ActionDispatch::DebugExceptions, ActionDispatch::RemoteIp, ActionDispatch::Reloader, ActionDispatch::Callbacks, ActiveRecord::ConnectionAdapters::ConnectionManagement, ActiveRecord::QueryCache, ActionDispatch::Cookies, ActionDispatch::Session::CookieStore, ActionDispatch::Flash, ActionDispatch::ParamsParser, ActionDispatch::Head, Rack::ConditionalGet, Rack::ETag, ActionDispatch::BestStandardsSupport +Middleware ActionDispatch::Static, Rack::Lock, Rack::Runtime, Rack::MethodOverride, ActionDispatch::RequestId, Rails::Rack::Logger, ActionDispatch::ShowExceptions, ActionDispatch::DebugExceptions, ActionDispatch::RemoteIp, ActionDispatch::Reloader, ActionDispatch::Callbacks, ActiveRecord::Migration::CheckPending, ActiveRecord::ConnectionAdapters::ConnectionManagement, ActiveRecord::QueryCache, ActionDispatch::Cookies, ActionDispatch::Session::EncryptedCookieStore, ActionDispatch::Flash, ActionDispatch::ParamsParser, Rack::Head, Rack::ConditionalGet, Rack::ETag, ActionDispatch::BestStandardsSupport Application root /home/foobar/commandsapp Environment development Database adapter sqlite3 diff --git a/guides/source/configuring.md b/guides/source/configuring.md index ac763d6e0e..5fe8e2fba6 100644 --- a/guides/source/configuring.md +++ b/guides/source/configuring.md @@ -1,10 +1,12 @@ Configuring Rails Applications ============================== -This guide covers the configuration and initialization features available to Rails applications. By referring to this guide, you will be able to: +This guide covers the configuration and initialization features available to Rails applications. -* Adjust the behavior of your Rails applications -* Add additional code to be run at application start time +After reading this guide, you will know: + +* How to adjust the behavior of your Rails applications. +* How to add additional code to be run at application start time. -------------------------------------------------------------------------------- @@ -37,7 +39,7 @@ config.filter_parameters += [:password] 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`: ```ruby -config.active_record.observers = [:hotel_observer, :review_observer] +config.active_record.schema_format = :ruby ``` Rails will use that particular setting to configure Active Record. @@ -103,14 +105,10 @@ These configuration methods are to be called on a `Rails::Railtie` object, such * `config.log_tags` accepts a list of methods that respond to `request` object. This makes it easy to tag log lines with debug information like subdomain and request id — both very helpful in debugging multi-user production applications. -* `config.logger` accepts a logger conforming to the interface of Log4r or the default Ruby `Logger` class. Defaults to an instance of `ActiveSupport::BufferedLogger`, with auto flushing off in production mode. +* `config.logger` accepts a logger conforming to the interface of Log4r or the default Ruby `Logger` class. Defaults to an instance of `ActiveSupport::Logger`, with auto flushing off in production mode. * `config.middleware` allows you to configure the application's middleware. This is covered in depth in the [Configuring Middleware](#configuring-middleware) section below. -* `config.queue` configures the default job queue for the application. Defaults to `ActiveSupport::Queue.new` which processes jobs in a background thread. If you change the queue, you're responsible for running the jobs as well. - -* `config.queue_consumer` configures a different job consumer for the default queue. Defaults to `ActiveSupport::ThreadedQueueConsumer`. The job consumer must respond to `start`. - * `config.reload_classes_only_on_change` enables or disables reloading of classes only when tracked files change. By default tracks everything on autoload paths and is set to true. If `config.cache_classes` is true, this option is ignored. * `config.secret_key_base` used for specifying a key which allows sessions for the application to be verified against a known secure key to prevent tampering. Applications get `config.secret_key_base` initialized to a random key in `config/initializers/secret_token.rb`. @@ -133,8 +131,6 @@ These configuration methods are to be called on a `Rails::Railtie` object, such ### Configuring Assets -Rails 3.1 and up, by default, is set up to use the `sprockets` gem to manage assets within an application. This gem concatenates and compresses assets in order to make serving them much less painful. - * `config.assets.enabled` a flag that controls whether the asset pipeline is enabled. It is explicitly initialized in `config/application.rb`. * `config.assets.compress` a flag that enables the compression of compiled assets. It is explicitly set to true in `config/production.rb`. @@ -163,7 +159,7 @@ Rails 3.1 and up, by default, is set up to use the `sprockets` gem to manage ass ### Configuring Generators -Rails 3 allows you to alter what generators are used with the `config.generators` method. This method takes a block: +Rails allows you to alter what generators are used with the `config.generators` method. This method takes a block: ```ruby config.generators do |g| @@ -201,7 +197,7 @@ Every Rails application comes with a standard set of middleware which it uses in * `Rails::Rack::Logger` notifies the logs that the request has began. After request is complete, flushes all the logs. * `ActionDispatch::ShowExceptions` rescues any exception returned by the application and renders nice exception pages if the request is local or if `config.consider_all_requests_local` is set to `true`. If `config.action_dispatch.show_exceptions` is set to `false`, exceptions will be raised regardless. * `ActionDispatch::RequestId` makes a unique X-Request-Id header available to the response and enables the `ActionDispatch::Request#uuid` method. -* `ActionDispatch::RemoteIp` checks for IP spoofing attacks. Configurable with the `config.action_dispatch.ip_spoofing_check` and `config.action_dispatch.trusted_proxies` settings. +* `ActionDispatch::RemoteIp` checks for IP spoofing attacks and gets valid `client_ip` from request headers. Configurable with the `config.action_dispatch.ip_spoofing_check`, and `config.action_dispatch.trusted_proxies` options. * `Rack::Sendfile` intercepts responses whose body is being served from a file and replaces it with a server specific X-Sendfile header. Configurable with `config.action_dispatch.x_sendfile_header`. * `ActionDispatch::Callbacks` runs the prepare callbacks before serving the request. * `ActiveRecord::ConnectionAdapters::ConnectionManagement` cleans active connections after each request, unless the `rack.test` key in the request environment is set to `true`. @@ -276,6 +272,8 @@ config.middleware.delete ActionDispatch::BestStandardsSupport * `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+. + The MySQL adapter adds one additional configuration option: * `ActiveRecord::ConnectionAdapters::MysqlAdapter.emulate_booleans` controls whether Active Record will consider all `tinyint(1)` columns in a MySQL database to be booleans and is true by default. @@ -429,11 +427,6 @@ There are a number of settings available on `config.action_mailer`: config.action_mailer.interceptors = ["MailInterceptor"] ``` -* `config.action_mailer.queue` registers the queue that will be used to deliver the mail. -```ruby -config.action_mailer.queue = SomeQueue.new -``` - ### Configuring Active Support There are a few configuration options available in Active Support: @@ -444,7 +437,7 @@ There are a few configuration options available in Active Support: * `config.active_support.use_standard_json_time_format` enables or disables serializing dates to ISO 8601 format. Defaults to `true`. -* `ActiveSupport::BufferedLogger.silencer` is set to `false` to disable the ability to silence logging in a block. The default is `true`. +* `ActiveSupport::Logger.silencer` is set to `false` to disable the ability to silence logging in a block. The default is `true`. * `ActiveSupport::Cache::Store.logger` specifies the logger to use within cache store operations. @@ -614,7 +607,7 @@ Rails.application.config.before_initialize do end ``` -WARNING: Some parts of your application, notably observers and routing, are not yet set up at the point where the `after_initialize` block is called. +WARNING: Some parts of your application, notably routing, are not yet set up at the point where the `after_initialize` block is called. ### `Rails::Railtie#initializer` @@ -644,7 +637,7 @@ Below is a comprehensive list of all the initializers found in Rails in the orde * `load_active_support` Requires `active_support/dependencies` which sets up the basis for Active Support. Optionally requires `active_support/all` if `config.active_support.bare` is un-truthful, which is the default. -* `initialize_logger` Initializes the logger (an `ActiveSupport::BufferedLogger` object) for the application and makes it accessible at `Rails.logger`, provided that no initializer inserted before this point has defined `Rails.logger`. +* `initialize_logger` Initializes the logger (an `ActiveSupport::Logger` object) for the application and makes it accessible at `Rails.logger`, provided that no initializer inserted before this point has defined `Rails.logger`. * `initialize_cache` If `Rails.cache` isn't set yet, initializes the cache by referencing the value in `config.cache_store` and stores the outcome as `Rails.cache`. If this object responds to the `middleware` method, its middleware is inserted before `Rack::Runtime` in the middleware stack. diff --git a/guides/source/contributing_to_ruby_on_rails.md b/guides/source/contributing_to_ruby_on_rails.md index feb32eb06f..7c5a472971 100644 --- a/guides/source/contributing_to_ruby_on_rails.md +++ b/guides/source/contributing_to_ruby_on_rails.md @@ -1,13 +1,15 @@ Contributing to Ruby on Rails ============================= -This guide covers ways in which _you_ can become a part of the ongoing development of Ruby on Rails. After reading it, you should be familiar with: +This guide covers ways in which _you_ can become a part of the ongoing development of Ruby on Rails. -* Using GitHub to report issues -* Cloning master and running the test suite -* Helping to resolve existing issues -* Contributing to the Ruby on Rails documentation -* Contributing to the Ruby on Rails code +After reading this guide, you will know: + +* How to use GitHub to report issues. +* How to clone master and run the test suite. +* How to help resolve existing issues. +* How to contribute to the Ruby on Rails documentation. +* How to contribute to the Ruby on Rails code. Ruby on Rails is not "someone else's framework." Over the years, hundreds of people have contributed to Ruby on Rails ranging from a single character to massive architectural changes or significant documentation — all with the goal of making Ruby on Rails better for everyone. Even if you don't feel up to writing code or documentation yet, there are a variety of other ways that you can contribute, from reporting issues to testing patches. @@ -89,7 +91,7 @@ You can invoke `test_jdbcmysql`, `test_jdbcsqlite3` or `test_jdbcpostgresql` als The test suite runs with warnings enabled. Ideally, Ruby on Rails should issue no warnings, but there may be a few, as well as some from third-party libraries. Please ignore (or fix!) them, if any, and submit patches that do not issue new warnings. -As of this writing (December, 2010) they are specially noisy with Ruby 1.9. If you are sure about what you are doing and would like to have a more clear output, there's a way to override the flag: +As of this writing (December, 2010) they are especially noisy with Ruby 1.9. If you are sure about what you are doing and would like to have a more clear output, there's a way to override the flag: ```bash $ RUBYOPT=-W0 bundle exec rake test @@ -203,7 +205,7 @@ TIP: Changes that are cosmetic in nature and do not add anything substantial to ### Follow the Coding Conventions -Rails follows a simple set of coding style conventions. +Rails follows a simple set of coding style conventions: * Two spaces, no tabs (for indentation). * No trailing whitespace. Blank lines should not have any spaces. @@ -212,7 +214,8 @@ Rails follows a simple set of coding style conventions. * Prefer `&&`/`||` over `and`/`or`. * Prefer class << self over self.method for class methods. * Use `MyClass.my_method(my_arg)` not `my_method( my_arg )` or `my_method my_arg`. -* Use a = b and not a=b. +* Use `a = b` and not `a=b`. +* Use assert_not methods instead of refute. * Follow the conventions in the source you see used already. The above are guidelines — please use your best judgment in using them. @@ -401,7 +404,7 @@ following: ```bash $ git fetch upstream -$ git checkout my_pull_request +$ git checkout my_pull_request $ git rebase upstream/master $ git rebase -i diff --git a/guides/source/debugging_rails_applications.md b/guides/source/debugging_rails_applications.md index d4415d9b76..524fe46408 100644 --- a/guides/source/debugging_rails_applications.md +++ b/guides/source/debugging_rails_applications.md @@ -1,12 +1,14 @@ Debugging Rails Applications ============================ -This guide introduces techniques for debugging Ruby on Rails applications. By referring to this guide, you will be able to: +This guide introduces techniques for debugging Ruby on Rails applications. -* Understand the purpose of debugging -* Track down problems and issues in your application that your tests aren't identifying -* Learn the different ways of debugging -* Analyze the stack trace +After reading this guide, you will know: + +* The purpose of debugging. +* How to track down problems and issues in your application that your tests aren't identifying. +* The different ways of debugging. +* How to analyze the stack trace. -------------------------------------------------------------------------------- @@ -27,7 +29,7 @@ The `debug` helper will return a \<pre>-tag that renders the object using the YA <%= debug @post %> <p> <b>Title:</b> - <%=h @post.title %> + <%= @post.title %> </p> ``` @@ -56,7 +58,7 @@ Displaying an instance variable, or any other object or method, in YAML format c <%= simple_format @post.to_yaml %> <p> <b>Title:</b> - <%=h @post.title %> + <%= @post.title %> </p> ``` @@ -86,7 +88,7 @@ Another useful method for displaying object values is `inspect`, especially when <%= [1, 2, 3, 4, 5].inspect %> <p> <b>Title:</b> - <%=h @post.title %> + <%= @post.title %> </p> ``` @@ -105,7 +107,7 @@ It can also be useful to save information to log files at runtime. Rails maintai ### What is the Logger? -Rails makes use of the `ActiveSupport::BufferedLogger` class to write log information. You can also substitute another logger such as `Log4r` if you wish. +Rails makes use of the `ActiveSupport::Logger` class to write log information. You can also substitute another logger such as `Log4r` if you wish. You can specify an alternative logger in your `environment.rb` or any environment file: diff --git a/guides/source/development_dependencies_install.md b/guides/source/development_dependencies_install.md index 7dfb39fb81..db43d62fcf 100644 --- a/guides/source/development_dependencies_install.md +++ b/guides/source/development_dependencies_install.md @@ -3,6 +3,8 @@ Development Dependencies Install This guide covers how to setup an environment for Ruby on Rails core development. +After reading this guide, you will know: + -------------------------------------------------------------------------------- The Easy Way @@ -143,6 +145,9 @@ We need first to delete `.bundle/config` because Bundler remembers in that 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: ```bash +$ mysql -uroot -p + +mysql> CREATE USER 'rails'@'localhost'; mysql> GRANT ALL PRIVILEGES ON activerecord_unittest.* to 'rails'@'localhost'; mysql> GRANT ALL PRIVILEGES ON activerecord_unittest2.* diff --git a/guides/source/documents.yaml b/guides/source/documents.yaml index 19425765b8..e779407fab 100644 --- a/guides/source/documents.yaml +++ b/guides/source/documents.yaml @@ -9,13 +9,21 @@ name: Models documents: - + name: Active Record Basics + url: active_record_basics.html + description: This guide will get you started with models, persistence to database and the Active Record pattern and library. + - name: Rails Database Migrations url: migrations.html description: This guide covers how you can use Active Record migrations to alter your database in a structured and organized manner. - - name: Active Record Validations and Callbacks - url: active_record_validations_callbacks.html - description: This guide covers how you can use Active Record validations and callbacks. + name: Active Record Validations + url: active_record_validations.html + description: This guide covers how you can use Active Record validations + - + name: Active Record Callbacks + url: active_record_callbacks.html + description: This guide covers how you can use Active Record callbacks. - name: Active Record Associations url: association_basics.html diff --git a/guides/source/engines.md b/guides/source/engines.md index f9bbff1c4c..116a7e67cd 100644 --- a/guides/source/engines.md +++ b/guides/source/engines.md @@ -1,13 +1,15 @@ Getting Started with Engines ============================ -In this guide you will learn about engines and how they can be used to provide additional functionality to their host applications through a clean and very easy-to-use interface. You will learn the following things in this guide: +In this guide you will learn about engines and how they can be used to provide additional functionality to their host applications through a clean and very easy-to-use interface. -* What makes an engine -* How to generate an engine -* Building features for the engine -* Hooking the engine into an application -* Overriding engine functionality in the application +After reading this guide, you will know: + +* What makes an engine. +* How to generate an engine. +* Building features for the engine. +* Hooking the engine into an application. +* Overriding engine functionality in the application. -------------------------------------------------------------------------------- @@ -33,7 +35,7 @@ Finally, engines would not have been possible without the work of James Adam, Pi Generating an engine -------------------- -To generate an engine with Rails 3.2, you will need to run the plugin generator and pass it options as appropriate to the need. For the "blorgh" example, you will need to create a "mountable" engine, running this command in a terminal: +To generate an engine, you will need to run the plugin generator and pass it options as appropriate to the need. For the "blorgh" example, you will need to create a "mountable" engine, running this command in a terminal: ```bash $ rails plugin new blorgh --mountable @@ -699,7 +701,7 @@ This section explains how to add and/or override engine MVC functionality in the ### Overriding Models and Controllers -Engine model and controller classes can be extended by open classing them in the main Rails application (since model and controller classes are just Ruby classes that inherit Rails specific functionality). Open classing an Engine class redefines it for use in the main applicaiton. This is usually implemented by using the decorator pattern. +Engine model and controller classes can be extended by open classing them in the main Rails application (since model and controller classes are just Ruby classes that inherit Rails specific functionality). Open classing an Engine class redefines it for use in the main application. This is usually implemented by using the decorator pattern. For simple class modifications use `Class#class_eval`, and for complex class modifications, consider using `ActiveSupport::Concern`. diff --git a/guides/source/form_helpers.md b/guides/source/form_helpers.md index fc317d4773..b7145c46dc 100644 --- a/guides/source/form_helpers.md +++ b/guides/source/form_helpers.md @@ -1,17 +1,17 @@ -Rails Form helpers -================== +Form Helpers +============ -Forms in web applications are an essential interface for user input. However, form markup can quickly become tedious to write and maintain because of form control naming and their numerous attributes. Rails deals away with these complexities by providing view helpers for generating form markup. However, since they have different use-cases, developers are required to know all the differences between similar helper methods before putting them to use. +Forms in web applications are an essential interface for user input. However, form markup can quickly become tedious to write and maintain because of form control naming and their numerous attributes. Rails does away with these complexities by providing view helpers for generating form markup. However, since they have different use-cases, developers are required to know all the differences between similar helper methods before putting them to use. -In this guide you will: +After reading this guide, you will know: -* Create search forms and similar kind of generic forms not representing any specific model in your application -* Make model-centric forms for creation and editing of specific database records -* Generate select boxes from multiple types of data -* Understand the date and time helpers Rails provides -* Learn what makes a file upload form different -* Learn some cases of building forms to external resources -* Find out how to build complex forms +* How to create search forms and similar kind of generic forms not representing any specific model in your application. +* How to make model-centric forms for creation and editing of specific database records. +* How to generate select boxes from multiple types of data. +* The date and time helpers Rails provides. +* What makes a file upload form different. +* Some cases of building forms to external resources. +* How to build complex forms. -------------------------------------------------------------------------------- @@ -148,7 +148,9 @@ Output: As with `check_box_tag`, the second parameter to `radio_button_tag` is the value of the input. Because these two radio buttons share the same name (age) the user will only be able to select one, and `params[:age]` will contain either "child" or "adult". -NOTE: Always use labels for checkbox and radio buttons. They associate text with a specific option and make it easier for users to click the inputs by expanding the clickable region. +NOTE: Always use labels for checkbox and radio buttons. They associate text with a specific option and, +by expanding the clickable region, +make it easier for users to click the inputs. ### Other Helpers of Interest @@ -215,7 +217,7 @@ will produce output similar to <input id="person_name" name="person[name]" type="text" value="Henry"/> ``` -Upon form submission the value entered by the user will be stored in `params[:person][:name]`. The `params[:person]` hash is suitable for passing to `Person.new` or, if `@person` is an instance of Person, `@person.update_attributes`. While the name of an attribute is the most common second parameter to these helpers this is not compulsory. In the example above, as long as person objects have a `name` and a `name=` method Rails will be happy. +Upon form submission the value entered by the user will be stored in `params[:person][:name]`. The `params[:person]` hash is suitable for passing to `Person.new` or, if `@person` is an instance of Person, `@person.update`. While the name of an attribute is the most common second parameter to these helpers this is not compulsory. In the example above, as long as person objects have a `name` and a `name=` method Rails will be happy. WARNING: You must pass the name of an instance variable, i.e. `:person` or `"person"`, not an actual instance of your model object. @@ -458,7 +460,7 @@ As with other helpers, if you were to use the `select` helper on a form builder <%= f.select(:city_id, ...) %> ``` -WARNING: If you are using `select` (or similar helpers such as `collection_select`, `select_tag`) to set a `belongs_to` association you must pass the name of the foreign key (in the example above `city_id`), not the name of association itself. If you specify `city` instead of `city_id` Active Record will raise an error along the lines of ` ActiveRecord::AssociationTypeMismatch: City(#17815740) expected, got String(#1138750) ` when you pass the `params` hash to `Person.new` or `update_attributes`. Another way of looking at this is that form helpers only edit attributes. You should also be aware of the potential security ramifications of allowing users to edit foreign keys directly. You may wish to consider the use of `attr_protected` and `attr_accessible`. For further details on this, see the [Ruby On Rails Security Guide](security.html#mass-assignment). +WARNING: If you are using `select` (or similar helpers such as `collection_select`, `select_tag`) to set a `belongs_to` association you must pass the name of the foreign key (in the example above `city_id`), not the name of association itself. If you specify `city` instead of `city_id` Active Record will raise an error along the lines of ` ActiveRecord::AssociationTypeMismatch: City(#17815740) expected, got String(#1138750) ` when you pass the `params` hash to `Person.new` or `update`. Another way of looking at this is that form helpers only edit attributes. You should also be aware of the potential security ramifications of allowing users to edit foreign keys directly. ### Option Tags from a Collection of Arbitrary Objects @@ -534,7 +536,7 @@ The `:prefix` option is the key used to retrieve the hash of date components fro ### Model Object Helpers `select_date` does not work well with forms that update or create Active Record objects as Active Record expects each element of the `params` hash to correspond to one attribute. -The model object helpers for dates and times submit parameters with special names, when Active Record sees parameters with such names it knows they must be combined with the other parameters and given to a constructor appropriate to the column type. For example: +The model object helpers for dates and times submit parameters with special names; when Active Record sees parameters with such names it knows they must be combined with the other parameters and given to a constructor appropriate to the column type. For example: ```erb <%= date_select :person, :birth_date %> @@ -554,7 +556,7 @@ which results in a `params` hash like {:person => {'birth_date(1i)' => '2008', 'birth_date(2i)' => '11', 'birth_date(3i)' => '22'}} ``` -When this is passed to `Person.new` (or `update_attributes`), Active Record spots that these parameters should all be used to construct the `birth_date` attribute and uses the suffixed information to determine in which order it should pass these parameters to functions such as `Date.civil`. +When this is passed to `Person.new` (or `update`), Active Record spots that these parameters should all be used to construct the `birth_date` attribute and uses the suffixed information to determine in which order it should pass these parameters to functions such as `Date.civil`. ### Common Options @@ -594,8 +596,6 @@ The following two forms both upload a file. <% end %> ``` -NOTE: Since Rails 3.1, forms rendered using `form_for` have their encoding set to `multipart/form-data` automatically once a `file_field` is used inside the block. Previous versions required you to set this explicitly. - Rails provides the usual pair of helpers: the barebones `file_field_tag` and the model oriented `file_field`. The only difference with other helpers is that you cannot set a default value for file inputs as this would have no meaning. As you would expect in the first case the uploaded file is in `params[:picture]` and in the second case in `params[:person][:picture]`. ### What Gets Uploaded @@ -622,7 +622,7 @@ Unlike other forms making an asynchronous file upload form is not as simple as p Customizing Form Builders ------------------------- -As mentioned previously the object yielded by `form_for` and `fields_for` is an instance of FormBuilder (or a subclass thereof). Form builders encapsulate the notion of displaying form elements for a single object. While you can of course write helpers for your forms in the usual way you can also subclass FormBuilder and add the helpers there. For example +As mentioned previously the object yielded by `form_for` and `fields_for` is an instance of FormBuilder (or a subclass thereof). Form builders encapsulate the notion of displaying form elements for a single object. While you can of course write helpers for your forms in the usual way, you can also subclass FormBuilder and add the helpers there. For example ```erb <%= form_for @person do |f| %> @@ -807,7 +807,7 @@ Sometimes when you submit data to an external resource, like payment gateway, fi <% end %> ``` -The same technique is available for the `form_for` too: +The same technique is also available for `form_for`: ```erb <%= form_for @invoice, url: external_url, authenticity_token: 'external_token' do |f| %> diff --git a/guides/source/generators.md b/guides/source/generators.md index d1ba19e078..62de5a70bb 100644 --- a/guides/source/generators.md +++ b/guides/source/generators.md @@ -3,20 +3,18 @@ Creating and Customizing Rails Generators & Templates Rails generators are an essential tool if you plan to improve your workflow. With this guide you will learn how to create generators and customize existing ones. -In this guide you will: +After reading this guide, you will know: -* Learn how to see which generators are available in your application -* Create a generator using templates -* Learn how Rails searches for generators before invoking them -* Customize your scaffold by creating new generators -* Customize your scaffold by changing generator templates -* Learn how to use fallbacks to avoid overwriting a huge set of generators -* Learn how to create an application template +* How to see which generators are available in your application. +* How to create a generator using templates. +* How Rails searches for generators before invoking them. +* How to customize your scaffold by creating new generators. +* How to customize your scaffold by changing generator templates. +* How to use fallbacks to avoid overwriting a huge set of generators. +* How to create an application template. -------------------------------------------------------------------------------- -NOTE: This guide is about generators in Rails 3, previous versions are not covered. - First Contact ------------- diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md index 76556761f7..aa841d5867 100644 --- a/guides/source/getting_started.md +++ b/guides/source/getting_started.md @@ -1,10 +1,11 @@ Getting Started with Rails ========================== -This guide covers getting up and running with Ruby on Rails. After reading it, -you should be familiar with: +This guide covers getting up and running with Ruby on Rails. -* Installing Rails, creating a new Rails application, and connecting your +After reading this guide, you will know: + +* How to install Rails, create a new Rails application, and connect your application to a database. * The general layout of a Rails application. * The basic principles of MVC (Model, View, Controller) and RESTful design. @@ -12,9 +13,6 @@ you should be familiar with: -------------------------------------------------------------------------------- -WARNING. This Guide is based on Rails 3.2. Some of the code shown here will not -work in earlier versions of Rails. - Guide Assumptions ----------------- @@ -24,7 +22,7 @@ with Rails. However, to get the most out of it, you need to have some prerequisites installed: * The [Ruby](http://www.ruby-lang.org/en/downloads) language version 1.9.3 or higher -* The [RubyGems](http://rubyforge.org/frs/?group_id=126) packaging system +* The [RubyGems](http://rubygems.org/) packaging system * To learn more about RubyGems, please read the [RubyGems User Guide](http://docs.rubygems.org/read/book/1) * A working installation of the [SQLite3 Database](http://www.sqlite.org) @@ -77,11 +75,14 @@ TIP: The examples below use # and $ to denote superuser and regular user termina ### Installing Rails -Open up a command line prompt. On a mac this is called terminal, on windows it is called command prompt. Any commands prefaced with a dollar sign `$` should be run in the command line. Verify sure you have a current version of Ruby installed: +Open up a command line prompt. On Mac OS X open Terminal.app, on Windows choose +"Run" from your Start menu and type 'cmd.exe'. Any commands prefaced with a +dollar sign `$` should be run in the command line. Verify that you have a +current version of Ruby installed: ```bash $ ruby -v -ruby 1.9.3p194 +ruby 1.9.3p327 ``` To install Rails, use the `gem install` command provided by RubyGems: @@ -100,11 +101,11 @@ To verify that you have everything installed correctly, you should be able to ru $ rails --version ``` -If it says something like "Rails 3.2.8" you are ready to continue. +If it says something like "Rails 3.2.9", you are ready to continue. ### Creating the Blog Application -Rails comes with a number of generators that are designed to make your development life easier. One of these is the new application generator, which will provide you with the foundation of a Rails application so that you don't have to write it yourself. +Rails comes with a number of scripts called generators that are designed to make your development life easier by creating everything that's necessary to start working on a particular task. One of these is the new application generator, which will provide you with the foundation of a fresh Rails application so that you don't have to write it yourself. To use this generator, open a terminal, navigate to a directory where you have rights to create files, and type: @@ -165,7 +166,7 @@ This will fire up WEBrick, a webserver built into Ruby by default. To see your a  -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 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. +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. The "Welcome Aboard" page is the _smoke test_ for a new Rails application: it makes sure that you have your software configured correctly enough to serve a page. You can also click on the _About your application’s environment_ link to see a summary of your application's environment. @@ -214,11 +215,7 @@ Open the `app/views/welcome/index.html.erb` file in your text editor and edit it ### Setting the Application Home Page -Now that we have made the controller and view, we need to tell Rails when we want Hello Rails! to show up. In our case, we want it to show up when we navigate to the root URL of our site, <http://localhost:3000>. At the moment, however, the "Welcome Aboard" smoke test is occupying that spot. - -To fix this, delete the `index.html` file located inside the `public` directory of the application. - -You need to do this because Rails will serve any static file in the `public` directory that matches a route in preference to any dynamic content you generate from the controllers. The `index.html` file is special: it will be served if a request comes in at the root route, e.g. <http://localhost:3000>. If another request such as <http://localhost:3000/welcome> happened, a static file at `public/welcome.html` would be served first, but only if it existed. +Now that we have made the controller and view, we need to tell Rails when we want Hello Rails! to show up. In our case, we want it to show up when we navigate to the root URL of our site, <http://localhost:3000>. At the moment, "Welcome Aboard" is occupying that spot. Next, you have to tell Rails where your actual home page is located. @@ -232,7 +229,6 @@ Blog::Application.routes.draw do # first created -> highest priority. # ... # You can have the root of your site routed with "root" - # just remember to delete public/index.html. # root to: "welcome#index" ``` @@ -519,7 +515,7 @@ invoking the command: `rake db:migrate RAILS_ENV=production`. ### Saving data in the controller Back in `posts_controller`, we need to change the `create` action -to use the new `Post` model to save the data in the database. Open that file +to use the new `Post` model to save the data in the database. Open `app/controllers/posts_controller.rb` and change the `create` action to look like this: ```ruby @@ -557,8 +553,8 @@ 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. -As we did before, we need to add the `show` action in the -`posts_controller` and its respective view. +As we did before, we need to add the `show` action in +`app/controllers/posts_controller.rb` and its respective view. ```ruby def show @@ -702,19 +698,6 @@ your Rails models for free, including basic database CRUD (Create, Read, Update, Destroy) operations, data validation, as well as sophisticated search support and the ability to relate multiple models to one another. -Rails includes methods to help you secure some of your model fields. -Open the `app/models/post.rb` file and edit it: - -```ruby -class Post < ActiveRecord::Base - attr_accessible :text, :title -end -``` - -This change will ensure that all changes made through HTML forms can edit the content of the text and title fields. -It will not be possible to define any other field value through forms. You can still define them by calling the `field=` method of course. -Accessible attributes and the mass assignment problem is covered in details in the [Security guide](security.html#mass-assignment) - ### Adding Some Validation Rails includes methods to help you validate the data that you send to models. @@ -722,8 +705,6 @@ Open the `app/models/post.rb` file and edit it: ```ruby class Post < ActiveRecord::Base - attr_accessible :text, :title - validates :title, presence: true, length: { minimum: 5 } end @@ -901,7 +882,7 @@ And then create the `update` action in `app/controllers/posts_controller.rb`: def update @post = Post.find(params[:id]) - if @post.update_attributes(params[:post]) + if @post.update(params[:post]) redirect_to action: :show, id: @post.id else render 'edit' @@ -909,13 +890,13 @@ def update end ``` -The new method, `update_attributes`, is used when you want to update a record +The new method, `update`, is used when you want to update a record that already exists, and it accepts a hash containing the attributes that you want to update. As before, if there was an error updating the post we want to show the form back to the user. -TIP: You don't need to pass all attributes to `update_attributes`. For -example, if you'd call `@post.update_attributes(title: 'A new title')` +TIP: You don't need to pass all attributes to `update`. For +example, if you'd call `@post.update(title: 'A new title')` Rails would only update the `title` attribute, leaving all other attributes untouched. @@ -960,35 +941,14 @@ And here's how our app looks so far: ### Using partials to clean up duplication in views -`partials` are what Rails uses to remove duplication in views. Here's a -simple example: - -```html+erb -# app/views/user/show.html.erb - -<h1><%= @user.name %></h1> - -<%= render 'user_details' %> - -# app/views/user/_user_details.html.erb - -<%= @user.location %> - -<%= @user.about_me %> -``` - -The `users/show` template will automatically include the content of the -`users/_user_details` template. Note that partials are prefixed by an underscore, -as to not be confused with regular views. However, you don't include the -underscore when including them with the `helper` method. +Our `edit` page looks very similar to the `new` page, in fact they +both share the same code for displaying the form. Let's remove some duplication +by using a view partial. By convention, partial files are prefixed by an +underscore. TIP: You can read more about partials in the [Layouts and Rendering in Rails](layouts_and_rendering.html) guide. -Our `edit` action looks very similar to the `new` action, in fact they -both share the same code for displaying the form. Let's clean them up by -using a partial. - Create a new file `app/views/posts/_form.html.erb` with the following content: @@ -1150,7 +1110,8 @@ together. <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 }, method: :delete, data: { confirm: 'Are you sure?' } %></td> + <td><%= link_to 'Destroy', { action: :destroy, id: post.id }, + method: :delete, data: { confirm: 'Are you sure?' } %></td> </tr> <% end %> </table> @@ -1250,12 +1211,11 @@ This command will generate four files: | test/models/comment_test.rb | Testing harness for the comments model | | test/fixtures/comments.yml | Sample comments for use in testing | -First, take a look at `comment.rb`: +First, take a look at `app/models/comment.rb`: ```ruby class Comment < ActiveRecord::Base belongs_to :post - attr_accessible :body, :commenter end ``` @@ -1312,7 +1272,7 @@ this way: * One post can have many comments. In fact, this is very close to the syntax that Rails uses to declare this -association. You've already seen the line of code inside the Comment model that +association. You've already seen the line of code inside the `Comment` model (app/models/comment.rb) that makes each comment belong to a Post: ```ruby @@ -1321,14 +1281,14 @@ class Comment < ActiveRecord::Base end ``` -You'll need to edit the `post.rb` file to add the other side of the association: +You'll need to edit `app/models/post.rb` to add the other side of the association: ```ruby class Post < ActiveRecord::Base + has_many :comments validates :title, presence: true, length: { minimum: 5 } - - has_many :comments + [...] end ``` @@ -1385,7 +1345,7 @@ the post show page to see their comment now listed. Due to this, our spam comments when they arrive. So first, we'll wire up the Post show template -(`/app/views/posts/show.html.erb`) to let us make a new comment: +(`app/views/posts/show.html.erb`) to let us make a new comment: ```html+erb <p> @@ -1428,7 +1388,7 @@ class CommentsController < ApplicationController def create @post = Post.find(params[:post_id]) @comment = @post.comments.create(params[:comment]) - redirect_to post_url(@post) + redirect_to post_path(@post) end end ``` @@ -1644,7 +1604,7 @@ So first, let's add the delete link in the Clicking this new "Destroy Comment" link will fire off a `DELETE /posts/:post_id/comments/:id` to our `CommentsController`, which can then use this to find the comment we want to delete, so let's add a destroy action to our -controller: +controller (`app/controllers/comments_controller.rb`): ```ruby class CommentsController < ApplicationController @@ -1679,9 +1639,10 @@ model, `app/models/post.rb`, as follows: ```ruby class Post < ActiveRecord::Base + has_many :comments, dependent: :destroy validates :title, presence: true, length: { minimum: 5 } - has_many :comments, dependent: :destroy + [...] end ``` @@ -1701,7 +1662,7 @@ action if that method allows it. To use the authentication system, we specify it at the top of our `PostsController`, in this case, we want the user to be authenticated on every -action, except for `index` and `show`, so we write that: +action, except for `index` and `show`, so we write that in `app/controllers/posts_controller.rb`: ```ruby class PostsController < ApplicationController @@ -1716,7 +1677,7 @@ class PostsController < ApplicationController ``` We also only want to allow authenticated users to delete comments, so in the -`CommentsController` we write: +`CommentsController` (`app/controllers/comments_controller.rb`) we write: ```ruby class CommentsController < ApplicationController diff --git a/guides/source/i18n.md b/guides/source/i18n.md index 5ffd955f66..2e61bea5ea 100644 --- a/guides/source/i18n.md +++ b/guides/source/i18n.md @@ -7,18 +7,20 @@ The process of "internationalization" usually means to abstract all strings and So, in the process of _internationalizing_ your Rails application you have to: -* Ensure you have support for i18n -* Tell Rails where to find locale dictionaries -* Tell Rails how to set, preserve and switch locales +* Ensure you have support for i18n. +* Tell Rails where to find locale dictionaries. +* Tell Rails how to set, preserve and switch locales. In the process of _localizing_ your application you'll probably want to do the following three things: * Replace or supplement Rails' default locale — e.g. date and time formats, month names, Active Record model names, etc. * Abstract strings in your application into keyed dictionaries — e.g. flash messages, static text in your views, etc. -* Store the resulting dictionaries somewhere +* Store the resulting dictionaries somewhere. This guide will walk you through the I18n API and contains a tutorial on how to internationalize a Rails application from the start. +After reading this guide, you will know: + -------------------------------------------------------------------------------- NOTE: The Ruby I18n framework provides you with all necessary means for internationalization/localization of your Rails application. You may, however, use any of various plugins and extensions available, which add additional functionality or features. See the Rails [I18n Wiki](http://rails-i18n.org/wiki) for more information. @@ -94,7 +96,7 @@ This means, that in the `:en` locale, the key _hello_ will map to the _Hello wor The I18n library will use **English** as a **default locale**, i.e. if you don't set a different locale, `:en` will be used for looking up translations. -NOTE: The i18n library takes a **pragmatic approach** to locale keys (after [some discussion](http://groups.google.com/group/rails-i18n/browse_thread/thread/14dede2c7dbe9470/80eec34395f64f3c?hl=en), including only the _locale_ ("language") part, like `:en`, `:pl`, not the _region_ part, like `:en-US` or `:en-GB`, which are traditionally used for separating "languages" and "regional setting" or "dialects". Many international applications use only the "language" element of a locale such as `:cs`, `:th` or `:es` (for Czech, Thai and Spanish). However, there are also regional differences within different language groups that may be important. For instance, in the `:en-US` locale you would have $ as a currency symbol, while in `:en-GB`, you would have £. Nothing stops you from separating regional and other settings in this way: you just have to provide full "English - United Kingdom" locale in a `:en-GB` dictionary. Various [Rails I18n plugins](http://rails-i18n.org/wiki) such as [Globalize2](https://github.com/joshmh/globalize2/tree/master) may help you implement it. +NOTE: The i18n library takes a **pragmatic approach** to locale keys (after [some discussion](http://groups.google.com/group/rails-i18n/browse_thread/thread/14dede2c7dbe9470/80eec34395f64f3c?hl=en), including only the _locale_ ("language") part, like `:en`, `:pl`, not the _region_ part, like `:en-US` or `:en-GB`, which are traditionally used for separating "languages" and "regional setting" or "dialects". Many international applications use only the "language" element of a locale such as `:cs`, `:th` or `:es` (for Czech, Thai and Spanish). However, there are also regional differences within different language groups that may be important. For instance, in the `:en-US` locale you would have $ as a currency symbol, while in `:en-GB`, you would have £. Nothing stops you from separating regional and other settings in this way: you just have to provide full "English - United Kingdom" locale in a `:en-GB` dictionary. Various [Rails I18n plugins](http://rails-i18n.org/wiki) such as [Globalize3](https://github.com/svenfuchs/globalize3) may help you implement it. The **translations load path** (`I18n.load_path`) is just a Ruby Array of paths to your translation files that will be loaded automatically and available in your application. You can pick whatever directory and translation file naming scheme makes sense for you. @@ -132,10 +134,10 @@ However, you would probably like to **provide support for more locales** in your WARNING: You may be tempted to store the chosen locale in a _session_ or a <em>cookie</em>, however **do not do this**. The locale should be transparent and a part of the URL. This way you won't break people's basic assumptions about the web itself: if you send a URL to a friend, they should see the same page and content as you. A fancy word for this would be that you're being [<em>RESTful</em>](http://en.wikipedia.org/wiki/Representational_State_Transfer. Read more about the RESTful approach in [Stefan Tilkov's articles](http://www.infoq.com/articles/rest-introduction). Sometimes there are exceptions to this rule and those are discussed below. -The _setting part_ is easy. You can set the locale in a `before_filter` in the `ApplicationController` like this: +The _setting part_ is easy. You can set the locale in a `before_action` in the `ApplicationController` like this: ```ruby -before_filter :set_locale +before_action :set_locale def set_locale I18n.locale = params[:locale] || I18n.default_locale @@ -158,7 +160,7 @@ One option you have is to set the locale from the domain name where your applica You can implement it like this in your `ApplicationController`: ```ruby -before_filter :set_locale +before_action :set_locale def set_locale I18n.locale = extract_locale_from_tld || I18n.default_locale @@ -201,7 +203,7 @@ This solution has aforementioned advantages, however, you may not be able or may ### Setting the Locale from the URL Params -The most usual way of setting (and passing) the locale would be to include it in URL params, as we did in the `I18n.locale = params[:locale]` _before_filter_ in the first example. We would like to have URLs like `www.example.com/books?locale=ja` or `www.example.com/ja/books` in this case. +The most usual way of setting (and passing) the locale would be to include it in URL params, as we did in the `I18n.locale = params[:locale]` _before_action_ in the first example. We would like to have URLs like `www.example.com/books?locale=ja` or `www.example.com/ja/books` in this case. This approach has almost the same set of advantages as setting the locale from the domain name: namely that it's RESTful and in accord with the rest of the World Wide Web. It does require a little bit more work to implement, though. @@ -694,7 +696,7 @@ en: long: "%B %d, %Y" ``` -So, all of the following equivalent lookups will return the `:short` date format `"%B %d"`: +So, all of the following equivalent lookups will return the `:short` date format `"%b %d"`: ```ruby I18n.t 'date.formats.short' diff --git a/guides/source/index.html.erb b/guides/source/index.html.erb index 71fe94a870..a8e4525c67 100644 --- a/guides/source/index.html.erb +++ b/guides/source/index.html.erb @@ -9,9 +9,7 @@ Ruby on Rails Guides <% content_for :index_section do %> <div id="subCol"> <dl> - <dd class="kindle">Rails Guides are also available for Kindle and <%= link_to 'Free Kindle Reading Apps', 'http://www.amazon.com/gp/kindle/kcp' %> for the iPad, -iPhone, Mac, Android, etc. Download them from <%= link_to 'here', @mobi %>. - </dd> + <dd class="kindle">Rails Guides are also available for <%= link_to 'Kindle', @mobi %>.</dd> <dd class="work-in-progress">Guides marked with this icon are currently being worked on and will not be available in the Guides Index menu. While still useful, they may contain incomplete information and even errors. You can help by reviewing them and posting your comments and corrections.</dd> </dl> </div> diff --git a/guides/source/initialization.md b/guides/source/initialization.md index 393bf51863..457e28383d 100644 --- a/guides/source/initialization.md +++ b/guides/source/initialization.md @@ -4,7 +4,9 @@ The Rails Initialization Process This guide explains the internals of the initialization process in Rails as of Rails 4. It is an extremely in-depth guide and recommended for advanced Rails developers. -* Using `rails server` +After reading this guide, you will know: + +* How to use `rails server`. -------------------------------------------------------------------------------- @@ -230,13 +232,13 @@ when 'server' Dir.chdir(File.expand_path('../../', APP_PATH)) unless File.exists?(File.expand_path("config.ru")) require 'rails/commands/server' - Rails::Server.new.tap { |server| + Rails::Server.new.tap do |server| # We need to require application after the server sets environment, # otherwise the --environment option given to the server won't propagate. require APP_PATH Dir.chdir(Rails.application.root) server.start - } + end ``` 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. diff --git a/guides/source/kindle/rails_guides.opf.erb b/guides/source/kindle/rails_guides.opf.erb index 4e07664fd0..547abcbc19 100644 --- a/guides/source/kindle/rails_guides.opf.erb +++ b/guides/source/kindle/rails_guides.opf.erb @@ -32,7 +32,7 @@ <item id="toc" media-type="application/x-dtbncx+xml" href="toc.ncx" /> - <item id="cover" media-type="image/jpeg" href="images/rails_guides_kindle_cover.jpg"/> + <item id="cover" media-type="image/jpg" href="images/rails_guides_kindle_cover.jpg"/> </manifest> <spine toc="toc"> diff --git a/guides/source/layouts_and_rendering.md b/guides/source/layouts_and_rendering.md index 141876b5a3..fa303745b8 100644 --- a/guides/source/layouts_and_rendering.md +++ b/guides/source/layouts_and_rendering.md @@ -3,10 +3,12 @@ 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: -* Use the various rendering methods built into Rails -* Create layouts with multiple content sections -* Use partials to DRY up your views -* Use nested layouts (sub-templates) +After reading this guide, you will know: + +* How to use the various rendering methods built into Rails. +* How to create layouts with multiple content sections. +* How to use partials to DRY up your views. +* How to use nested layouts (sub-templates). -------------------------------------------------------------------------------- @@ -135,7 +137,7 @@ If you want to render the view that corresponds to a different template within t ```ruby def update @book = Book.find(params[:id]) - if @book.update_attributes(params[:book]) + if @book.update(params[:book]) redirect_to(@book) else render "edit" @@ -143,14 +145,14 @@ def update end ``` -If the call to `update_attributes` fails, calling the `update` action in this controller will render the `edit.html.erb` template belonging to the same controller. +If the call to `update` fails, calling the `update` action in this controller will render the `edit.html.erb` template belonging to the same controller. If you prefer, you can use a symbol instead of a string to specify the action to render: ```ruby def update @book = Book.find(params[:id]) - if @book.update_attributes(params[:book]) + if @book.update(params[:book]) redirect_to(@book) else render :edit @@ -158,21 +160,6 @@ def update end ``` -To be explicit, you can use `render` with the `:action` option (though this is no longer necessary in Rails 3.0): - -```ruby -def update - @book = Book.find(params[:id]) - if @book.update_attributes(params[:book]) - redirect_to(@book) - else - render action: "edit" - end -end -``` - -WARNING: Using `render` with `:action` is a frequent source of confusion for Rails newcomers. The specified action is used to determine which view to render, but Rails does _not_ run any of the code for that action in the controller. Any instance variables that you require in the view must be set up in the current action before calling `render`. - #### Rendering an Action's Template from Another Controller What if you want to render a template from an entirely different controller from the one that contains the action code? You can also do that with `render`, which accepts the full path (relative to `app/views`) of the template to render. For example, if you're running code in an `AdminProductsController` that lives in `app/controllers/admin`, you can render the results of an action to a template in `app/views/products` this way: @@ -672,7 +659,7 @@ There are three tag options available for the `auto_discovery_link_tag`: The `javascript_include_tag` helper returns an HTML `script` tag for each source provided. -If you are using Rails with the [Asset Pipeline](asset_pipeline.html) enabled, this helper will generate a link to `/assets/javascripts/` rather than `public/javascripts` which was used in earlier versions of Rails. This link is then served by the Sprockets gem, which was introduced in Rails 3.1. +If you are using Rails with the [Asset Pipeline](asset_pipeline.html) enabled, this helper will generate a link to `/assets/javascripts/` rather than `public/javascripts` which was used in earlier versions of Rails. This link is then served by the asset pipeline. A JavaScript file within a Rails application or Rails engine goes in one of three locations: `app/assets`, `lib/assets` or `vendor/assets`. These locations are explained in detail in the [Asset Organization section in the Asset Pipeline Guide](asset_pipeline.html#asset-organization) @@ -795,7 +782,7 @@ To include `app/assets/stylesheets/main.css` and `app/assets/stylesheets/columns To include `app/assets/stylesheets/main.css` and `app/assets/stylesheets/photos/columns.css`: ```erb -<%= stylesheet_link_tag "main", "/photos/columns" %> +<%= stylesheet_link_tag "main", "photos/columns" %> ``` To include `http://example.com/main.css`: @@ -841,7 +828,7 @@ You can even use dynamic paths such as `cache/#{current_site}/main/display`. The `image_tag` helper builds an HTML `<img />` tag to the specified file. By default, files are loaded from `public/images`. -WARNING: Note that you must specify the extension of the image. Previous versions of Rails would allow you to just use the image name and would append `.png` if no extension was given but Rails 3.0 does not. +WARNING: Note that you must specify the extension of the image. ```erb <%= image_tag "header.png" %> @@ -1089,8 +1076,6 @@ Every partial also has a local variable with the same name as the partial (minus Within the `customer` partial, the `customer` variable will refer to `@new_customer` from the parent view. -WARNING: In previous versions of Rails, the default local variable would look for an instance variable with the same name as the partial in the parent. This behavior was deprecated in 2.3 and has been removed in Rails 3.0. - If you have an instance of a model to render into a partial, you can use a shorthand syntax: ```erb @@ -1118,7 +1103,7 @@ Partials are very useful in rendering collections. When you pass a collection to When a partial is called with a pluralized collection, then the individual instances of the partial have access to the member of the collection being rendered via a variable named after the partial. In this case, the partial is `_product`, and within the `_product` partial, you can refer to `product` to get the instance that is being rendered. -In Rails 3.0, there is also a shorthand for this. Assuming `@products` is a collection of `product` instances, you can simply write this in the `index.html.erb` to produce the same result: +There is also a shorthand for this. Assuming `@products` is a collection of `product` instances, you can simply write this in the `index.html.erb` to produce the same result: ```html+erb <h1>Products</h1> diff --git a/guides/source/migrations.md b/guides/source/migrations.md index a1131f1f79..617e01bd15 100644 --- a/guides/source/migrations.md +++ b/guides/source/migrations.md @@ -1,41 +1,39 @@ -Migrations -========== +Active Record Migrations +======================== -Migrations are a convenient way for you to alter your database in a structured -and organized manner. You could edit fragments of SQL by hand but you would then -be responsible for telling other developers that they need to go and run them. -You'd also have to keep track of which changes need to be run against the -production machines next time you deploy. +Migrations are a feature of Active Record that allows you to evolve your +database schema over time. Rather than write schema modifications in pure SQL, +migrations allow you to use an easy Ruby DSL to describe changes to your +tables. -Active Record tracks which migrations have already been run so all you have to -do is update your source and run `rake db:migrate`. Active Record will work out -which migrations should be run. Active Record will also update your `db/schema.rb` file to match the up-to-date structure of your database. +After reading this guide, you will know: -Migrations also allow you to describe these transformations using Ruby. The -great thing about this is that (like most of Active Record's functionality) it -is database independent: you don't need to worry about the precise syntax of -`CREATE TABLE` any more than you worry about variations on `SELECT *` (you can -drop down to raw SQL for database specific features). For example, you could use -SQLite3 in development, but MySQL in production. +* The generators you can use to create them. +* The methods Active Record provides to manipulate your database. +* The Rake tasks that manipulate migrations and your schema. +* How migrations relate to `schema.rb`. -In this guide, you'll learn all about migrations including: +-------------------------------------------------------------------------------- -* The generators you can use to create them -* The methods Active Record provides to manipulate your database -* The Rake tasks that manipulate them -* How they relate to `schema.rb` +Migration Overview +------------------ --------------------------------------------------------------------------------- +Migrations are a convenient way to alter your database schema over time in a +consistent and easy way. They use a Ruby DSL so that you don't have to write +SQL by hand, allowing your schema and changes to be database independent. -Anatomy of a Migration ----------------------- +You can think of each migration as being a new 'version' of the database. A +schema starts off with nothing in it, and each migration modifies it to add or +remove tables, columns, or entries. Active Record knows how to update your +schema along this timeline, bringing it from whatever point it is in the +history to the latest version. Active Record will also update your +`db/schema.rb` file to match the up-to-date structure of your database. -Before we dive into the details of a migration, here are a few examples of the -sorts of things you can do: +Here's an example of a migration: ```ruby class CreateProducts < ActiveRecord::Migration - def up + def change create_table :products do |t| t.string :name t.text :description @@ -43,102 +41,64 @@ class CreateProducts < ActiveRecord::Migration t.timestamps end end - - def down - drop_table :products - end end ``` -This migration adds a table called `products` with a string column called `name` -and a text column called `description`. A primary key column called `id` will -also be added, however since this is the default we do not need to explicitly specify it. -The timestamp columns `created_at` and `updated_at` which Active Record -populates automatically will also be added. Reversing this migration is as -simple as dropping the table. +This migration adds a table called `products` with a string column called +`name` and a text column called `description`. A primary key column called `id` +will also be added implicitly, as it's the default primary key for all Active +Record models. The `timestamps` macro adds two columns, `created_at` and +`updated_at`. These special columns are automatically managed by Active Record +if they exist. + +Note that we define the change that we want to happen moving forward in time. +Before this migration is run, there will be no table. After, the table will +exist. Active Record knows how to reverse this migration as well: if we roll +this migration back, it will remove the table. + +On databases that support transactions with statements that change the schema, +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. -Migrations are not limited to changing the schema. You can also use them to fix -bad data in the database or populate new fields: +If you wish for a migration to do something that Active Record doesn't know how +to reverse, you can use `reversible`: ```ruby -class AddReceiveNewsletterToUsers < ActiveRecord::Migration - def up - change_table :users do |t| - t.boolean :receive_newsletter, default: false +class ChangeProductsPrice < ActiveRecord::Migration + def change + reversible do |dir| + change_table :products do |t| + dir.up { t.change :price, :string } + dir.down { t.change :price, :integer } + end end - User.update_all receive_newsletter: true - end - - def down - remove_column :users, :receive_newsletter end end ``` -NOTE: Some [caveats](#using-models-in-your-migrations) apply to using models in -your migrations. - -This migration adds a `receive_newsletter` column to the `users` table. We want -it to default to `false` for new users, but existing users are considered to -have already opted in, so we use the User model to set the flag to `true` for -existing users. - -### Using the change method - -Rails 3.1 and up makes migrations smarter by providing a `change` method. -This method is preferred for writing constructive migrations (adding columns or -tables). The migration knows how to migrate your database and reverse it when -the migration is rolled back without the need to write a separate `down` method. +Alternatively, you can use `up` and `down` instead of `change`: ```ruby -class CreateProducts < ActiveRecord::Migration - def change - create_table :products do |t| - t.string :name - t.text :description +class ChangeProductsPrice < ActiveRecord::Migration + def up + change_table :products do |t| + t.change :price, :string + end + end - t.timestamps + def down + change_table :products do |t| + t.change :price, :integer end end end ``` -### Migrations are Classes - -A migration is a subclass of `ActiveRecord::Migration` that implements -two methods: `up` (perform the required transformations) and `down` (revert -them). - -Active Record provides methods that perform common data definition tasks in a -database independent way (you'll read about them in detail later): - -* `add_column` -* `add_reference` -* `add_index` -* `change_column` -* `change_table` -* `create_table` -* `create_join_table` -* `drop_table` -* `remove_column` -* `remove_index` -* `rename_column` -* `remove_reference` - -If you need to perform tasks specific to your database (e.g., create a -[foreign key](#active-record-and-referential-integrity) constraint) then the -`execute` method allows you to execute arbitrary SQL. A migration is just a -regular Ruby class so you're not limited to these functions. For example, after -adding a column you could write code to set the value of that column for -existing records (if necessary using your models). - -On databases that support transactions with statements that change the schema -(such as PostgreSQL or SQLite3), migrations are wrapped in a transaction. If the -database does not support this (for example MySQL) 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. +Creating a Migration +-------------------- -### What's in a Name +### Creating a Standalone Migration Migrations are stored as files in the `db/migrate` directory, one for each migration class. The name of the file is of the form @@ -148,119 +108,12 @@ of the migration. The name of the migration class (CamelCased version) should match the latter part of the file name. For example `20080906120000_create_products.rb` should define class `CreateProducts` and `20080906120001_add_details_to_products.rb` should define -`AddDetailsToProducts`. If you do feel the need to change the file name then you -<em>have to</em> update the name of the class inside or Rails will complain -about a missing class. - -Internally Rails only uses the migration's number (the timestamp) to identify -them. Prior to Rails 2.1 the migration number started at 1 and was incremented -each time a migration was generated. With multiple developers it was easy for -these to clash requiring you to rollback migrations and renumber them. With -Rails 2.1+ this is largely avoided by using the creation time of the migration -to identify them. You can revert to the old numbering scheme by adding the -following line to `config/application.rb`. - -```ruby -config.active_record.timestamped_migrations = false -``` - -The combination of timestamps and recording which migrations have been run -allows Rails to handle common situations that occur with multiple developers. - -For example, Alice adds migrations `20080906120000` and `20080906123000` and Bob -adds `20080906124500` and runs it. Alice finishes her changes and checks in her -migrations and Bob pulls down the latest changes. When Bob runs `rake db:migrate`, -Rails knows that it has not run Alice's two migrations so it executes the `up` method for each migration. - -Of course this is no substitution for communication within the team. For -example, if Alice's migration removed a table that Bob's migration assumed to -exist, then trouble would certainly strike. - -### Changing Migrations +`AddDetailsToProducts`. Rails uses this timestamp to determine which migration +should be run and in what order, so if you're copying a migration from another +application or generate a file yourself, be aware of its position in the order. -Occasionally you will make a mistake when writing a migration. If you have -already run the migration then you cannot just edit the migration and run the -migration again: Rails thinks it has already run the migration and so will do -nothing when you run `rake db:migrate`. You must rollback the migration (for -example with `rake db:rollback`), edit your migration and then run `rake db:migrate` to run the corrected version. - -In general, editing existing migrations is not a good idea. You will be creating -extra work for yourself and your co-workers and cause major headaches if the -existing version of the migration has already been run on production machines. -Instead, you should write a new migration that performs the changes you require. -Editing a freshly generated migration that has not yet been committed to source -control (or, more generally, which has not been propagated beyond your -development machine) is relatively harmless. - -### Supported Types - -Active Record supports the following database column types: - -* `:binary` -* `:boolean` -* `:date` -* `:datetime` -* `:decimal` -* `:float` -* `:integer` -* `:primary_key` -* `:string` -* `:text` -* `:time` -* `:timestamp` - -These will be mapped onto an appropriate underlying database type. For example, -with MySQL the type `:string` is mapped to `VARCHAR(255)`. You can create -columns of types not supported by Active Record when using the non-sexy syntax such as - -```ruby -create_table :products do |t| - t.column :name, 'polygon', null: false -end -``` - -This may however hinder portability to other databases. - -Creating a Migration --------------------- - -### Creating a Model - -The model and scaffold generators will create migrations appropriate for adding -a new model. This migration will already contain instructions for creating the -relevant table. If you tell Rails what columns you want, then statements for -adding these columns will also be created. For example, running - -```bash -$ rails generate model Product name:string description:text -``` - -TIP: All lines starting with a dollar sign `$` are intended to be run on the command line. - -will create a migration that looks like this - -```ruby -class CreateProducts < ActiveRecord::Migration - def change - create_table :products do |t| - t.string :name - t.text :description - - t.timestamps - end - end -end -``` - -You can append as many column name/type pairs as you want. By default, the -generated migration will include `t.timestamps` (which creates the -`updated_at` and `created_at` columns that are automatically populated -by Active Record). - -### Creating a Standalone Migration - -If you are creating migrations for other purposes (e.g., to add a column -to an existing table) then you can also use the migration generator: +Of course, calculating timestamps is no fun, so Active Record provides a +generator to handle making it for you: ```bash $ rails generate migration AddPartNumberToProducts @@ -303,12 +156,8 @@ generates ```ruby class RemovePartNumberFromProducts < ActiveRecord::Migration - def up - remove_column :products, :part_number - end - - def down - add_column :products, :part_number, :string + def change + remove_column :products, :part_number, :string end end ``` @@ -334,11 +183,8 @@ 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. -NOTE: The generated migration file for destructive migrations will still be -old-style using the `up` and `down` methods. This is because Rails needs to know -the original data types defined when you made the original changes. - -Also, the generator accepts column type as `references`(also available as `belongs_to`). For instance +Also, the generator accepts column type as `references`(also available as +`belongs_to`). For instance ```bash $ rails generate migration AddUserRefToProducts user:references @@ -354,12 +200,59 @@ class AddUserRefToProducts < ActiveRecord::Migration end ``` -This migration will create a user_id column and appropriate index. +This migration will create a `user_id` column and appropriate index. + +There is also a generator which will produce join tables if `JoinTable` is part of the name: + +```bash +rails g migration CreateJoinTableCustomerProduct customer product +``` + +will produce the following migration: + +```ruby +class CreateJoinTableCustomerProduct < ActiveRecord::Migration + def change + create_join_table :customers, :products do |t| + # t.index [:customer_id, :product_id] + # t.index [:product_id, :customer_id] + end + end +end +``` + +### Model Generators + +The model and scaffold generators will create migrations appropriate for adding +a new model. This migration will already contain instructions for creating the +relevant table. If you tell Rails what columns you want, then statements for +adding these columns will also be created. For example, running + +```bash +$ rails generate model Product name:string description:text +``` + +will create a migration that looks like this + +```ruby +class CreateProducts < ActiveRecord::Migration + def change + create_table :products do |t| + t.string :name + t.text :description + + t.timestamps + end + end +end +``` + +You can append as many column name/type pairs as you want. ### Supported Type Modifiers -You can also specify some options just after the field type between curly braces. You can use the -following modifiers: +You can also specify some options just after the field type between curly +braces. You can use the following modifiers: * `limit` Sets the maximum size of the `string/text/binary/integer` fields * `precision` Defines the precision for the `decimal` fields @@ -391,8 +284,9 @@ get to work! ### Creating a Table -Migration method `create_table` will be one of your workhorses. A typical use -would be +The `create_table` method is one of the most fundamental, but most of the time, +will be generated for you from using a model or scaffold generator. A typical +use would be ```ruby create_table :products do |t| @@ -403,31 +297,11 @@ end which creates a `products` table with a column called `name` (and as discussed below, an implicit `id` column). -The object yielded to the block allows you to create columns on the table. There -are two ways of doing it. The first (traditional) form looks like - -```ruby -create_table :products do |t| - t.column :name, :string, null: false -end -``` - -The second form, the so called "sexy" migration, drops the somewhat redundant -`column` method. Instead, the `string`, `integer`, etc. methods create a column -of that type. Subsequent parameters are the same. - -```ruby -create_table :products do |t| - t.string :name, null: false -end -``` - By default, `create_table` will create a primary key called `id`. You can change the name of the primary key with the `:primary_key` option (don't forget to -update the corresponding model) or, if you don't want a primary key at all (for -example for a HABTM join table), you can pass the option `id: false`. If you -need to pass database specific options you can place an SQL fragment in the -`:options` option. For example, +update the corresponding model) or, if you don't want a primary key at all, you +can pass the option `id: false`. If you need to pass database specific options +you can place an SQL fragment in the `:options` option. For example, ```ruby create_table :products, options: "ENGINE=BLACKHOLE" do |t| @@ -447,10 +321,12 @@ would be 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. +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. -You can pass the option `:table_name` with you want to customize the table name. For example, +You can pass the option `:table_name` with you want to customize the table +name. For example, ```ruby create_join_table :products, :categories, table_name: :categorization @@ -458,20 +334,21 @@ 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, +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, ```ruby create_join_table :products, :categories, column_options: {null: true} ``` -will create the `product_id` and `category_id` with the `:null` option as `true`. +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 -tables. It is used in a similar fashion to `create_table` but the object yielded -to the block knows more tricks. For example +tables. It is used in a similar fashion to `create_table` but the object +yielded to the block knows more tricks. For example ```ruby change_table :products do |t| @@ -485,71 +362,19 @@ end removes the `description` and `name` columns, creates a `part_number` string column and adds an index on it. Finally it renames the `upccode` column. -### Special Helpers - -Active Record provides some shortcuts for common functionality. It is for -example very common to add both the `created_at` and `updated_at` columns and so -there is a method that does exactly that: - -```ruby -create_table :products do |t| - t.timestamps -end -``` - -will create a new products table with those two columns (plus the `id` column) -whereas - -```ruby -change_table :products do |t| - t.timestamps -end -``` -adds those columns to an existing table. - -Another helper is called `references` (also available as `belongs_to`). In its -simplest form it just adds some readability. - -```ruby -create_table :products do |t| - t.references :category -end -``` - -will create a `category_id` column of the appropriate type. Note that you pass -the model name, not the column name. Active Record adds the `_id` for you. If -you have polymorphic `belongs_to` associations then `references` will add both -of the columns required: - -```ruby -create_table :products do |t| - t.references :attachment, polymorphic: {default: 'Photo'} -end -``` +### When Helpers aren't Enough -will add an `attachment_id` column and a string `attachment_type` column with -a default value of 'Photo'. `references` also allows you to define an -index directly, instead of using `add_index` after the `create_table` call: +If the helpers provided by Active Record aren't enough you can use the `execute` +method to execute arbitrary SQL: ```ruby -create_table :products do |t| - t.references :category, index: true -end +Products.connection.execute('UPDATE `products` SET `price`=`free` WHERE 1') ``` -will create an index identical to calling `add_index :products, :category_id`. - -NOTE: The `references` helper does not actually create foreign key constraints -for you. You will need to use `execute` or a plugin that adds [foreign key -support](#active-record-and-referential-integrity). - -If the helpers provided by Active Record aren't enough you can use the `execute` -method to execute arbitrary SQL. - For more details and examples of individual methods, check the API documentation. In particular the documentation for [`ActiveRecord::ConnectionAdapters::SchemaStatements`](http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/SchemaStatements.html) -(which provides the methods available in the `up` and `down` methods), +(which provides the methods available in the `change`, `up` and `down` methods), [`ActiveRecord::ConnectionAdapters::TableDefinition`](http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/TableDefinition.html) (which provides the methods available on the object yielded by `create_table`) and @@ -558,30 +383,89 @@ and ### Using the `change` Method -The `change` method removes the need to write both `up` and `down` methods in -those cases that Rails knows how to revert the changes automatically. Currently, -the `change` method supports only these migration definitions: +The `change` method is the primary way of writing migrations. It works for the +majority of cases, where Active Record knows how to reverse the migration +automatically. Currently, the `change` method supports only these migration +definitions: * `add_column` * `add_index` +* `add_reference` * `add_timestamps` * `create_table` +* `create_join_table` +* `drop_table` (must supply a block) +* `drop_join_table` (must supply a block) * `remove_timestamps` * `rename_column` * `rename_index` +* `remove_reference` * `rename_table` -If you're going to need to use any other methods, you'll have to write the -`up` and `down` methods instead of using the `change` method. +`change_table` is also reversible, as long as the block does not call `change`, +`change_default` or `remove`. + +If you're going to need to use any other methods, you should use `reversible` +or write the `up` and `down` methods instead of using the `change` method. + +### Using `reversible` + +Complex migrations may require processing that Active Record doesn't know how +to reverse. You can use `reversible` to specify what to do when running a +migration what else to do when reverting it. For example, + +```ruby +class ExampleMigration < ActiveRecord::Migration + def change + create_table :products do |t| + t.references :category + end + + reversible do |dir| + dir.up do + #add a foreign key + execute <<-SQL + ALTER TABLE products + ADD CONSTRAINT fk_products_categories + FOREIGN KEY (category_id) + REFERENCES categories(id) + SQL + end + dir.down do + execute <<-SQL + ALTER TABLE products + DROP FOREIGN KEY fk_products_categories + SQL + end + end + + add_column :users, :home_page_url, :string + rename_column :users, :email, :email_address + end +``` + +Using `reversible` will insure 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. + +Sometimes your migration will do something which is just plain irreversible; for +example, it might destroy some data. In such cases, you can raise +`ActiveRecord::IrreversibleMigration` in your `down` block. If someone tries +to revert your migration, an error message will be displayed saying that it +can't be done. ### Using the `up`/`down` Methods -The `down` method of your migration should revert the transformations done by -the `up` method. In other words, the database schema should be unchanged if you -do an `up` followed by a `down`. For example, if you create a table in the `up` -method, you should drop it in the `down` method. It is wise to reverse the -transformations in precisely the reverse order they were made in the `up` -method. For example, +You can also use the old style of migration using `up` and `down` methods +instead of the `change` method. +The `up` method should describe the transformation you'd like to make to your +schema, and the `down` method of your migration should revert the +transformations done by the `up` method. In other words, the database schema +should be unchanged if you do an `up` followed by a `down`. For example, if you +create a table in the `up` method, you should drop it in the `down` method. It +is wise to reverse the transformations in precisely the reverse order they were +made in the `up` method. The example in the `reversible` section is equivalent to: ```ruby class ExampleMigration < ActiveRecord::Migration @@ -589,6 +473,7 @@ class ExampleMigration < ActiveRecord::Migration create_table :products do |t| t.references :category end + #add a foreign key execute <<-SQL ALTER TABLE products @@ -596,6 +481,7 @@ class ExampleMigration < ActiveRecord::Migration FOREIGN KEY (category_id) REFERENCES categories(id) SQL + add_column :users, :home_page_url, :string rename_column :users, :email, :email_address end @@ -603,38 +489,112 @@ class ExampleMigration < ActiveRecord::Migration def down rename_column :users, :email_address, :email remove_column :users, :home_page_url + execute <<-SQL ALTER TABLE products DROP FOREIGN KEY fk_products_categories SQL + drop_table :products end end ``` -Sometimes your migration will do something which is just plain irreversible; for -example, it might destroy some data. In such cases, you can raise +If your migration is irreversible, you should raise `ActiveRecord::IrreversibleMigration` from your `down` method. If someone tries to revert your migration, an error message will be displayed saying that it can't be done. +### Reverting Previous Migrations + +You can use Active Record's ability to rollback migrations using the `revert` method: + +```ruby +require_relative '2012121212_example_migration' + +class FixupExampleMigration < ActiveRecord::Migration + def change + revert ExampleMigration + + create_table(:apples) do |t| + t.string :variety + end + end +end +``` + +The `revert` method also accepts a block of instructions to reverse. +This could be useful to revert selected parts of previous migrations. +For example, let's imagine that `ExampleMigration` is committed and it +is later decided it would be best to serialize the product list instead. +One could write: + +```ruby +class SerializeProductListMigration < ActiveRecord::Migration + def change + add_column :categories, :product_list + + reversible do |dir| + dir.up do + # transfer data from Products to Category#product_list + end + dir.down do + # create Products from Category#product_list + end + end + + revert do + # copy-pasted code from ExampleMigration + create_table :products do |t| + t.references :category + end + + reversible do |dir| + dir.up do + #add a foreign key + execute <<-SQL + ALTER TABLE products + ADD CONSTRAINT fk_products_categories + FOREIGN KEY (category_id) + REFERENCES categories(id) + SQL + end + dir.down do + execute <<-SQL + ALTER TABLE products + DROP FOREIGN KEY fk_products_categories + SQL + end + end + + # The rest of the migration was ok + end + end +end +``` + +The same migration could also have been written without using `revert` +but this would have involved a few more steps: reversing the order +of `create_table` and `reversible`, replacing `create_table` +by `drop_table`, and finally replacing `up` by `down` and vice-versa. +This is all taken care of by `revert`. + Running Migrations ------------------ -Rails provides a set of rake tasks to work with migrations which boil down to -running certain sets of migrations. +Rails provides a set of Rake tasks to run certain sets of migrations. -The very first migration related rake task you will use will probably be -`rake db:migrate`. In its most basic form it just runs the `up` or `change` +The very first migration related Rake task you will use will probably be +`rake db:migrate`. In its most basic form it just runs the `change` or `up` method for all the migrations that have not yet been run. If there are no such migrations, it exits. It will run these migrations in order based on the date of the migration. Note that running the `db:migrate` also invokes the `db:schema:dump` task, which -will update your db/schema.rb file to match the structure of your database. +will update your `db/schema.rb` file to match the structure of your database. If you specify a target version, Active Record will run the required migrations -(up, down or change) until it has reached the specified version. The version +(change, up, down) until it has reached the specified version. The version is the numerical prefix on the migration's filename. For example, to migrate to version 20080906120000 run @@ -643,7 +603,8 @@ $ rake db:migrate VERSION=20080906120000 ``` If version 20080906120000 is greater than the current version (i.e., it is -migrating upwards), this will run the `up` method on all migrations up to and +migrating upwards), this will run the `change` (or `up`) method +on all migrations up to and including 20080906120000, and will not execute any later migrations. If migrating downwards, this will run the `down` method on all the migrations down to, but not including, 20080906120000. @@ -658,14 +619,15 @@ number associated with the previous migration you can run $ rake db:rollback ``` -This will run the `down` method from the latest migration. If you need to undo +This will rollback the latest migration, either by reverting the `change` +method or by running the `down` method. If you need to undo several migrations you can provide a `STEP` parameter: ```bash $ rake db:rollback STEP=3 ``` -will run the `down` method from the last 3 migrations. +will revert the last 3 migrations. The `db:migrate:redo` task is a shortcut for doing a rollback and then migrating back up again. As with the `db:rollback` task, you can use the `STEP` parameter @@ -684,28 +646,33 @@ version to migrate to. The `rake db:reset` task will drop the database, recreate it and load the current schema into it. -NOTE: This is not the same as running all the migrations. It will only use the contents -of the current schema.rb file. If a migration can't be rolled back, 'rake db:reset' -may not help you. To find out more about dumping the schema see [schema.rb](#schema-dumping-and-you). +NOTE: This is not the same as running all the migrations. It will only use the +contents of the current schema.rb file. If a migration can't be rolled back, +'rake db:reset' may not help you. To find out more about dumping the schema see +'[schema dumping and you](#schema-dumping-and-you).' ### Running Specific Migrations If you need to run a specific migration up or down, the `db:migrate:up` and `db:migrate:down` tasks will do that. Just specify the appropriate version and -the corresponding migration will have its `up` or `down` method invoked, for -example, +the corresponding migration will have its `change`, `up` or `down` method +invoked, for example, ```bash $ rake db:migrate:up VERSION=20080906120000 ``` -will run the `up` method from the 20080906120000 migration. This task will first -check whether the migration is already performed and will do nothing if Active Record believes -that it has already been run. +will run the 20080906120000 migration by running the `change` method (or the +`up` method). This task will +first check whether the migration is already performed and will do nothing if +Active Record believes that it has already been run. ### Running Migrations in Different Environments -By default running `rake db:migrate` will run in the `development` environment. To run migrations against another environment you can specify it using the `RAILS_ENV` environment variable while running the command. For example to run migrations against the `test` environment you could run: +By default running `rake db:migrate` will run in the `development` environment. +To run migrations against another environment you can specify it using the +`RAILS_ENV` environment variable while running the command. For example to run +migrations against the `test` environment you could run: ```bash $ rake db:migrate RAILS_ENV=test @@ -743,9 +710,12 @@ class CreateProducts < ActiveRecord::Migration t.timestamps end end + say "Created a table" + suppress_messages {add_index :products, :name} say "and an index!", true + say_with_time 'Waiting for a while' do sleep 10 250 @@ -769,11 +739,33 @@ generates the following output If you want Active Record to not output anything, then running `rake db:migrate VERBOSE=false` will suppress all output. +Changing Existing Migrations +---------------------------- + +Occasionally you will make a mistake when writing a migration. If you have +already run the migration then you cannot just edit the migration and run the +migration again: Rails thinks it has already run the migration and so will do +nothing when you run `rake db:migrate`. You must rollback the migration (for +example with `rake db:rollback`), edit your migration and then run +`rake db:migrate` to run the corrected version. + +In general, editing existing migrations is not a good idea. You will be +creating extra work for yourself and your co-workers and cause major headaches +if the existing version of the migration has already been run on production +machines. Instead, you should write a new migration that performs the changes +you require. Editing a freshly generated migration that has not yet been +committed to source control (or, more generally, which has not been propagated +beyond your development machine) is relatively harmless. + +The `revert` method can be helpful when writing a new migration to undo +previous migrations in whole or in part +(see [Reverting Previous Migrations](#reverting-previous-migrations) above). + Using Models in Your Migrations ------------------------------- -When creating or updating data in a migration it is often tempting to use one of -your models. After all, they exist to provide easy access to the underlying +When creating or updating data in a migration it is often tempting to use one +of your models. After all, they exist to provide easy access to the underlying data. This can be done, but some caution should be observed. For example, problems occur when the model uses database columns which are (1) @@ -786,7 +778,7 @@ which contains a `Product` model: Bob goes on vacation. Alice creates a migration for the `products` table which adds a new column and -initializes it. She also adds a validation to the `Product` model for the new +initializes it. She also adds a validation to the `Product` model for the new column. ```ruby @@ -795,6 +787,9 @@ column. class AddFlagToProduct < ActiveRecord::Migration def change add_column :products, :flag, :boolean + reversible do |dir| + dir.up { Product.update_all flag: false } + end Product.update_all flag: false end end @@ -818,7 +813,9 @@ column. class AddFuzzToProduct < ActiveRecord::Migration def change add_column :products, :fuzz, :string - Product.update_all fuzz: 'fuzzy' + reversible do |dir| + dir.up { Product.update_all fuzz: 'fuzzy' } + end end end ``` @@ -835,8 +832,8 @@ Both migrations work for Alice. Bob comes back from vacation and: -* Updates the source - which contains both migrations and the latest version of - the Product model. +* Updates the source - which contains both migrations and the latest version + of the Product model. * Runs outstanding migrations with `rake db:migrate`, which includes the one that updates the `Product` model. @@ -851,10 +848,10 @@ An error has occurred, this and all later migrations canceled: undefined method `fuzz' for #<Product:0x000001049b14a0> ``` -A fix for this is to create a local model within the migration. This keeps Rails -from running the validations, so that the migrations run to completion. +A fix for this is to create a local model within the migration. This keeps +Rails from running the validations, so that the migrations run to completion. -When using a faux model, it's a good idea to call +When using a local model, it's a good idea to call `Product.reset_column_information` to refresh the `ActiveRecord` cache for the `Product` model prior to updating data in the database. @@ -870,7 +867,9 @@ class AddFlagToProduct < ActiveRecord::Migration def change add_column :products, :flag, :boolean Product.reset_column_information - Product.update_all flag: false + reversible do |dir| + dir.up { Product.update_all flag: false } + end end end ``` @@ -885,7 +884,9 @@ class AddFuzzToProduct < ActiveRecord::Migration def change add_column :products, :fuzz, :string Product.reset_column_information - Product.update_all fuzz: 'fuzzy' + reversible do |dir| + dir.up { Product.update_all fuzz: 'fuzzy' } + end end end ``` @@ -893,20 +894,20 @@ end There are other ways in which the above example could have gone badly. For example, imagine that Alice creates a migration that selectively -updates the +description+ field on certain products. She runs the +updates the `description` field on certain products. She runs the migration, commits the code, and then begins working on the next feature, -which is to add a new column +fuzz+ to the products table. +which is to add a new column `fuzz` to the products table. She creates two migrations for this new feature, one which adds the new -column, and a second which selectively updates the +fuzz+ column based on +column, and a second which selectively updates the `fuzz` column based on other product attributes. These migrations run just fine, but when Bob comes back from his vacation and calls `rake db:migrate` to run all the outstanding migrations, he gets a -subtle bug: The descriptions have defaults, and the +fuzz+ column is present, -but +fuzz+ is nil on all products. +subtle bug: The descriptions have defaults, and the `fuzz` column is present, +but `fuzz` is nil on all products. -The solution is again to use +Product.reset_column_information+ before +The solution is again to use `Product.reset_column_information` before referencing the Product model in a migration, ensuring the Active Record's knowledge of the table structure is current before manipulating data in those records. @@ -939,12 +940,13 @@ you desire that functionality. ### Types of Schema Dumps -There are two ways to dump the schema. This is set in `config/application.rb` by -the `config.active_record.schema_format` setting, which may be either `:sql` or -`:ruby`. +There are two ways to dump the schema. This is set in `config/application.rb` +by the `config.active_record.schema_format` setting, which may be either `:sql` +or `:ruby`. If `:ruby` is selected then the schema is stored in `db/schema.rb`. If you look -at this file you'll find that it looks an awful lot like one very big migration: +at this file you'll find that it looks an awful lot like one very big +migration: ```ruby ActiveRecord::Schema.define(version: 20080906171750) do @@ -967,8 +969,8 @@ end In many ways this is exactly what it is. This file is created by inspecting the database and expressing its structure using `create_table`, `add_index`, and so on. Because this is database-independent, it could be loaded into any database -that Active Record supports. This could be very useful if you were to distribute -an application that is able to run against multiple databases. +that Active Record supports. This could be very useful if you were to +distribute an application that is able to run against multiple databases. There is however a trade-off: `db/schema.rb` cannot express database specific items such as foreign key constraints, triggers, or stored procedures. While in @@ -976,15 +978,15 @@ a migration you can execute custom SQL statements, the schema dumper cannot reconstitute those statements from the database. If you are using features like this, then you should set the schema format to `:sql`. -Instead of using Active Record's schema dumper, the database's structure will be -dumped using a tool specific to the database (via the `db:structure:dump` Rake task) -into `db/structure.sql`. For example, for the PostgreSQL RDBMS, the -`pg_dump` utility is used. For MySQL, this file will contain the output of -`SHOW CREATE TABLE` for the various tables. +Instead of using Active Record's schema dumper, the database's structure will +be dumped using a tool specific to the database (via the `db:structure:dump` +Rake task) into `db/structure.sql`. For example, for PostgreSQL, the `pg_dump` +utility is used. For MySQL, this file will contain the output of `SHOW CREATE +TABLE` for the various tables. -Loading these schemas is simply a question of executing the SQL statements they -contain. By definition, this will create a perfect copy of the database's -structure. Using the `:sql` schema format will, however, prevent loading the +Loading these schemas is simply a question of executing the SQL statements they +contain. By definition, this will create a perfect copy of the database's +structure. Using the `:sql` schema format will, however, prevent loading the schema into a RDBMS other than the one used to create it. ### Schema Dumps and Source Control @@ -1001,14 +1003,47 @@ which push some of that intelligence back into the database, are not heavily used. Validations such as `validates :foreign_key, uniqueness: true` are one way in -which models can enforce data integrity. The `:dependent` option on associations -allows models to automatically destroy child objects when the parent is -destroyed. Like anything which operates at the application level, these cannot -guarantee referential integrity and so some people augment them with foreign key -constraints 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 plugin like [foreigner](https://github.com/matthuhiggins/foreigner) -which add foreign key support to Active Record (including support for dumping -foreign keys in `db/schema.rb`). +which models can enforce data integrity. The `:dependent` option on +associations allows models to automatically destroy child objects when the +parent is destroyed. Like anything which operates at the application level, +these cannot guarantee referential integrity and so some people augment them +with foreign key constraints 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 plugin like +[foreigner](https://github.com/matthuhiggins/foreigner) which add foreign key +support to Active Record (including support for dumping foreign keys in +`db/schema.rb`). + +Migrations and Seed Data +------------------------ + +Some people use migrations to add data to the database: + +```ruby +class AddInitialProducts < ActiveRecord::Migration + def up + 5.times do |i| + Product.create(name: "Product ##{i}", description: "A product.") + end + end + + def down + Product.delete_all + end +end +``` + +However, Rails has a 'seeds' feature that should be used for seeding a database +with initial data. It's a really simple feature: just fill up `db/seeds.rb` +with some Ruby code, and run `rake db:seed`: + +```ruby +5.times do |i| + Product.create(name: "Product ##{i}", description: "A product.") +end +``` + +This is generally a much cleaner way to set up the database of a blank +application. diff --git a/guides/source/nested_model_forms.md b/guides/source/nested_model_forms.md index b5f112e6c9..2b46a9d51e 100644 --- a/guides/source/nested_model_forms.md +++ b/guides/source/nested_model_forms.md @@ -3,9 +3,9 @@ Rails nested model forms Creating a form for a model _and_ its associations can become quite tedious. Therefore Rails provides helpers to assist in dealing with the complexities of generating these forms _and_ the required CRUD operations to create, update, and destroy associations. -In this guide you will: +After reading this guide, you will know: -* do stuff +* do stuff. -------------------------------------------------------------------------------- diff --git a/guides/source/performance_testing.md b/guides/source/performance_testing.md index 248a9643c8..ee0059623c 100644 --- a/guides/source/performance_testing.md +++ b/guides/source/performance_testing.md @@ -2,14 +2,16 @@ Performance Testing Rails Applications ====================================== This guide covers the various ways of performance testing a Ruby on Rails -application. By referring to this guide, you will be able to: +application. + +After reading this guide, you will know: -* Understand the various types of benchmarking and profiling metrics. -* Generate performance and benchmarking tests. -* Install and use a GC-patched Ruby binary to measure memory usage and object +* The various types of benchmarking and profiling metrics. +* How to generate performance and benchmarking tests. +* How to install and use a GC-patched Ruby binary to measure memory usage and object allocation. -* Understand the benchmarking information provided by Rails inside the log files. -* Learn about various tools facilitating benchmarking and profiling. +* The benchmarking information provided by Rails inside the log files. +* Various tools facilitating benchmarking and profiling. Performance testing is an integral part of the development cycle. It is very important that you don't make your end users wait for too long before the page @@ -413,7 +415,7 @@ tests will set the following configuration parameters: ```bash ActionController::Base.perform_caching = true ActiveSupport::Dependencies.mechanism = :require -Rails.logger.level = ActiveSupport::BufferedLogger::INFO +Rails.logger.level = ActiveSupport::Logger::INFO ``` As `ActionController::Base.perform_caching` is set to `true`, performance tests @@ -557,9 +559,9 @@ Usage: rails profiler 'Ruby.code' 'Ruby.more_code' ... [OPTS] Default: 1 -o, --output PATH Directory to use when writing the results. Default: tmp/performance - --metrics a,b,c Metrics to use. + -m, --metrics a,b,c Metrics to use. Default: process_time,memory,objects - -m, --formats x,y,z Formats to output to. + -f, --formats x,y,z Formats to output to. Default: flat,graph_html,call_tree ``` diff --git a/guides/source/plugins.md b/guides/source/plugins.md index c657281741..f8f04c3c67 100644 --- a/guides/source/plugins.md +++ b/guides/source/plugins.md @@ -7,15 +7,15 @@ A Rails plugin is either an extension or a modification of the core framework. P * a segmented architecture so that units of code can be fixed or updated on their own release schedule * an outlet for the core developers so that they don’t have to include every cool new feature under the sun -After reading this guide you should be familiar with: +After reading this guide, you will know: -* Creating a plugin from scratch -* Writing and running tests for the plugin +* How to create a plugin from scratch. +* How to write and run tests for the plugin. This guide describes how to build a test-driven plugin that will: -* Extend core Ruby classes like Hash and String -* Add methods to ActiveRecord::Base in the tradition of the 'acts_as' plugins +* Extend core Ruby classes like Hash and String. +* Add methods to ActiveRecord::Base in the tradition of the 'acts_as' plugins. * Give you information about where to put generators in your plugin. For the purpose of this guide pretend for a moment that you are an avid bird watcher. @@ -27,16 +27,13 @@ goodness. Setup ----- -_"vendored plugins"_ were available in previous versions of Rails, but they are deprecated in -Rails 3.2, and will not be available in the future. - Currently, Rails plugins are built as gems, _gemified plugins_. They can be shared across different rails applications using RubyGems and Bundler if desired. ### Generate a gemified plugin. -Rails 3.1 ships with a `rails plugin new` command which creates a +Rails ships with a `rails plugin new` command which creates a skeleton for developing any kind of Rails extension with the ability to run integration tests using a dummy Rails application. See usage and options by asking for help: diff --git a/guides/source/rails_application_templates.md b/guides/source/rails_application_templates.md index 6cd19eb8e9..9e694acb98 100644 --- a/guides/source/rails_application_templates.md +++ b/guides/source/rails_application_templates.md @@ -3,10 +3,10 @@ Rails Application Templates Application templates are simple Ruby files containing DSL for adding gems/initializers etc. to your freshly created Rails project or an existing Rails project. -By referring to this guide, you will be able to: +After reading this guide, you will know: -* Use templates to generate/customize Rails applications -* Write your own reusable application templates using the Rails template API +* How to use templates to generate/customize Rails applications. +* How to write your own reusable application templates using the Rails template API. -------------------------------------------------------------------------------- diff --git a/guides/source/rails_on_rack.md b/guides/source/rails_on_rack.md index afd1638ed9..a6119eb433 100644 --- a/guides/source/rails_on_rack.md +++ b/guides/source/rails_on_rack.md @@ -1,12 +1,14 @@ Rails on Rack ============= -This guide covers Rails integration with Rack and interfacing with other Rack components. By referring to this guide, you will be able to: +This guide covers Rails integration with Rack and interfacing with other Rack components. -* Create Rails Metal applications -* Use Rack Middlewares in your Rails applications -* Understand Action Pack's internal Middleware stack -* Define a custom Middleware stack +After reading this guide, you will know: + +* How to create Rails Metal applications. +* How to use Rack Middlewares in your Rails applications. +* Action Pack's internal Middleware stack. +* How to define a custom Middleware stack. -------------------------------------------------------------------------------- @@ -35,11 +37,11 @@ Rails on Rack Here's how `rails server` creates an instance of `Rack::Server` ```ruby -Rails::Server.new.tap { |server| +Rails::Server.new.tap do |server| require APP_PATH Dir.chdir(Rails.application.root) server.start -} +end ``` The `Rails::Server` inherits from `Rack::Server` and calls the `Rack::Server#start` method this way: @@ -227,7 +229,7 @@ Much of Action Controller's functionality is implemented as Middlewares. The fol **`Rack::Lock`** -* Sets `env["rack.multithread"]` flag to `true` and wraps the application within a Mutex. +* Sets `env["rack.multithread"]` flag to `false` and wraps the application within a Mutex. **`ActiveSupport::Cache::Strategy::LocalCache::Middleware`** diff --git a/guides/source/routing.md b/guides/source/routing.md index 53f037c25b..14f23d4020 100644 --- a/guides/source/routing.md +++ b/guides/source/routing.md @@ -1,13 +1,15 @@ Rails Routing from the Outside In ================================= -This guide covers the user-facing features of Rails routing. By referring to this guide, you will be able to: +This guide covers the user-facing features of Rails routing. -* Understand the code in `routes.rb` -* Construct your own routes, using either the preferred resourceful style or the `match` method -* Identify what parameters to expect an action to receive -* Automatically create paths and URLs using route helpers -* Use advanced techniques such as constraints and Rack endpoints +After reading this guide, you will know: + +* How to interpret the code in `routes.rb`. +* How to construct your own routes, using either the preferred resourceful style or the `match` method. +* What parameters to expect an action to receive. +* How to automatically create paths and URLs using route helpers. +* Advanced techniques such as constraints and Rack endpoints. -------------------------------------------------------------------------------- @@ -18,13 +20,13 @@ The Rails router recognizes URLs and dispatches them to a controller's action. I ### Connecting URLs to Code -When your Rails application receives an incoming request +When your Rails application receives an incoming request for: ``` GET /patients/17 ``` -it asks the router to match it to a controller action. If the first matching route is +it asks the router to match it to a controller action. If the first matching route is: ```ruby get '/patients/:id', to: 'patients#show' @@ -34,23 +36,25 @@ 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' ``` -If your application contains this code: +and your application contains this code in the controller: ```ruby @patient = Patient.find(17) ``` +and this in the corresponding view: + ```erb <%= link_to 'Patient Record', patient_path(@patient) %> ``` -The router will generate the path `/patients/17`. This reduces the brittleness of your view and makes your code easier to understand. Note that the id does not need to be specified in the route helper. +then the router will generate the path `/patients/17`. This reduces the brittleness of your view and makes your code easier to understand. Note that the id does not need to be specified in the route helper. Resource Routing: the Rails Default ----------------------------------- @@ -61,13 +65,13 @@ Resource routing allows you to quickly declare all of the common routes for a gi Browsers request pages from Rails by making a request for a URL using a specific HTTP method, such as `GET`, `POST`, `PATCH`, `PUT` and `DELETE`. Each method is a request to perform an operation on the resource. A resource route maps a number of related requests to actions in a single controller. -When your Rails application receives an incoming request for +When your Rails application receives an incoming request for: ``` DELETE /photos/17 ``` -it asks the router to map it to a controller action. If the first matching route is +it asks the router to map it to a controller action. If the first matching route is: ```ruby resources :photos @@ -77,7 +81,7 @@ Rails would dispatch that request to the `destroy` method on the `photos` contro ### CRUD, Verbs, and Actions -In Rails, a resourceful route provides a mapping between HTTP verbs and URLs to controller actions. By convention, each action also maps to particular CRUD operations in a database. A single entry in the routing file, such as +In Rails, a resourceful route provides a mapping between HTTP verbs and URLs to controller actions. By convention, each action also maps to particular CRUD operations in a database. A single entry in the routing file, such as: ```ruby resources :photos @@ -85,7 +89,7 @@ resources :photos creates seven different routes in your application, all mapping to the `Photos` controller: -| HTTP Verb | Path | action | used for | +| HTTP Verb | Path | Action | Used for | | --------- | ---------------- | ------- | -------------------------------------------- | | GET | /photos | index | display a list of all photos | | GET | /photos/new | new | return an HTML form for creating a new photo | @@ -95,9 +99,11 @@ creates seven different routes in your application, all mapping to the `Photos` | PATCH/PUT | /photos/:id | update | update a specific photo | | DELETE | /photos/:id | destroy | delete a specific photo | +NOTE: Because the router uses the HTTP verb and URL to match inbound requests, four URLs map to seven different actions. + NOTE: Rails routes are matched in the order they are specified, so if you have a `resources :photos` above a `get 'photos/poll'` the `show` action's route for the `resources` line will be matched before the `get` line. To fix this, move the `get` line **above** the `resources` line so that it is matched first. -### Paths and URLs +### Path and URL Helpers Creating a resourceful route will also expose a number of helpers to the controllers in your application. In the case of `resources :photos`: @@ -108,8 +114,6 @@ Creating a resourceful route will also expose a number of helpers to the control Each of these helpers has a corresponding `_url` helper (such as `photos_url`) which returns the same path prefixed with the current host, port and path prefix. -NOTE: Because the router uses the HTTP verb and URL to match inbound requests, four URLs map to seven different actions. - ### Defining Multiple Resources at the Same Time If you need to create routes for more than one resource, you can save a bit of typing by defining them all with a single call to `resources`: @@ -118,7 +122,7 @@ If you need to create routes for more than one resource, you can save a bit of t resources :photos, :books, :videos ``` -This works exactly the same as +This works exactly the same as: ```ruby resources :photos @@ -128,13 +132,13 @@ resources :videos ### Singular Resources -Sometimes, you have a resource that clients always look up without referencing an ID. For example, you would like `/profile` to always show the profile of the currently logged in user. In this case, you can use a singular resource to map `/profile` (rather than `/profile/:id`) to the `show` action. +Sometimes, you have a resource that clients always look up without referencing an ID. For example, you would like `/profile` to always show the profile of the currently logged in user. In this case, you can use a singular resource to map `/profile` (rather than `/profile/:id`) to the `show` action: ```ruby get 'profile', to: 'users#show' ``` -This resourceful route +This resourceful route: ```ruby resource :geocoder @@ -142,7 +146,7 @@ resource :geocoder creates six different routes in your application, all mapping to the `Geocoders` controller: -| HTTP Verb | Path | action | used for | +| HTTP Verb | Path | Action | Used for | | --------- | -------------- | ------- | --------------------------------------------- | | GET | /geocoder/new | new | return an HTML form for creating the geocoder | | POST | /geocoder | create | create the new geocoder | @@ -173,7 +177,7 @@ end This will create a number of routes for each of the `posts` and `comments` controller. For `Admin::PostsController`, Rails will create: -| HTTP Verb | Path | action | used for | +| HTTP Verb | Path | Action | Used for | | --------- | --------------------- | ------- | ------------------------- | | GET | /admin/posts | index | admin_posts_path | | GET | /admin/posts/new | new | new_admin_post_path | @@ -183,7 +187,7 @@ This will create a number of routes for each of the `posts` and `comments` contr | PATCH/PUT | /admin/posts/:id | update | admin_post_path(:id) | | DELETE | /admin/posts/:id | destroy | admin_post_path(:id) | -If you want to route `/posts` (without the prefix `/admin`) to `Admin::PostsController`, you could use +If you want to route `/posts` (without the prefix `/admin`) to `Admin::PostsController`, you could use: ```ruby scope module: 'admin' do @@ -191,13 +195,13 @@ scope module: 'admin' do end ``` -or, for a single case +or, for a single case: ```ruby resources :posts, module: 'admin' ``` -If you want to route `/admin/posts` to `PostsController` (without the `Admin::` module prefix), you could use +If you want to route `/admin/posts` to `PostsController` (without the `Admin::` module prefix), you could use: ```ruby scope '/admin' do @@ -205,7 +209,7 @@ scope '/admin' do end ``` -or, for a single case +or, for a single case: ```ruby resources :posts, path: '/admin/posts' @@ -213,7 +217,7 @@ resources :posts, path: '/admin/posts' In each of these cases, the named routes remain the same as if you did not use `scope`. In the last case, the following paths map to `PostsController`: -| HTTP Verb | Path | action | named helper | +| HTTP Verb | Path | Action | Named Helper | | --------- | --------------------- | ------- | ------------------- | | GET | /admin/posts | index | posts_path | | GET | /admin/posts/new | new | new_post_path | @@ -247,7 +251,7 @@ end In addition to the routes for magazines, this declaration will also route ads to an `AdsController`. The ad URLs require a magazine: -| HTTP Verb | Path | action | used for | +| HTTP Verb | Path | Action | Used for | | --------- | ------------------------------------ | ------- | -------------------------------------------------------------------------- | | GET | /magazines/:magazine_id/ads | index | display a list of all ads for a specific magazine | | GET | /magazines/:magazine_id/ads/new | new | return an HTML form for creating a new ad belonging to a specific magazine | @@ -271,7 +275,7 @@ resources :publishers do end ``` -Deeply-nested resources quickly become cumbersome. In this case, for example, the application would recognize paths such as +Deeply-nested resources quickly become cumbersome. In this case, for example, the application would recognize paths such as: ``` /publishers/1/magazines/2/photos/3 @@ -281,9 +285,94 @@ The corresponding route helper would be `publisher_magazine_photo_url`, requirin TIP: _Resources should never be nested more than 1 level deep._ +#### Shallow Nesting + +One way to avoid deep nesting (as recommended above) is to generate the collection actions scoped under the parent, so as to get a sense of the hierarchy, but to not nest the member actions. In other words, to only build routes with the minimal amount of information to uniquely identify the resource, like this: + +```ruby +resources :posts do + resources :comments, only: [:index, :new, :create] +end +resources :comments, only: [:show, :edit, :update, :destroy] +``` + +This idea strikes a balance between descriptive routes and deep nesting. There exists shorthand syntax to achieve just that, via the `:shallow` option: + +```ruby +resources :posts do + resources :comments, shallow: true +end +``` + +This will generate the exact same routes as the first example. You can also specify the `:shallow` option in the parent resource, in which case all of the nested resources will be shallow: + +```ruby +resources :posts, shallow: true do + resources :comments + resources :quotes + resources :drafts +end +``` + +The `shallow` method of the DSL creates a scope inside of which every nesting is shallow. This generates the same routes as the previous example: + +```ruby +shallow do + resources :posts do + resources :comments + resources :quotes + resources :drafts + end +end +``` + +There exists two options for `scope` to customize shallow routes. `:shallow_path` prefixes member paths with the specified parameter: + +```ruby +scope shallow_path: "sekret" do + resources :posts do + resources :comments, shallow: true + end +end +``` + +The comments resource here will have the following routes generated for it: + +| HTTP Verb | Path | Named Helper | +| --------- | -------------------------------------- | ------------------- | +| GET | /posts/:post_id/comments(.:format) | post_comments | +| POST | /posts/:post_id/comments(.:format) | post_comments | +| GET | /posts/:post_id/comments/new(.:format) | new_post_comment | +| GET | /sekret/comments/:id/edit(.:format) | edit_comment | +| GET | /sekret/comments/:id(.:format) | comment | +| PATCH/PUT | /sekret/comments/:id(.:format) | comment | +| DELETE | /sekret/comments/:id(.:format) | comment | + +The `:shallow_prefix` option adds the specified parameter to the named helpers: + +```ruby +scope shallow_prefix: "sekret" do + resources :posts do + resources :comments, shallow: true + end +end +``` + +The comments resource here will have the following routes generated for it: + +| HTTP Verb | Path | Named Helper | +| --------- | -------------------------------------- | ------------------- | +| GET | /posts/:post_id/comments(.:format) | post_comments | +| POST | /posts/:post_id/comments(.:format) | post_comments | +| GET | /posts/:post_id/comments/new(.:format) | new_post_comment | +| GET | /comments/:id/edit(.:format) | edit_sekret_comment | +| GET | /comments/:id(.:format) | sekret_comment | +| PATCH/PUT | /comments/:id(.:format) | sekret_comment | +| DELETE | /comments/:id(.:format) | sekret_comment | + ### Routing concerns -Routing Concerns allows you to declare common routes that can be reused inside others resources and routes. +Routing Concerns allows you to declare common routes that can be reused inside others resources and routes. To define a concern: ```ruby concern :commentable do @@ -295,7 +384,7 @@ concern :image_attachable do end ``` -These concerns can be used in resources to avoid code duplication and share behavior across routes. +These concerns can be used in resources to avoid code duplication and share behavior across routes: ```ruby resources :messages, concerns: :commentable @@ -303,6 +392,19 @@ resources :messages, concerns: :commentable resources :posts, concerns: [:commentable, :image_attachable] ``` +The above is equivalent to: + +```ruby +resources :messages do + resources :comments +end + +resources :posts do + resources :comments + resources :images, only: :index +end +``` + Also you can use them in any place that you want inside the routes, for example in a scope or namespace call: ```ruby @@ -321,7 +423,7 @@ resources :magazines do end ``` -When using `magazine_ad_path`, you can pass in instances of `Magazine` and `Ad` instead of the numeric IDs. +When using `magazine_ad_path`, you can pass in instances of `Magazine` and `Ad` instead of the numeric IDs: ```erb <%= link_to 'Ad details', magazine_ad_path(@magazine, @ad) %> @@ -369,7 +471,7 @@ resources :photos do end ``` -This will recognize `/photos/1/preview` with GET, and route to the `preview` action of `PhotosController`. It will also create the `preview_photo_url` and `preview_photo_path` helpers. +This will recognize `/photos/1/preview` with GET, and route to the `preview` action of `PhotosController`, with the resource id value passed in `params[:id]`. It will also create the `preview_photo_url` and `preview_photo_path` helpers. Within the block of member routes, each route name specifies the HTTP verb that it will recognize. You can use `get`, `patch`, `put`, `post`, or `delete` here. If you don't have multiple `member` routes, you can also pass `:on` to a route, eliminating the block: @@ -379,6 +481,8 @@ resources :photos do end ``` +You can leave out the `:on` option, this will create the same member route except that the resource id value will be available in `params[:photo_id]` instead of `params[:id]`. + #### Adding Collection Routes To add a route to the collection: @@ -413,9 +517,7 @@ end This will enable Rails to recognize paths such as `/comments/new/preview` with GET, and route to the `preview` action of `CommentsController`. It will also create the `preview_new_comment_url` and `preview_new_comment_path` route helpers. -#### A Note of Caution - -If you find yourself adding many extra actions to a resourceful route, it's time to stop and ask yourself whether you're disguising the presence of another resource. +TIP: If you find yourself adding many extra actions to a resourceful route, it's time to stop and ask yourself whether you're disguising the presence of another resource. Non-Resourceful Routes ---------------------- @@ -452,11 +554,11 @@ NOTE: You can't use `:namespace` or `:module` with a `:controller` path segment. get ':controller(/:action(/:id))', controller: /admin\/[^\/]+/ ``` -TIP: By default dynamic segments don't accept dots - this is because the dot is used as a separator for formatted routes. If you need to use a dot within a dynamic segment, add a constraint that overrides this – for example, `id: /[^\/]+/` allows anything except a slash. +TIP: By default, dynamic segments don't accept dots - this is because the dot is used as a separator for formatted routes. If you need to use a dot within a dynamic segment, add a constraint that overrides this – for example, `id: /[^\/]+/` allows anything except a slash. ### Static Segments -You can specify static segments when creating a route: +You can specify static segments when creating a route by not prepending a colon to a fragment: ```ruby get ':controller/:action/:id/with_user/:user_id' @@ -494,7 +596,7 @@ Rails would match `photos/12` to the `show` action of `PhotosController`, and se ### Naming Routes -You can specify a name for any route using the `:as` option. +You can specify a name for any route using the `:as` option: ```ruby get 'exit', to: 'sessions#destroy', as: :logout @@ -524,7 +626,7 @@ You can match all verbs to a particular route using `via: :all`: match 'photos', to: 'photos#show', via: :all ``` -You should avoid routing all verbs to an action unless you have a good reason to, as routing both `GET` requests and `POST` requests to a single action has security implications. +NOTE: Routing both `GET` and `POST` requests to a single action has security implications. In general, you should avoid routing all verbs to an action unless you have a good reason to. ### Segment Constraints @@ -534,7 +636,7 @@ You can use the `:constraints` option to enforce a format for a dynamic segment: get 'photos/:id', to: 'photos#show', constraints: { id: /[A-Z]\d{5}/ } ``` -This route would match paths such as `/photos/A12345`. You can more succinctly express the same route this way: +This route would match paths such as `/photos/A12345`, but not `/photos/893`. You can more succinctly express the same route this way: ```ruby get 'photos/:id', to: 'photos#show', id: /[A-Z]\d{5}/ @@ -607,17 +709,17 @@ end Both the `matches?` method and the lambda gets the `request` object as an argument. -### Route Globbing +### Route Globbing and Wildcard Segments -Route globbing is a way to specify that a particular parameter should be matched to all the remaining parts of a route. For example +Route globbing is a way to specify that a particular parameter should be matched to all the remaining parts of a route. For example: ```ruby get 'photos/*other', to: 'photos#unknown' ``` -This route would match `photos/12` or `/photos/long/path/to/12`, setting `params[:other]` to `"12"` or `"long/path/to/12"`. +This route would match `photos/12` or `/photos/long/path/to/12`, setting `params[:other]` to `"12"` or `"long/path/to/12"`. The fragments prefixed with a star are called "wildcard segments". -Wildcard segments can occur anywhere in a route. For example, +Wildcard segments can occur anywhere in a route. For example: ```ruby get 'books/*section/:title', to: 'books#show' @@ -625,7 +727,7 @@ get 'books/*section/:title', to: 'books#show' would match `books/some/section/last-words-a-memoir` with `params[:section]` equals `'some/section'`, and `params[:title]` equals `'last-words-a-memoir'`. -Technically a route can have even more than one wildcard segment. The matcher assigns segments to parameters in an intuitive way. For example, +Technically, a route can have even more than one wildcard segment. The matcher assigns segments to parameters in an intuitive way. For example: ```ruby get '*a/foo/*b', to: 'test#index' @@ -633,12 +735,6 @@ get '*a/foo/*b', to: 'test#index' would match `zoo/woo/foo/bar/baz` with `params[:a]` equals `'zoo/woo'`, and `params[:b]` equals `'bar/baz'`. -NOTE: Starting from Rails 3.1, wildcard routes will always match the optional format segment by default. For example if you have this route: - -```ruby -get '*pages', to: 'pages#show' -``` - NOTE: By requesting `'/foo/bar.json'`, your `params[:pages]` will be equals to `'foo/bar'` with the request format of JSON. If you want the old 3.0.x behavior back, you could supply `format: false` like this: ```ruby @@ -678,7 +774,7 @@ In all of these cases, if you don't provide the leading host (`http://www.exampl ### Routing to Rack Applications -Instead of a String, like `'posts#index'`, which corresponds to the `index` action in the `PostsController`, you can specify any <a href="rails_on_rack.html">Rack application</a> as the endpoint for a matcher. +Instead of a String like `'posts#index'`, which corresponds to the `index` action in the `PostsController`, you can specify any <a href="rails_on_rack.html">Rack application</a> as the endpoint for a matcher: ```ruby match '/application.js', to: Sprockets, via: :all @@ -697,13 +793,13 @@ root to: 'pages#main' root 'pages#main' # shortcut for the above ``` -You should put the `root` route at the top of the file, because it is the most popular route and should be matched first. You also need to delete the `public/index.html` file for the root route to take effect. +You should put the `root` route at the top of the file, because it is the most popular route and should be matched first. NOTE: The `root` route only routes `GET` requests to the action. ### Unicode character routes -You can specify unicode character routes directly. For example +You can specify unicode character routes directly. For example: ```ruby get 'こんにちは', to: 'welcome#index' @@ -724,7 +820,7 @@ resources :photos, controller: 'images' will recognize incoming paths beginning with `/photos` but route to the `Images` controller: -| HTTP Verb | Path | action | named helper | +| HTTP Verb | Path | Action | Named Helper | | --------- | ---------------- | ------- | -------------------- | | GET | /photos | index | photos_path | | GET | /photos/new | new | new_photo_path | @@ -769,7 +865,7 @@ resources :photos, as: 'images' will recognize incoming paths beginning with `/photos` and route the requests to `PhotosController`, but use the value of the :as option to name the helpers. -| HTTP Verb | Path | action | named helper | +| HTTP Verb | Path | Action | Named Helper | | --------- | ---------------- | ------- | -------------------- | | GET | /photos | index | images_path | | GET | /photos/new | new | new_image_path | @@ -787,7 +883,7 @@ The `:path_names` option lets you override the automatically-generated "new" and resources :photos, path_names: { new: 'make', edit: 'change' } ``` -This would cause the routing to recognize paths such as +This would cause the routing to recognize paths such as: ``` /photos/make @@ -806,7 +902,7 @@ end ### Prefixing the Named Route Helpers -You can use the `:as` option to prefix the named route helpers that Rails generates for a route. Use this option to prevent name collisions between routes using a path scope. +You can use the `:as` option to prefix the named route helpers that Rails generates for a route. Use this option to prevent name collisions between routes using a path scope. For example: ```ruby scope 'admin' do @@ -874,7 +970,7 @@ end Rails now creates routes to the `CategoriesController`. -| HTTP Verb | Path | action | used for | +| HTTP Verb | Path | Action | Used for | | --------- | -------------------------- | ------- | ----------------------- | | GET | /kategorien | index | categories_path | | GET | /kategorien/neu | new | new_category_path | @@ -886,7 +982,7 @@ Rails now creates routes to the `CategoriesController`. ### Overriding the Singular Form -If you want to define the singular form of a resource, you should add additional rules to the `Inflector`. +If you want to define the singular form of a resource, you should add additional rules to the `Inflector`: ```ruby ActiveSupport::Inflector.inflections do |inflect| @@ -896,7 +992,7 @@ end ### Using `:as` in Nested Resources -The `:as` option overrides the automatically-generated name for the resource in nested route helpers. For example, +The `:as` option overrides the automatically-generated name for the resource in nested route helpers. For example: ```ruby resources :magazines do @@ -911,7 +1007,7 @@ Inspecting and Testing Routes Rails offers facilities for inspecting and testing your routes. -### Seeing Existing Routes +### Listing Existing Routes To get a complete list of the available routes in your application, visit `http://localhost:3000/rails/info/routes` in your browser while your server is running in the **development** environment. You can also execute the `rake routes` command in your terminal to produce the same output. @@ -949,7 +1045,7 @@ Routes should be included in your testing strategy (just like the rest of your a #### The `assert_generates` Assertion -`assert_generates` asserts that a particular set of options generate a particular path and can be used with default routes or custom routes. +`assert_generates` asserts that a particular set of options generate a particular path and can be used with default routes or custom routes. For example: ```ruby assert_generates '/photos/1', { controller: 'photos', action: 'show', id: '1' } @@ -958,7 +1054,7 @@ assert_generates '/about', controller: 'pages', action: 'about' #### The `assert_recognizes` Assertion -`assert_recognizes` is the inverse of `assert_generates`. It asserts that a given path is recognized and routes it to a particular spot in your application. +`assert_recognizes` is the inverse of `assert_generates`. It asserts that a given path is recognized and routes it to a particular spot in your application. For example: ```ruby assert_recognizes({ controller: 'photos', action: 'show', id: '1' }, '/photos/1') @@ -972,7 +1068,7 @@ assert_recognizes({ controller: 'photos', action: 'create' }, { path: 'photos', #### The `assert_routing` Assertion -The `assert_routing` assertion checks the route both ways: it tests that the path generates the options, and that the options generate the path. Thus, it combines the functions of `assert_generates` and `assert_recognizes`. +The `assert_routing` assertion checks the route both ways: it tests that the path generates the options, and that the options generate the path. Thus, it combines the functions of `assert_generates` and `assert_recognizes`: ```ruby assert_routing({ path: 'photos', method: :post }, { controller: 'photos', action: 'create' }) diff --git a/guides/source/ruby_on_rails_guides_guidelines.md b/guides/source/ruby_on_rails_guides_guidelines.md index e589a3d093..a78711f4b2 100644 --- a/guides/source/ruby_on_rails_guides_guidelines.md +++ b/guides/source/ruby_on_rails_guides_guidelines.md @@ -3,6 +3,11 @@ Ruby on Rails Guides Guidelines This guide documents guidelines for writing Ruby on Rails Guides. This guide follows itself in a graceful loop, serving itself as an example. +After reading this guide, you will know: + +* About the conventions to be used in Rails documentation. +* How to generate guides locally. + -------------------------------------------------------------------------------- Markdown @@ -60,7 +65,7 @@ HTML Guides ### Generation -To generate all the guides, just `cd` into the **`guides`** directory and execute: +To generate all the guides, just `cd` into the **`guides`** directory, run `bundle install` and execute: ``` bundle exec rake guides:generate @@ -72,8 +77,6 @@ or bundle exec rake guides:generate:html ``` -(You may need to run `bundle install` first to install the required gems.) - To process `my_guide.md` and nothing else use the `ONLY` environment variable: ``` diff --git a/guides/source/security.md b/guides/source/security.md index 4902f83f8a..0b0cfe69c4 100644 --- a/guides/source/security.md +++ b/guides/source/security.md @@ -1,15 +1,17 @@ Ruby On Rails Security Guide ============================ -This manual describes common security problems in web applications and how to avoid them with Rails. After reading it, you should be familiar with: +This manual describes common security problems in web applications and how to avoid them with Rails. -* All countermeasures _that are highlighted_ -* The concept of sessions in Rails, what to put in there and popular attack methods -* How just visiting a site can be a security problem (with CSRF) -* What you have to pay attention to when working with files or providing an administration interface -* The Rails-specific mass assignment problem -* How to manage users: Logging in and out and attack methods on all layers -* And the most popular injection attack methods +After reading this guide, you will know: + +* All countermeasures _that are highlighted_. +* The concept of sessions in Rails, what to put in there and popular attack methods. +* How just visiting a site can be a security problem (with CSRF). +* What you have to pay attention to when working with files or providing an administration interface. +* The Rails-specific mass assignment problem. +* How to manage users: Logging in and out and attack methods on all layers. +* And the most popular injection attack methods. -------------------------------------------------------------------------------- @@ -92,16 +94,15 @@ Rails 2 introduced a new default session storage, CookieStore. CookieStore saves * The client can see everything you store in a session, because it is stored in clear-text (actually Base64-encoded, so not encrypted). So, of course, _you don't want to store any secrets here_. To prevent session hash tampering, a digest is calculated from the session with a server-side secret and inserted into the end of the cookie. -That means the security of this storage depends on this secret (and on the digest algorithm, which defaults to SHA512, which has not been compromised, yet). So _don't use a trivial secret, i.e. a word from a dictionary, or one which is shorter than 30 characters_. Put the secret in your environment.rb: +That means the security of this storage depends on this secret (and on the digest algorithm, which defaults to SHA512, which has not been compromised, yet). So _don't use a trivial secret, i.e. a word from a dictionary, or one which is shorter than 30 characters_. -```ruby -config.action_dispatch.session = { - key: '_app_session', - secret: '0x0dkfj3927dkc7djdh36rkckdfzsg...' -} -``` +`config.secret_key_base` is used for specifying a key which allows sessions for the application to be verified against a known secure key to prevent tampering. Applications get `config.secret_key_base` initialized to a random key in `config/initializers/secret_token.rb`, e.g.: -There are, however, derivatives of CookieStore which encrypt the session hash, so the client cannot see it. + YourApp::Application.config.secret_key_base = '49d3f3de9ed86c74b94ad6bd0...' + +Older versions of Rails use CookieStore, which uses `secret_token` instead of `secret_key_base` that is used by EncryptedCookieStore. Read the upgrade documentation for more information. + +If you have received an application where the secret was exposed (e.g. an application whose source was shared), strongly consider changing the secret. ### Replay Attacks for CookieStore Sessions @@ -372,141 +373,6 @@ The common admin interface works like this: it's located at www.example.com/admi * _Put the admin interface to a special sub-domain_ such as admin.application.com and make it a separate application with its own user management. This makes stealing an admin cookie from the usual domain, www.application.com, impossible. This is because of the same origin policy in your browser: An injected (XSS) script on www.application.com may not read the cookie for admin.application.com and vice-versa. -Mass Assignment ---------------- - -WARNING: _Without any precautions `Model.new(params[:model]`) allows attackers to set -any database column's value._ - -The mass-assignment feature may become a problem, as it allows an attacker to set -any model's attributes by manipulating the hash passed to a model's `new()` method: - -```ruby -def signup - params[:user] # => {name:"ow3ned", admin:true} - @user = User.new(params[:user]) -end -``` - -Mass-assignment saves you much work, because you don't have to set each value -individually. Simply pass a hash to the `new` method, or `assign_attributes=` -a hash value, to set the model's attributes to the values in the hash. The -problem is that it is often used in conjunction with the parameters (params) -hash available in the controller, which may be manipulated by an attacker. -He may do so by changing the URL like this: - -``` -http://www.example.com/user/signup?user[name]=ow3ned&user[admin]=1 -``` - -This will set the following parameters in the controller: - -```ruby -params[:user] # => {name:"ow3ned", admin:true} -``` - -So if you create a new user using mass-assignment, it may be too easy to become -an administrator. - -Note that this vulnerability is not restricted to database columns. Any setter -method, unless explicitly protected, is accessible via the `attributes=` method. -In fact, this vulnerability is extended even further with the introduction of -nested mass assignment (and nested object forms) in Rails 2.3. The -`accepts_nested_attributes_for` declaration provides us the ability to extend -mass assignment to model associations (`has_many`, `has_one`, -`has_and_belongs_to_many`). For example: - -```ruby - class Person < ActiveRecord::Base - has_many :children - - accepts_nested_attributes_for :children - end - - class Child < ActiveRecord::Base - belongs_to :person - end -``` - -As a result, the vulnerability is extended beyond simply exposing column -assignment, allowing attackers the ability to create entirely new records -in referenced tables (children in this case). - -### Countermeasures - -To avoid this, Rails provides an interface for protecting attributes from -end-user assignment called Strong Parameters. This makes Action Controller -parameters forbidden until they have been whitelisted, so you will have to -make a conscious choice about which attributes to allow for mass assignment -and thus prevent accidentally exposing that which shouldn’t be exposed. - -NOTE. Before Strong Parameters arrived, mass-assignment protection was a -model's task provided by Active Model. This has been extracted to the -[ProtectedAttributes](https://github.com/rails/protected_attributes) -gem. In order to use `attr_accessible` and `attr_protected` helpers in -your models, you should add `protected_attributes` to your Gemfile. - -Why we moved mass-assignment protection out of the model and into -the controller? The whole point of the controller is to control the -flow between user and application, including authentication, authorization, -and, as part of that, access control. - -Strong Parameters provides two methods to the `params` hash to control -access to your attributes: `require` and `permit`. The former is used -to mark parameters as required and the latter limits which attributes -should be allowed for mass updating using the slice pattern. For example: - -```ruby -def signup - params[:user] - # => {name:"ow3ned", admin:true} - permitted_params = params.require(:user).permit(:name) - # => {name:"ow3ned"} - - @user = User.new(permitted_params) -end -``` - -In the example above, `require` is checking whether a `user` key is present or not -in the parameters, if it's not present, it'll raise an `ActionController::MissingParameter` -exception, which will be caught by `ActionController::Base` and turned into a -400 Bad Request reply. Then `permit` whitelists the attributes that should be -allowed for mass assignment. - -A good pattern to encapsulate the permissible parameters is to use a private method -since you'll be able to reuse the same permit list between different actions. - -```ruby -def signup - @user = User.new(user_params) - # ... -end - -def update - @user = User.find(params[:id] - @user.update_attributes!(user_params) - # ... -end - -private - def user_params - params.require(:user).permit(:name) - end -``` - -Also, you can specialize this method with per-user checking of permissible -attributes. - -```ruby -def user_params - if current_user.admin? - params.require(:user).permit(:name, :admin) - else - params.require(:user).permit(:name) - end -end -``` - User Management --------------- @@ -686,8 +552,7 @@ NOTE: _When sanitizing, protecting or verifying something, whitelists over black A blacklist can be a list of bad e-mail addresses, non-public actions or bad HTML tags. This is opposed to a whitelist which lists the good e-mail addresses, public actions, good HTML tags and so on. Although sometimes it is not possible to create a whitelist (in a SPAM filter, for example), _prefer to use whitelist approaches_: -* Use before_filter only: [...] instead of except: [...]. This way you don't forget to turn it off for newly added actions. -* Use attr_accessible instead of attr_protected. See the mass-assignment section for details +* Use before_action only: [...] instead of except: [...]. This way you don't forget to turn it off for newly added actions. * Allow <strong> instead of removing <script> against Cross-Site Scripting (XSS). See below for details. * Don't try to correct user input by blacklists: * This will make the attack work: "<sc<script>ript>".gsub("<script>", "") @@ -1093,6 +958,11 @@ Used to control which sites are allowed to bypass same origin policies and send * Strict-Transport-Security [Used to control if the browser is allowed to only access a site over a secure connection](http://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security) +Environmental Security +---------------------- + +It is beyond the scope of this guide to inform you on how to secure your application code and environments. However, please secure your database configuration, e.g. `config/database.yml`, and your server-side secret, e.g. stored in `config/initializers/secret_token.rb`. You may want to further restrict access, using environment-specific versions of these files and any others that may contain sensitive information. + Additional Resources -------------------- diff --git a/guides/source/testing.md b/guides/source/testing.md index f898456d39..7747318d32 100644 --- a/guides/source/testing.md +++ b/guides/source/testing.md @@ -2,11 +2,13 @@ A Guide to Testing Rails Applications ===================================== This guide covers built-in mechanisms offered by Rails to test your -application. By referring to this guide, you will be able to: +application. -* Understand Rails testing terminology -* Write unit, functional, and integration tests for your application -* Identify other popular testing approaches and plugins +After reading this guide, you will know: + +* Rails testing terminology. +* How to write unit, functional, and integration tests for your application. +* Other popular testing approaches and plugins. -------------------------------------------------------------------------------- @@ -75,7 +77,7 @@ steve: profession: guy with keyboard ``` -Each fixture is given a name followed by an indented list of colon-separated key/value pairs. Records are typically separated by a blank space. You can place comments in a fixture file by using the # character in the first column. +Each fixture is given a name followed by an indented list of colon-separated key/value pairs. Records are typically separated by a blank space. You can place comments in a fixture file by using the # character in the first column. Keys which resemble YAML keywords such as 'yes' and 'no' are quoted so that the YAML Parser correctly interprets them. #### ERB'in It Up @@ -97,9 +99,9 @@ Rails by default automatically loads all fixtures from the `test/fixtures` folde * Load the fixture data into the table * Dump the fixture data into a variable in case you want to access it directly -#### Fixtures are ActiveRecord objects +#### Fixtures are Active Record objects -Fixtures are instances of ActiveRecord. As mentioned in point #3 above, you can access the object directly because it is automatically setup as a local variable of the test case. For example: +Fixtures are instances of Active Record. As mentioned in point #3 above, you can access the object directly because it is automatically setup as a local variable of the test case. For example: ```ruby # this will return the User object for the fixture named david @@ -828,7 +830,7 @@ Above, the `setup` method is called before each test and so `@post` is available Let's see the earlier example by specifying `setup` callback by specifying a method name as a symbol: ```ruby -require '../test_helper' +require 'test_helper' class PostsControllerTest < ActionController::TestCase diff --git a/guides/source/upgrading_ruby_on_rails.md b/guides/source/upgrading_ruby_on_rails.md index 6fb10693ff..b4a59fe3da 100644 --- a/guides/source/upgrading_ruby_on_rails.md +++ b/guides/source/upgrading_ruby_on_rails.md @@ -3,8 +3,6 @@ A Guide for Upgrading Ruby on Rails This guide provides steps to be followed when you upgrade your applications to a newer version of Ruby on Rails. These steps are also available in individual release guides. --------------------------------------------------------------------------------- - General Advice -------------- @@ -29,7 +27,7 @@ Upgrading from Rails 3.2 to Rails 4.0 NOTE: This section is a work in progress. -If your application is currently on any version of Rails older than 3.2.x, you should upgrade to Rails 3.2 before attempting an update to Rails 4.0. +If your application is currently on any version of Rails older than 3.2.x, you should upgrade to Rails 3.2 before attempting one to Rails 4.0. The following changes are meant for upgrading your application to Rails 4.0. @@ -37,28 +35,21 @@ The following changes are meant for upgrading your application to Rails 4.0. 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`. -### Identity Map - -Rails 4.0 has removed the identity map from Active Record, due to [some inconsistencies with associations](https://github.com/rails/rails/commit/302c912bf6bcd0fa200d964ec2dc4a44abe328a6). If you have manually enabled it in your application, you will have to remove the following config that has no effect anymore: `config.active_record.identity_map`. - ### Active Record -The `delete` method in collection associations can now receive `Fixnum` or `String` arguments as record ids, besides records, pretty much like the `destroy` method does. Previously it raised `ActiveRecord::AssociationTypeMismatch` for such arguments. From Rails 4.0 on `delete` automatically tries to find the records matching the given ids before deleting them. +* Rails 4.0 has removed the identity map from Active Record, due to [some inconsistencies with associations](https://github.com/rails/rails/commit/302c912bf6bcd0fa200d964ec2dc4a44abe328a6). If you have manually enabled it in your application, you will have to remove the following config that has no effect anymore: `config.active_record.identity_map`. -Rails 4.0 has changed how orders get stacked in `ActiveRecord::Relation`. In previous versions of rails new order was applied after previous defined order. But this is no long true. Check [ActiveRecord Query guide](active_record_querying.html#ordering) for more information. +* The `delete` method in collection associations can now receive `Fixnum` or `String` arguments as record ids, besides records, pretty much like the `destroy` method does. Previously it raised `ActiveRecord::AssociationTypeMismatch` for such arguments. From Rails 4.0 on `delete` automatically tries to find the records matching the given ids before deleting them. -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 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`. ### 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: +* 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: ```ruby # Disable root element in JSON by default. @@ -69,18 +60,17 @@ in the `config/initializers/wrap_parameters.rb` file: ### Action Pack -Rails 4.0 removed the `ActionController::Base.asset_path` option. Use the assets pipeline feature. +* 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 removed the `ActionController::Base.asset_path` option. Use the assets pipeline feature. -Rails 4.0 has deprecated `ActionController::Base.page_cache_extension` option. Use -`ActionController::Base.default_static_extension` instead. +* Rails 4.0 has deprecated `ActionController::Base.page_cache_extension` option. Use `ActionController::Base.default_static_extension` instead. -Rails 4.0 has removed Action and Page caching from ActionPack. 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 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 changed how `assert_generates`, `assert_recognizes`, and `assert_routing` work. Now all these assertions raise `Assertion` instead of `ActionController::RoutingError`. +* 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 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: +* 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 get Rack::Utils.escape('こんにちは'), controller: 'welcome', action: 'index' @@ -94,11 +84,11 @@ get 'こんにちは', controller: 'welcome', action: 'index' ### Active Support -Rails 4.0 Removed the `j` alias for `ERB::Util#json_escape` since `j` is already used for `ActionView::Helpers::JavaScriptHelper#escape_javascript`. +Rails 4.0 removes the `j` alias for `ERB::Util#json_escape` since `j` is already used for `ActionView::Helpers::JavaScriptHelper#escape_javascript`. ### Helpers Loading Order -The loading order of helpers from more than one directory has changed in Rails 4.0. Previously, helpers from all directories were gathered and then sorted alphabetically. After upgrade 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 `helpers_path` parameter, this change will only impact the way of loading helpers from engines. If you rely on the fact that particular helper from engine loads before or after another helper from application or another engine, you should check if correct methods are available after upgrade. If you would like to change order in which engines are loaded, you can use `config.railties_order=` method. +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. Upgrading from Rails 3.1 to Rails 3.2 ------------------------------------- diff --git a/guides/source/working_with_javascript_in_rails.md b/guides/source/working_with_javascript_in_rails.md index 10b9dddd02..a7ca531123 100644 --- a/guides/source/working_with_javascript_in_rails.md +++ b/guides/source/working_with_javascript_in_rails.md @@ -3,13 +3,15 @@ Working with JavaScript in Rails This guide covers the built-in Ajax/JavaScript functionality of Rails (and more); it will enable you to create rich and dynamic Ajax applications with -ease! We will cover the following topics: +ease! -* Quick introduction to Ajax -* Unobtrusive JavaScript -* How Rails' built-in helpers assist you -* Handling Ajax on the server side -* The Turbolinks gem +After reading this guide, you will know: + +* The basics of Ajax. +* Unobtrusive JavaScript. +* How Rails' built-in helpers assist you. +* How to handle Ajax on the server side. +* The Turbolinks gem. ------------------------------------------------------------------------------- @@ -64,37 +66,38 @@ Here's the simplest way to write JavaScript. You may see it referred to as 'inline JavaScript': ```html -<a href="#" onclick="alert('Hello, world.')">Here</a> +<a href="#" onclick="this.style.backgroundColor='#990000'">Paint it red</a> ``` - -When clicked, the alert will trigger. Here's the problem: what happens when -we have lots of JavaScript we want to execute on a click? +When clicked, the link background will become red. Here's the problem: what +happens when we have lots of JavaScript we want to execute on a click? ```html -<a href="#" onclick="function fib(n){return n<2?n:fib(n-1)+fib(n-2);};alert('fib of 15 is: ' + fib(15) + '.');">Calculate</a> +<a href="#" onclick="this.style.backgroundColor='#009900';this.style.color='#FFFFFF';">Paint it green</a> ``` Awkward, right? We could pull the function definition out of the click handler, and turn it into CoffeeScript: ```coffeescript -fib = (n) -> - (if n < 2 then n else fib(n - 1) + fib(n - 2)) +paintIt = (element, backgroundColor, textColor) -> + element.style.backgroundColor = backgroundColor + if textColor? + element.style.color = textColor ``` And then on our page: ```html -<a href="#" onclick="alert('fib of 15 is: ' + fib(15) + '.');">Calculate</a> +<a href="#" onclick="paintIt(this, '#990000')">Paint it red</a> ``` That's a little bit better, but what about multiple links that have the same effect? ```html -<a href="#" onclick="alert('fib of 16 is: ' + fib(16) + '.');">Calculate</a> -<a href="#" onclick="alert('fib of 17 is: ' + fib(17) + '.');">Calculate</a> -<a href="#" onclick="alert('fib of 18 is: ' + fib(18) + '.');">Calculate</a> +<a href="#" onclick="paintIt(this, '#990000')">Paint it red</a> +<a href="#" onclick="paintIt(this, '#009900', '#FFFFFF')">Paint it green</a> +<a href="#" onclick="paintIt(this, '#000099', '#FFFFFF')">Paint it blue</a> ``` Not very DRY, eh? We can fix this by using events instead. We'll add a `data-*` @@ -102,19 +105,21 @@ attribute to our link, and then bind a handler to the click event of every link that has that attribute: ```coffeescript -fib = (n) -> - (if n < 2 then n else fib(n - 1) + fib(n - 2)) - -$(document).ready -> - $("a[data-fib]").click (e) -> - count = $(this).data("fib") - alert "fib of #{count} is: #{fib(count)}." - -... later ... - -<a href="#" data-fib="15">Calculate</a> -<a href="#" data-fib="16">Calculate</a> -<a href="#" data-fib="17">Calculate</a> +paintIt = (element, backgroundColor, textColor) -> + element.style.backgroundColor = backgroundColor + if textColor? + element.style.color = textColor + +$ -> + $("a[data-color]").click -> + backgroundColor = $(this).data("background-color") + textColor = $(this).data("text-color") + paintIt(this, backgroundColor, textColor) +``` +```html +<a href="#" data-background-color="#990000">Paint it red</a> +<a href="#" data-background-color="#009900" data-text-color="#FFFFFF">Paint it green</a> +<a href="#" data-background-color="#000099" data-text-color="#FFFFFF">Paint it blue</a> ``` We call this 'unobtrusive' JavaScript because we're no longer mixing our @@ -202,7 +207,7 @@ is a helper that assists with generating links. It has a `:remote` option you can use like this: ```erb -<%= link_to "first post", @post, remote: true %> +<%= link_to "a post", @post, remote: true %> ``` which generates @@ -212,20 +217,19 @@ which generates ``` You can bind to the same Ajax events as `form_for`. Here's an example. Let's -assume that we have a resource `/fib/:n` that calculates the `n`th Fibonacci -number. We would generate some HTML like this: +assume that we have a list of posts that can be deleted with just one +click. We would generate some HTML like this: ```erb -<%= link_to "Calculate", "/fib/15", remote: true, data: { fib: 15 } %> +<%= link_to "Delete post", @post, remote: true, method: :delete %> ``` and write some CoffeeScript like this: ```coffeescript -$(document).ready -> - $("a[data-fib]").on "ajax:success", (e, data, status, xhr) -> - count = $(this).data("fib") - alert "fib of #{count} is: #{data}." +$ -> + $("a[data-remote]").on "ajax:success", (e, data, status, xhr) -> + alert "The post was deleted." ``` ### button_to |